// -*- c++ -*- // Distributed under the BSD 2-Clause License. // See accompanying file LICENSE for details. #include "../../src/util.cc" #include "../../src/pointerlist.cc" #include "../../src/execute.cc" #include #include #include #include #include #include using namespace std::chrono_literals; int fail(int value = 1, const std::source_location location = std::source_location::current()) { std::cout << "*** Failure at line " << location.line() << '\n'; exit(value); } #if _MSC_VER && !__INTEL_COMPILER const std::string ctor_exe{"ctor.exe"}; const std::string obj_ext{".obj"}; #else const std::string ctor_exe{"./ctor"}; const std::string obj_ext{".o"}; #endif void run_ctor(const std::vector& 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"; std::filesystem::copy("ctor_files/ctor.cc." + cfg, "ctor.cc", std::filesystem::copy_options::overwrite_existing); if(std::filesystem::exists(ctor_exe)) { auto ctor_exe_time = std::filesystem::last_write_time(ctor_exe); std::filesystem::last_write_time("ctor.cc", ctor_exe_time + 1s); } } 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{}; settings.verbose = 2; auto paths = get_paths(); std::string CXX = "g++"; get_env("CXX", CXX); std::string CTORDIR = "../../build"; get_env("CTORDIR", CTORDIR); std::string BUILDDIR = "build"; get_env("BUILDDIR", BUILDDIR); std::string CXXFLAGS; get_env("CXXFLAGS", CXXFLAGS); std::string LDFLAGS; get_env("LDFLAGS", LDFLAGS); // Wipe the board std::filesystem::remove_all(BUILDDIR); #if _MSC_VER && !__INTEL_COMPILER std::filesystem::create_directory(BUILDDIR); #endif std::filesystem::remove("configuration.cc"); std::filesystem::remove("config.h"); std::filesystem::remove(ctor_exe); ////////////////////////////////////////////////////////////////////////////// // bootstrap // TODO: add support for quoted strings with spaces { auto cxx_prog = locate(CXX, paths); std::vector args = #if _MSC_VER && !__INTEL_COMPILER {"/nologo", "/MT", "/std:c++20", "/D_X86_", "/EHsc", "/I..\\..\\src", "ctor.cc", "/link", "/LIBPATH:"+CTORDIR, "libctor.lib", "/subsystem:console", "/out:" + ctor_exe}; #else {"-pthread", "-std=c++20", "-L", CTORDIR, "-lctor", "-I", "../../src", "ctor.cc", "-o", ctor_exe}; #endif if(!CXXFLAGS.empty()) { auto tokens = argsplit(CXXFLAGS); for(const auto& token : tokens) { args.push_back(token); } } if(!LDFLAGS.empty()) { auto tokens = argsplit(LDFLAGS); for(const auto& token : tokens) { args.push_back(token); } } // 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 { #if _MSC_VER && !__INTEL_COMPILER #else // No build files should have been created yet assert_not_exists(BUILDDIR); #endif // 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 assert_changed(ctor_bin); // configuration.cc should have been generated now assert_exists("configuration.cc"); assert_exists("config.h"); // Shouldn't compile anything yet - only configure assert_not_exists(BUILDDIR + "/hello-hello_cc" + obj_ext); ctor_bin.capture(); // Run normally to build project run_ctor({"-v"}); // Compiled object should now exist assert_exists(BUILDDIR + "/hello-hello_cc" + obj_ext); // ctor should not have been rebuilt, so binary should be the same assert_not_changed(ctor_bin); 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 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) { fail(); } } ////////////////////////////////////////////////////////////////////////////// // 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(); } assert_changed(configuration_cc_bin); assert_changed(ctor_bin); } ////////////////////////////////////////////////////////////////////////////// // check if included files in ctor.cc is tracked for changes { 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); // 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("generated"); std::filesystem::remove(BUILDDIR + "/world.cc"); std::filesystem::remove(BUILDDIR + "/foo.cc"); Tracker configuration_cc_bin("configuration.cc"); Tracker ctor_bin(ctor_exe); std::this_thread::sleep_for(1100ms); // Run normally to reconfigure, rebuild ctor and build world.cc run_ctor({"-v", "world"}); assert_changed(configuration_cc_bin); assert_changed(ctor_bin); // foo.cc should not be generated at this point assert_not_exists(BUILDDIR+"/foo.cc"); 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); // 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 { std::filesystem::remove(BUILDDIR + "/world.cc"); std::filesystem::remove(BUILDDIR + "/foo.cc"); std::filesystem::remove(BUILDDIR + "/many_to_one.cc"); run_ctor({"-v", "many_to_one"}); // world.cc should not be generated at this point assert_not_exists(BUILDDIR+"/world.cc"); // foo.cc should have been generated at this point assert_exists(BUILDDIR+"/foo.cc"); // many_to_one.cc should have been generated assert_exists(BUILDDIR+"/many_to_one.cc"); auto time = std::filesystem::last_write_time(BUILDDIR + "/many_to_one.cc"); std::this_thread::sleep_for(1100ms); Tracker ctor_bin(ctor_exe); // remove "foo.cc" from sources list, so it only contains hello.cc copy_config("generated2"); // 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; }