1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
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";
|