summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBent Bisballe Nyeng <deva@aasimon.org>2025-12-28 21:48:09 +0100
committerBent Bisballe Nyeng <deva@aasimon.org>2025-12-28 23:57:19 +0100
commit7ef83fab5d9515db2aa302f00844acd2703b5765 (patch)
tree2c93620dc80aa654ed7d5979fe35af86894aef20
parent3294ff2f9b7aa92b6dccc653c590ce27fa434f8c (diff)
-rw-r--r--Jenkinsfile6
-rw-r--r--test/suite/ctor_files/ctor.cc.generated48
-rw-r--r--test/suite/test.cc425
3 files changed, 476 insertions, 3 deletions
diff --git a/Jenkinsfile b/Jenkinsfile
index e518b6c..aad3052 100644
--- a/Jenkinsfile
+++ b/Jenkinsfile
@@ -15,7 +15,7 @@ pipeline {
echo 'Testing (clang) ...'
sh './ctor check'
echo 'Testing suite (clang) ...'
- sh '(cd test/suite; CTORDIR=../../build CXXFLAGS=-Werror CXX=/usr/local/opt/llvm/bin/clang++ LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind" ./test.sh)'
+ sh '(cd test/suite; /usr/local/opt/llvm/bin/clang++ -std=c++20 test.cc -L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind -o test && CTORDIR=../../build CXXFLAGS=-Werror CXX=/usr/local/opt/llvm/bin/clang++ LDFLAGS="-L/usr/local/opt/llvm/lib/c++ -L/usr/local/opt/llvm/lib/unwind -lunwind" ./test)'
}
post {
always {
@@ -36,7 +36,7 @@ pipeline {
echo 'Testing (gcc) ...'
sh './ctor check'
echo 'Testing suite (gcc) ...'
- sh '(cd test/suite; CTORDIR=../../build CXXFLAGS=-Werror CXX=g++ ./test.sh)'
+ sh '(cd test/suite; g++ -std=c++20 test.cc -o test && CTORDIR=../../build CXXFLAGS=-Werror CXX=g++ ./test)'
}
post {
always {
@@ -57,7 +57,7 @@ pipeline {
echo 'Testing (clang) ...'
sh './ctor check'
echo 'Testing suite (clang) ...'
- sh '(cd test/suite; CTORDIR=../../build CXXFLAGS=-Werror CXX=clang++ ./test.sh)'
+ sh '(cd test/suite; clang++ -std=c++20 test.cc -o test && CTORDIR=../../build CXXFLAGS=-Werror CXX=clang++ ./test)'
}
post {
always {
diff --git a/test/suite/ctor_files/ctor.cc.generated b/test/suite/ctor_files/ctor.cc.generated
new file mode 100644
index 0000000..5f82fd4
--- /dev/null
+++ b/test/suite/ctor_files/ctor.cc.generated
@@ -0,0 +1,48 @@
+// -*- c++ -*-
+// Distributed under the BSD 2-Clause License.
+// See accompanying file LICENSE for details.
+#include <ctor.h>
+#include <filesystem>
+#include <iostream>
+
+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;
+ }
+ },
+ };
+}
+}
+
+REG(ctorConfigs);
diff --git a/test/suite/test.cc b/test/suite/test.cc
new file mode 100644
index 0000000..ff9ecd6
--- /dev/null
+++ b/test/suite/test.cc
@@ -0,0 +1,425 @@
+#include "../../src/util.cc"
+#include "../../src/pointerlist.cc"
+#include "../../src/execute.cc"
+
+#include <iostream>
+#include <string>
+#include <filesystem>
+#include <thread>
+#include <chrono>
+#include <sstream>
+
+//function fail
+//{
+// echo "*** Failure at line $1"
+// exit 1
+//}
+//
+//function ctor
+//{
+// echo "*** Running: ./ctor $*"
+// ./ctor $*
+//}
+
+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;
+}
+
+int main()
+{
+ using namespace std::chrono_literals;
+ ctor::settings settings{};
+ settings.verbose = 2;
+ auto paths = get_paths();
+
+ //: ${CXX:=g++}
+ std::string CXX = "g++";
+ get_env("CXX", CXX);
+ //: ${CTORDIR:=../../build}
+ std::string CTORDIR = "../../build";
+ get_env("CTORDIR", CTORDIR);
+ //: ${BUILDDIR:=build}
+ std::string BUILDDIR = "build";
+ get_env("BUILDDIR", BUILDDIR);
+
+ std::string CXXFLAGS;
+ get_env("CXXFLAGS", CXXFLAGS);
+
+ std::string LDFLAGS;
+ get_env("LDFLAGS", LDFLAGS);
+
+ //
+ //CXX=$(which $CXX)
+ auto cxx_prog = locate(CXX, paths);
+ //
+ //
+ //STAT_FORMAT="-c %Y"
+ //if [[ "$OSTYPE" == "darwin"* ]]; then
+ // # Mac OSX
+ // STAT_FORMAT="-f %B"
+ //fi
+ //
+ //# Wipe the board
+ //rm -Rf ${BUILDDIR}
+ std::filesystem::remove_all(BUILDDIR);
+ //rm -f configuration.cc
+ std::filesystem::remove("configuration.cc");
+ std::filesystem::remove("config.h");
+ //rm -f ctor
+ std::filesystem::remove("ctor");
+
+ //
+ //echo "** ctor_files/ctor.cc.base"
+ std::cout << "** ctor_files/ctor.cc.base\n";
+ //cp ctor_files/ctor.cc.base ctor.cc
+ std::filesystem::copy("ctor_files/ctor.cc.base", "ctor.cc",
+ std::filesystem::copy_options::overwrite_existing);
+ //
+ //# Compile bootstrap binary
+ //$CXX -pthread $LDFLAGS $CXXFLAGS -std=c++20 -L${CTORDIR} -lctor -I../../src ctor.cc -o ctor || fail ${LINENO}
+ std::vector<std::string> args =
+ {"-pthread", "-std=c++20", "-L", CTORDIR, "-lctor", "-I", "../../src",
+ "ctor.cc", "-o", "ctor"};
+
+ // TODO:
+ if(!CXXFLAGS.empty())
+ {
+ auto tokens = tokenize(CXXFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
+ }
+ if(!LDFLAGS.empty())
+ {
+ auto tokens = tokenize(LDFLAGS);
+ for(const auto& token : tokens)
+ {
+ args.push_back(token);
+ }
+ }
+
+ auto ret = execute(settings, cxx_prog, args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //
+ //# No build files should have been created yet
+ //[ -d ${BUILDDIR} ] && fail ${LINENO}
+ if(std::filesystem::exists(BUILDDIR))
+ {
+ return fail();
+ }
+ //
+ //# capture md5 sum of ctor binary before configure is called
+ //MD5=`md5sum ctor`
+ auto ctor_bin = readFile("ctor");
+ //ctor configure --ctor-includedir ../../src --ctor-libdir=${CTORDIR} --build-dir=${BUILDDIR}
+ args = {"configure", "--ctor-includedir", "../../src",
+ "--ctor-libdir="+CTORDIR, "--build-dir="+BUILDDIR};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //
+ //# ctor should be rebuilt at this point, so md5 sum should have changed
+ //(echo $MD5 | md5sum --status -c) && fail ${LINENO}
+ auto ctor_bin2 = readFile("ctor");
+ if(ctor_bin == ctor_bin2)
+ {
+ return fail();
+ }
+
+ //
+ //# configuration.cc should have been generated now
+ //[ ! -f configuration.cc ] && fail ${LINENO}
+ if(!std::filesystem::exists("configuration.cc"))
+ {
+ return fail();
+ }
+ if(!std::filesystem::exists("config.h"))
+ {
+ return fail();
+ }
+
+ //
+ //# Shouldn't compile anything yet - only configure
+ //[ -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO}
+ if(std::filesystem::exists(BUILDDIR+"/hello-hello_cc.o"))
+ {
+ return fail();
+ }
+
+ //
+ //MD5=`md5sum ctor`
+ ctor_bin = readFile("ctor");
+ //
+ //# Run normally to build project
+ //ctor -v
+ args = {"-v"};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+
+ //
+ //# Compiled object should now exist
+ //[ ! -f ${BUILDDIR}/hello-hello_cc.o ] && fail ${LINENO}
+ if(!std::filesystem::exists(BUILDDIR+"/hello-hello_cc.o"))
+ {
+ return fail();
+ }
+ //
+ //# ctor should not have been rebuilt, so md5 sum should be the same
+ //(echo $MD5 | md5sum --status -c) || fail ${LINENO}
+ ctor_bin2 = readFile("ctor");
+ if(ctor_bin != ctor_bin2)
+ {
+ return fail();
+ }
+
+ std::this_thread::sleep_for(1100ms);
+ //
+ //MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
+ auto time = std::filesystem::last_write_time(BUILDDIR+"/hello-hello_cc.o");
+ //touch hello.cc
+ std::filesystem::last_write_time("hello.cc", time + 1s);
+ //sleep 1.1
+ std::this_thread::sleep_for(1100ms);
+ //
+ //# Run normally to rebuild hello.cc
+ //ctor -v
+ args = {"-v"};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //
+ //# Object file should have been recompiled
+ //MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
+ auto time2 = std::filesystem::last_write_time(BUILDDIR+"/hello-hello_cc.o");
+ //[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
+ if(time == time2)
+ {
+ return fail();
+ }
+ //
+ //# Replace -DFOO with -DBAR in foo external.cxxflags
+ //echo "** ctor_files/ctor.cc.bar"
+ std::cout << "** ctor_files/ctor.cc.bar\n";
+ //cp ctor_files/ctor.cc.bar ctor.cc
+ std::filesystem::copy("ctor_files/ctor.cc.bar", "ctor.cc",
+ std::filesystem::copy_options::overwrite_existing);
+ //
+ //MD5C=`md5sum configuration.cc`
+ auto configuration_cc_bin = readFile("configuration.cc");
+ //MD5=`md5sum ctor`
+ ctor_bin = readFile("ctor");
+ //MOD1=`stat $STAT_FORMAT build/hello-hello_cc.o`
+ time = std::filesystem::last_write_time(BUILDDIR+"/hello-hello_cc.o");
+ //sleep 1.1
+ std::this_thread::sleep_for(1100ms);
+ //
+ //# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ //ctor -v
+ args = {"-v"};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //
+ //MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
+ time2 = std::filesystem::last_write_time(BUILDDIR+"/hello-hello_cc.o");
+ //[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
+ if(time == time2)
+ {
+ return fail();
+ }
+ //(echo $MD5C | md5sum --status -c) && fail ${LINENO}
+ auto configuration_cc_bin2 = readFile("configuration.cc");
+ if(configuration_cc_bin == configuration_cc_bin2)
+ {
+ return fail();
+ }
+ //(echo $MD5 | md5sum --status -c) && fail ${LINENO}
+ ctor_bin2 = readFile("ctor");
+ if(ctor_bin == ctor_bin2)
+ {
+ return fail();
+ }
+ //
+ //echo "** ctor_files/ctor.cc.multi"
+ std::cout << "** ctor_files/ctor.cc.multi\n";
+ //cp ctor_files/ctor.cc.multi ctor.cc
+ std::filesystem::copy("ctor_files/ctor.cc.multi", "ctor.cc",
+ std::filesystem::copy_options::overwrite_existing);
+
+ //
+ //MD5C=`md5sum configuration.cc`
+ configuration_cc_bin = readFile("configuration.cc");
+ //MD5=`md5sum ctor`
+ ctor_bin = readFile("ctor");
+ //MOD1=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
+ time = std::filesystem::last_write_time(BUILDDIR+"/hello-hello_cc.o");
+ //sleep 1.1
+ std::this_thread::sleep_for(1100ms);
+ //
+ //# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ //ctor -v
+ args = {"-v"};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //
+ //MOD2=`stat $STAT_FORMAT ${BUILDDIR}/hello-hello_cc.o`
+ time2 = std::filesystem::last_write_time(BUILDDIR+"/hello-hello_cc.o");
+ //[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
+ if(time == time2)
+ {
+ return fail();
+ }
+ //(echo $MD5C | md5sum --status -c) && fail ${LINENO}
+ configuration_cc_bin2 = readFile("configuration.cc");
+ if(configuration_cc_bin == configuration_cc_bin2)
+ {
+ return fail();
+ }
+ //(echo $MD5 | md5sum --status -c) && fail ${LINENO}
+ ctor_bin2 = readFile("ctor");
+ if(ctor_bin == ctor_bin2)
+ {
+ return fail();
+ }
+ //
+ //# now touching foobar.h, should retrigger re-configuration
+ //touch foobar.h
+ time = std::filesystem::last_write_time("ctor");
+ std::filesystem::last_write_time("foobar.h", time + 1s);
+ //
+ //MOD1=`stat $STAT_FORMAT ctor`
+ time = std::filesystem::last_write_time("ctor");
+ //sleep 1.1
+ std::this_thread::sleep_for(1100ms);
+ //
+ //# Run normally to reconfigure, rebuild ctor and rebuild hello.cc
+ //ctor -v
+ args = {"-v"};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //
+ //MOD2=`stat $STAT_FORMAT ctor`
+ time2 = std::filesystem::last_write_time("ctor");
+ //[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
+ if(time == time2)
+ {
+ return fail();
+ }
+ //
+ //echo "** ctor_files/ctor.cc.generated"
+ std::cout << "** ctor_files/ctor.cc.generated\n";
+ //cp ctor_files/ctor.cc.generated ctor.cc
+ std::filesystem::copy("ctor_files/ctor.cc.generated", "ctor.cc",
+ std::filesystem::copy_options::overwrite_existing);
+ //rm -f ${BUILDDIR}/world.cc ${BUILDDIR}/foo.cc
+ std::filesystem::remove(BUILDDIR+"/world.cc");
+ std::filesystem::remove(BUILDDIR+"/foo.cc");
+
+ //
+ //MD5C=`md5sum configuration.cc`
+ configuration_cc_bin = readFile("configuration.cc");
+ //MD5=`md5sum ctor`
+ ctor_bin = readFile("ctor");
+ //sleep 1.1
+ std::this_thread::sleep_for(1100ms);
+ //
+ //# Run normally to reconfigure, rebuild ctor and build world.cc
+ //ctor -v world
+ args = {"-v", "world"};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //(echo $MD5C | md5sum --status -c) && fail ${LINENO}
+ configuration_cc_bin2 = readFile("configuration.cc");
+ if(configuration_cc_bin == configuration_cc_bin2)
+ {
+ return fail();
+ }
+ //(echo $MD5 | md5sum --status -c) && fail ${LINENO}
+ ctor_bin2 = readFile("ctor");
+ if(ctor_bin == ctor_bin2)
+ {
+ return fail();
+ }
+ //
+ //# foo.cc should not be generated at this point
+ //ls ${BUILDDIR}/foo.cc
+ //[[ $? != 0 ]] || fail ${LINENO}
+ if(std::filesystem::exists(BUILDDIR+"/foo.cc"))
+ {
+ return fail();
+ }
+ //
+ //MOD1W=`stat $STAT_FORMAT ${BUILDDIR}/world.cc`
+ auto time_w = std::filesystem::last_write_time(BUILDDIR+"/world.cc");
+ //MOD1=`stat $STAT_FORMAT ${BUILDDIR}/${BUILDDIR}/world-world_cc.o`
+ auto time_wo = std::filesystem::last_write_time(BUILDDIR+"/"+BUILDDIR+"/world-world_cc.o");
+ //
+ //sleep 1.1
+ std::this_thread::sleep_for(1100ms);
+ //
+ //# now touching hello.cc, should trigger regeneration of world.cc and rebuild
+ //touch hello.cc
+ std::filesystem::last_write_time("hello.cc", time_w + 1s);
+ //ctor -v world
+ args = {"-v", "world"};
+ ret = execute(settings, "./ctor", args);
+ if(ret != 0)
+ {
+ return fail(ret);
+ }
+ //
+ //MOD2W=`stat $STAT_FORMAT ${BUILDDIR}/world.cc`
+ auto time_w2 = std::filesystem::last_write_time(BUILDDIR+"/world.cc");
+ //MOD2=`stat $STAT_FORMAT ${BUILDDIR}/${BUILDDIR}/world-world_cc.o`
+ auto time_wo2 = std::filesystem::last_write_time(BUILDDIR+"/"+BUILDDIR+"/world-world_cc.o");
+ //[[ $MOD1W == $MOD2W ]] && fail ${LINENO}
+ if(time_w == time_w2)
+ {
+ return fail();
+ }
+ //[[ $MOD1 == $MOD2 ]] && fail ${LINENO}
+ if(time_wo == time_wo2)
+ {
+ return fail();
+ }
+ //
+ //exit 0
+ return 0;
+}