mirror of
https://github.com/loot/libloot.git
synced 2026-07-27 14:16:01 -07:00
Fix and document Linux build process
This commit is contained in:
@@ -14,7 +14,6 @@ If this experiment is successful it will probably end up being merged into the l
|
||||
|
||||
## Outstanding issues
|
||||
|
||||
- I haven't yet attempted to build it on Linux.
|
||||
- There is no CI.
|
||||
- It relies on some unreleased improvements to the Rust APIs of libloadorder and loot-condition-interpreter.
|
||||
- It uses more memory than the C++ implementation, and I haven't finished investigating why or if there's anything I can/should do about that.
|
||||
@@ -25,19 +24,51 @@ If this experiment is successful it will probably end up being merged into the l
|
||||
|
||||
Make sure you have [Rust](https://www.rust-lang.org/) installed.
|
||||
|
||||
To build the library, set the `LIBLOOT_REVISION` env var and then run Cargo. Using PowerShell:
|
||||
To build the library, set the `LIBLOOT_REVISION` env var and then run Cargo.
|
||||
|
||||
Using PowerShell:
|
||||
|
||||
```powershell
|
||||
$env:LIBLOOT_REVISION = git rev-parse --short HEAD
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
The tests include a complete port of libloot's tests, and can be run by first extracting the [testing-plugins](https://github.com/Ortham/testing-plugins) archive to this readme's directory (so that there's a `testing-plugins` directory there), then running:
|
||||
Using a POSIX shell:
|
||||
|
||||
```sh
|
||||
export LIBLOOT_REVISION=$(git rev-parse --short HEAD)
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
`LIBLOOT_REVISION` is used to embed the commit hash into the build, if it's not defined then `unknown` will be used instead.
|
||||
|
||||
### Tests
|
||||
|
||||
The tests include a complete port of libloot's tests, and can be run by first extracting the [testing-plugins](https://github.com/Ortham/testing-plugins) archive to this readme's directory (so that there's a `testing-plugins` directory there).
|
||||
|
||||
To do that using `curl` and `tar` in a POSIX shell:
|
||||
|
||||
```sh
|
||||
curl -sSfL https://github.com/Ortham/testing-plugins/archive/refs/tags/1.6.2.tar.gz | tar -xz --strip=1 --one-top-level=testing-plugins
|
||||
```
|
||||
|
||||
To do that in PowerShell:
|
||||
|
||||
```powershell
|
||||
Invoke-WebRequest https://github.com/Ortham/testing-plugins/archive/refs/tags/1.6.2.zip -OutFile testing-plugins-1.6.2.zip
|
||||
Expand-Archive testing-plugins-1.6.2.zip .
|
||||
Move-Item testing-plugins-1.6.2 testing-plugins
|
||||
Remove-Item testing-plugins-1.6.2.zip
|
||||
```
|
||||
|
||||
The tests can then be run using:
|
||||
|
||||
```
|
||||
cargo test
|
||||
```
|
||||
|
||||
### API documentation
|
||||
|
||||
The public API has doc comments copied from libloot, and the API documentation can be built and viewed using:
|
||||
|
||||
```
|
||||
|
||||
+5
-11
@@ -25,9 +25,10 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
##############################
|
||||
|
||||
add_library(libloot-cxx STATIC IMPORTED)
|
||||
# TODO: Define the Rust libloot library path here for Linux builds.
|
||||
if(MSVC)
|
||||
set(LIBLOOT_CXX_FILENAME "libloot_cxx.lib")
|
||||
else()
|
||||
set(LIBLOOT_CXX_FILENAME "liblibloot_cxx.a")
|
||||
endif()
|
||||
|
||||
set_target_properties(libloot-cxx PROPERTIES
|
||||
@@ -145,20 +146,11 @@ target_include_directories(loot SYSTEM PRIVATE
|
||||
${LIBLOOT_COMMON_SYSTEM_INCLUDE_DIRS})
|
||||
|
||||
if(CMAKE_SYSTEM_NAME STREQUAL "Windows")
|
||||
target_compile_definitions(loot PRIVATE
|
||||
UNICODE _UNICODE LOOT_EXPORT YAML_CPP_STATIC_DEFINE)
|
||||
target_compile_definitions(loot PRIVATE UNICODE _UNICODE LOOT_EXPORT)
|
||||
|
||||
set(LOOT_LIBS ntdll ws2_32 bcrypt)
|
||||
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
set(LOOT_LIBS ${LOOT_LIBS} tbb_static)
|
||||
endif()
|
||||
|
||||
target_link_libraries(loot PRIVATE ${LOOT_LIBS})
|
||||
else()
|
||||
set(LOOT_LIBS ICU::data ICU::uc pthread TBB::tbb)
|
||||
|
||||
target_link_libraries(loot PUBLIC ${LOOT_LIBS})
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
@@ -282,6 +274,8 @@ install(FILES
|
||||
########################################
|
||||
|
||||
if(NOT DEFINED CPACK_PACKAGE_VERSION)
|
||||
find_package(Git)
|
||||
|
||||
if(GIT_FOUND)
|
||||
execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --long --always --abbrev=7
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
|
||||
+33
-9
@@ -2,39 +2,63 @@
|
||||
|
||||
This is an **experimental** wrapper around the Rust reimplementation of libloot that provides a C++ interface that's ABI-compatible with libloot v0.25.5.
|
||||
|
||||
## Building
|
||||
|
||||
The wrapper has two layers:
|
||||
|
||||
- a static library built using Cargo, which provides a C++ interface
|
||||
- a shared library built using CMake, which wraps that C++ interface to provide another that is ABI-compatible with C++ libloot.
|
||||
|
||||
To build the wrapper:
|
||||
## Building
|
||||
|
||||
### Windows
|
||||
|
||||
To build a release build with debug info:
|
||||
|
||||
```
|
||||
cargo build --release
|
||||
cmake -B build .
|
||||
cmake --build build --config RelWithDebInfo
|
||||
cmake --build build --parallel --config RelWithDebInfo
|
||||
```
|
||||
|
||||
Debug builds need a little extra config to get the static and shared libraries to use the same C runtime library on Windows.
|
||||
Debug builds need a little extra config to get the static and shared libraries to use the same C runtime library on Windows:
|
||||
|
||||
```
|
||||
cargo build --config ../.cargo/msvcd-config.toml
|
||||
cmake -B build .
|
||||
cmake --build build --config Debug
|
||||
cmake --build build --parallel --config Debug
|
||||
```
|
||||
|
||||
This also builds a copy of the public API tests from C++ libloot v0.25.5, which can be run using:
|
||||
### Linux
|
||||
|
||||
To build a release build with debug info:
|
||||
|
||||
```
|
||||
ctest --test-dir build --output-on-failure -V
|
||||
cargo build --release
|
||||
cmake -B build . -DCMAKE_BUILD_TYPE=RelWithDebInfo
|
||||
cmake --build build --parallel
|
||||
```
|
||||
|
||||
to build a debug build:
|
||||
|
||||
```
|
||||
cargo build
|
||||
cmake -B build . -DCMAKE_BUILD_TYPE=Debug
|
||||
cmake --build build --parallel
|
||||
```
|
||||
|
||||
### Tests & Packaging
|
||||
|
||||
The build process also builds a copy of the public API tests from C++ libloot v0.25.5 by default. To skip building the tests, pass `-DLIBLOOT_BUILD_TESTS=OFF` when first running CMake.
|
||||
|
||||
If built, the tests can be run using:
|
||||
|
||||
```
|
||||
ctest --test-dir build --output-on-failure --parallel -V
|
||||
```
|
||||
|
||||
To package the build:
|
||||
|
||||
```
|
||||
cpack --config build/CPackConfig.cmake
|
||||
cpack --config build/CPackConfig.cmake -C RelWithDebInfo
|
||||
```
|
||||
|
||||
This repository (and so the created package) doesn't currently include any of libloot's documentation, besides the API documentation included in the Rust source code.
|
||||
|
||||
@@ -121,10 +121,10 @@ target_compile_definitions(libloot_internals_tests PRIVATE
|
||||
if(NOT CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
|
||||
target_compile_definitions(libloot_tests PRIVATE LOOT_STATIC)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_link_libraries(libloot_internals_tests PRIVATE ${LOOT_LIBS})
|
||||
target_link_libraries(libloot_tests PRIVATE ${LOOT_LIBS})
|
||||
target_link_libraries(libloot_internals_tests PRIVATE ${LOOT_LIBS})
|
||||
target_link_libraries(libloot_tests PRIVATE ${LOOT_LIBS})
|
||||
endif()
|
||||
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
||||
target_compile_options(libloot_internals_tests PRIVATE "-Wall" "-Wextra")
|
||||
|
||||
@@ -69,11 +69,10 @@ rust::Box<loot::rust::Game> constructGame(
|
||||
const std::filesystem::path& localDataPath) {
|
||||
try {
|
||||
if (localDataPath.empty()) {
|
||||
return std::move(
|
||||
loot::rust::new_game(convert(gameType), gamePath.u8string()));
|
||||
return loot::rust::new_game(convert(gameType), gamePath.u8string());
|
||||
} else {
|
||||
return std::move(loot::rust::new_game_with_local_path(
|
||||
convert(gameType), gamePath.u8string(), localDataPath.u8string()));
|
||||
return loot::rust::new_game_with_local_path(
|
||||
convert(gameType), gamePath.u8string(), localDataPath.u8string());
|
||||
}
|
||||
} catch (const ::rust::Error& e) {
|
||||
std::rethrow_exception(loot::mapError(e));
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "loot/metadata/plugin_metadata.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <regex>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
+17
-11
@@ -4,26 +4,32 @@ An **experimental** Python wrapper around the libloot Rust implementation, built
|
||||
|
||||
## Build
|
||||
|
||||
To build, first set up a Python venv and install [maturin](https://github.com/PyO3/maturin):
|
||||
To build, first set up a Python virtual environment and install [maturin](https://github.com/PyO3/maturin):
|
||||
|
||||
```
|
||||
```powershell
|
||||
python -m venv .venv
|
||||
.\.venv\Scripts\activate
|
||||
pip install maturin
|
||||
```
|
||||
|
||||
or in a POSIX shell:
|
||||
|
||||
```sh
|
||||
python -m venv .venv
|
||||
. .venv/bin/activate
|
||||
pip install maturin
|
||||
```
|
||||
|
||||
Then build the library in the virtual environment:
|
||||
|
||||
```
|
||||
maturin develop
|
||||
```
|
||||
|
||||
Then build the library:
|
||||
The library can then be imported in Python:
|
||||
|
||||
```
|
||||
.\.venv\Scripts\activate
|
||||
maturin develop
|
||||
```
|
||||
|
||||
Then import it in Python:
|
||||
|
||||
```
|
||||
py
|
||||
python
|
||||
> import loot
|
||||
```
|
||||
|
||||
|
||||
+3
-3
@@ -174,7 +174,7 @@ fn are_file_paths_equivalent(lhs: &Path, rhs: &Path) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
use std::fs::unix::fs::MetadataExt;
|
||||
use std::os::unix::fs::MetadataExt;
|
||||
|
||||
let lhs_metadata = match lhs.metadata() {
|
||||
Ok(m) => m,
|
||||
@@ -474,7 +474,7 @@ mod tests {
|
||||
#[test]
|
||||
#[cfg(not(windows))]
|
||||
fn should_be_false_if_given_case_insensitively_equal_paths_that_exist() {
|
||||
let tmp_dir = tempdir();
|
||||
let tmp_dir = tempdir().unwrap();
|
||||
let file_path1 = tmp_dir.path().join("test");
|
||||
let file_path2 = tmp_dir.path().join("TEST");
|
||||
|
||||
@@ -483,7 +483,7 @@ mod tests {
|
||||
|
||||
assert!(file_path1.exists());
|
||||
assert!(file_path2.exists());
|
||||
assert!(!are_file_paths_equivalent(file_path1, file_path2));
|
||||
assert!(!are_file_paths_equivalent(&file_path1, &file_path2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -893,6 +893,8 @@ mod tests {
|
||||
#[cfg(not(windows))]
|
||||
#[apply(all_game_types)]
|
||||
fn should_succeed_for_morrowind_if_given_valid_game_path(game_type: GameType) {
|
||||
let fixture = Fixture::new(game_type);
|
||||
|
||||
if matches!(game_type, GameType::TES3 | GameType::OpenMW) {
|
||||
assert!(Game::new(fixture.game_type, &fixture.game_path).is_ok());
|
||||
} else {
|
||||
@@ -902,7 +904,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_succeed_if_given_a_relative_game_path() {
|
||||
let fixture = Fixture::new(GameType::TES4);
|
||||
let fixture = Fixture::new(GameType::TES3);
|
||||
|
||||
let game_path = make_relative(&fixture.game_path);
|
||||
assert!(game_path.is_relative());
|
||||
@@ -912,7 +914,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_succeed_if_given_an_absolute_game_path() {
|
||||
let fixture = Fixture::new(GameType::TES4);
|
||||
let fixture = Fixture::new(GameType::TES3);
|
||||
|
||||
assert!(fixture.game_path.is_absolute());
|
||||
assert!(Game::new(fixture.game_type, &fixture.game_path).is_ok());
|
||||
@@ -920,7 +922,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn should_succeed_if_given_a_symlink_path() {
|
||||
let fixture = Fixture::new(GameType::TES4);
|
||||
let fixture = Fixture::new(GameType::TES3);
|
||||
|
||||
let game_path = fixture.game_path.with_extension("symlink");
|
||||
symlink_dir(&fixture.game_path, &game_path);
|
||||
@@ -932,7 +934,7 @@ mod tests {
|
||||
#[cfg(windows)]
|
||||
#[test]
|
||||
fn should_succeed_if_given_a_junction_link_path() {
|
||||
let fixture = Fixture::new(GameType::TES4);
|
||||
let fixture = Fixture::new(GameType::TES3);
|
||||
|
||||
let game_path = fixture.game_path.with_extension("junction");
|
||||
junction_link(&fixture.game_path, &game_path);
|
||||
@@ -943,7 +945,7 @@ mod tests {
|
||||
#[test]
|
||||
fn should_error_if_given_a_game_path_that_does_not_exist() {
|
||||
let game_path = Path::new("missing");
|
||||
match Game::new(GameType::TES4, game_path) {
|
||||
match Game::new(GameType::TES3, game_path) {
|
||||
Err(GameHandleCreationError::NotADirectory(p)) => {
|
||||
assert_eq!(game_path, p)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user