trick/libexec/trick/make_makefile_src
2019-10-07 14:12:06 -05:00

375 lines
13 KiB
Perl
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/perl
use FindBin qw($RealBin);
use lib "$RealBin/pm" ;
use strict ;
use File::Basename ;
use Cwd ;
use Cwd 'abs_path';
use trick_version ;
use get_lib_deps ;
use verbose_build ;
my %processed_files ;
my %non_lib_processed_files ;
my $any_deps_changed = 0 ;
my $verbose_build = verbose_build() ;
sub exist_lib_deps(@) {
my (@files_to_process) = @_ ;
foreach my $l ( @files_to_process ) {
next if ( $l eq "" ) ;
next if ( $l =~ /^-|\.a$/ ) ;
next if ( ! -e $l ) ;
my ( $file, $dir, $suffix) = fileparse($l, qr/\.[^.]*/) ;
my ($lib_dep_file_name) = "build$dir${file}${suffix}.lib_deps" ;
if ( ! -e $lib_dep_file_name ) {
$any_deps_changed = 1 ;
print "New Dep $l\n" ;
return 1 ;
}
}
return 0 ;
}
sub read_lib_deps($@) {
my ($indent , @files_to_process) = @_ ;
foreach my $l ( @files_to_process ) {
next if ( $l eq "" ) ;
if ( ! exists $processed_files{$l} ) {
$processed_files{$l} = 1 ;
next if ( $l =~ /^-|\.a$/ ) ;
$non_lib_processed_files{$l} = 1 ;
my ( $file, $dir, $suffix) = fileparse($l, qr/\.[^.]*/) ;
my ($lib_dep_file_name) = "build$dir${file}${suffix}.lib_deps" ;
if ( -e $lib_dep_file_name ) {
open FH, "$lib_dep_file_name" or die 'cannot open $lib_dep_file_name' ;
my (@all_lines) = <FH> ;
close FH ;
chomp @all_lines ;
read_lib_deps($indent + 1 , @all_lines) ;
} else {
print "DepTracing " , " " x $indent, "$l\n" ;
if ( -e $l ) {
my $deps_changed ;
my @resolved_files ;
($deps_changed , @resolved_files) = write_lib_deps($l) ;
$any_deps_changed |= $deps_changed ;
read_lib_deps($indent + 1 , @resolved_files) ;
}
}
} elsif ( $verbose_build ) {
print "Skipping Previously processed file \"$l\"\n" ;
}
}
}
# Update any possibly out of date lib_dep files
if ( scalar @ARGV ) {
# Arguments are all files (headers and source) that are newer than the makefile.
# Keep track if any dependencies changed
for my $f ( @ARGV ) {
# Filter out Makefile_io_src_deps, Makefie_io_src, and S_source.hh from the argument list.
# These are dependencies in the makefile.
# S_source.hh will be passed in as a full path again if the file has changed.
next if ( $f eq "build/Makefile_src_deps" or $f eq "build/Makefile_io_src" or $f eq "S_source.hh") ;
my $deps_changed ;
my @resolved_files ;
print "DepTracing " , "$f\n" ;
($deps_changed , @resolved_files ) = write_lib_deps($f) ;
$any_deps_changed |= $deps_changed ;
}
} else {
# no arguments mean we are calling this for the first time. Always make makefile
$any_deps_changed = 1 ;
}
if ( ! -e "build/Makefile_src") {
$any_deps_changed = 1 ;
}
# Read in dependency tree starting at the roots. The dependency tree starts with all of the
# header files ICG processed and the lib deps listed in the S_define file.
open FILE, "build/ICG_processed" or die 'cannot open build/ICG_processed' ;
my (@top_file_names) = <FILE> ;
close FILE ;
open FILE, "build/ICG_no_found" or die 'cannot open build/ICG_no_found' ;
my (@ICG_no_file_names) = <FILE> ;
close FILE ;
push @top_file_names , @ICG_no_file_names ;
open FILE, "build/S_define.lib_deps" or die 'cannot open build/S_define.lib_deps' ;
my (@s_define_lib_deps) = <FILE> ;
close FILE ;
push @top_file_names , @s_define_lib_deps ;
chomp @top_file_names ;
# See if any depenendices lack a .lib_deps file. If it does we need to continue
$any_deps_changed |= exist_lib_deps(@top_file_names) ;
# if no dependencies have changed, "touch" Makefile_src and exit
if ( $any_deps_changed == 0 ) {
utime(undef, undef, "build/Makefile_src") ;
exit ;
}
# We are here if dependencies have changed or we're running for the first time.
# Read in all of the lib_dep files.
# read_lib_deps wil create lib_dep files that don't exist and read them in too.
read_lib_deps(0, @top_file_names) ;
#print map {"$_\n"} (sort keys %processed_files) ;
my ($n , $f , $k , $i , $m);
my @all_cfly_files ;
my @all_read_only_libs ;
my @all_compile_libs ;
my %files_by_dir ;
@all_cfly_files = keys %processed_files ;
@all_read_only_libs = sort (grep /^-/ , @all_cfly_files) ;
@all_compile_libs = grep /\.a$/ , @all_cfly_files ;
@all_compile_libs = sort (grep !/trick_source/ , @all_compile_libs) ;
@all_cfly_files = sort (grep !/^-|trick_source|a$/ , @all_cfly_files) ;
sub add_file($) {
my ($name, $path, $extension) = fileparse($_[0], qr/\.[^.]*/) ;
push @{$files_by_dir{$path}{$extension}} , $name ;
}
sub add_files_in_directory($) {
opendir THISDIR, "$_[0]" or die "Could not open $_[0]" ;
my @files = grep !/^\./ , readdir THISDIR ;
foreach ( @files ) {
add_file($_[0] . "/" . $_) ;
}
closedir THISDIR ;
}
# split off files by directory
foreach ( @all_cfly_files ) {
add_file($_);
}
# get all of the files required by compiled libraries
# compile all files as normal files, we're not going to make a library anymore.
foreach ( @all_compile_libs ) {
my $path = abs_path(dirname($_));
add_files_in_directory($path) ;
$path .= "/src" ;
add_files_in_directory($path) if -e "$path" ;
}
# sort and weed out duplicate files
foreach my $directory ( keys %files_by_dir ) {
my %temp_hash ;
foreach my $extension ( keys %{$files_by_dir{$directory}} ) {
undef %temp_hash ;
@{$files_by_dir{$directory}{$extension}} = sort grep ++$temp_hash{$_} < 2, @{$files_by_dir{$directory}{$extension}} ;
}
}
my $wd = abs_path(cwd()) ;
my $dt = localtime();
my ($trick_ver) = get_trick_version() ;
chomp $trick_ver ;
open MAKEFILE , ">build/Makefile_src" or return ;
print MAKEFILE
"################################################################################
# Makefile:
# This is a makefile for maintaining the
# '$wd'
# simulation directory. This makefile was automatically generated by trick-CP
#
################################################################################
# Creation:
# Author: Trick Configuration Processor - trick-CP Version $trick_ver
# Date: $dt
#
################################################################################
ifdef VERBOSE
TRICK_VERBOSE_BUILD=1
endif
ifndef TRICK_VERBOSE_BUILD
PRINT_COMPILE = \$(info \$(call COLOR,Compiling) \$<)
PRINT_EXE_LINK = \$(info \$(call COLOR,Linking) \$@)
PRINT_SIE = \$(info \$(call COLOR,Writing) \$@)
endif
S_MAIN = S_main_\${TRICK_HOST_CPU}.exe
ifeq (\$(MAKECMDGOALS), test)
TRICK_HOST_CPU := \$(shell \$(TRICK_HOME)/bin/trick-gte TRICK_HOST_CPU)_test
S_MAIN = T_main_\${TRICK_HOST_CPU}.exe
endif
# S_OBJECTS ====================================================================
S_OBJECTS = build/S_source.o
build/S_source.o: build/S_source.cpp | build/S_source.d
\t\$(PRINT_COMPILE)
\t\$(call ECHO_AND_LOG,\$(TRICK_CXX) \$(TRICK_CXXFLAGS) \$(TRICK_SYSTEM_CXXFLAGS) -MMD -MP -c -o \$\@ \$\<)
build/S_source.d: ;
-include build/S_source.d
# MODEL_OBJECTS ================================================================
# This function allows users to specify target-specific variables for individual
# or groups of source files. For example:
# \$(call FIND_MODEL_OBJECTS, /path/to/file /path/to/directory \$(LIST_OF_PATHS)): TRICK_CXXFLAGS += -Werror
FIND_MODEL_OBJECTS = \$(foreach PATH, \$1, \$(filter \$(addprefix build, \$(addsuffix %, \$(PATH))), \$(MODEL_OBJECTS)))
" ;
# List out all of the object files and put the list in a file that we can pass to the linker.
# Passing all of them directly to the linker in the command line can exceed the line limit.
open MODEL_LINK_LIST, ">build/model_link_list" or die "Could not open build/model_link_list" ;
my %files_by_extension ;
foreach my $directory ( keys %files_by_dir ) {
foreach my $extension ( grep { /^\.(c|cc|C|cxx|cpp|c\+\+)$/ } keys %{$files_by_dir{$directory}} ) {
foreach my $file ( @{$files_by_dir{$directory}{$extension}} ) {
push @{$files_by_extension{$extension}} , "build$directory$file.o" ;
}
}
}
foreach my $extension ( keys %files_by_extension ) {
print MAKEFILE "MODEL_OBJECTS${extension} :=" ;
foreach my $file ( @{$files_by_extension{$extension}} ) {
print MAKEFILE " \\\n $file" ;
print MODEL_LINK_LIST "$file\n" ;
}
print MAKEFILE "\n\n"
}
close MODEL_LINK_LIST ;
print MAKEFILE "MODEL_OBJECTS :=" ;
foreach my $extension ( keys %files_by_extension ) {
print MAKEFILE " \${MODEL_OBJECTS$extension}" ;
}
# Write out the compile rules for each type of file.
print MAKEFILE "
# We use .SECONDEXPANSION here to allow us to use automatic vairiables in the prerequisite list
# in order to add to each target an order-only dependency on its directory.
# See https://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html
# and https://www.gnu.org/software/make/manual/html_node/Secondary-Expansion.html
.SECONDEXPANSION:" ;
foreach my $extension ( keys %files_by_extension ) {
my $compiler = "TRICK_" . ($extension eq ".c" ? "CC" : "CXX") ;
my $flags = $extension eq ".c" ? "C" : "CXX" ;
my $command = "\$($compiler) \$(TRICK_${flags}FLAGS) \$(TRICK_SYSTEM_${flags}FLAGS) -I\$(<D)/../include -MMD -MP -c -o \$\@ \$<" ;
print MAKEFILE "
\${MODEL_OBJECTS$extension} : build/%.o : /%$extension | build/%.d \$\$(dir \$\$\@)
\t\$(PRINT_COMPILE)
\t\$(call ECHO_AND_LOG,$command)" ;
}
print MAKEFILE "
\$(sort \$(dir \$(MODEL_OBJECTS))):
\t\@mkdir -p \$\@
\$(MODEL_OBJECTS:.o=.d): ;
-include \$(MODEL_OBJECTS:.o=.d)
LINK_LISTS += \$(LD_FILELIST)build/model_link_list
" ;
# print out the libraries we link
print MAKEFILE "
# S_MAIN =======================================================================
READ_ONLY_LIBS =";
foreach ( @all_read_only_libs ) {
print MAKEFILE " \\\n $_" ;
}
print MAKEFILE "
all: S_sie.resource
\$(S_MAIN): S_source.hh \$(TRICK_STATIC_LIB) \$(S_OBJECTS) \$(MODEL_OBJECTS)
\t\$(PRINT_EXE_LINK)
\t\$(call ECHO_AND_LOG,\$(TRICK_CXX) -o \$@ \$(TRICK_SYSTEM_LDFLAGS) \$(S_OBJECTS) \$(LINK_LISTS) \$(TRICK_LDFLAGS) \$(TRICK_USER_LINK_LIBS) \$(READ_ONLY_LIBS) \$(LD_WHOLE_ARCHIVE) \$(TRICK_LIBS) \$(LD_NO_WHOLE_ARCHIVE) \$(TRICK_EXEC_LINK_LIBS))
# SIE ==========================================================================
S_sie.resource: \$(S_MAIN)
\t\$(PRINT_SIE)
\t\$(call ECHO_AND_LOG,./\$(S_MAIN) sie)\n" ;
# write out the override files we have read in
open MAKEFILEOVER, ">build/Makefile_overrides" or die "Could not open build/Makefile_overrides" ;
foreach $k ( sort keys %files_by_dir ) {
# Look for makefile_overrides in the current directory.
# If no such file exists AND this directory is named "src", look for it one level up.
# Silly, but baggage we're stuck with.
my $makefile_overrides = "${k}makefile_overrides" ;
if (not -e $makefile_overrides and $k =~ /\/src\/$/) {
$makefile_overrides = dirname($k) . "/makefile_overrides" ;
}
if (open OV_FILE, $makefile_overrides) {
while ( <OV_FILE> ) {
s/(#.*)// ;
my ($comment) = $1 ;
s/\$[{(]CURDIR[})]\/(\S+)/$k\/$1/g ;
s/(?:\$[{(]CURDIR[})]\/)?(\S*)\$[{(]OBJ_DIR[})]/$k\/$1object_\${TRICK_HOST_CPU}/g ;
s/\$[{(]CURDIR[})]/$k/g ;
while ( s,/[^/.]+/\.\.,, ) {}
s//$comment/ ;
if ( s/^objects\s*:\s*// ) {
foreach my $extension ( keys %files_by_extension ) {
foreach my $file (@{$files_by_dir{$k}{$extension}}) {
$files_by_dir{$k}{overrides} .= "build$k${file}.o \\\n" ;
}
}
$files_by_dir{$k}{overrides} .= ": $_"
}
elsif ( s/(.+)_objects\s*:\s*// ) {
if (scalar @{$files_by_dir{$k}{".$1"}}) {
foreach my $file (@{$files_by_dir{$k}{".$1"}}) {
$files_by_dir{$k}{overrides} .= "build$k$file.o \\\n" ;
}
$files_by_dir{$k}{overrides} .= ": $_"
}
}
else {
$files_by_dir{$k}{overrides} .= $_ ;
}
}
close OV_FILE ;
print MAKEFILEOVER "# Overrides from $makefile_overrides\n" ;
print MAKEFILEOVER "MAKEFILE_LIST += $makefile_overrides\n\n" ;
print MAKEFILEOVER "$files_by_dir{$k}{overrides}\n" ;
print MAKEFILEOVER "MAKEFILE_LIST := \$(filter-out $makefile_overrides,\$(MAKEFILE_LIST))\n\n" ;
}
}
close MAKEFILEOVER ;
# write out all of files we processed as dependencies to Makefile_src
open MAKEFILEDEPS, ">build/Makefile_src_deps" or die "Could not open build/Makefile_src_deps" ;
print MAKEFILEDEPS "build/Makefile_src:" ;
print MAKEFILEDEPS map {"\\\n $_"} (sort keys %non_lib_processed_files) ;
print MAKEFILEDEPS "\n\n" ;
print MAKEFILEDEPS map {"$_:\n"} (sort keys %non_lib_processed_files) ;
close MAKEFILEDEPS ;
# write out all of the files we used to S_library_list
open LIB_LIST, ">build/S_library_list" or die "Could not open build/S_library_list" ;
print LIB_LIST map {"$_\n"} (sort keys %processed_files) ;
close LIB_LIST ;