summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2025-12-27 15:08:07 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2026-01-05 22:47:12 +0100
commitfa99825fa800f0faf4a6e6b1d639b261c7aa43c9 (patch)
tree17945e16aa7f33c8aede1694c90a08b7cc05e0e1
parent5ac072c0f42bf3216d16f177c795e9324bf3c52b (diff)
Add many-to-one generator function type.develop
-rw-r--r--src/ctor.h19
-rw-r--r--src/task.cc2
-rw-r--r--src/task.h2
-rw-r--r--src/task_fn.cc187
-rw-r--r--src/task_fn.h5
-rw-r--r--src/task_ld.cc5
-rw-r--r--src/task_ld.h1
-rw-r--r--src/tasks.cc16
-rw-r--r--test/generated_sources_test.cc55
-rw-r--r--test/suite/ctor_files/ctor.cc.generated39
-rw-r--r--test/suite/ctor_files/ctor.cc.generated286
-rw-r--r--test/suite/test.cc496
12 files changed, 630 insertions, 283 deletions
diff --git a/src/ctor.h b/src/ctor.h
index f145eef..dddc5ef 100644
--- a/src/ctor.h
+++ b/src/ctor.h
@@ -282,10 +282,17 @@ struct settings
};
struct build_configuration;
-using GeneratorCb = std::function<int(const std::string& input,
- const std::string& output,
- const build_configuration& config,
- const ctor::settings& settings)>;
+using GeneratorOneToOne =
+ std::function<int(const std::string& input,
+ const std::string& output,
+ const build_configuration& config,
+ const ctor::settings& settings)>;
+
+using GeneratorManyToOne =
+ std::function<int(const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)>;
struct build_configuration
{
@@ -297,7 +304,9 @@ struct build_configuration
std::vector<std::string> depends; // internal target dependencies
ctor::flags flags;
std::vector<std::string> externals; // externals used by this configuration
- GeneratorCb function;
+ std::variant<std::monostate,
+ GeneratorOneToOne,
+ GeneratorManyToOne> function;
};
using build_configurations = std::vector<build_configuration>;
diff --git a/src/task.cc b/src/task.cc
index 5484c23..17559bf 100644
--- a/src/task.cc
+++ b/src/task.cc
@@ -12,7 +12,7 @@ Task::Task(const ctor::build_configuration& config_, const ctor::settings& setti
: config(config_)
, output_system(config.system)
, settings(settings_)
- , sourceDir(std::move(sourceDir_))
+ , sourceDir(sourceDir_)
{
}
diff --git a/src/task.h b/src/task.h
index 32d0de3..c884186 100644
--- a/src/task.h
+++ b/src/task.h
@@ -79,5 +79,5 @@ protected:
ctor::language source_language{ctor::language::automatic};
ctor::output_system output_system{ctor::output_system::host};
const ctor::settings& settings;
- std::string sourceDir;
+ std::filesystem::path sourceDir;
};
diff --git a/src/task_fn.cc b/src/task_fn.cc
index ebfdac6..69e36ac 100644
--- a/src/task_fn.cc
+++ b/src/task_fn.cc
@@ -11,46 +11,129 @@
#include "execute.h"
#include "util.h"
-TaskFn::TaskFn(const ctor::build_configuration& config_, const ctor::settings& settings_,
- const std::string& sourceDir_, const ctor::source& source)
+TaskFn::TaskFn(const ctor::build_configuration& config_,
+ const ctor::settings& settings_,
+ const std::string& sourceDir_,
+ const ctor::source& source)
: Task(config_, settings_, sourceDir_)
- , sourceFile(sourceDir_)
, config(config_)
, settings(settings_)
{
- sourceFile /= source.file;
-
- std::filesystem::create_directories(std::filesystem::path(settings.builddir) / sourceFile.parent_path());
-
+ std::filesystem::create_directories(std::filesystem::path(settings.builddir) /
+ sourceDir.parent_path());
target_type = config.type;
source_language = source.language;
- std::filesystem::path base = sourceFile.parent_path();
- if(source.output.empty())
+ if(std::holds_alternative<ctor::GeneratorOneToOne>(config.function))
{
- std::cerr << "Missing output file for functional target\n";
- exit(1);
+ if(source.source_type == ctor::source_type::generated)
+ {
+ sourceFile = std::filesystem::path(settings.builddir);
+ }
+ sourceFile /= sourceDir / source.file;
+
+ std::filesystem::path base = sourceFile.parent_path();
+
+ if(source.output.empty())
+ {
+ std::cerr << "Missing target/output file for functional target.\n";
+ exit(1);
+ }
+ _targetFile = base / source.output;
}
+ else if(std::holds_alternative<ctor::GeneratorManyToOne>(config.function))
+ {
+ for(const auto& src : config.sources)
+ {
+ std::filesystem::path _src;
+ if(src.source_type == ctor::source_type::generated)
+ {
+ _src = std::filesystem::path(settings.builddir);
+ }
+ _src /= sourceDir / src.file;
+ sources.push_back(_src.string());
+ }
- _targetFile = base / source.output;
+ std::filesystem::path base = sourceDir;
+ if(config.target.empty())
+ {
+ std::cerr << "Missing target file for functional target\n";
+ exit(1);
+ }
+ _targetFile = base / config.target;
+ sourceListFile = targetFile().parent_path() / targetFile().filename();
+ sourceListFile += ".sources";
+ }
}
bool TaskFn::dirtyInner()
{
- if(!std::filesystem::exists(sourceFile))
+ if(!std::filesystem::exists(targetFile()))
{
- //std::cout << "Missing source file: " << std::string(sourceFile) << "\n";
+ //std::cout << "Missing targetFile\n";
return true;
}
- if(!std::filesystem::exists(targetFile()))
+ if(std::holds_alternative<ctor::GeneratorManyToOne>(config.function))
+ {
+ if(!std::filesystem::exists(sourceListFile))
+ {
+ //std::cout << "Missing sourceListFile\n";
+ return true;
+ }
+
+ {
+ auto lastSourceList = readFile(sourceListFile.string());
+ if(sourceListString() != lastSourceList)
+ {
+ //std::cout << "The compiler sourceList changed\n";
+ return true;
+ }
+ }
+ }
+
+ std::filesystem::file_time_type last_changed{};
+ bool missing{false};
+ if(std::holds_alternative<ctor::GeneratorOneToOne>(config.function))
+ {
+ if(!std::filesystem::exists(sourceFile))
+ {
+ missing = true;
+ }
+ last_changed = std::filesystem::last_write_time(sourceFile);
+ }
+ else if(std::holds_alternative<ctor::GeneratorManyToOne>(config.function))
+ {
+ bool first{true};
+ for(const auto& source : sources)
+ {
+ if(!std::filesystem::exists(source))
+ {
+ missing |= true;
+ }
+ else
+ {
+ if(first)
+ {
+ last_changed = std::filesystem::last_write_time(source);
+ }
+ else
+ {
+ last_changed =
+ std::max(last_changed,
+ std::filesystem::last_write_time(source));
+ }
+ }
+ first = false;
+ }
+ }
+
+ if(missing)
{
- //std::cout << "Missing targetFile\n";
return true;
}
- if(std::filesystem::last_write_time(sourceFile) >
- std::filesystem::last_write_time(targetFile()))
+ if(last_changed > std::filesystem::last_write_time(targetFile()))
{
//std::cout << "The targetFile older than sourceFile\n";
return true;
@@ -61,10 +144,11 @@ bool TaskFn::dirtyInner()
int TaskFn::runInner()
{
- if(!std::filesystem::exists(sourceFile))
+ if(std::holds_alternative<ctor::GeneratorManyToOne>(config.function))
{
- std::cout << "Missing source file: " << sourceFile.string() << "\n";
- return 1;
+ // Write sourceList to file.
+ std::ofstream sourceListStream(sourceListFile.string());
+ sourceListStream << sourceListString();
}
if(settings.verbose >= 0)
@@ -75,10 +159,27 @@ int TaskFn::runInner()
std::cout << output << std::flush;
}
- auto res = config.function(sourceFile.string(),
- targetFile().string(),
- config,
- settings);
+ int res{};
+ if(std::holds_alternative<std::monostate>(config.function))
+ {
+ }
+ else if(std::holds_alternative<ctor::GeneratorOneToOne>(config.function))
+ {
+ auto func = std::get<ctor::GeneratorOneToOne>(config.function);
+ res = func(sourceFile.string(),
+ targetFile().string(),
+ config,
+ settings);
+ }
+ else if(std::holds_alternative<ctor::GeneratorManyToOne>(config.function))
+ {
+ auto func = std::get<ctor::GeneratorManyToOne>(config.function);
+ res = func(sources,
+ targetFile().string(),
+ config,
+ settings);
+ }
+
if(res != 0)
{
std::filesystem::remove(targetFile());
@@ -95,12 +196,34 @@ int TaskFn::clean()
std::filesystem::remove(targetFile());
}
+ if(std::holds_alternative<ctor::GeneratorManyToOne>(config.function))
+ {
+ if(std::filesystem::exists(sourceListFile))
+ {
+ std::cout << "Removing " << sourceListFile.string() << "\n";
+ std::filesystem::remove(sourceListFile);
+ }
+ }
+
return 0;
}
std::vector<std::string> TaskFn::depends() const
{
- return {};
+ std::vector<std::string> deps;
+ if(std::holds_alternative<ctor::GeneratorManyToOne>(config.function))
+ {
+ for(const auto& src : config.sources)
+ {
+ if(src.source_type == ctor::source_type::generated)
+ {
+ std::filesystem::path _src = std::filesystem::path(settings.builddir);
+ _src /= src.file;
+ deps.push_back(_src.string());
+ }
+ }
+ }
+ return deps;
}
std::string TaskFn::target() const
@@ -110,7 +233,17 @@ std::string TaskFn::target() const
std::filesystem::path TaskFn::targetFile() const
{
- return std::filesystem::path(settings.builddir) / sourceDir / _targetFile;
+ return std::filesystem::path(settings.builddir) / _targetFile;
+}
+
+std::string TaskFn::sourceListString() const
+{
+ std::string sourceListStr;
+ for(const auto& source : config.sources)
+ {
+ sourceListStr += " " + source.file;
+ }
+ return sourceListStr;
}
bool TaskFn::derived() const
diff --git a/src/task_fn.h b/src/task_fn.h
index 1bad32c..84b4c2a 100644
--- a/src/task_fn.h
+++ b/src/task_fn.h
@@ -28,6 +28,7 @@ public:
std::string target() const override;
std::filesystem::path targetFile() const override;
+ std::string sourceListString() const;
bool derived() const override;
std::string toJSON() const override;
@@ -37,8 +38,10 @@ public:
protected:
std::filesystem::path sourceFile;
std::filesystem::path _targetFile;
+ std::filesystem::path sourceListFile;
const ctor::build_configuration& config;
const ctor::settings& settings;
- std::filesystem::path sourceDir;
+
+ std::vector<std::string> sources;
};
diff --git a/src/task_ld.cc b/src/task_ld.cc
index a3f02d1..a1cff34 100644
--- a/src/task_ld.cc
+++ b/src/task_ld.cc
@@ -20,7 +20,6 @@ TaskLD::TaskLD(const ctor::build_configuration& config_,
: Task(config_, settings_, sourceDir_)
, config(config_)
, settings(settings_)
- , sourceDir(sourceDir_)
, is_self(is_self_)
{
target_type = config.type;
@@ -40,8 +39,8 @@ TaskLD::TaskLD(const ctor::build_configuration& config_,
objectFiles.push_back(objectFile);
dependsStr.push_back(objectFile.string());
}
-
- flagsFile = std::filesystem::path(settings.builddir) / cleanUp(sourceDir) / targetFile().stem();
+ auto cleaned_source_dir = cleanUp(sourceDir.string());
+ flagsFile = std::filesystem::path(settings.builddir) / cleaned_source_dir / targetFile().stem();
flagsFile += ".flags";
source_language = ctor::language::c;
diff --git a/src/task_ld.h b/src/task_ld.h
index 85f680a..9dce4b4 100644
--- a/src/task_ld.h
+++ b/src/task_ld.h
@@ -43,6 +43,5 @@ private:
const ctor::build_configuration& config;
const ctor::settings& settings;
- std::string sourceDir;
bool is_self;
};
diff --git a/src/tasks.cc b/src/tasks.cc
index 7de8af9..fb7d72b 100644
--- a/src/tasks.cc
+++ b/src/tasks.cc
@@ -85,7 +85,7 @@ std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration&
ctor::target_type target_type{config.type};
if(target_type == ctor::target_type::automatic)
{
- if(config.function != nullptr)
+ if(!std::holds_alternative<std::monostate>(config.function))
{
target_type = ctor::target_type::function;
}
@@ -114,12 +114,22 @@ std::vector<std::shared_ptr<Task>> taskFactory(const ctor::build_configuration&
#ifndef BOOTSTRAP
else
{
- for(const auto& source : config.sources)
+ bool multi{std::holds_alternative<ctor::GeneratorManyToOne>(config.function)};
+ if(multi)
{
- auto task = std::make_shared<TaskFn>(config, settings, sourceDir, source);
+ auto task = std::make_shared<TaskFn>(config, settings, sourceDir, "");
tasks.push_back(task);
objects.push_back(task->target());
}
+ else
+ {
+ for(const auto& source : config.sources)
+ {
+ auto task = std::make_shared<TaskFn>(config, settings, sourceDir, source);
+ tasks.push_back(task);
+ objects.push_back(task->target());
+ }
+ }
}
#endif
diff --git a/test/generated_sources_test.cc b/test/generated_sources_test.cc
index 1ea7538..8c46983 100644
--- a/test/generated_sources_test.cc
+++ b/test/generated_sources_test.cc
@@ -14,6 +14,7 @@ public:
GeneratedSourcesTest()
{
uTEST(GeneratedSourcesTest::test_custom_output);
+ uTEST(GeneratedSourcesTest::test_many_to_one_output);
}
void setup()
@@ -74,6 +75,60 @@ public:
}
uASSERT(found);
}
+
+ void test_many_to_one_output()
+ {
+ using namespace std::string_literals;
+
+ ctor::reg(
+ [](const ctor::settings&)
+ {
+ return ctor::build_configurations{
+ {
+ .target = "test1",
+ .sources = {
+ {"foo.cc", ctor::source_type::generated}
+ }
+ },
+ {
+ .target = "foo.cc",
+ .sources = {
+ {"bar.x"},
+ {"bar.y"},
+ },
+ .function = [](const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ return 0;
+ }
+ },
+ };
+ });
+ ctor::settings settings{};
+ auto tasks = getTasks(settings);
+ for(auto task : tasks)
+ {
+ uASSERT(task->registerDepTasks(tasks) == 0);
+ }
+ uASSERT_EQUAL(4u, tasks.size());
+ bool found{false};
+ for(const auto& task : tasks)
+ {
+ if(task->target() == "test1")
+ {
+ auto deps_test1 = task->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_test1.size());
+ auto deps_foo_o = deps_test1[0]->getDependsTasks();
+ uASSERT_EQUAL(1u, deps_foo_o.size());
+ uASSERT_EQUAL("test/bar.x"s, deps_foo_o[0]->source());
+ found = true;
+ }
+ }
+ uASSERT(found);
+ }
+
};
// Registers the fixture into the 'registry'
diff --git a/test/suite/ctor_files/ctor.cc.generated b/test/suite/ctor_files/ctor.cc.generated
index 5f82fd4..5bc2940 100644
--- a/test/suite/ctor_files/ctor.cc.generated
+++ b/test/suite/ctor_files/ctor.cc.generated
@@ -4,6 +4,7 @@
#include <ctor.h>
#include <filesystem>
#include <iostream>
+#include <fstream>
namespace
{
@@ -41,6 +42,44 @@ ctor::build_configurations ctorConfigs(const ctor::settings& settings)
return 0;
}
},
+
+ {
+ .target = "many_to_one",
+ .sources = {
+ {"many_to_one.cc", ctor::source_type::generated}
+ }
+ },
+ {
+ .target = "many_to_one.cc",
+ .sources = {
+ {"foo.cc", ctor::source_type::generated},
+ {"hello.cc"},
+ },
+ .function = [](const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ std::cout << "Output: " << output << '\n';
+ std::ofstream ofs(output);
+ bool comment{true};
+ for(const auto& input_file : input)
+ {
+ std::cout << "Input: " << input_file << '\n';
+ std::ifstream ifs(input_file);
+ std::string line;
+ while(std::getline(ifs, line))
+ {
+ ofs << line << '\n';
+ }
+ if(comment) ofs << "/*\n";
+ comment = false;
+ }
+ ofs << "*/\n";
+ return 0;
+ }
+ },
+
};
}
}
diff --git a/test/suite/ctor_files/ctor.cc.generated2 b/test/suite/ctor_files/ctor.cc.generated2
new file mode 100644
index 0000000..c78489f
--- /dev/null
+++ b/test/suite/ctor_files/ctor.cc.generated2
@@ -0,0 +1,86 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <ctor.h>
+#include <filesystem>
+#include <iostream>
+#include <fstream>
+
+namespace
+{
+ctor::build_configurations ctorConfigs(const ctor::settings& settings)
+{
+ return
+ {
+ {
+ .target = "world",
+ .sources = {
+ { "world.cc", ctor::source_type::generated },
+ },
+ },
+ {
+ .target = "foo",
+ .sources = {
+ { "foo.cc", ctor::source_type::generated },
+ },
+ },
+ {
+ .target = "this_is_unused",
+ .sources = {
+ {"hello.cc", ctor::output_file{"world.cc"}},
+ {"hello.cc", ctor::output_file{"foo.cc"}},
+ },
+ .function = [](const std::string& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ namespace fs = std::filesystem;
+ std::cout << "Input: " << input << '\n';
+ std::cout << "Output: " << output << '\n';
+ fs::copy_file(input, output, fs::copy_options::overwrite_existing);
+ return 0;
+ }
+ },
+
+ {
+ .target = "many_to_one",
+ .sources = {
+ {"many_to_one.cc", ctor::source_type::generated}
+ }
+ },
+ {
+ .target = "many_to_one.cc",
+ .sources = {
+ {"hello.cc"},
+ },
+ .function = [](const std::vector<std::string>& input,
+ const std::string& output,
+ const ctor::build_configuration& config,
+ const ctor::settings& settings)
+ {
+ std::cout << "Output: " << output << '\n';
+ std::ofstream ofs(output);
+ bool comment{true};
+ for(const auto& input_file : input)
+ {
+ std::cout << "Input: " << input_file << '\n';
+ std::ifstream ifs(input_file);
+ std::string line;
+ while(std::getline(ifs, line))
+ {
+ ofs << line << '\n';
+ }
+ if(comment) ofs << "/*\n";
+ comment = false;
+ }
+ ofs << "*/\n";
+ return 0;
+ }
+ },
+
+ };
+}
+}
+
+REG(ctorConfigs);
diff --git a/test/suite/test.cc b/test/suite/test.cc
index 0ab1299..bb62d9d 100644
--- a/test/suite/test.cc
+++ b/test/suite/test.cc
@@ -14,28 +14,45 @@
using namespace std::chrono_literals;
-std::vector<std::string> tokenize(const std::string& str)
-{
- std::vector<std::string> tokens;
- std::stringstream ss(str);
- std::string token;
- while(getline(ss, token, ' '))
- {
- tokens.push_back(token);
- }
- return tokens;
-}
-
int fail(int value = 1,
const std::source_location location = std::source_location::current())
{
std::cout << "*** Failure at line " << location.line() << '\n';
- return value;
+ exit(value);
}
const std::string ctor_exe{"./ctor"};
const std::string obj_ext{".o"};
+void run_ctor(const std::vector<std::string>& args,
+ const std::source_location location = std::source_location::current())
+{
+ ctor::settings settings{.verbose = 2};
+ auto ret = execute(settings, ctor_exe, args);
+ if(ret != 0)
+ {
+ fail(ret, location);
+ }
+}
+
+void assert_not_exists(const std::string& path,
+ const std::source_location location = std::source_location::current())
+{
+ if(!std::filesystem::exists(path))
+ {
+ fail(1, location);
+ }
+}
+
+void assert_exists(const std::string& path,
+ const std::source_location location = std::source_location::current())
+{
+ if(std::filesystem::exists(path))
+ {
+ fail(1, location);
+ }
+}
+
void copy_config(std::string cfg)
{
std::cout << "** ctor_files/ctor.cc." + cfg + "\n";
@@ -48,6 +65,50 @@ void copy_config(std::string cfg)
}
}
+class Tracker
+{
+public:
+ Tracker(const std::string& file_) : file(file_)
+ {
+ capture(); // capture initial value
+ }
+
+ void capture()
+ {
+ changed();
+ }
+
+ bool changed()
+ {
+ auto tmp = readFile(file);
+ auto content_changed = tmp != content;
+ content = tmp;
+ return content_changed;
+ }
+
+private:
+ std::string file;
+ std::string content;
+};
+
+void assert_not_changed(Tracker& tracker,
+ const std::source_location location = std::source_location::current())
+{
+ if(tracker.changed())
+ {
+ fail(1, location);
+ }
+}
+
+void assert_changed(Tracker& tracker,
+ const std::source_location location = std::source_location::current())
+{
+ if(!tracker.changed())
+ {
+ fail(1, location);
+ }
+}
+
int main()
{
ctor::settings settings{};
@@ -65,291 +126,244 @@ int main()
std::string LDFLAGS;
get_env("LDFLAGS", LDFLAGS);
- auto cxx_prog = locate(CXX, paths);
-
// Wipe the board
std::filesystem::remove_all(BUILDDIR);
std::filesystem::remove("configuration.cc");
std::filesystem::remove("config.h");
- std::filesystem::remove("ctor");
+ std::filesystem::remove(ctor_exe);
//////////////////////////////////////////////////////////////////////////////
- copy_config("base");
-
- // Compile bootstrap binary
- std::vector<std::string> args =
- {"-pthread", "-std=c++20", "-L", CTORDIR, "-lctor", "-I", "../../src",
- "ctor.cc", "-o", ctor_exe};
-
- // TODO: add support for quoted strings with spaces
- if(!CXXFLAGS.empty())
+ // bootstrap
{
- auto tokens = tokenize(CXXFLAGS);
- for(const auto& token : tokens)
+ auto cxx_prog = locate(CXX, paths);
+
+ std::vector<std::string> args =
+ {"-pthread", "-std=c++20", "-L", CTORDIR, "-lctor", "-I", "../../src",
+ "ctor.cc", "-o", ctor_exe};
+ if(!CXXFLAGS.empty())
{
- args.push_back(token);
+ auto tokens = argsplit(CXXFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
}
- }
- if(!LDFLAGS.empty())
- {
- auto tokens = tokenize(LDFLAGS);
- for(const auto& token : tokens)
+ if(!LDFLAGS.empty())
{
- args.push_back(token);
+ auto tokens = argsplit(LDFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
}
- }
- auto ret = execute(settings, cxx_prog, args);
- if(ret != 0)
- {
- return fail(ret);
+ // Compile bootstrap binary
+ copy_config("base");
+ auto ret = execute(settings, cxx_prog, args);
+ if(ret != 0)
+ {
+ fail(ret);
+ }
}
-
//////////////////////////////////////////////////////////////////////////////
+ // check if source file changes are tracked
{
- // No build files should have been created yet
- if(std::filesystem::exists(BUILDDIR))
- {
- return fail();
- }
+ // No build files should have been created yet
+ assert_exists(BUILDDIR);
- // capture ctor binary before configure is called
- auto ctor_bin = readFile(ctor_exe);
- args = {"configure", "--ctor-includedir", "../../src",
- "--ctor-libdir=" + CTORDIR, "--build-dir=" + BUILDDIR};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ // capture ctor binary before configure is called
+ Tracker ctor_bin(ctor_exe);
+ run_ctor(
+ {"configure", "--ctor-includedir", "../../src",
+ "--ctor-libdir=" + CTORDIR, "--build-dir=" + BUILDDIR});
- // ctor should be rebuilt at this point, so binary should have changed
- auto ctor_bin2 = readFile(ctor_exe);
- if(ctor_bin == ctor_bin2)
- {
- return fail();
- }
+ // ctor should be rebuilt at this point, so binary should have changed
+ assert_changed(ctor_bin);
- // configuration.cc should have been generated now
- if(!std::filesystem::exists("configuration.cc"))
- {
- return fail();
- }
- if(!std::filesystem::exists("config.h"))
- {
- return fail();
- }
+ // configuration.cc should have been generated now
+ assert_not_exists("configuration.cc");
+ assert_not_exists("config.h");
- // Shouldn't compile anything yet - only configure
- if(std::filesystem::exists(BUILDDIR + "/hello-hello_cc" + obj_ext))
- {
- return fail();
- }
+ // Shouldn't compile anything yet - only configure
+ assert_exists(BUILDDIR + "/hello-hello_cc" + obj_ext);
- ctor_bin = readFile(ctor_exe);
+ ctor_bin.capture();
- // Run normally to build project
- args = {"-v"};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ // Run normally to build project
+ run_ctor({"-v"});
- // Compiled object should now exist
- if(!std::filesystem::exists(BUILDDIR + "/hello-hello_cc" + obj_ext))
- {
- return fail();
- }
+ // Compiled object should now exist
+ assert_not_exists(BUILDDIR + "/hello-hello_cc" + obj_ext);
- // ctor should not have been rebuilt, so binary should be the same
- ctor_bin2 = readFile(ctor_exe);
- if(ctor_bin != ctor_bin2)
- {
- return fail();
- }
+ // ctor should not have been rebuilt, so binary should be the same
+ assert_not_changed(ctor_bin);
- std::this_thread::sleep_for(1100ms);
+ std::this_thread::sleep_for(1100ms);
- auto time =
- std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
- std::filesystem::last_write_time("hello.cc", time + 1s);
- std::this_thread::sleep_for(1100ms);
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::filesystem::last_write_time("hello.cc", time + 1s);
+ std::this_thread::sleep_for(1100ms);
- // Run normally to rebuild hello.cc
- args = {"-v"};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ // Run normally to rebuild hello.cc
+ run_ctor({"-v"});
- // Object file should have been recompiled
- auto time2 =
- std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
- if(time == time2)
- {
- return fail();
- }
+ // Object file should have been recompiled
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
}
//////////////////////////////////////////////////////////////////////////////
- {
- // Replace -DFOO with -DBAR in foo external.cxxflags
- copy_config("bar");
- auto configuration_cc_bin = readFile("configuration.cc");
- auto ctor_bin = readFile(ctor_exe);
- auto time =
- std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
- std::this_thread::sleep_for(1100ms);
-
- // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
- args = {"-v"};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ // check if flags change trigger rebuild
+ {
+ // Replace -DFOO with -DBAR in foo external.cxxflags
+ copy_config("bar");
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
- auto time2 =
- std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
- if(time == time2)
- {
- return fail();
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
}
- auto configuration_cc_bin2 = readFile("configuration.cc");
- if(configuration_cc_bin == configuration_cc_bin2)
+ //////////////////////////////////////////////////////////////////////////////
+ // check if included files in ctor.cc is tracked for changes
{
- return fail();
- }
+ copy_config("multi");
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ auto time =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
- auto ctor_bin2 = readFile(ctor_exe);
- if(ctor_bin == ctor_bin2)
- {
- return fail();
- }
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
+ if(time == time2)
+ {
+ fail();
+ }
+
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
+
+ // now touching foobar.h, should retrigger re-configuration
+ time = std::filesystem::last_write_time(ctor_exe);
+ std::filesystem::last_write_time("foobar.h", time + 1s);
+ std::this_thread::sleep_for(1100ms);
+
+ // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ run_ctor({"-v"});
+
+ time2 = std::filesystem::last_write_time(ctor_exe);
+ if(time == time2)
+ {
+ fail();
+ }
}
//////////////////////////////////////////////////////////////////////////////
+ // generated part1: one-to-one generation
{
- copy_config("multi");
- auto configuration_cc_bin = readFile("configuration.cc");
- auto ctor_bin = readFile(ctor_exe);
- auto time =
- std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
- std::this_thread::sleep_for(1100ms);
-
- // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
- args = {"-v"};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ copy_config("generated");
+ std::filesystem::remove(BUILDDIR + "/world.cc");
+ std::filesystem::remove(BUILDDIR + "/foo.cc");
- auto time2 =
- std::filesystem::last_write_time(BUILDDIR + "/hello-hello_cc" + obj_ext);
- if(time == time2)
- {
- return fail();
- }
+ Tracker configuration_cc_bin("configuration.cc");
+ Tracker ctor_bin(ctor_exe);
+ std::this_thread::sleep_for(1100ms);
- auto configuration_cc_bin2 = readFile("configuration.cc");
- if(configuration_cc_bin == configuration_cc_bin2)
- {
- return fail();
- }
+ // Run normally to reconfigure, rebuild ctor and build world.cc
+ run_ctor({"-v", "world"});
- auto ctor_bin2 = readFile(ctor_exe);
- if(ctor_bin == ctor_bin2)
- {
- return fail();
- }
+ assert_changed(configuration_cc_bin);
+ assert_changed(ctor_bin);
- // now touching foobar.h, should retrigger re-configuration
- time = std::filesystem::last_write_time(ctor_exe);
- std::filesystem::last_write_time("foobar.h", time + 1s);
- std::this_thread::sleep_for(1100ms);
+ // foo.cc should not be generated at this point
+ assert_exists(BUILDDIR+"/foo.cc");
- // Run normally to reconfigure, rebuild ctor and rebuild hello.cc
- args = {"-v"};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ auto time_w = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
+ auto time_wo =
+ std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR+
+ "/world-world_cc" + obj_ext);
+ std::this_thread::sleep_for(1100ms);
- time2 = std::filesystem::last_write_time(ctor_exe);
- if(time == time2)
- {
- return fail();
- }
+ // now touching hello.cc, should trigger regeneration of world.cc and
+ // rebuild
+ std::filesystem::last_write_time("hello.cc", time_w + 1s);
+ run_ctor({"-v", "world"});
+
+ auto time_w2 = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
+ auto time_wo2 =
+ std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR +
+ "/world-world_cc" + obj_ext);
+ if(time_w == time_w2)
+ {
+ fail();
+ }
+ if(time_wo == time_wo2)
+ {
+ fail();
+ }
}
//////////////////////////////////////////////////////////////////////////////
+ // generated part2: many-to-one generation
{
- copy_config("generated");
- std::filesystem::remove(BUILDDIR + "/world.cc");
- std::filesystem::remove(BUILDDIR + "/foo.cc");
+ std::filesystem::remove(BUILDDIR + "/world.cc");
+ std::filesystem::remove(BUILDDIR + "/foo.cc");
+ std::filesystem::remove(BUILDDIR + "/many_to_one.cc");
- auto configuration_cc_bin = readFile("configuration.cc");
- auto ctor_bin = readFile(ctor_exe);
- std::this_thread::sleep_for(1100ms);
+ run_ctor({"-v", "many_to_one"});
- // Run normally to reconfigure, rebuild ctor and build world.cc
- args = {"-v", "world"};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ // world.cc should not be generated at this point
+ assert_exists(BUILDDIR+"/world.cc");
- auto configuration_cc_bin2 = readFile("configuration.cc");
- if(configuration_cc_bin == configuration_cc_bin2)
- {
- return fail();
- }
+ // foo.cc should have been generated at this point
+ assert_not_exists(BUILDDIR+"/foo.cc");
- auto ctor_bin2 = readFile(ctor_exe);
- if(ctor_bin == ctor_bin2)
- {
- return fail();
- }
+ // many_to_one.cc should have been generated
+ assert_not_exists(BUILDDIR+"/many_to_one.cc");
- // foo.cc should not be generated at this point
- if(std::filesystem::exists(BUILDDIR+"/foo.cc"))
- {
- return fail();
- }
+ auto time = std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc");
+ std::this_thread::sleep_for(1100ms);
- auto time_w = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
- auto time_wo =
- std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR+
- "/world-world_cc" + obj_ext);
- std::this_thread::sleep_for(1100ms);
+ Tracker ctor_bin(ctor_exe);
- // now touching hello.cc, should trigger regeneration of world.cc and rebuild
- std::filesystem::last_write_time("hello.cc", time_w + 1s);
- args = {"-v", "world"};
- ret = execute(settings, ctor_exe, args);
- if(ret != 0)
- {
- return fail(ret);
- }
+ // remove "foo.cc" from sources list, so it only contains hello.cc
+ copy_config("generated2");
- auto time_w2 = std::filesystem::last_write_time(BUILDDIR + "/world.cc");
- auto time_wo2 =
- std::filesystem::last_write_time(BUILDDIR + "/" + BUILDDIR +
- "/world-world_cc" + obj_ext);
- if(time_w == time_w2)
- {
- return fail();
- }
- if(time_wo == time_wo2)
- {
- return fail();
- }
+ // rebuild
+ run_ctor({"-v", "many_to_one"});
+
+ // Verify that the change resulted in a ctor rebuild
+ assert_changed(ctor_bin);
+
+ // Verify that source-list change triggered a new build of many_to_one.cc
+ auto time2 =
+ std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc");
+ if(time == time2)
+ {
+ fail();
+ }
}
return 0;