diff options
Diffstat (limited to 'genmake.pl')
-rwxr-xr-x | genmake.pl | 128 |
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"; |