#!/usr/bin/env perl use strict; use Carp; use warnings; use Cwd; # Trap warnings $SIG {__WARN__} = sub { die @_; }; #--------------------------------------------------------------------- # basic constants #--------------------------------------------------------------------- use constant ZERO => 0; # Zero use constant ONE => 1; # One use constant TWO => 2; # Two use constant FALSE => 0; # Boolean FALSE use constant TRUE => 1; # Boolean TRUE #--------------------------------------------------------------------- # Future change: Document this code. my @PATTERN_NO = qw ( no false off zero 0 disabled? nothing none n/a ); my @PATTERN_YES = qw ( yes true on one 1 enabled? absolutely certainly positively sure ); my $PATTERN_NO = join '|', @PATTERN_NO ; @PATTERN_NO = (); my $PATTERN_YES = join '|', @PATTERN_YES ; @PATTERN_YES = (); my $PkgTree = '/laclin64/pkg'; # Internal-error message prefix string my $IE = 'Internal error'; my $iebase = 'gcc wrapper'; #--------------------------------------------------------------------- use lib '/laclin/sysutil/toolchain/' ; use laclin_arch ; &laclin_arch ('LACARCH' => ''); #--------------------------------------------------------------------- # global variables #--------------------------------------------------------------------- $ENV {'LACLLVMPKG'} = "llvmgcc"; my %LACDONTFIND = (); my $AddLinkFlags; my $nostdlibs; my $IsKernelCompile; #--------------------------------------------------------------------- my $USE_CLANG = FALSE; my $USE_MUSL = FALSE; sub check_clang_musl { my ($ProgName) = @_; my ($x1, $x2); $x1 = $ENV {LACSYSTREE}; $x1 = "" unless defined $x1; $USE_MUSL = TRUE if $x1 eq '/musl64'; $x1 = $ENV {USE_MUSL}; $x1 = "" unless defined $x1; $x1 = $ENV {USEMUSL } unless length $x1; $x1 = "" unless defined $x1; $x2 = $ENV {LACGCCPKG} ; $x2 = "" unless defined $x2 ; $USE_CLANG = TRUE if $ProgName =~ m@\bclang\b@; $USE_CLANG = TRUE if $x2 =~ m@^clang@; $USE_MUSL = TRUE if $ProgName =~ m@\bmusl\b@; $USE_MUSL = TRUE if $x1 =~ m@^($PATTERN_YES)\z@i; $USE_MUSL = TRUE if $x2 =~ m@musl@; for (@ARGV) { $USE_MUSL = TRUE if $_ =~ m@ld-musl@; } if ($USE_MUSL) { $PkgTree = '/musl64/pkg' ; $ENV {LACGCCPKG} = 'clang' ; $USE_CLANG = TRUE ; } elsif ($USE_CLANG) { $ENV {LACGCCPKG} = 'clang' ; } undef; } #--------------------------------------------------------------------- # Note: For the RPATH_LIBS feature to work correctly, the distro's # "ldconfig" script needs to purge all temporary files of the follow- # ing form before it exits: # # /tmp/rpath_info-*.delete my %RPATH_MAP = (); my %APATH_MAP = (); my $HAVE_RPATH_MAP = FALSE; #--------------------------------------------------------------------- # Future change: Document this routine. sub Build_RPATH_MAP { my $data; my $str; my $SaveSlash = $/; #--------------------------------------------------------------------- # Future change: Document this code. return if $HAVE_RPATH_MAP; #--------------------------------------------------------------------- # Future change: Document this code. die "Error07\n" unless opendir (DIR1, $PkgTree); while (defined (my $dirent1 = readdir (DIR1))) { next if defined $LACDONTFIND {$dirent1}; my $path = "$PkgTree/$dirent1"; next if -l $path; next unless -d $path; $path .= '/lib'; next unless -d $path; next if -f "$path/hidden"; die "$IE: $iebase-08\n" unless opendir (DIR2, $path); while (defined (my $dirent2 = readdir (DIR2))) { my $path2 = "$path/$dirent2"; next unless -f $path2; next unless $path2 =~ s@^.+/lib/lib([^/]+)@$1@; if ($path2 =~ s@\.so(\.\d+)*\z@@) { $RPATH_MAP {$path2} = $path; } elsif ($path2 =~ s@\.a\z@@) { $APATH_MAP {$path2} = $path; } } closedir DIR2; } closedir DIR1; #--------------------------------------------------------------------- # Future change: Document this code. $HAVE_RPATH_MAP = TRUE; undef; } #--------------------------------------------------------------------- sub PauseN00ms { my ($n) = @_; # 20 * 100ms = 2000ms = 2 sec $n = ONE unless defined ($n) && ($n =~ m@^\d+\z@); $n = 20 if $n > 20; for (my $ii = ONE; $ii <= $n; $ii++) { select (undef, undef, undef, 0.10); } undef; } #--------------------------------------------------------------------- sub GetDegrees { my $cmd = << 'END'; cat /sys/devices/platform/coretemp.*/hwmon/hwmon*/temp*_input END $cmd =~ s@\s*\n\s*@ @gs; my $result = `$cmd 2>/dev/null`; $result = "" unless defined $result; my $num = ZERO; for (split (/\s+/, $result)) { next unless m@^\d+\z@; $num = $_ if $num < $_; } $num = int ($num / 1000); $num; } #--------------------------------------------------------------------- sub WaitCool { # while (&GetDegrees() >= 87) { sleep ONE; } } #--------------------------------------------------------------------- # main routine #--------------------------------------------------------------------- sub Main { my $ProgName = $0; my $n; my $nn; my $str; #--------------------------------------------------------------------- # Initial setup. select STDERR; $| = ONE; select STDOUT; $| = ONE; my $IS_CPP_PROG = $ProgName =~ m@\+\+@ ? TRUE : FALSE; my $IS_CPP_LANG = $IS_CPP_PROG; my $logbase = $IS_CPP_PROG ? 'clang++' : 'clang'; &check_clang_musl ($ProgName); for my $ii (ZERO .. $#ARGV) { next unless $ARGV [$ii] =~ m@\.c\z@; $IS_CPP_LANG = FALSE; last; } open (OFD, ">>/tmp/$logbase.log"); print OFD "================== $logbase start\n"; for (@ARGV) { print OFD "$_\n"; } close OFD; #--------------------------------------------------------------------- # Pause if requested. $str = $ENV {'CCPAUSE'}; $str = "" unless defined $str; $n = ZERO; $n = 5 if $str =~ m@^[ty]@i; $n = $str if $str =~ m@^\d+\z@; &PauseN00ms ($n); #--------------------------------------------------------------------- # Pause if necessary. &WaitCool(); #--------------------------------------------------------------------- while (defined (my $r = readlink ($ProgName))) { $ProgName = $r; } #--------------------------------------------------------------------- # These tests are approximate. They're accurate enough for our immedi- # ate purposes. However, changes may be required here in the future. $AddLinkFlags = !grep { m@^-[cES]\z@; } @ARGV; $nostdlibs = grep { m@^-+nostdlibs\z@; } @ARGV; @ARGV = grep { !m@^-fuse-ld=lld\z@; } @ARGV; $IsKernelCompile = grep { m@(-D__KERNEL__|^-+kernel|\binclude/linux/kconfig.h)\b@; } @ARGV; $IsKernelCompile = FALSE if grep (/^conftest\.c\z/ , @ARGV); # "kqemu" kludge $IsKernelCompile = FALSE if grep (/^genoffsets\.c\z/ , @ARGV); #--------------------------------------------------------------------- # Make a "ld"-related check work. if ((scalar (@ARGV) == ONE) && ($ARGV [ZERO] eq '-Wl,--version')) { print << 'END'; GNU ld (GNU Binutils) 2.20.1.20100303 Copyright 2009 Free Software Foundation, Inc. This program is free software; you may redistribute it under the terms of the GNU General Public License version 3 or (at your option) a later version. This program has absolutely no warranty. END exit ONE; } #--------------------------------------------------------------------- # Future change: Document this code. $str = $ENV {LACDONTFIND}; $str = "" unless defined $str; for my $pkg (split (/[\011\040,]+/, $str)) { next unless length $pkg; next unless -d "$PkgTree/$pkg"; $LACDONTFIND {$pkg} = TRUE; } #--------------------------------------------------------------------- # Future change: Document this code. $str = $ENV {LACNOWERROR}; $str = "" unless defined $str; my $LACNOWERROR; $LACNOWERROR = FALSE; $LACNOWERROR = TRUE if $str =~ m@^\s*($PATTERN_YES)\s*\z@i; @ARGV = grep { !m@^-Werror([=\-].+|)\z@; } @ARGV if $LACNOWERROR; #--------------------------------------------------------------------- # Future change: Document this code. @ARGV = grep { !m@^-Wno-delayed-template-parsing-in-cxx20\z@ ; } @ARGV; @ARGV = grep { !m@^-Wno-thread-safety-reference-return\z@ ; } @ARGV; #--------------------------------------------------------------------- # gcc-related wrapper feature. # Needed by "pybsddb3". # To enable: # # export FIXGCCOPTR=yes # CLI level # $ENV {'FIXGCCOPTR'} = 'yes'; # Perl scripts $str = $ENV {FIXGCCOPTR}; $str = "" unless defined $str; if (($str =~ m@^($PATTERN_YES)\z@i) && ($ProgName !~ m@(g77|gfortran)@)) { for (@ARGV) { s@^-R(/\S+)\z@-Wl,--rpath,$1@; s@^(--export-dynamic)\z@-Wl,$1@; } } #--------------------------------------------------------------------- # gcc-related wrapper feature. # This code lets the user force large-file support for gcc-compiled # programs using glibc-compatible libraries. # To enable: # # export FORCECFLAGS64=yes # CLI level # $ENV {'FORCECFLAGS64'} = 'yes'; # Perl scripts $str = $ENV {'FORCECFLAGS64'}; $str = "" unless defined $str; if ($str =~ m@^($PATTERN_YES)\z@i) { unshift (@ARGV, '-D_LARGEFILE64_SOURCE'); unshift (@ARGV, '-D_FILE_OFFSET_BITS=64'); } #--------------------------------------------------------------------- $str = $ENV {'BUILDLO'}; $str = "" unless defined $str; if ($str =~ m@^($PATTERN_YES)\z@i) { unshift (@ARGV, '-DBUILD_IRIDIUM=1'); } #--------------------------------------------------------------------- $str = $ENV {'FIXOPTZERO'}; $str = "" unless defined $str; if ($str =~ m@^($PATTERN_YES)\z@i) { my $found_c = FALSE; my $found_o = FALSE; for (@ARGV) { $found_c = TRUE if m@^-c\z@; if (s@^-O[0123s]\z@-O2@) { $_ = "" if $found_o; $found_o = TRUE; } } unshift (@ARGV, '-O2') if $found_c && !$found_o; @ARGV = grep { length; } @ARGV; } #--------------------------------------------------------------------- my $CPPSTDLAC; $CPPSTDLAC = $ENV {CPPSTDLAC}; $CPPSTDLAC = "" unless defined ($CPPSTDLAC); $CPPSTDLAC =~ s@\s+@ @gs; if (length ($CPPSTDLAC)) { my $has_c = FALSE; for (my $ii = ZERO; $ii <= $#ARGV; $ii++) { next unless $ARGV [$ii] =~ m@\.(c|cc|c\+\+|cpp)\z@; $has_c = TRUE; last; } unshift (@ARGV, "-std=$CPPSTDLAC") if $has_c; } #--------------------------------------------------------------------- $str = $ENV {'LACDISABLEO3'}; $str = "" unless defined $str; $nn = FALSE; $nn = TRUE if $str =~ m@^($PATTERN_YES)\z@i; if ($nn) { for (my $ii = ZERO; $ii <= $#ARGV; $ii++) { $ARGV [$ii] =~ s@^-O3\z@-O2@g; } } #--------------------------------------------------------------------- # Support code for LACCOPYLSWITCHES feature. # Setting the environment variable LACCOPYLSWITCHES to 1, on, one, # true, or yes (etc.) tells this wrapper to implement a kludge. # If the kludge is requested, this wrapper will modify command lines # that meet certain conditions (specifically, they're performing con- # ventional program-link operations, as opposed to compiling without # linking, compiling kernel sources, or linking without standard lib- # raries). When the wrapper processes a command line that meets these # conditions, it'll copy any/all "-lLIBNAME" switches listed in the # command line to the end of the command line. This may fix problems # related to buggy "makefiles" or "build" systems. # Note: If you're using "bash" or a related shell, don't forget to # "export" the variable. my $CopyLSwitches; $str = $ENV {'LACCOPYLSWITCHES'}; $str = "" unless defined $str; $CopyLSwitches = FALSE; $CopyLSwitches = TRUE if $str =~ m@^($PATTERN_YES)\z@i; #--------------------------------------------------------------------- # Future change: Document this code. my @LSwitches = (); my %FoundLib = (); if (!$nostdlibs && !$IsKernelCompile && $AddLinkFlags) { my $is_lxdialog = FALSE; for (@ARGV) { s@^/\w+/toolchain\w*/lib/libstdc\+\+\.a\z@-lstdc++@; $is_lxdialog = TRUE if m@/lxdialog/@; } push (@ARGV, "-lncurses") if $is_lxdialog; my %DidLib = (); # Future change: RPATH feature[s] should be user-controllable. my $ii = ZERO; my $MaxIndex = $#ARGV; while ($ii++ <= $MaxIndex) { my $jj = $ii - ONE; next unless $ARGV [$jj] =~ m@^-l([a-z0-9_\-\+\.]+)\z@i; my $libbase = $1; push (@LSwitches, $ARGV [$jj]) if $CopyLSwitches; next if defined $DidLib {$libbase}; &Build_RPATH_MAP(); $DidLib {$libbase} = TRUE; my $LibDir = $RPATH_MAP {$libbase}; #--------------------------------------------------------------------- # Handle some special cases. # This section handles some special cases: # # a. Some of these cases are related to libraries that are requir- # ed by other libraries or programs in the same packages. # Build_RPATH_MAP can't always detect cases where packages # build things that depend on the package's own libraries, # because the libraries aren't necessarily installed until # "build" procedures are completed. # # b. Additionally, some of these cases are related to situations # where two or more packages have libraries with the same # names. This kind of thing may cause conflicts. # If you need to address similar problems, try adding appropriate # statements to this section. Note: Heuristics are usually O.K. here. # False positives may not be fatal. if (!defined ($LibDir)) { # Handle some special cases my $p; $p = 'boost' if $libbase =~ m@^boost_@; $p = 'leptonica' if $libbase eq 'lept'; $p = 'ncurses' if $libbase eq 'ncurses'; $p = 'mysql' if $libbase =~ m@^mysql@; $p = 'mythtv' if $libbase =~ m@^myth@; $p = 'qt3' if $libbase =~ m@^(qui|qt-mt)\z@; $p = 'wine' if $libbase eq 'wine'; $p = 'xmlrpc' if $libbase =~ m@^xmlrpc(|[_\+])\z@; $p = 'xulrunner' if $libbase eq 'xul'; next if defined ($p) && defined $LACDONTFIND {$p}; $LibDir = "$PkgTree/$p/lib" if defined ($p); } #--------------------------------------------------------------------- # Future change: Document this code. next unless defined $LibDir; next if $LibDir =~ m@/(musl|gcc|glibc|uclibc)(\b|\d)@; next if $LibDir =~ m@/perl\w*\z@; $FoundLib {$libbase} = TRUE; splice (@ARGV, $jj, ZERO, "-L$LibDir", "-Wl,--rpath,$LibDir"); $ii += TWO; $MaxIndex += TWO; } } #--------------------------------------------------------------------- # Future change: Document this code. if (!$nostdlibs && !$IsKernelCompile && $AddLinkFlags) { my %DidLib = (); my $ii = ZERO; my $MaxIndex = $#ARGV; while ($ii++ <= $MaxIndex) { my $jj = $ii - ONE; next unless $ARGV [$jj] =~ m@^-l([a-z0-9_\-\+\.]+)\z@i; $str = $1; next if defined $DidLib {$str}; next if defined $FoundLib {$str}; &Build_RPATH_MAP(); $DidLib {$str} = TRUE; my $LibDir = $APATH_MAP {$str}; next unless defined $LibDir; next if $LibDir =~ m@/(glibc|musl)@; next if $LibDir =~ m@/perl\w*\z@; splice (@ARGV, $jj, ZERO, "-L$LibDir"); $ii += 1; $MaxIndex += 1; } } #--------------------------------------------------------------------- # Future change: Document this code. my $nostdinc = FALSE; my $nostdincpp = FALSE; my $LACINCDIRS = $ENV {'LACINCDIRS' }; $LACINCDIRS = "" unless defined ($LACINCDIRS ); my $NOSTDINC = $ENV {'NOSTDINC' }; $NOSTDINC = "" unless defined ($NOSTDINC ); for (@ARGV) { $nostdinc = TRUE if m@^-+nostdinc\z@ ; } for (@ARGV) { $nostdincpp = TRUE if m@^-+nostdinc\+\+\z@ ; } $nostdinc = TRUE if $NOSTDINC =~ m@^[ty1]@i; $nostdincpp = TRUE if $nostdinc; if ($NOSTDINC =~ m@^(1|true|yes|da|on|si)\b@i) { $nostdinc = $nostdincpp = TRUE; } # c++/v1 needs to come before glibc/include: # https://github.com/termux/termux-packages/issues/1149 $LACINCDIRS .= "\n"; if (!$nostdinc) { $LACINCDIRS .= << "END" unless $USE_MUSL; /laclin64/include /laclin64/pkg/glibc/include END $LACINCDIRS .= << "END" if $USE_MUSL; /musl64/include /musl64/pkg/musl/include END } $LACINCDIRS =~ s@\s+@ @gs; $LACINCDIRS =~ s@\s+\z@@s; for my $incdir (reverse split (/[ :;,]+/, $LACINCDIRS)) { next if !length ($incdir); $incdir =~ s@/+\z@@; next if $incdir !~ m@^/.@; next if !-d $incdir; if ($incdir =~ s@^!@@) { unshift (@ARGV, $incdir ); unshift (@ARGV, "-idirafter" ); } else { unshift (@ARGV, "-I$incdir" ); } } #--------------------------------------------------------------------- # Future change: Document this code. $str = $ENV {LACDONTFIND}; $str = "" unless defined $str; $str =~ s@\s+@ @gs; for my $pkg (split (/[ ,]+/, $str)) { next unless length $pkg; next unless -d "$PkgTree/$pkg"; $LACDONTFIND {$pkg} = TRUE; } #--------------------------------------------------------------------- # Future change: Document this code. if (scalar keys %LACDONTFIND) { for (@ARGV) { for my $pkg (sort keys %LACDONTFIND) { # This code may need some work s@^/\w+/pkg/[a-z0-9_\-]+/lib/lib($pkg)\.so\z@-l$1@i; $_ = "" if m@^-I/\w+/pkg/$pkg/@; $_ = "" if m@^-L/\w+/pkg/$pkg/@; $_ = "" if m@^-Wl,-L/\w+/pkg/$pkg/@; $_ = "" if m@^-Wl,--rpath,/\w+/pkg/$pkg/@; $_ = "" if m@^-Wl,/\w+/pkg/$pkg/@; } } @ARGV = grep { length; } @ARGV; } #--------------------------------------------------------------------- my $CWD = getcwd(); for my $ii (ZERO .. $#ARGV) { my $jj = $ii+ONE; $ARGV [$ii] =~ s@^-fuse-ld=gold\z@@; $ARGV [$ii] =~ s@^-L\.\z@-L$CWD@; $ARGV [$ii] =~ s@^-R\.\z@@; $ARGV [$ii] =~ s@^-R@-Wl,-R@; if (($ARGV [$ii] eq '-Xclangbacon') && ($jj <= $#ARGV) && ($ARGV [$jj] eq '-emit-pth')) { $ARGV [$ii] = $ARGV [$jj] = ""; } } @ARGV = grep { length; } @ARGV; #--------------------------------------------------------------------- if ($IS_CPP_LANG && $AddLinkFlags && 0) { $str = $USE_MUSL ? 'llvm-musl' : 'llvm'; push (@ARGV, "-L/laclin64/pkg/$str/lib" ); push (@ARGV, "-Wl,-R/laclin64/pkg/$str/lib" ); push (@ARGV, "-lc++" ); push (@ARGV, "-lunwind" ); } #--------------------------------------------------------------------- if (!$USE_MUSL) { my $crt1o = "/$LACARCH/pkg/glibc/lib/crt1.o"; $crt1o = "/$LACARCH/toolchain/lib/crt1.o" unless -f $crt1o; $crt1o = "" unless length $crt1o; if (length ($crt1o)) { $crt1o =~ s@/[^/]+\z@@; my $crt1dir = $crt1o; for my $ii (ZERO .. $#ARGV) { next unless $ARGV [$ii] =~ m@^\w?crt[in1]\.o\z@; $ARGV [$ii] =~ s@^(.+)\z@$crt1dir/$1@; } } } #--------------------------------------------------------------------- my %dup00 = (); for my $ii (ZERO .. $#ARGV) { if ($ARGV [$ii] =~ m@^(-I|-L|-R|-Wl,--rpath)\S@) { $str = $ARGV [$ii]; if (defined $dup00 {$str}) { $ARGV [$ii] = ""; } else { $dup00 {$str} = ONE; } } elsif (($ARGV [$ii] =~ m@^-I\z@) && ($ii < $#ARGV)) { my $jj = $ii + ONE; $str = $ARGV [$ii] . $ARGV [$jj]; if (defined $dup00 {$str}) { $ARGV [$ii] = $ARGV [$jj] = ""; } else { $dup00 {$str} = ONE; } } } @ARGV = grep { length; } @ARGV; %dup00 = (); unshift (@ARGV, '-fPIC'); #--------------------------------------------------------------------- open (OFD, ">>/tmp/$logbase.log"); print OFD "================== $logbase exec\n"; for (@ARGV) { print OFD "$_\n"; } close OFD; my $TargetProg = $ProgName; $TargetProg =~ s@[^/]+\z@clang-17@; $ENV {'CLANGC'} = 'no'; #--------------------------------------------------------------------- my $df; my $CCACHE_DIR = $ENV {'CCACHE_DIR'}; $CCACHE_DIR = "" unless defined $CCACHE_DIR; $CCACHE_DIR = "" if length ($CCACHE_DIR) && !-d $CCACHE_DIR; die unless $ProgName =~ m@clang@; if (1) { my $suffix = "-clang"; if (!length ($CCACHE_DIR)) { $CCACHE_DIR = "/ram/ccache$suffix"; $CCACHE_DIR .= "-$>" if $>; } my $d = $CCACHE_DIR; unlink $d if -l $d; system "mkdir -p $d" unless -d $d; $df = ZERO; if (-d $d && -r $d && -w $d && -x $d) { $str = `/bin/df -m "$d/" 2>&1`; $str = "" unless defined $str; ($df) = $str =~ m@\s(\d+)\s+\d+\%@; } else { print STDERR << "END"; Error: Tried to create and/or use the following CCACHE_DIR directory but there was a permissions or filesystem error of some type. If the directory exists, delete it and try again: $d END exit ONE; } } #--------------------------------------------------------------------- $str = $ENV {'USECCACHE'}; $str = "" unless defined $str; $str = $ENV {'USE_CCACHE'} unless length $str; $str = "yes" unless defined $str; my $USE_CCACHE = $str; if ($USE_CCACHE =~ m@^(enabled?|true|y|yes|on)\z@i) { # $MINCCFREE specifies a number of megabytes. If free disk space on # the "ccache" cache file system used falls below this number, # "ccache" is disabled until free space increases. my $MINCCFREE = 500; if (defined ($df) && ($df >= $MINCCFREE)) { $df = int ($df / 5); $df = 10 if $df < 10; $ENV { 'CCACHE_COMPRESS' } = 'yes'; $ENV { 'CCACHE_DIR' } = $CCACHE_DIR; $ENV { 'CCACHE_CC' } = "$ProgName.bin"; $ENV { 'CCACHE_MAXSIZE' } = "${df}M"; exec 'ccache', $TargetProg, @ARGV; } } #--------------------------------------------------------------------- unshift (@ARGV, $ProgName); exec { $TargetProg } @ARGV; } #--------------------------------------------------------------------- # main program #--------------------------------------------------------------------- &Main();