89 lines
3.7 KiB
C++
89 lines
3.7 KiB
C++
|
|
// SPDX-License-Identifier: LGPL-3.0-only
|
||
|
|
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
|
||
|
|
|
||
|
|
import std;
|
||
|
|
import Crafter.Build;
|
||
|
|
namespace fs = std::filesystem;
|
||
|
|
using namespace Crafter;
|
||
|
|
|
||
|
|
// A CMake dependency's newest library decides when its consumers are stale
|
||
|
|
// (BuildExternal's latestArtifact). Only files directly in the CMake build
|
||
|
|
// directory named *.a or *.so were looked at, so msquic's
|
||
|
|
// bin/Release/libmsquic.so.2, in one of the dependency's libDirs and with a
|
||
|
|
// version after the .so, never counted: a rebuilt msquic left everything
|
||
|
|
// built against the old one in place.
|
||
|
|
//
|
||
|
|
// The dependency here is a local repository whose CMake project puts a
|
||
|
|
// versioned shared library in bin/Release, declared as a libDir. Everything
|
||
|
|
// is local; XDG_CACHE_HOME keeps the clone out of the real cache.
|
||
|
|
|
||
|
|
extern "C" int setenv(const char* name, const char* value, int overwrite);
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
std::int32_t failures = 0;
|
||
|
|
|
||
|
|
void Check(bool cond, std::string_view msg) {
|
||
|
|
if (!cond) {
|
||
|
|
std::println(std::cerr, "FAIL: {}", msg);
|
||
|
|
++failures;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void Git(const fs::path& dir, std::string_view args) {
|
||
|
|
const std::string command = std::format("git -C {} {}", ShellQuote(dir.string()), args);
|
||
|
|
auto result = RunCommandWithTimeout(command, std::chrono::seconds(60));
|
||
|
|
if (result.exitCode != 0) throw std::runtime_error(std::format("{} failed: {}", command, result.output));
|
||
|
|
}
|
||
|
|
|
||
|
|
void Write(const fs::path& file, std::string_view text) {
|
||
|
|
std::ofstream(file, std::ios::binary | std::ios::trunc) << text;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
int main() {
|
||
|
|
const fs::path scratch = fs::temp_directory_path() / "crafter-build-external-artifact-time";
|
||
|
|
fs::remove_all(scratch);
|
||
|
|
fs::create_directories(scratch);
|
||
|
|
setenv("XDG_CACHE_HOME", (scratch / "cache").c_str(), 1);
|
||
|
|
setenv("GIT_CONFIG_NOSYSTEM", "1", 1);
|
||
|
|
setenv("GIT_CONFIG_GLOBAL", "/dev/null", 1);
|
||
|
|
for (const char* name : { "GIT_AUTHOR_NAME", "GIT_COMMITTER_NAME" }) setenv(name, "crafter-build test", 1);
|
||
|
|
for (const char* name : { "GIT_AUTHOR_EMAIL", "GIT_COMMITTER_EMAIL" }) setenv(name, "test@example.invalid", 1);
|
||
|
|
|
||
|
|
const fs::path repo = scratch / "versioned";
|
||
|
|
fs::create_directories(repo);
|
||
|
|
Write(repo / "lib.c", "int Versioned(void) { return 2; }\n");
|
||
|
|
Write(repo / "CMakeLists.txt",
|
||
|
|
"cmake_minimum_required(VERSION 3.16)\n"
|
||
|
|
"project(versioned C)\n"
|
||
|
|
"add_library(versioned SHARED lib.c)\n"
|
||
|
|
"set_target_properties(versioned PROPERTIES VERSION 2.0.0 SOVERSION 2\n"
|
||
|
|
" LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/Release)\n");
|
||
|
|
Git(repo, "init -q -b main");
|
||
|
|
Git(repo, "add .");
|
||
|
|
Git(repo, "commit -q -m versioned");
|
||
|
|
|
||
|
|
ExternalDependency dep;
|
||
|
|
dep.name = "versioned";
|
||
|
|
dep.source.url = std::format("file://{}", repo.string());
|
||
|
|
dep.builder = ExternalBuilder::CMake;
|
||
|
|
dep.libDirs = { "bin/Release" };
|
||
|
|
dep.libs = { "versioned" };
|
||
|
|
std::atomic<bool> cancelled = false;
|
||
|
|
const ExternalBuildResult result = BuildExternal(dep, HostTarget(), cancelled);
|
||
|
|
Check(result.error.empty(), std::format("the dependency builds: {}", result.error));
|
||
|
|
|
||
|
|
const fs::path library = ExternalCloneDir(dep, HostTarget()) / "build" / "bin" / "Release" / "libversioned.so.2.0.0";
|
||
|
|
Check(fs::exists(library), std::format("the library is where its CMake project puts it: {}", library.string()));
|
||
|
|
if (fs::exists(library)) {
|
||
|
|
Check(result.latestArtifact == fs::last_write_time(library), "its newest artifact is the versioned library in the libDir");
|
||
|
|
}
|
||
|
|
|
||
|
|
std::error_code ec;
|
||
|
|
fs::remove_all(scratch, ec);
|
||
|
|
if (failures > 0) {
|
||
|
|
std::println(std::cerr, "{} assertions failed", failures);
|
||
|
|
return 1;
|
||
|
|
}
|
||
|
|
return 0;
|
||
|
|
}
|