aboutsummaryrefslogtreecommitdiff
path: root/genmake.pl
diff options
context:
space:
mode:
Diffstat (limited to 'genmake.pl')
-rwxr-xr-xgenmake.pl128
1 files changed, 128 insertions, 0 deletions
diff --git a/genmake.pl b/genmake.pl
new file mode 100755
index 0000000..c4165a6
--- /dev/null
+++ b/genmake.pl
@@ -0,0 +1,128 @@
+#!/usr/bin/perl
+
+use File::Basename;
+use File::Find;
+use Digest::SHA qw(sha1_hex);
+
+# This script is designed to introspect C files and generate a makefile to use.
+
+sub header_deps {
+ my $file = @_[0];
+ my @headers;
+
+ if (open(my $fh, '<:encoding(UTF-8)', $file)) {
+ print STDERR "\x1b[35m[Trace] - Reading file $file\x1b[00m\n";
+ push(@headers, $file);
+
+ while (<$fh>) {
+ /#include\s+"(.*)"\s*$/ && push(@headers, header_deps("include/$1"));
+ }
+ }
+
+ return @headers;
+}
+
+my @files;
+find(sub {
+ push @files, "$File::Find::name" if $_ =~ /.*\.c/
+ }, "src/");
+
+my @test_files;
+find(sub {
+ push @test_files, "$File::Find::name" if $_ =~ /.*\.c/
+ }, "tests/");
+
+my @header_files;
+find(sub {
+ push @header_files, "$File::Find::name" if $_ =~ /.*\.h/;
+ }, "include/" );
+
+my $idempotency_hash=sha1_hex("@files @test_files @header_files");
+
+if ("$ARGV[0]" eq "hash") {
+ print "$idempotency_hash\n";
+ exit 0
+}
+
+my @obj_files;
+
+open(my $fh, '<:encoding(UTF-8)', "Makefile.preamble")
+ or die "Missing Makefile.preamble";
+
+while (<$fh>) {
+ print "$_";
+}
+
+# Emit a rule that will rerun genmake if the c files do not match.
+my $idempotency_cmd_make =
+ "/usr/bin/perl ./genmake.pl hash";
+
+print "IDEMPOTENCY_HASH=$idempotency_hash\n";
+
+my $arch_obs_dir = "_\$(PREFIX)_obs";
+print "CHEAT_PRE_MAKE := \$(shell mkdir -p $arch_obs_dir)\n";
+
+foreach $file (@files) {
+ my $c_file = $file;
+ (my $file_no_ext = $file) =~ s/src\/(.*)\.c$/\1/g;
+
+ my $obj_file = "$arch_obs_dir/${file_no_ext}.o";
+ my $s_file = "${file_no_ext}.s";
+
+ push(@obj_files, $obj_file);
+ my @deps = header_deps($c_file);
+
+ my $deps_as_join = join(" ", @deps);
+
+ # Emit the rule to make the object file.
+ print "$obj_file: $deps_as_join\n\t";
+ print "\@mkdir -p " . dirname($obj_file) . "\n\t";
+ print '$(CC) -c ' . $c_file . ' -o ' . $obj_file . ' $(CFLAGS)' . "\n\n";
+
+ # Emit the rule to make the assembly file.
+ print "$s_file: $deps_as_join\n\t";
+ print "\@mkdir -p " . dirname($obj_file) . "\n\t";
+ print '$(CC) -S ' . $c_file . ' -o ' . $s_file . ' $(CFLAGS)' . "\n\n";
+}
+
+my $obj_files_deps = join(' ', @obj_files);
+
+my @tests_tgts;
+foreach $file (@test_files) {
+ my $c_file = $file;
+
+ my($basename, $directories, $suffix) = fileparse($file, qr/\.[^.]*/);
+
+ my $outdir = $directories . "build/";
+ my $outbinary = $outdir . $basename;
+
+ my @deps = header_deps($c_file);
+ my $deps_as_join = join(" ", @deps);
+
+ push (@test_tgts, "${outbinary}");
+ print "${outbinary}: $deps_as_join $obj_files_deps test_harness/test_harness.a\n\t";
+ print "mkdir -p " . dirname($outbinary) . "\n\t";
+ print '$(CC) $(CFLAGS) -o' . ${outbinary} . ' ' . $c_file . ' ' . $obj_files_deps . " test_harness/test_harness.a\n\n";
+
+ print "$directories$basename:\n\t";
+ print 'mkdir -p ' . $outdir . "\n\t";
+ print 'PREFIX=$(TEST_PREFIX) CFLAGS="$(TEST_CFLAGS)" $(MAKE) ' . $outbinary . "\n\t";
+ print $outbinary . "\n\n";
+}
+
+print "test_harness/test_harness.a: test_harness/test_harness.h test_harness/test_harness.c\n\t";
+print 'cd test_harness; $(MAKE) test_harness.a; cd ..' . "\n\n";
+
+print "tests_: " . join(" ", @test_tgts) . "\n\t";
+print "" . join("\n\t", @test_tgts) . "\n\n";
+
+print ".PHONY: tests\n";
+print "tests:\n\t";
+print 'PREFIX=$(TEST_PREFIX) CFLAGS="$(TEST_CFLAGS)" $(MAKE) tests_' . "\n\n";
+
+print "FORCE:\n\t\n\n";
+print "$arch_obs_dir/main.elf: FORCE $obj_files_deps linker/linker_script.ld\n\t";
+print "([ \"\$\$($idempotency_cmd_make)\" != \"\$(IDEMPOTENCY_HASH)\" ] "
+ . "&& ./genmake.pl > Makefile && make $arch_obs_dir/main.elf ) "
+ . "|| "
+ . "\$(LD) -o $arch_obs_dir/main.elf \$(LD_FLAGS) $obj_files_deps\n\n";