#!/usr/bin/env perl # # Copyright (C) 2001-2022 Graeme Walker # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . # === # # make2unity # # Generates "unity build" source files by parsing autoconf/automake # artifacts throughout the source tree. Also optionally creates a # compilation database ("compile_commands.json") for running # clang-tidy. # # usage: # make2unity --out= [options] # make2unity [options] [ [ ...]] # --base= -- base directory for makefile search # --config-status= -- path of config.status file # --out= -- output source file (if one ) # --cdb -- create a compilation database # --cdb-top= -- top_srcdir (needed if --cdb) # --cdb-cxx= -- compiler (needed if --cdb) # # The arguments are allowed to have a ".cpp" suffix. # # Eg: # $ cd src # $ ../bin/make2unity emailrelay.cpp # $ g++ -pthread -I.... -c emailrelay.cpp # $ g++ -pthread -o emailrelay emailrelay.o -lpam -lssl -lcrypto # use strict ; use warnings ; use FileHandle ; use File::Basename ; use Getopt::Long ; use Data::Dumper ; use lib dirname($0) ; use CompilationDatabase ; use ConfigStatus ; use AutoMakeParser ; $AutoMakeParser::debug = 0 ; my %opt = () ; GetOptions( \%opt , "out=s" , "cdb" , "cdb-cxx=s" , "cdb-top=s" , "base=s" , "config-status=s" ) or die "make2unity: usage error" ; die "make2unity: usage error" if scalar(@ARGV) == 0 ; die "make2unity: usage error" if ( $opt{out} && scalar(@ARGV) != 1 ) ; my $cfg_out = $opt{out} ; my $cfg_cdb = exists $opt{cdb} ; my $cfg_top_srcdir = $opt{'cdb-top'} ; my $cfg_base_dir = exists $opt{base} ? $opt{base} : File::Basename::dirname($0)."/../src" ; my $cfg_config_status = $opt{'config-status'} ; my @cfg_programs = @ARGV ; push @cfg_programs , "emailrelay" if !@cfg_programs ; my $cfg_cxx = $opt{'cdb-cxx'} || "/usr/bin/c++" ; my $cs = new ConfigStatus( $cfg_config_status ) ; my %switches = $cs->switches() ; my %vars = $cs->vars() ; $vars{top_srcdir} = "." ; $vars{top_builddir} = "." ; my @makefiles = AutoMakeParser::readall( $cfg_base_dir , \%switches , \%vars ) ; my $fh_cdb ; if( $cfg_cdb ) { $fh_cdb = new FileHandle( "compile_commands.json" , "w" ) or die ; print $fh_cdb "[\n" ; } for my $cfg_program ( @cfg_programs ) { my $program = File::Basename::basename( $cfg_program , ".cpp" ) ; my $out = $cfg_out ? $cfg_out : "${program}.cpp" ; my $fh_out = new FileHandle( $out , "w" ) or die ; print $fh_out "/* autogenerated by make2unity */\n" ; my $stanza = undef ; my %libs = () ; my @out_lines = () ; for my $m ( @makefiles ) { my $dir = File::Basename::dirname( $m->path() ) ; for my $p ( $m->programs() ) { if( $p eq $program || ($p eq "$program.real") ) { map { $libs{"lib".$_.".a"} = 1 } $m->our_libs( $p ) ; push @out_lines , "/* exe [$dir] */\n" ; print $fh_out "/* c++ -pthread".join(" -I ../",("",$m->includes("",0,0)))." -o $p $p.cpp ".join(" -l",("",$m->sys_libs($p)))." */\n" ; for my $src ( $m->sources($p) ) { push @out_lines , "#include \"$src\"\n" ; } $stanza = stanza( $program , $m ) ; } } } for my $m ( @makefiles ) { my $dir = File::Basename::dirname( $m->path() ) ; print $fh_out "/* lib [$dir] */\n" ; for my $library ( $m->libraries() ) { if( exists($libs{$library}) ) # ignore this library if not linked in to $program { for my $src ( $m->sources($library) ) { print $fh_out "#include \"$src\"\n" ; } } else { print $fh_out "/* (not linked) */\n" ; } } } print $fh_out @out_lines ; # (after all the library sources) $fh_out->close() or die ; print $fh_cdb $stanza , "\n" if ( $fh_cdb && defined($stanza) ) ; } if( $fh_cdb ) { print $fh_cdb "]\n" ; $fh_cdb->close() or die ; } sub stanza { my ( $program , $m ) = @_ ; my $dir = cwd() ; my $src = "$program.cpp" ; my $autoconf_dir = "../src" ; my $program_dir = File::Basename::dirname( $m->path() ) ; my $moc_dir = "." ; my @includes = ( $autoconf_dir , $program_dir , $moc_dir , $m->includes($cfg_top_srcdir,0,1) ) ; my $includes = join( " -I" , ("",@includes) ) ; my $options = $m->compile_options() ; my $cmd = "$cfg_cxx $options $includes -c $program.cpp" ; my $s = '{ "directory" : "__DIR__" , "command" : "__CMD__" , "file" : "__SRC__" , },' ; $s =~ s/\t//gm ; $s =~ s/__DIR__/$dir/m ; $s =~ s/__CMD__/$cmd/m ; $s =~ s/__SRC__/$src/m ; return $s ; }