aboutsummaryrefslogtreecommitdiff
path: root/runtime/tools
diff options
context:
space:
mode:
authorJim Cownie <james.h.cownie@intel.com>2013-09-27 10:38:44 +0000
committerJim Cownie <james.h.cownie@intel.com>2013-09-27 10:38:44 +0000
commitb608d468074bcf29336a09705a7d4218fe109594 (patch)
treee1c731d67f06bb4b96b8faf4afe8e00e1338089c /runtime/tools
parent957356b77333bc313421c85d66e39d47f41310a6 (diff)
First attempt to import OpenMP runtime
git-svn-id: https://llvm.org/svn/llvm-project/openmp/trunk@191506 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'runtime/tools')
-rwxr-xr-xruntime/tools/build.pl770
-rwxr-xr-xruntime/tools/check-depends.pl489
-rwxr-xr-xruntime/tools/check-execstack.pl138
-rwxr-xr-xruntime/tools/check-instruction-set.pl336
-rwxr-xr-xruntime/tools/check-tools.pl546
-rw-r--r--runtime/tools/common.inc91
-rwxr-xr-xruntime/tools/expand-vars.pl306
-rwxr-xr-xruntime/tools/extract-objects.pl258
-rwxr-xr-xruntime/tools/generate-def.pl321
-rw-r--r--runtime/tools/lib/Build.pm264
-rw-r--r--runtime/tools/lib/LibOMP.pm85
-rw-r--r--runtime/tools/lib/Platform.pm386
-rw-r--r--runtime/tools/lib/Uname.pm623
-rw-r--r--runtime/tools/lib/tools.pm1981
-rw-r--r--runtime/tools/linux.inc35
-rw-r--r--runtime/tools/macos.inc37
-rwxr-xr-xruntime/tools/message-converter.pl775
-rw-r--r--runtime/tools/objcopy.cpp522
-rwxr-xr-xruntime/tools/required-objects.pl629
-rw-r--r--runtime/tools/src/common-checks.mk85
-rw-r--r--runtime/tools/src/common-defs.mk228
-rw-r--r--runtime/tools/src/common-rules.mk188
-rw-r--r--runtime/tools/src/common-tools.mk410
-rw-r--r--runtime/tools/windows.inc27
-rwxr-xr-xruntime/tools/wipe-string.pl183
25 files changed, 9713 insertions, 0 deletions
diff --git a/runtime/tools/build.pl b/runtime/tools/build.pl
new file mode 100755
index 0000000..0056498
--- /dev/null
+++ b/runtime/tools/build.pl
@@ -0,0 +1,770 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+# Pragmas.
+use strict;
+use warnings;
+
+# Standard modules.
+use Data::Dumper; # Not actually used, but useful for debugging dumps.
+
+# Enable `libomp/tools/lib/' module directory.
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+# LIBOMP modules.
+use Build;
+use LibOMP;
+use Platform ":vars";
+use Uname;
+use tools;
+
+our $VERSION = "0.017";
+
+# --------------------------------------------------------------------------------------------------
+# Important variables.
+# --------------------------------------------------------------------------------------------------
+
+my $root_dir = $ENV{ LIBOMP_WORK };
+
+my %makefiles = (
+ rtl => cat_file( $root_dir, "src", "makefile.mk" ),
+ timelimit => cat_file( $root_dir, "tools", "src", "timelimit", "makefile.mk" ),
+);
+
+# --------------------------------------------------------------------------------------------------
+# Parse command line.
+# --------------------------------------------------------------------------------------------------
+
+# Possible options.
+# * targets: comma separated list of targets the option has meaning for. For example,
+# "version" option (4 or 5) has a meaning only for "rtl" target, while "mode" option has
+# meaning for all targets.
+# * base: If base is true this is a base option. All the possible values of base options are
+# iterated if "--all" option is specified. If base is 0, this is an extra option.
+# * params: A hash of possible option values. "*" denotes default option value. For example,
+# if "versio" option is not specified, "--version=5" will be used implicitly.
+# * suffux: Only for extra options. Subroutine returning suffix for build and output
+# directories.
+my $opts = {
+ "target" => { targets => "", base => 1, parms => { map( ( $_ => "" ), keys( %makefiles ) ), rtl => "*" }, },
+ "version" => { targets => "rtl", base => 1, parms => { 5 => "*", 4 => "" }, },
+ "lib-type" => { targets => "rtl", base => 1, parms => { normal => "*", stubs => "" }, },
+ "link-type" => { targets => "rtl", base => 1, parms => { dynamic => "*", static => "" }, },
+ "target-compiler" => { targets => "rtl,dsl", base => 0, parms => { 12 => "*", 11 => "" }, suffix => sub { $_[ 0 ]; } },
+ "mode" => { targets => "rtl,dsl,timelimit", base => 0, parms => { release => "*", diag => "", debug => "" }, suffix => sub { substr( $_[ 0 ], 0, 3 ); } },
+ "omp-version" => { targets => "rtl", base => 0, parms => { 40 => "*", 30 => "", 25 => "" }, suffix => sub { $_[ 0 ]; } },
+ "coverage" => { targets => "rtl", base => 0, parms => { off => "*", on => "" }, suffix => sub { $_[ 0 ] eq "on" ? "c1" : "c0"; } },
+ "tcheck" => { targets => "rtl", base => 0, parms => { 0 => "*", 1 => "", 2 => "" }, suffix => sub { "t" . $_[ 0 ]; } },
+ "mic-arch" => { targets => "rtl", base => 0, parms => { knf => "*", knc => "", knl => "" }, suffix => sub { $_[ 0 ]; } },
+ "mic-os" => { targets => "rtl", base => 0, parms => { bsd => "*", lin => "" }, suffix => sub { $_[ 0 ]; } },
+ "mic-comp" => { targets => "rtl", base => 0, parms => { native => "*", offload => "" }, suffix => sub { substr( $_[ 0 ], 0, 3 ); } },
+};
+my $synonyms = {
+ "debug" => [ qw{ dbg debg } ],
+};
+# This array specifies order of options to process, so it cannot be initialized with keys( %$opts ).
+my @all_opts = qw{ target version lib-type link-type target-compiler mode omp-version coverage tcheck mic-arch mic-os mic-comp };
+# This is the list of base options.
+my @base_opts = grep( $opts->{ $_ }->{ base } == 1, @all_opts );
+# This is the list of extra options.
+my @extra_opts = grep( $opts->{ $_ }->{ base } == 0, @all_opts );
+
+sub suffix($$$) {
+ my ( $opt, $value, $skip_if_default ) = @_;
+ my $suffix = "";
+ if ( not $skip_if_default or $value ne $opts->{ $opt }->{ dflt } ) {
+ $suffix = $opts->{ $opt }->{ suffix }->( $value );
+ }; # if
+ return $suffix;
+}; # sub suffix
+
+my $scuts = {}; # Shortcuts. Will help to locate proper item in $opts.
+foreach my $opt ( keys( %$opts ) ) {
+ foreach my $parm ( keys( %{ $opts->{ $opt }->{ parms } } ) ) {
+ if ( $parm !~ m{\A(?:[012]|on|off)\z} ) {
+ $scuts->{ $parm } = $opts->{ $opt };
+ }; # if
+ if ( $opts->{ $opt }->{ parms }->{ $parm } eq "*" ) {
+ $opts->{ $opt }->{ dflt } = $parm;
+ }; # if
+ }; # foreach $parm
+}; # foreach $opt
+
+sub parse_option(@) {
+ # This function is called to process every option. $name is option name, $value is option value.
+ # For boolean options $value is either 1 or 0,
+ my ( $name, $value ) = @_;
+ if ( $name eq "all" or $name eq "ALL" ) {
+ foreach my $opt ( keys( %$opts ) ) {
+ if ( $opts->{ $opt }->{ base } or $name eq "ALL" ) {
+ foreach my $parm ( keys( %{ $opts->{ $opt }->{ parms } } ) ) {
+ $opts->{ $opt }->{ parms }->{ $parm } = 1;
+ }; # foreach $parm
+ }; # if
+ }; # foreach $opt
+ return;
+ }; # if
+ if ( exists( $opts->{ $name } ) ) {
+ # Suppose it is option with explicit value, like "target=normal".
+ if ( $value eq "all" ) {
+ foreach my $parm ( keys( %{ $opts->{ $name }->{ parms } } ) ) {
+ $opts->{ $name }->{ parms }->{ $parm } = 1;
+ }; # foreach
+ return;
+ } elsif ( exists( $opts->{ $name }->{ parms }->{ $value } ) ) {
+ $opts->{ $name }->{ parms }->{ $value } = 1;
+ return;
+ } elsif ( $value eq "" and exists( $opts->{ $name }->{ parms }->{ on } ) ) {
+ $opts->{ $name }->{ parms }->{ on } = 1;
+ return;
+ } else {
+ cmdline_error( "Illegal value of \"$name\" option: \"$value\"" );
+ }; # if
+ }; # if
+ # Ok, it is not an option with explicit value. Try to treat is as a boolean option.
+ if ( exists( $scuts->{ $name } ) ) {
+ ( $value eq "1" or $value eq "0" ) or die "Internal error; stopped";
+ $scuts->{ $name }->{ parms }->{ $name } = $value;
+ return;
+ }; # if
+ # No, it is not a valid option at all.
+ cmdline_error( "Illegal option: \"$name\"" );
+}; # sub parse_option
+
+my $clean = 0;
+my $clean_common = 0;
+my $clobber = 0;
+my $test_deps = 1;
+my $test_touch = 1;
+my @goals;
+
+sub synonyms($) {
+ my ( $opt ) = @_;
+ return exists( $synonyms->{ $opt } ) ? "|" . join( "|", @{ $synonyms->{ $opt } } ) : "";
+}; # sub synonyms
+
+my @specs = (
+ map( ( "$_" . synonyms( $_ ) . "=s" => \&parse_option ), keys( %$opts ) ),
+ map( ( "$_" . synonyms( $_ ) . "!" => \&parse_option ), keys( %$scuts ) ),
+);
+my $answer;
+get_options(
+ @specs,
+ Platform::target_options(),
+ "all" => \&parse_option,
+ "ALL" => \&parse_option,
+ "answer=s" => \$answer,
+ "test-deps!" => \$test_deps,
+ "test-touch!" => \$test_touch,
+ "version|ver:s" =>
+ sub {
+ # It is a tricky option. It specifies library version to build and it is also a standard
+ # option to request tool version.
+ if ( $_[ 1 ] eq "" ) {
+ # No arguments => version request.
+ print( "$tool version $VERSION\n" );
+ exit( 0 );
+ } else {
+ # Arguments => version to build.
+ parse_option( @_ )
+ };
+ },
+);
+@goals = @ARGV;
+if ( grep( $_ eq "clobber", @goals ) ) {
+ $clobber = 1;
+}; # if
+if ( grep( $_ eq "clean", @goals ) ) {
+ $clean = 1;
+}; # if
+
+# Ok, now $opts is fulfilled with 0, 1 (explicitly set by the user) and "" and "*" (original
+# values). In each option at least one 1 should be present (otherwise there is nothing to build).
+foreach my $opt ( keys( %$opts ) ) {
+ if ( not grep( $_ eq "1", values( %{ $opts->{ $opt }->{ parms } } ) ) ) {
+ # No explicit "1" found. Enable default choice by replacing "*" with "1".
+ foreach my $parm ( keys( %{ $opts->{ $opt }->{ parms } } ) ) {
+ if ( $opts->{ $opt }->{ parms }->{ $parm } eq "*" ) {
+ $opts->{ $opt }->{ parms }->{ $parm } = 1;
+ }; # if
+ }; # foreach $parm
+ }; # if
+}; # foreach $opt
+
+# Clear $opts. Leave only "1".
+foreach my $opt ( keys( %$opts ) ) {
+ foreach my $parm ( keys( %{ $opts->{ $opt }->{ parms } } ) ) {
+ if ( $opts->{ $opt }->{ parms }->{ $parm } ne "1" ) {
+ delete( $opts->{ $opt }->{ parms }->{ $parm } );
+ }; # if
+ }; # foreach $parm
+}; # foreach $opt
+
+# --------------------------------------------------------------------------------------------------
+# Fill job queue.
+# --------------------------------------------------------------------------------------------------
+
+sub enqueue_jobs($$@);
+sub enqueue_jobs($$@) {
+ my ( $jobs, $set, @rest ) = @_;
+ if ( @rest ) {
+ my $opt = shift( @rest );
+ if (
+ exists( $set->{ target } )
+ and
+ $opts->{ $opt }->{ targets } !~ m{(?:\A|,)$set->{ target }(?:,|\z)}
+ ) {
+ # This option does not have meananing for the target,
+ # do not iterate, just use default value.
+ enqueue_jobs( $jobs, { $opt => $opts->{ $opt }->{ dflt }, %$set }, @rest );
+ } else {
+ foreach my $parm ( sort( keys( %{ $opts->{ $opt }->{ parms } } ) ) ) {
+ enqueue_jobs( $jobs, { $opt => $parm, %$set }, @rest );
+ }; # foreach $parm
+ }; # if
+ } else {
+ my $makefile = $makefiles{ $set->{ target } };
+ my @base = map( substr( $set->{ $_ }, 0, 3 ), @base_opts );
+ my @extra = map( suffix( $_, $set->{ $_ }, 0 ), @extra_opts );
+ my @ex = grep( $_ ne "", map( suffix( $_, $set->{ $_ }, 1 ), @extra_opts ) );
+ # Shortened version of @extra -- only non-default values.
+ my $suffix = ( @extra ? "." . join( ".", @extra ) : "" );
+ my $knights = index( $suffix, "kn" ) - 1;
+ if ( $target_platform !~ "lrb" and $knights > 0 ) {
+ $suffix = substr( $suffix, 0, $knights );
+ }
+ my $suf = ( @ex ? "." . join( ".", @ex ) : "" );
+ # Shortened version of $siffix -- only non-default values.
+ my $build_dir = join( "-", $target_platform, join( "_", @base ) . $suffix, Uname::host_name() );
+ my $out_arch_dir = cat_dir( $ENV{ LIBOMP_EXPORTS }, $target_platform . $suf );
+ my $out_cmn_dir = cat_dir( $ENV{ LIBOMP_EXPORTS }, "common" );
+ push(
+ @$jobs,
+ {
+ makefile => $makefile,
+ make_args => [
+ "os=" . $target_os,
+ "arch=" . $target_arch,
+ "MIC_OS=" . $set->{ "mic-os" },
+ "MIC_ARCH=" . $set->{ "mic-arch" },
+ "MIC_COMP=" . $set->{ "mic-comp" },
+ "date=" . Build::tstr( $Build::start ),
+ "TEST_DEPS=" . ( $test_deps ? "on" : "off" ),
+ "TEST_TOUCH=" . ( $test_touch ? "on" : "off" ),
+ "CPLUSPLUS=on",
+ "COVERAGE=" . $set->{ coverage },
+ # Option "mode" controls 3 make flags:
+ # debug => Full debugging : diagnostics, debug info, no optimization.
+ # diag => Only diagnostics : diagnostics, debug info, optimization.
+ # release => Production build : no diagnostics, no debug info, optimization.
+ "DEBUG_INFO=" . ( $set->{ mode } ne "release" ? "on" : "off" ),
+ "DIAG=" . ( $set->{ mode } ne "release" ? "on" : "off" ),
+ "OPTIMIZATION=" . ( $set->{ mode } ne "debug" ? "on" : "off" ),
+ "LIB_TYPE=" . substr( $set->{ "lib-type" }, 0, 4 ),
+ "LINK_TYPE=" . substr( $set->{ "link-type" }, 0, 4 ),
+ "OMP_VERSION=" . $set->{ "omp-version" },
+ "USE_TCHECK=" . $set->{ tcheck },
+ "VERSION=" . $set->{ version },
+ "TARGET_COMPILER=" . $set->{ "target-compiler" },
+ "suffix=" . $suf,
+ @goals,
+ ],
+ build_dir => $build_dir
+ }
+ ); # push
+ }; # if
+}; # sub enqueue_jobs
+
+my @jobs;
+enqueue_jobs( \@jobs, {}, @all_opts );
+
+# --------------------------------------------------------------------------------------------------
+# Do the work.
+# --------------------------------------------------------------------------------------------------
+
+my $exit = 0;
+
+Build::init();
+
+if ( $clobber ) {
+ my @dirs = ( $ENV{ LIBOMP_TMP }, $ENV{ LIBOMP_EXPORTS }, cat_dir( $root_dir, "tools", "bin" ) );
+ my $rc = 0;
+ question(
+ "Clobber " . join( ", ", map( "\"" . Build::shorter( $_ ) . "\"", @dirs ) ) . " dirs? ",
+ $answer,
+ qr{\A(y|yes|n|no)\z}i
+ );
+ if ( $answer =~ m{\Ay}i ) {
+ info( "Clobbering..." );
+ $rc = Build::clean( @dirs );
+ info( Build::rstr( $rc ) );
+ }; # if
+ if ( $rc != 0 ) {
+ $exit = 3;
+ }; # if
+} else { # Build or clean.
+ if ( @jobs ) {
+ my $total = @jobs; # Total number of jobs.
+ my $n = 0; # Current job number.
+ Build::progress( "", "" ); # Output empty line to log file.
+ my $goals = join( " ", @goals );
+ Build::progress( "Goals", $goals eq "" ? "(all)" : $goals );
+ Build::progress( "Configurations", scalar( @jobs ) );
+ foreach my $job ( @jobs ) {
+ ++ $n;
+ my $base = get_file( $job->{ build_dir } );
+ Build::progress( "Making", "%3d of %3d : %s", $n, $total, $base );
+ $job->{ rc } = Build::make( $job, $clean, sprintf( "%d/%d", $n, $total ) );
+ }; # my $job
+ my $failures = Build::summary();
+ if ( $failures > 0 ) {
+ $exit = 3;
+ }; # if
+ } else {
+ info( "Nothing to do." );
+ }; # if
+}; # if
+
+# And exit.
+exit( $exit );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+
+B<build.pl> -- Build one or more configurations of OMP RTL libraries.
+
+=head1 SYNOPSIS
+
+B<build.pl> I<option>... [B<-->] I<make-option>... I<variable>... I<goal>...
+
+=head1 OPTIONS
+
+=over
+
+=item B<--all>
+
+Build all base configurations.
+
+=item B<--ALL>
+
+Build really all configurations, including extra ones.
+
+=item B<--answer=>I<str>
+
+Use specified string as default answer to all questions.
+
+=item B<--architecture=>I<arch>
+
+Specify target architecture to build. Default is architecture of host machine. I<arch> can be C<32>,
+C<32e>, or one of known aliases like C<IA32>.
+
+If architecture is not specified explicitly, value of LIBOMP_ARCH environment variable is used.
+If LIBOMP_ARCH is not defined, host architecture detected.
+
+=item B<--os=>I<os>
+
+Specify target OS. Default is OS of host machine. I<os> can be C<lin>, C<lrb>, C<mac>, C<win>,
+or one of known aliases like C<Linux>, C<WinNT>, etc.
+
+=item B<--mic-os=>I<os>
+
+Specify OS on Intel(R) Many Integrated Core Architecture card. Default is C<bsd>. I<os> can be C<bsd>, C<lin>.
+
+=item B<--mic-arch=>I<arch>
+
+Specify architecture of Intel(R) Many Integrated Core Architecture card. Default is C<knf>. I<arch> can be C<knf>, C<knc>, C<knl>.
+
+=item B<--mic-comp=>I<compiler-type>
+
+Specify whether the Intel(R) Many Integrated Core Compiler is native or offload. Default is C<native>.
+I<compiler-type> can be C<native> or C<offload>.
+
+=item B<-->[B<no->]B<test-deps>
+
+Enable or disable C<test-deps>. The test runs in any case, but result of disabled test is ignored.
+By default, test is enabled.
+
+=item B<-->[B<no->]B<test-touch>
+
+Enable or disable C<test-touch>. The test runs in any case, but result of disabled test is ignored.
+By default, test is enabled.
+
+=item Base Configuration Selection Options
+
+=over
+
+=item B<--target=>I<target>
+
+Build specified target, either C<rtl> (OMP Runtime Library; default),
+or C<timelimit> (program used in testing), or C<all>.
+
+=item B<--lib-type=>I<lib>
+
+Build specified library, either C<normal> (default), or C<stubs>, or C<all>.
+
+=item B<--link-type=>I<type>
+
+Build specified link type, either C<dynamic> (default) or C<all>.
+
+=back
+
+=item Extra Configuration Selection Options
+
+=over
+
+=item B<--cover=>I<switch>
+
+Build for code coverage data collection. I<switch> can be C<off> (default), C<on>
+or C<all>.
+
+=item B<--mode=>I<mode>
+
+Build library of specified I<mode>, either C<debug>, C<diag>, C<release> (default), or C<all>.
+Mode controls 3 features:
+
+ ---------------------------------------------------
+ feature/mode debug diag release
+ ---------------------------------------------------
+ debug info o o
+ diagnostics (asserts, traces) o o
+ code optimization o o
+ ---------------------------------------------------
+
+=item B<--target-compiler=>I<version>
+
+Build files for specified target compiler, C<11> or C<12>.
+
+=back
+
+=item Shortcuts
+
+If option with C<no> prefix is used, corresponding configuration will B<not> be built.
+Useful for excluding some configurations if one or more other options specified with C<all>
+value (see Examples).
+
+=over
+
+=item B<-->[B<no>]B<11>
+
+Build files for compiler C<11>.
+
+=item B<-->[B<no>]B<12>
+
+Build files for compiler C<12>.
+
+=item B<-->[B<no>]B<debug>
+
+=item B<-->[B<no>]B<debg>
+
+=item B<-->[B<no>]B<dbg>
+
+Build debuggable library.
+
+=item B<-->[B<no>]B<diag>
+
+Build library with diagnostics enabled.
+
+=item B<-->[B<no>]B<dynamic>
+
+Build dynamic library (default).
+
+=item B<-->[B<no>]B<normal>
+
+Build normal library (default).
+
+=item B<-->[B<no>]B<release>
+
+Build release library (default).
+
+=item B<-->[B<no>]B<rtl>
+
+Build OMP RTL (default).
+
+=item B<-->[B<no>]B<stubs>
+
+Build stubs library.
+
+=item B<-->[B<no>]B<timelimit>
+
+Build timelimit utility program.
+
+=back
+
+=item Standard Options
+
+=over
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full help message and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--usage>
+
+Print very short usage message and exit.
+
+=item B<--verbose>
+
+Do print informational messages.
+
+=item B<--version>
+
+Print program version and exit.
+
+=item B<--quiet>
+
+Work quiet, do not print informational messages.
+
+=back
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<make-option>
+
+Any option for makefile, for example C<-k> or C<-n>. If you pass some options to makefile, C<-->
+delimiter is mandatory, otherwise C<build.pl> processes all the options internally.
+
+=item I<variable>
+
+Define makefile variable in form I<name>B<=>I<value>. Most makefile capabilities are
+accessible through C<build.pl> options, so there is no need in defining make variables in command
+line.
+
+=item I<goal>
+
+Makefile goal to build (or clean).
+
+=over
+
+=item B<all>
+
+Build C<lib>, C<tests>, C<inc>.
+
+=item B<common>
+
+Build common (architecture-independent) files. Common files are not configuration-dependent, so
+there is no point in building it for more than one configuration (thought it is harmless).
+However, do not build common files on many machines simultaneously.
+
+=item B<clean>
+
+Delete the export files and clean build directory of configuration(s) specified by options. Note
+that C<clean> goal cannot be mixed with other goals (except for C<clean-common>).
+
+=item B<clean-common>
+
+Delete the common files in F<exports/> directory.
+
+=item B<clobber>
+
+Clean F<export/> and F<tmp/> directories. If C<clobber> is specified, other goals and/or options
+do not matter.
+
+Note: Clobbering is potentialy dangerous operation, because it deletes content of directory
+pointed by If C<LIBOMP_TMP> environment variable, so C<build.pl> asks a confirmation before
+clobbering. To suppress the question, use option C<--answer=yes>.
+
+=item B<fat>
+
+C<mac_32e> only: Build fat libraries for both mac_32 and mac_32e. Should be run when C<lib>
+goal is built on both C<mac_32> and C<mac_32e>.
+
+=item I<file.o>
+
+(Windows* OS: I<file.obj>) Build specified object file only.
+
+=item I<file.i>
+
+Create preprocessed source file.
+
+=item B<force-tests>
+
+Force performing tests.
+
+=item B<force-test-deps>
+
+Force performing test-deps.
+
+=item B<force-test-instr>
+
+Force performing test-instr.
+
+=item B<force-test-relo>
+
+Force performing test-relo.
+
+=item B<force-test-touch>
+
+Force performing test-touch.
+
+=item B<inc>
+
+Build Fortran include files, omp_lib.h, omp_lib.mod and omp_lib_kinds.mod.
+
+=item B<lib>
+
+Build library (on Windows* OS in case of dynamic linking, it also builds import library).
+
+=item B<tests>
+
+Perform tests: C<test-deps>, C<test-instr>, C<test-relo>, and C<test-touch>.
+
+=item B<test-deps>
+
+Check the library dependencies.
+
+=item B<test-instr>
+
+Intel(R) Many Integrated Core Architecture only: check the library does not contain undesired instructions.
+
+=item B<test-relo>
+
+Linux* OS with dynamic linking only: check the library does not contain position-dependent
+code.
+
+=item B<test-touch>
+
+Build a very simple application with native compiler (GNU on Linux* OS and OS X*, MS
+on Windows* OS), check it does not depend on C<libirc> library, and run it.
+
+=back
+
+=back
+
+=head1 DESCRIPTION
+
+C<build.pl> constructs the name of a build directory, creates the directory if it
+does not exist, changes to it, and runs make to build the goals in specified configuration.
+If more than one configuration are specified in command line C<build.pl> builds them all.
+
+Being run with C<clean> goal, C<build.pl> does not build but deletes export files and
+cleans build directories of configuration specified by other options. For example,
+C<build.pl --all clean> means "clean build directories for all configurations",
+it does B<not> mean "clean then build all".
+
+C<clear-common> goal deletes common files in F<exports/> directory.
+Since common files are really common and not architecture and/or configuration dependent,
+there are no much meaning in combining C<clear-common> with configuration selection options.
+For example, C<build.pl --all clean-common> deletes the same files 13 times.
+However, it does not hurt and can be used in conjunction with C<clear> goal.
+
+C<clobber> goal instructs C<build.pl> to clean exports and all build
+directories, e. g. clean everything under F<exports/> and F<tmp/> directories.
+
+Logs are saved automatically, there is no need in explicit output redirection.
+Log file for each particular configuration is named F<build.log> and located in build directory.
+Summary log file (just result of each configuration) is saved in F<tmp/> directory.
+
+Log files are never overwritten. C<build.pl> always appends output to log files.
+However (obviously), C<clear> deletes log file for cleared configurations,
+and C<clobber> deletes all summary log files.
+
+=head2 Environment Variables
+
+=over
+
+=item B<LIBOMP_ARCH>
+
+Specifies target architecture. If not present, host architecture is used. Environment variable may
+be overriden by C<--architecture> command line option.
+
+=item B<LIBOMP_EXPORTS>
+
+Specifies directory for output files. If not set, C<$LIBOMP_WORK/exports/> used by default.
+
+=item B<LIBOMP_OS>
+
+Specifies target OS. If not present, host OS is used. Environment variable may
+be overriden by C<--os> command line option.
+
+=item B<LIBOMP_TMP>
+
+Directory for temporary files. C<build.pl> creates build directories there. If not set,
+C<$LIBOMP_WORK/tmp/> used by default.
+
+On Windows* OS F<tmp/> directory on local drive speeds up the build process.
+
+=item B<LIBOMP_WORK>
+
+Root of libomp directory tree, contains F<src/>, F<tools/>, and F<exports/> subdirs.
+If not set, C<build.pl> guesses the root dir (it is a parent of dir containing C<build.pl>).
+
+Note: Guessing it not reliable. Please set C<LIBOMP_WORK> environment variable appropriately.
+
+=back
+
+=head1 EXAMPLES
+
+=head2 Development
+
+Build normal (performance) dynamic library for debugging:
+
+ $ build.pl --debug
+
+Build all libraries (normal, stub; dynamic RTL) for debugging:
+
+ $ build.pl --all --debug
+
+Do a clean build for all:
+
+ $ build.pl --all --debug clean && build.pl --all --debug
+
+Debugging libraries are saved in F<exports/I<platform>.deb/>.
+
+=head2 Promotion
+
+=over
+
+=item 1
+
+Clobber everything; on one machine:
+
+ $ build.pl clobber
+
+=item 2
+
+Build common headers, on one machine:
+
+ $ build.pl common
+
+=item 3
+
+Build all platform-dependent files, on all machines:
+
+ $ build.pl --all
+
+=item 4
+
+Build OS X* universal (fat) libraries, on C<mac_32e>:
+
+ $ build.pl fat
+
+=back
+
+=cut
+
+# end of file #
diff --git a/runtime/tools/check-depends.pl b/runtime/tools/check-depends.pl
new file mode 100755
index 0000000..cce111f
--- /dev/null
+++ b/runtime/tools/check-depends.pl
@@ -0,0 +1,489 @@
+#!/usr/bin/env perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+use Platform ":vars";
+
+our $VERSION = "0.005";
+
+# --------------------------------------------------------------------------------------------------
+# Ouput parse error.
+# $tool -- Name of tool.
+# @bulk -- Output of the tool.
+# $n -- Number of line caused parse error.
+sub parse_error($\@$) {
+ my ( $tool, $bulk, $n ) = @_;
+ my @bulk;
+ for ( my $i = 0; $i < @$bulk; ++ $i ) {
+ push( @bulk, ( $i == $n ? ">>> " : " " ) . $bulk->[ $i ] );
+ }; # for $i
+ runtime_error( "Fail to parse $tool output:", @bulk, "(eof)" );
+}; # sub parse_error
+
+
+# --------------------------------------------------------------------------------------------------
+# Linux* OS version of get_deps() parses output of ldd:
+#
+# $ ldd libname.so
+# libc.so.6 => /lib64/libc.so.6 (0x00002b60fedd8000)
+# libdl.so.2 => /lib64/libdl.so.2 (0x00002b60ff12b000)
+# libpthread.so.0 => /lib64/libpthread.so.0 (0x00002b60ff32f000)
+# /lib64/ld-linux-x86-64.so.2 (0x0000003879400000)
+#
+# Note: ldd printd all the dependencies, direct and indirect. (For example, if specified library
+# requires libdl.so, and libdl.so requires /lib/ld-linux.so, ldd prints both libdl.so and
+# /lib/ld-linux.so). If you do not want indirect dependencies, look at readelf tool.
+#
+sub get_deps_ldd($) {
+
+ my $lib = shift ( @_ );
+ my $tool = "ldd";
+ my @bulk;
+ my @deps;
+
+ execute( [ $tool, $lib ], -stdout => \@bulk );
+ debug( @bulk, "(eof)" );
+
+ foreach my $i ( 0 .. @bulk - 1 ) {
+ my $line = $bulk[ $i ];
+ if ( $line !~ m{^\s*(?:([_a-z0-9.+-/]*)\s+=>\s+)?([_a-z0-9.+-/]*)\s+\(0x[0-9a-z]*\)$}i ) {
+ parse_error( $tool, @bulk, $i );
+ }; # if
+ my $dep = ( defined( $1 ) ? $1 : $2 );
+ push( @deps, $dep );
+ }; # foreach $i
+
+ return @deps;
+
+}; # sub get_deps_ldd
+
+
+# --------------------------------------------------------------------------------------------------
+# Another Linux* OS version of get_deps() parses output of readelf:
+#
+# $ readelf -d exports/lin_32e/lib/libiomp5.so
+#
+# Dynamic segment at offset 0x87008 contains 24 entries:
+# Tag Type Name/Value
+# 0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
+# 0x0000000000000001 (NEEDED) Shared library: [libdl.so.2]
+# 0x0000000000000001 (NEEDED) Shared library: [libpthread.so.0]
+# 0x000000000000000e (SONAME) Library soname: [libiomp5.so]
+# 0x000000000000000d (FINI) 0x51caa
+# 0x0000000000000004 (HASH) 0x158
+# 0x0000000000000005 (STRTAB) 0x9350
+# ...
+#
+# Note: In contrast to ldd, readlef shows only direct dependencies.
+#
+sub get_deps_readelf($) {
+
+ my $file = shift ( @_ );
+ my $tool = "readelf";
+ my @bulk;
+ my @deps;
+
+ execute( [ $tool, "-d", $file ], -stdout => \@bulk );
+ debug( @bulk, "(eof)" );
+
+ my $i = 0;
+ # Parse header.
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} )
+ or parse_error( $tool, @bulk, $i );
+ ++ $i;
+ if ( $i == @bulk - 1 and $bulk[ $i ] =~ m{^There is no dynamic section in this file\.\s*$} ) {
+ # This is not dynamic executable => no dependencies.
+ return @deps;
+ }; # if
+ ( $i < @bulk and $bulk[ $i ] =~ m{^Dynamic (?:segment|section) at offset 0x[0-9a-f]+ contains \d+ entries:\s*$} )
+ or parse_error( $tool, @bulk, $i );
+ ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\s*Tag\s+Type\s+Name/Value\s*$} )
+ or parse_error( $tool, @bulk, $i );
+ ++ $i;
+ # Parse body.
+ while ( $i < @bulk ) {
+ my $line = $bulk[ $i ];
+ if ( $line !~ m{^\s*0x[0-9a-f]+\s+\(([_A-Z0-9]+)\)\s+(.*)\s*$}i ) {
+ parse_error( $tool, @bulk, $i );
+ }; # if
+ my ( $type, $value ) = ( $1, $2 );
+ if ( $type eq "NEEDED" ) {
+ if ( $value !~ m{\AShared library: \[(.*)\]\z} ) {
+ parse_error( $tool, @bulk, $i );
+ }; # if
+ my $dep = $1;
+ push( @deps, $dep );
+ }; # if
+ ++ $i;
+ }; # foreach $i
+
+ return @deps;
+
+}; # sub get_deps_readelf
+
+
+# --------------------------------------------------------------------------------------------------
+# OS X* version of get_deps() parses output of otool:
+#
+# $ otool -L libname.dylib
+# exports/mac_32/lib.thin/libiomp5.dylib:
+# libiomp5.dylib (compatibility version 5.0.0, current version 5.0.0)
+# /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 88.1.3)
+#
+sub get_deps_otool($) {
+
+ my $file = shift ( @_ );
+ my $name = get_file( $file );
+ my $tool = "otool";
+ my @bulk;
+ my @deps;
+
+ if ( $target_arch eq "32e" ) {
+ # On older (Tiger) systems otool does not recognize 64-bit binaries, so try to locate
+ # otool64.
+ my $path = which( "otool64" );
+ if ( defined ( $path ) ) {
+ $tool = "otool64";
+ }; # if
+ }; # if
+
+ execute( [ $tool, "-L", $file ], -stdout => \@bulk );
+ debug( @bulk, "(eof)" );
+
+ my $i = 0;
+ # Parse the first one or two lines separately.
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\Q$file\E:$} )
+ or parse_error( $tool, @bulk, $i );
+ ++ $i;
+ if ( $name =~ m{\.dylib\z} ) {
+ # In case of dynamic library otool print the library itself as a dependent library.
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\s+\Q$name\E\s+\(compatibility version.*\)$} )
+ or parse_error( $tool, @bulk, $i );
+ ++ $i;
+ }; # if
+
+ # Then parse the rest.
+ while ( $i < @bulk ) {
+ my $line = $bulk[ $i ];
+ if ( $line !~ m/^\s*(.*)\s+\(compatibility version\s.*\)$/ ) {
+ parse_error( $tool, @bulk, $i );
+ }; # if
+ my ( $dep ) = ( $1 );
+ push( @deps, $dep );
+ ++ $i;
+ }; # while
+
+ return @deps;
+
+}; # sub get_deps_otool
+
+
+# --------------------------------------------------------------------------------------------------
+# Windows* OS version of get_deps() parses output of link:
+#
+# > link -dump -dependents libname.dll
+# Microsoft (R) COFF/PE Dumper Version 8.00.40310.39
+# Copyright (C) Microsoft Corporation. All rights reserved.
+# Dump of file S:\Projects.OMP\users\omalyshe\omp\libomp\exports\win_64\lib\libiomp5md.dll
+# File Type: DLL
+# Image has the following dependencies:
+# KERNEL32.dll
+# Summary
+# C000 .data
+# 6000 .pdata
+# 18000 .rdata
+# ...
+#
+# > link -dump -directives libname.lib
+# Microsoft (R) COFF/PE Dumper Version 8.00.40310.39
+# Copyright (C) Microsoft Corporation. All rights reserved.
+# Dump of file S:\Projects.OMP\users\omalyshe\omp\libomp\exports\win_32e\lib\libimp5mt.lib
+# File Type: LIBRARY
+# Linker Directives
+# -----------------
+# -defaultlib:"uuid.lib"
+# -defaultlib:"uuid.lib"
+# .....
+# Summary
+# 3250 .bss
+# 3FBC .data
+# 34 .data1
+# ....
+sub get_deps_link($) {
+
+ my ( $lib ) = @_;
+ my $tool = "link";
+ my @bulk;
+ my @deps;
+
+ my $ext = lc( get_ext( $lib ) );
+ if ( $ext !~ m{\A\.(?:lib|dll|exe)\z}i ) {
+ runtime_error( "Incorrect file is specified: `$lib'; only `lib', `dll' or `exe' file expected" );
+ }; # if
+
+ execute(
+ [ $tool, "/dump", ( $ext eq ".lib" ? "/directives" : "/dependents" ), $lib ],
+ -stdout => \@bulk
+ );
+
+ debug( @bulk, "(eof)" );
+
+ my $i = 0;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^Microsoft \(R\) COFF\/PE Dumper Version.*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^Copyright \(C\) Microsoft Corporation\..*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^Dump of file\s\Q$lib\E$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^File Type:\s(.*)$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+ ( $i < @bulk and $bulk[ $i ] =~ m{^\s*$} ) or parse_error( $tool, @bulk, $i ); ++ $i;
+
+ if ( $ext eq ".lib" ) {
+
+ my %deps;
+ while ( $i < @bulk ) {
+ my $line = $bulk[ $i ];
+ if ( 0 ) {
+ } elsif ( $line =~ m{^\s*[-/]defaultlib\:(.*)\s*$}i ) {
+ my $dep = $1;
+ # Normalize library name:
+ $dep = lc( $1 ); # Convert to lower case.
+ $dep =~ s{\A"(.*)"\z}{$1}; # Drop surrounding quotes (if any).
+ $dep =~ s{\.lib\z}{}; # Drop .lib suffix (if any).
+ $deps{ $dep } = 1;
+ } elsif ( $line =~ m{^\s*Linker Directives\s*$} ) {
+ } elsif ( $line =~ m{^\s*-+\s*$} ) {
+ } elsif ( $line =~ m{^\s*/alternatename\:.*$} ) {
+ } elsif ( $line =~ m{^\s*$} ) {
+ } elsif ( $line =~ m{^\s*Summary\s*$} ) {
+ last;
+ } else {
+ parse_error( $tool, @bulk, $i );
+ }; # if
+ ++ $i;
+ } # while
+ @deps = keys( %deps );
+
+ } else {
+
+ ( $i < @bulk and $bulk[ $i ] =~ m{\s*Image has the following dependencies\:$} )
+ or parse_error( $tool, @bulk, $i );
+ ++ $i;
+ while ( $i < @bulk ) {
+ my $line = $bulk[ $i ];
+ if ( 0 ) {
+ } elsif ( $line =~ m{^\s*$} ) {
+ # Ignore empty lines.
+ } elsif ( $line =~ m{^\s*(.*\.dll)$}i ) {
+ my $dep = lc( $1 );
+ push( @deps, $dep );
+ } elsif ( $line =~ m{^\s*Summary$} ) {
+ last;
+ } else {
+ parse_error( $tool, @bulk, $i );
+ }; # if
+ ++ $i;
+ }; # while
+
+ }; # if
+
+ return @deps;
+
+}; # sub get_deps_link
+
+
+# --------------------------------------------------------------------------------------------------
+# Main.
+# --------------------------------------------------------------------------------------------------
+
+# Parse command line.
+my $expected;
+my $bare;
+Getopt::Long::Configure( "permute" );
+get_options(
+ Platform::target_options(),
+ "bare" => \$bare,
+ "expected=s" => \$expected,
+);
+my @expected;
+if ( defined( $expected ) ) {
+ if ( $expected ne "none" ) {
+ @expected = sort( split( ",", $expected ) );
+ if ( $target_os eq "win" ) {
+ @expected = map( lc( $_ ), @expected );
+ }; # if
+ }; # if
+}; # if
+if ( @ARGV < 1 ) {
+ cmdline_error( "Specify a library name to check for dependencies" );
+}; # if
+if ( @ARGV > 1 ) {
+ cmdline_error( "Too many arguments" );
+}; # if
+my $lib = shift( @ARGV );
+if ( not -e $lib ){
+ runtime_error( "Specified file does not exist: \"$lib\"" );
+}; # if
+
+# Select appropriate get_deps implementation.
+if ( 0 ) {
+} elsif ( $target_os eq "lin" or $target_os eq "lrb" ) {
+ *get_deps = \*get_deps_readelf;
+} elsif ( $target_os eq "mac" ) {
+ *get_deps = \*get_deps_otool;
+} elsif ( $target_os eq "win" ) {
+ *get_deps = \*get_deps_link;
+} else {
+ runtime_error( "OS \"$target_os\" not supported" );
+}; # if
+
+# Do the work.
+my @deps = sort( get_deps( $lib ) );
+if ( $bare ) {
+ print( map( "$_\n", @deps ) );
+} else {
+ info( "Dependencies:", @deps ? map( " $_", @deps ) : "(none)" );
+}; # if
+if ( defined( $expected ) ) {
+ my %deps = map( ( $_ => 1 ), @deps );
+ foreach my $dep ( @expected ) {
+ delete( $deps{ $dep } );
+ }; # foreach
+ my @unexpected = sort( keys( %deps ) );
+ if ( @unexpected ) {
+ runtime_error( "Unexpected dependencies:", map( " $_", @unexpected ) );
+ }; # if
+}; # if
+
+exit( 0 );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<check-depends.pl> -- Check dependencies for a specified library.
+
+=head1 SYNOPSIS
+
+B<check-depends.pl> I<OPTIONS>... I<library>
+
+=head1 DESCRIPTION
+
+C<check-depends.pl> finds direct dependencies for a specified library. List of actual dependencies
+is sorted alphabetically and printed. If list of expected dependencies is specified, the scripts
+checks the library has only allowed dependencies. In case of not expected depndencies the script
+issues error message and exits with non-zero code.
+
+Linux* OS and OS X*: The script finds dependencies only for dymamic libraries. Windows* OS: The script
+finds dependencies for either static or dymamic libraries.
+
+The script uses external tools. On Linux* OS, it runs F<readelf>, on OS X* -- F<otool> (or F<otool64>),
+on Windows* OS -- F<link>.
+
+On Windows* OS dependencies are printed in lower case, case of expected dependencies ignored.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--bare>
+
+Do not use fancy formatting; produce plain, bare output: just a list of libraries,
+a library per line.
+
+=item B<--expected=>I<list>
+
+I<list> is comma-separated list of expected dependencies (or C<none>).
+If C<--expected> option specified, C<check-depends.pl> checks the specified library
+has only expected dependencies.
+
+=item B<--os=>I<str>
+
+Specify target OS (tool to use) manually.
+Useful for cross-build, when host OS is not the same as target OS.
+I<str> should be either C<lin>, C<mac>, or C<win>.
+
+=back
+
+=head2 Standard Options
+
+=over
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full documentation and exit.
+
+=item B<--quiet>
+
+Do not output informational messages.
+
+=item B<--version>
+
+Print version and exit.
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<library>
+
+A name of library to find or check dependencies.
+
+=back
+
+=head1 EXAMPLES
+
+Just print library dependencies (Windows* OS):
+
+ > check-depends.pl exports/win_32/lib/libiomp5.dll
+ check-depends.pl: (i) Dependencies:
+ check-depends.pl: (i) kernel32.dll
+
+Print library dependencies, use bare output (Linux* OS):
+
+ $ check-depends.pl --bare exports/lin_32e/lib/libomp_db.so
+ libc.so.6
+ libdl.so.2
+ libpthread.so.0
+
+Check the library does not have any dependencies (OS X*):
+
+ $ check-depends.pl --expected=none exports/mac_32/lib/libiomp5.dylib
+ check-depends.pl: (i) Dependencies:
+ check-depends.pl: (i) /usr/lib/libSystem.B.dylib
+ check-depends.pl: (x) Unexpected dependencies:
+ check-depends.pl: (x) /usr/lib/libSystem.B.dylib
+ $ echo $?
+ 2
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/check-execstack.pl b/runtime/tools/check-execstack.pl
new file mode 100755
index 0000000..f1417c7
--- /dev/null
+++ b/runtime/tools/check-execstack.pl
@@ -0,0 +1,138 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+
+our $VERSION = "0.002";
+
+sub execstack($) {
+ my ( $file ) = @_;
+ my @output;
+ my @stack;
+ execute( [ "readelf", "-l", "-W", $file ], -stdout => \@output );
+ @stack = grep( $_ =~ m{\A\s*(?:GNU_)?STACK\s+}, @output );
+ if ( not @stack ) {
+ # Interpret missed "STACK" line as error.
+ runtime_error( "$file: No stack segment found; looks like stack would be executable." );
+ }; # if
+ if ( @stack > 1 ) {
+ runtime_error( "$file: More than one stack segment found.", "readelf output:", @output, "(eof)" );
+ }; # if
+ # Typical stack lines are:
+ # Linux* OS IA-32 architecture:
+ # GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x4
+ # Linux* OS Intel(R) 64:
+ # GNU_STACK 0x000000 0x0000000000000000 0x0000000000000000 0x000000 0x000000 RWE 0x8
+ if ( $stack[ 0 ] !~ m{\A\s*(?:GNU_)?STACK(?:\s+0x[0-9a-f]+){5}\s+([R ][W ][E ])\s+0x[0-9a-f]+\s*\z} ) {
+ runtime_error( "$file: Cannot parse stack segment line:", ">>> $stack[ 0 ]" );
+ }; # if
+ my $attrs = $1;
+ if ( $attrs =~ m{E} ) {
+ runtime_error( "$file: Stack is executable" );
+ }; # if
+}; # sub execstack
+
+get_options(
+);
+
+foreach my $file ( @ARGV ) {
+ execstack( $file );
+}; # foreach $file
+
+exit( 0 );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<check-execstack.pl> -- Check whether stack is executable, issue an error if so.
+
+=head1 SYNOPSIS
+
+B<check-execstack.pl> I<optiion>... I<file>...
+
+=head1 DESCRIPTION
+
+The script checks whether stack of specified executable file, and issues error if stack is
+executable. If stack is not executable, the script exits silently with zero exit code.
+
+The script runs C<readelf> utility to get information about specified executable file. So, the
+script fails if C<readelf> is not available. Effectively it means the script works only on Linux* OS
+(and, probably, Intel(R) Many Integrated Core Architecture).
+
+=head1 OPTIONS
+
+=over
+
+=item Standard Options
+
+=over
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full help message and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--usage>
+
+Print very short usage message and exit.
+
+=item B<--verbose>
+
+Do print informational messages.
+
+=item B<--version>
+
+Print program version and exit.
+
+=item B<--quiet>
+
+Work quiet, do not print informational messages.
+
+=back
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<file>
+
+A name of executable or shared object to check. Multiple files may be specified.
+
+=back
+
+=head1 EXAMPLES
+
+Check libiomp5.so library:
+
+ $ check-execstack.pl libiomp5.so
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/check-instruction-set.pl b/runtime/tools/check-instruction-set.pl
new file mode 100755
index 0000000..1fa787e
--- /dev/null
+++ b/runtime/tools/check-instruction-set.pl
@@ -0,0 +1,336 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use Platform ":vars";
+use tools;
+
+our $VERSION = "0.004";
+
+my $hex = qr{[0-9a-f]}i; # hex digit.
+
+# lrb_32e-specific details.
+
+my $mic_arch; # either knf or knc
+my $mic_os; # either bsd or lin
+sub bad_lrb_fmt($) {
+ # Before we allowed both elf64-x86-64-freebsd and elf-l1om-freebsd.
+ # Now the first one is obsolete, only elf64-l1om-freebsd is allowed.
+ my ( $fmt ) = @_;
+ if ( 0 ) {
+ } elsif ( "$mic_os" eq "bsd" ) {
+ if ( "$mic_arch" eq "knf" ) {
+ return $fmt !~ m{\Aelf64-l1om(?:-freebsd)?\z};
+ } else {
+ return $fmt !~ m{\Aelf64-x86-64(?:-freebsd)?\z};
+ };
+ } elsif ( "$mic_os" eq "lin" ) {
+ if ( 0 ) {
+ } elsif ( "$mic_arch" eq "knf" ) {
+ return $fmt !~ m{\Aelf64-l1om?\z};
+ } elsif ( "$mic_arch" eq "knc" ) {
+ return $fmt !~ m{\Aelf64-k1om?\z};
+ } else {
+ return 1;
+ };
+ } else {
+ return 1;
+ };
+}; # sub bad_lrb_fmt
+
+# Undesired instructions for lrb: all x87 and some other.
+# AC: Since compiler 2010-06-30 x87 instructions are supported, removed the check of x87.
+my $lrb_bad_re;
+sub bad_lrb_instr($$) {
+ my ( $instr, $args ) = @_;
+# if ( "$mic_os" eq "lin" and "$mic_arch" eq "knf" ) {
+ if ( "$mic_os" eq "lin" or "$mic_arch" eq "knc" ) {
+ # workaround of bad code generation on KNF Linux* OS:
+ return ( defined( $instr ) and $instr =~ $lrb_bad_re );
+ } else {
+ return ( defined( $instr ) and $instr =~ $lrb_bad_re or defined( $args ) and $args =~ m{xmm}i );
+ }
+}; # sub bad_lrb_instr
+
+# lin_32-specific details.
+
+sub bad_ia32_fmt($) {
+ my ( $fmt ) = @_;
+ return $fmt !~ m{\Aelf32-i386\z};
+}; # sub bad_ia32_fmt
+
+my @sse2 =
+ qw{
+ movapd movupd movhpd movlpd movmskpd movsd
+ addpd addsd subpd subsd mulpd mulsd divpd divsd sqrtpd sqrtsd maxpd maxsd minpd minsd
+ andpd andnpd orpd xorpd
+ cmppd cmpsd comisd ucomisd
+ shufpd unpckhpd unpcklpd
+ cvtpd2pi cvttpd2pi cvtpi2pd cvtpd2dq cvttpd2dq cvtdq2pd cvtps2pd cvtpd2ps cvtss2sd cvtsd2ss
+ cvtsd2si cvttsd2si cvtsi2sd cvtdq2ps cvtps2dq cvttps2dq movdqa movdqu movq2dq movdq2q
+ pmuludq paddq psubq pshuflw pshufhw pshufd pslldq psrldq punpckhqdq punpcklqdq clflush
+ lfence mfence maskmovdqu movntpd movntdq movnti
+ };
+my @sse3 =
+ qw{
+ fisttp lddqu addsubps addsubpd haddps hsubps haddpd hsubpd movshdup movsldup movddup monitor
+ mwait
+ };
+my @ssse3 =
+ qw{
+ phaddw phaddsw phaddd phsubw phsubsw phsubd pabsb pabsw pabsd pmaddubsw pmulhrsw pshufb
+ psignb psignw psignd palignr
+ };
+my @sse4 =
+ (
+ # SSE4.1
+ qw{
+ pmulld pmuldq dppd dpps movntdqa blendpd blendps blendvpd blendvps pblendvb pblendw pminuw
+ pminud pminsb pminsd pmaxuw pmaxud pmaxsb pmaxsd roundps roundpd roundss roundsd extractps
+ insertps pinsrb pinsrd pinsrq pextrb pextrw pextrd pextrq pmovsxbw pmovzxbw pmovsxbd
+ pmovzxbd pmovsxwd pmovzxwd pmovsxbq pmovzxbq pmovsxwq pmovzxwq pmovsxdq pmovzxdq mpsadbw
+ phminposuw ptest pcmpeqq packusdw
+ },
+ # SSE4.2
+ qw{
+ pcmpestri pcmpestrm pcmpistri pcmpistrm pcmpgtq crc32 popcnt
+ }
+ );
+
+# Undesired instructions for IA-32 architecture: Pentium 4 (SSE2) and newer.
+# TODO: It would be much more reliable to list *allowed* instructions rather than list undesired
+# instructions. In such a case the list will be stable and not require update when SSE5 is released.
+my @ia32_bad_list = ( @sse2, @sse3, @ssse3, @sse4 );
+
+my $ia32_bad_re = qr{@{[ "^(?:" . join( "|", @ia32_bad_list ) . ")" ]}}i;
+
+sub bad_ia32_instr($$) {
+ my ( $instr, $args ) = @_;
+ return ( defined( $instr ) and $instr =~ $ia32_bad_re );
+}; # sub bad_ia32_instr
+
+sub check_file($;$$) {
+
+ my ( $file, $show_instructions, $max_instructions ) = @_;
+ my @bulk;
+
+ if ( not defined( $max_instructions ) ) {
+ $max_instructions = 100;
+ }; # if
+
+ if ( "$mic_os" eq "bsd" ) {
+ execute( [ "x86_64-freebsd-objdump", "-d", $file ], -stdout => \@bulk );
+ } else {
+ execute( [ "objdump", "-d", $file ], -stdout => \@bulk );
+ }
+
+ my $n = 0;
+ my $errors = 0;
+ my $current_func = ""; # Name of current fuction.
+ my $reported_func = ""; # name of last reported function.
+ foreach my $line ( @bulk ) {
+ ++ $n;
+ if ( 0 ) {
+ } elsif ( $line =~ m{^\s*$} ) {
+ # Empty line.
+ # Ignore.
+ } elsif ( $line =~ m{^In archive (.*?):\s*$} ) {
+ # In archive libiomp5.a:
+ } elsif ( $line =~ m{^(?:.*?):\s*file format (.*?)\s*$} ) {
+ # libiomp5.so: file format elf64-x86-64-freebsd
+ # kmp_ftn_cdecl.o: file format elf64-x86-64
+ my $fmt = $1;
+ if ( bad_fmt( $fmt ) ) {
+ runtime_error( "Invalid file format: $fmt." );
+ }; # if
+ } elsif ( $line =~ m{^Disassembly of section (.*?):\s*$} ) {
+ # Disassembly of section .plt:
+ } elsif ( $line =~ m{^$hex+ <([^>]+)>:\s*$} ) {
+ # 0000000000017e98 <__kmp_str_format@plt-0x10>:
+ $current_func = $1;
+ } elsif ( $line =~ m{^\s*\.{3}\s*$} ) {
+ } elsif ( $line =~ m{^\s*($hex+):\s+($hex$hex(?: $hex$hex)*)\s+(?:lock\s+|rex[.a-z]*\s+)?([^ ]+)(?:\s+([^#]+?))?\s*(?:#|$)} ) {
+ # 17e98: ff 35 fa 7d 26 00 pushq 0x267dfa(%rip) # 27fc98 <_GLOBAL_OFFSET_TABLE>
+ my ( $addr, $dump, $instr, $args ) = ( $1, $2, $3, $4 );
+ # Check this is not a bad instruction and xmm registers are not used.
+ if ( bad_instr( $instr, $args ) ) {
+ if ( $errors == 0 ) {
+ warning( "Invalid instructions found in `$file':" );
+ }; # if
+ if ( $current_func ne $reported_func ) {
+ warning( " $current_func" );
+ $reported_func = $current_func;
+ }; # if
+ ++ $errors;
+ if ( $show_instructions ) {
+ warning( " $line" );
+ }; # if
+ if ( $errors >= $max_instructions ) {
+ info( "$errors invalid instructions found; scanning stopped." );
+ last;
+ }; # if
+ }; # if
+ } else {
+ runtime_error( "Error parsing objdump output line $n:\n>>>> $line\n" );
+ }; # if
+ }; # foreach $line
+
+ return $errors;
+
+}; # sub check_file
+
+# --------------------------------------------------------------------------------------------------
+
+# Parse command line.
+my $max_instructions;
+my $show_instructions;
+get_options(
+ "max-instructions=i" => \$max_instructions,
+ "show-instructions!" => \$show_instructions,
+ "mic-arch=s" => \$mic_arch,
+ "mic-os=s" => \$mic_os,
+ Platform::target_options(),
+);
+if ( "$mic_os" eq "lin" and "$mic_arch" eq "knf" ) {
+ $lrb_bad_re = qr{^(?:pause|[slm]fence|scatter|gather|cmpxchg16b|clevict[12])}i;
+} else {
+ $lrb_bad_re = qr{^(?:pause|[slm]fence|scatter|gather|cmov|cmpxchg16b|clevict[12])}i;
+};
+if ( 0 ) {
+} elsif ( $target_platform eq "lrb_32e" ) {
+ *bad_instr = \*bad_lrb_instr;
+ *bad_fmt = \*bad_lrb_fmt;
+} elsif ( $target_platform eq "lin_32" ) {
+ *bad_instr = \*bad_ia32_instr;
+ *bad_fmt = \*bad_ia32_fmt;
+} else {
+ runtime_error( "Only works on lin_32 and lrb_32e platforms." );
+}; # if
+
+# Do the work.
+my $rc = 0;
+if ( not @ARGV ) {
+ info( "No arguments specified -- nothing to do." );
+} else {
+ foreach my $arg ( @ARGV ) {
+ my $errs = check_file( $arg, $show_instructions, $max_instructions );
+ if ( $errs > 0 ) {
+ $rc = 3;
+ }; # if
+ }; # foreach $arg
+}; # if
+
+exit( $rc );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<check-instruction-set.pl> -- Make sure binary file does not contain undesired instructions.
+
+=head1 SYNOPSIS
+
+B<check-instructions.pl> I<option>... I<file>...
+
+=head1 OPTIONS
+
+=over
+
+=item B<--architecture=>I<arch>
+
+Specify target architecture.
+
+=item B<--max-instructions=>I<number>
+
+Stop scanning if I<number> invalid instructions found. 100 by default.
+
+=item B<--os=>I<os>
+
+Specify target OS.
+
+=item B<-->[B<no->]B<show-instructions>
+
+Show invalid instructions found in the file. Bu default, instructions are not shown.
+
+=item Standard Options
+
+=over
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full help message and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--usage>
+
+Print very short usage message and exit.
+
+=item B<--verbose>
+
+Do print informational messages.
+
+=item B<--version>
+
+Print program version and exit.
+
+=item B<--quiet>
+
+Work quiet, do not print informational messages.
+
+=back
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<file>
+
+File (object file or library, either static or dynamic) to check.
+
+=back
+
+=head1 DESCRIPTION
+
+The script runs F<objdump> utility to get disassembler listing and checks the file does not contain
+unwanted instructions.
+
+Currently the script works only for:
+
+=over
+
+=item C<lrb_32e>
+
+Intel(R) Many Integrated Core Architecture target OS. Undesired unstructions are: all x87 instructions and some others.
+
+=item C<lin_32>
+
+Undesired instructions are instructions not valid for Pentium 3 processor (SSE2 and newer).
+
+=back
+
+=cut
+
diff --git a/runtime/tools/check-tools.pl b/runtime/tools/check-tools.pl
new file mode 100755
index 0000000..8140e11
--- /dev/null
+++ b/runtime/tools/check-tools.pl
@@ -0,0 +1,546 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+# Pragmas.
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+# LIBOMP modules.
+use Platform ":vars";
+use tools;
+
+our $VERSION = "0.015";
+
+my $pedantic;
+
+# --------------------------------------------------------------------------------------------------
+# Helper functions
+# --------------------------------------------------------------------------------------------------
+
+
+sub run($\$\$;\$) {
+ my ( $cmd, $stdout, $stderr, $path ) = @_;
+ my ( @path, $rc );
+ @path = which( $cmd->[ 0 ], -all => 1 );
+ if ( @path > 0 ) {
+ if ( @path > 1 and $pedantic ) {
+ warning( "More than one \"$cmd->[ 0 ]\" found in PATH:", map( " $_", @path ) );
+ }; # if
+ debug( "\"$cmd->[ 0 ]\" full path is \"$path[ 0 ]\"." );
+ if ( defined( $path ) ) {
+ $$path = $path[ 0 ];
+ }; # if
+ debug( "Executing command: \"" . join ( " ", @$cmd ) . "\"." );
+ $rc =
+ execute(
+ $cmd,
+ -ignore_signal => 1, -ignore_status => 1,
+ -stdout => $stdout, -stderr => $stderr, -stdin => undef
+ );
+ if ( $rc < 0 ) {
+ warning( "Cannot run \"$cmd->[ 0 ]\": $@" );
+ }; # if
+ debug( "stdout:", $$stdout, "(eof)", "stderr:", $$stderr, "(eof)" );
+ } else {
+ warning( "No \"$cmd->[ 0 ]\" found in PATH." );
+ $rc = -1;
+ }; # if
+ return $rc;
+}; # sub run
+
+
+sub get_arch($$$) {
+ my ( $name, $str, $exps ) = @_;
+ my ( $arch, $count );
+ $count = 0;
+ foreach my $re ( keys( %$exps ) ) {
+ if ( $str =~ $re ) {
+ $arch = $exps->{ $re };
+ ++ $count;
+ }; # if
+ }; # for
+ if ( $count != 1 or not Platform::canon_arch( $arch ) ) {
+ warning( "Cannot detect $name architecture: $str" );
+ return undef;
+ }; # if
+ return $arch;
+}; # sub get_arch
+
+sub encode($) {
+ my ( $str ) = @_;
+ $str =~ s{ }{_}g;
+ return $str;
+}; # sub encode
+
+
+# --------------------------------------------------------------------------------------------------
+# get_xxx_version subroutines.
+# --------------------------------------------------------------------------------------------------
+#
+# Some of get_xxx_version() subroutines accept an argument -- a tool name. For example,
+# get_intel_compiler_version() can report version of C, C++, or Fortran compiler. The tool for
+# report should be specified by argument, for example: get_intel_compiler_version( "ifort" ).
+#
+# get_xxx_version() subroutines returns list of one or two elements:
+# 1. The first element is short tool name (like "gcc", "g++", "icl", etc).
+# 2. The second element is version string.
+# If returned list contain just one element, it means there is a problem with the tool.
+#
+
+sub get_perl_version() {
+ my ( $rc, $stdout, $stderr, $version );
+ my $tool = "perl";
+ my ( @ret ) = ( $tool );
+ $rc = run( [ $tool, "--version" ], $stdout, $stderr );
+ if ( $rc >= 0 ) {
+ # Typical perl output:
+ # This is perl, v5.10.0 built for x86_64-linux-thread-multi
+ # This is perl, v5.8.8 built for MSWin32-x64-multi-thread
+ # This is perl, v5.10.1 (*) built for x86_64-linux-thread-multi
+ if ( $stdout !~ m{^This is perl.*v(\d+\.\d+(?:\.\d+)).*built for}m ) {
+ warning( "Cannot parse perl output:", $stdout, "(oef)" );
+ }; # if
+ $version = $1;
+ if ( $target_os eq "win" ) {
+ if ( $stdout !~ m{Binary build (.*) provided by ActiveState } ) {
+ warning( "Perl is not ActiveState one" );
+ }; # if
+ }; # if
+ }; # if
+ push( @ret, $version );
+ return @ret;
+}; # sub get_perl_version
+
+
+sub get_gnu_make_version() {
+ my ( $rc, $stdout, $stderr, $version );
+ my $tool = "make";
+ my ( @ret ) = ( $tool );
+ my ( $path );
+ $rc = run( [ $tool, "--version" ], $stdout, $stderr, $path );
+ if ( $rc >= 0 ) {
+ # Typical make output:
+ # GNU Make version 3.79.1, by Richard Stallman and Roland McGrath.
+ # GNU Make 3.81
+ if ( $stdout =~ m{^GNU Make (?:version )?(\d+\.\d+(?:\.\d+)?)(?:,|\s)} ) {
+ $version = $1;
+ }; # if
+ if ( $target_os eq "win" and $stdout =~ m{built for ([a-z0-9-]+)} ) {
+ my $built_for = $1;
+ debug( "GNU Make built for: \"$built_for\"." );
+ if ( $built_for =~ m{cygwin}i ) {
+ warning( "\"$path\" is a Cygwin make, it is *not* suitable." );
+ return @ret;
+ }; # if
+ }; # if
+ }; # if
+ push( @ret, $version );
+ return @ret;
+}; # sub get_gnu_make_version
+
+
+sub get_intel_compiler_version($) {
+ my ( $tool ) = @_; # Tool name, like "icc", "icpc", "icl", or "ifort".
+ my ( @ret ) = ( $tool );
+ my ( $rc, $stdout, $stderr, $tool_re );
+ my $version;
+ my $ic_archs = {
+ qr{32-bit|IA-32} => "32",
+ qr{Intel\(R\) 64} => "32e",
+ qr{Intel\(R\) [M][I][C] Architecture} => "32e",
+ };
+ $tool_re = quotemeta( $tool );
+ $rc = run( [ $tool, ( $target_os eq "win" ? () : ( "-V" ) ) ], $stdout, $stderr );
+ if ( $rc < 0 ) {
+ return @ret;
+ }; # if
+ # Intel compiler version string is in the first line of stderr. Get it.
+ #$stderr =~ m{\A(.*\n?)};
+ # AC: Let's look for version string in the first line which contains "Intel" string.
+ # This allows to use 11.1 and 12.0 compilers on new MAC machines by ignoring
+ # huge number of warnings issued by old compilers.
+ $stderr =~ m{^(Intel.*)$}m;
+ my $vstr = $1;
+ my ( $apl, $ver, $bld, $pkg );
+ if ( 0 ) {
+ } elsif ( $vstr =~ m{^Intel.*?Compiler\s+(.*?),?\s+Version\s+(.*?)\s+Build\s+(\S+)(?:\s+Package ID: (\S+))?} ) {
+ # 9.x, 10.x, 11.0.
+ ( $apl, $ver, $bld, $pkg ) = ( $1, $2, $3, $4 );
+ } elsif ( $vstr =~ m{^Intel's (.*?) Compiler,?\s+Version\s+(.*?)\s+Build\s+(\S+)} ) {
+ # 11.1
+ ( $apl, $ver, $bld ) = ( $1, $2, $3 );
+ } else {
+ warning( "Cannot parse ${tool}'s stderr:", $stderr, "(eof)" );
+ return @ret;
+ }; # if
+ my $ic_arch = get_arch( "Intel compiler", $apl, $ic_archs );
+ if ( not defined( $ic_arch ) ) {
+ return @ret;
+ }; # if
+ if ( Platform::canon_arch( $ic_arch ) ne $target_arch ) {
+ warning( "Target architecture is $target_arch, $tool for $ic_arch found." );
+ return @ret;
+ }; # if
+ # Normalize version.
+ my $stage;
+ $ver =~ s{\s+}{ }g;
+ $ver = lc( $ver );
+ if ( $ver =~ m{\A(\d+\.\d+(?:\.\d+)?) ([a-z]+)\a}i ) {
+ ( $version, $stage ) = ( $1, $2 );
+ } else {
+ ( $version, $stage ) = ( $ver, "" );
+ }; # if
+ # Parse package.
+ if ( defined( $pkg ) ) {
+ if ( $pkg !~ m{\A[lwm]_[a-z]+_[a-z]_(\d+\.\d+\.\d+)\z}i ) {
+ warning( "Cannot parse Intel compiler package: $pkg" );
+ return @ret;
+ }; # if
+ $pkg = $1;
+ $version = $pkg;
+ }; # if
+ push( @ret, "$version " . ( $stage ? "$stage " : "" ) . "($bld) for $ic_arch" );
+ # Ok, version of Intel compiler found successfully. Now look at config file.
+ # Installer of Intel compiler tends to add a path to MS linker into compiler config file.
+ # It leads to troubles. For example, all the environment set up for MS VS 2005, but Intel
+ # compiler uses lnker from MS VS 2003 because it is specified in config file.
+ # To avoid such troubles, make sure:
+ # ICLCFG/IFORTCFG environment variable exists or
+ # compiler config file does not exist, or
+ # compiler config file does not specify linker.
+ if ( $target_os eq "win" ) {
+ if ( not exists( $ENV{ uc( $tool . "cfg" ) } ) ) {
+ # If ICLCFG/IFORTCFG environment varianle exists, everything is ok.
+ # Otherwise check compiler's config file.
+ my $path = which( $tool );
+ $path =~ s{\.exe\z}{}i; # Drop ".exe" suffix.
+ $path .= ".cfg"; # And add ".cfg" one.
+ if ( -f $path ) {
+ # If no config file exists, it is ok.
+ # Otherwise analyze its content.
+ my $bulk = read_file( $path );
+ $bulk =~ s{#.*\n}{}g; # Remove comments.
+ my @options = ( "Qvc", "Qlocation,link," );
+ foreach my $opt ( @options ) {
+ if ( $bulk =~ m{[-/]$opt} ) {
+ warning( "Compiler config file \"$path\" contains \"-$opt\" option." );
+ }; # if
+ }; # foreach
+ }; # if
+ }; # if
+ }; # if
+ return @ret;
+}; # sub get_intel_compiler_version
+
+
+sub get_gnu_compiler_version($) {
+ my ( $tool ) = @_;
+ my ( @ret ) = ( $tool );
+ my ( $rc, $stdout, $stderr, $version );
+ $rc = run( [ $tool, "--version" ], $stdout, $stderr );
+ if ( $rc >= 0 ) {
+ my ( $ver, $bld );
+ if ( $target_os eq "mac" ) {
+ # i686-apple-darwin8-gcc-4.0.1 (GCC) 4.0.1 (Apple Computer, Inc. build 5367)
+ # i686-apple-darwin9-gcc-4.0.1 (GCC) 4.0.1 (Apple Inc. build 5484)
+ # i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.9.00)
+ $stdout =~ m{^.*? \(GCC\) (\d+\.\d+\.\d+) \(.*Apple.*?Inc\. build (\d+)\)}m;
+ ( $ver, $bld ) = ( $1, $2 );
+ } else {
+ if ( 0 ) {
+ } elsif ( $stdout =~ m{^.*? \(GCC\) (\d+\.\d+\.\d+)(?: (\d+))?}m ) {
+ # g++ (GCC) 3.2.3 20030502 (Red Hat Linux 3.2.3-20)
+ # GNU Fortran (GCC) 4.3.2 20081105 (Red Hat 4.3.2-7)
+ ( $ver, $bld ) = ( $1, $2 );
+ } elsif ( $stdout =~ m{^.*? \(SUSE Linux\) (\d+\.\d+\.\d+)\s+\[.*? (\d+)\]}m ) {
+ # gcc (SUSE Linux) 4.3.2 [gcc-4_3-branch revision 141291]
+ ( $ver, $bld ) = ( $1, $2 );
+ } elsif ( $stdout =~ m{^.*? \((Debian|Ubuntu).*?\) (\d+\.\d+\.\d+)}m ) {
+ # gcc (Debian 4.7.2-22) 4.7.2
+ # Debian support from Sylvestre Ledru
+ # Thanks!
+ $ver = $2;
+ }; # if
+ }; # if
+ if ( defined( $ver ) ) {
+ $version = $ver . ( defined( $bld ) ? " ($bld)" : "" );
+ } else {
+ warning( "Cannot parse GNU compiler version:", $stdout, "(eof)" );
+ }; # if
+ }; # if
+ push( @ret, $version );
+ return @ret;
+}; # sub get_gnu_compiler_version
+
+
+sub get_ms_compiler_version() {
+ my ( $rc, $stdout, $stderr, $version );
+ my $tool = "cl";
+ my ( @ret ) = ( $tool );
+ my $mc_archs = {
+ qr{80x86} => "IA-32 architecture",
+ qr{AMD64|x64} => "Intel(R) 64",
+ };
+ $rc = run( [ $tool ], $stdout, $stderr );
+ if ( $rc < 0 ) {
+ return @ret;
+ }; # if
+ if ( $stderr !~ m{^Microsoft .* Compiler Version (.*?) for (.*)\s*$}m ) {
+ warning( "Cannot parse MS compiler output:", $stderr, "(eof)" );
+ return @ret;
+ }; # if
+ my ( $ver, $apl ) = ( $1, $2 );
+ if ( $ver !~ m{\A\d+(?:\.\d+)+\z} ) {
+ warning( "Cannot parse MS compiler version: $ver" );
+ return @ret;
+ }; # if
+ my $mc_arch = get_arch( "MS compiler", $apl, $mc_archs );
+ if ( not defined( $mc_arch ) ) {
+ return @ret;
+ }; # if
+ if ( Platform::canon_arch( $mc_arch ) ne $target_arch ) {
+ warning( "Target architecture is $target_arch, $tool for $mc_arch found" );
+ return @ret;
+ }; # if
+ $version = "$ver for $target_arch";
+ push( @ret, $version );
+ return @ret;
+}; # sub get_ms_compiler_version
+
+
+sub get_ms_linker_version() {
+ my ( $rc, $stdout, $stderr, $version );
+ my $tool = "link";
+ my ( @ret ) = ( $tool );
+ my ( $path );
+ $rc = run( [ $tool ], $stdout, $stderr, $path );
+ if ( $rc < 0 ) {
+ return @ret;
+ }; # if
+ if ( $stdout !~ m{^Microsoft \(R\) Incremental Linker Version (\d+(?:\.\d+)+)\s*$}m ) {
+ warning( "Cannot parse MS linker output:", $stdout, "(eof)" );
+ if ( $stderr =~ m{^link: missing operand} ) {
+ warning( "Seems \"$path\" is a Unix-like \"link\" program, not MS linker." );
+ }; # if
+ return @ret;
+ }; # if
+ $version = ( $1 );
+ push( @ret, $version );
+ return @ret;
+}; # sub get_ms_linker_version
+
+
+# --------------------------------------------------------------------------------------------------
+# "main" program.
+# --------------------------------------------------------------------------------------------------
+
+my $make;
+my $intel = 1; # Check Intel compilers.
+my $gnu_fortran = 0; # Check GNU Fortran.
+my $intel_compilers = {
+ "lin" => { c => "icc", cpp => "icpc", f => "ifort" },
+ "lrb" => { c => "icc", cpp => "icpc", f => "ifort" },
+ "mac" => { c => "icc", cpp => "icpc", f => "ifort" },
+ "win" => { c => "icl", cpp => undef, f => "ifort" },
+};
+
+get_options(
+ Platform::target_options(),
+ "intel!" => \$intel,
+ "gnu-fortran!" => \$gnu_fortran,
+ "make" => \$make,
+ "pedantic" => \$pedantic,
+);
+
+my @versions;
+push( @versions, [ "Perl", get_perl_version() ] );
+push( @versions, [ "GNU Make", get_gnu_make_version() ] );
+if ( $intel ) {
+ my $ic = $intel_compilers->{ $target_os };
+ push( @versions, [ "Intel C Compiler", get_intel_compiler_version( $ic->{ c } ) ] );
+ if ( defined( $ic->{ cpp } ) ) {
+ # If Intel C++ compiler has a name different from C compiler, check it as well.
+ push( @versions, [ "Intel C++ Compiler", get_intel_compiler_version( $ic->{ cpp } ) ] );
+ }; # if
+ if ( defined( $ic->{ f } ) ) {
+ push( @versions, [ "Intel Fortran Compiler", get_intel_compiler_version( $ic->{ f } ) ] );
+ }; # if
+}; # if
+if ( $target_os eq "lin" or $target_os eq "mac" ) {
+ push( @versions, [ "GNU C Compiler", get_gnu_compiler_version( "gcc" ) ] );
+ push( @versions, [ "GNU C++ Compiler", get_gnu_compiler_version( "g++" ) ] );
+ if ( $gnu_fortran ) {
+ push( @versions, [ "GNU Fortran Compiler", get_gnu_compiler_version( "gfortran" ) ] );
+ }; # if
+}; # if
+if ( $target_os eq "win" ) {
+ push( @versions, [ "MS C/C++ Compiler", get_ms_compiler_version() ] );
+ push( @versions, [ "MS Linker", get_ms_linker_version() ] );
+}; # if
+my $count = 0;
+foreach my $item ( @versions ) {
+ my ( $title, $tool, $version ) = @$item;
+ if ( not defined( $version ) ) {
+ $version = "--- N/A ---";
+ ++ $count;
+ }; # if
+ if ( $make ) {
+ printf( "%s=%s\n", encode( $tool ), encode( $version ) );
+ } else {
+ printf( "%-25s: %s\n", $title, $version );
+ }; # if
+}; # foreach
+
+exit( $count == 0 ? 0 : 1 );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<check-tools.pl> -- Check development tools availability and versions.
+
+=head1 SYNOPSIS
+
+B<check-tools.pl> I<OPTION>...
+
+=head1 OPTIONS
+
+=over
+
+=item B<--make>
+
+Produce output suitable for using in makefile: short tool names (e. g. "icc" instead of "Intel C
+Compiler"), spaces in version strings replaced with underscores.
+
+=item Tools selection
+
+=over
+
+=item B<-->[B<no->]B<-gnu-fortran>
+
+Check GNU Fortran compiler. By default, it is not checked.
+
+=item B<-->[B<no->]B<intel>
+
+Check Intel C, C++ and Fortran compilers. This is default.
+
+=back
+
+=item Platform selection
+
+=over
+
+=item B<--architecture=>I<str>
+
+Specify target architecture. Used in cross-builds, for example when building 32-bit applications on
+Intel(R) 64 machine.
+
+If architecture is not specified explicitly, value of LIBOMP_ARCH environment variable is used.
+If LIBOMP_ARCH is not defined, host architecture detected.
+
+=item B<--os=>I<str>
+
+Specify target OS name. Used in cross-builds, for example when building Intel(R) Many Integrated Core Architecture applications on
+Windows* OS.
+
+If OS is not specified explicitly, value of LIBOMP_OS environment variable is used.
+If LIBOMP_OS is not defined, host OS detected.
+
+=back
+
+=back
+
+=head2 Standard Options
+
+=over
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full help message and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--usage>
+
+Print very short usage message and exit.
+
+=item B<--verbose>
+
+Do print informational messages.
+
+=item B<--version>
+
+Print version and exit.
+
+=item B<--quiet>
+
+Work quiet, do not print informational messages.
+
+=back
+
+=head1 DESCRIPTION
+
+This script checks availability and versions of development tools. By default, the script checks:
+Perl, GNU Make, Intel compilers, GNU C and C++ compilers (Linux* OS and OS X*),
+Microsoft C/C++ compiler and linker (Windows* OS).
+
+The sript prints nice looking table or machine-readable strings.
+
+=head2 EXIT
+
+=over
+
+=item *
+
+0 -- All programs found.
+
+=item *
+
+1 -- Some of tools are not found.
+
+=back
+
+=head1 EXAMPLES
+
+ $ check-tools.pl
+ Perl : 5.8.0
+ GNU Make : 3.79.1
+ Intel C Compiler : 11.0 (20080930) for 32e
+ Intel C++ Compiler : 11.0 (20080930) for 32e
+ Intel Fortran Compiler : 10.1.008 (20070913) for 32e
+ GNU C Compiler : 3.2.3 (20030502)
+ GNU C++ Compiler : 3.2.3 (20030502)
+
+ > check-tools.pl --make
+ perl=5.8.8
+ make=3.81
+ icl=10.1_(20070913)_for_32e
+ ifort=10.1_(20070913)_for_32e
+ cl=14.00.40310.41_for_32e
+ link=8.00.40310.39
+
+=back
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/common.inc b/runtime/tools/common.inc
new file mode 100644
index 0000000..4154b29
--- /dev/null
+++ b/runtime/tools/common.inc
@@ -0,0 +1,91 @@
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+ifndef omp_os
+
+ # Windows sets environment variable OS; for other systems, ask uname
+ ifeq ($(OS),)
+ OS:=$(shell uname)
+ ifeq ($(OS),)
+ $(error "Cannot detect operating system")
+ endif
+ export omp_os=$(OS)
+ endif
+
+ ifeq ($(OS), Windows_NT)
+ export omp_os=windows
+ endif
+ ifeq ($(OS), Linux)
+ export omp_os=linux
+ endif
+ ifeq ($(OS), Darwin)
+ export omp_os=macos
+ endif
+
+endif # !omp_os
+
+# Compiling for the Intel(R) Many Integrated Core architecture is non-trivial at the next layer
+# of script down, but we can make it consistent here.
+ifeq "$(arch)" "mic"
+ # I really do mean this...
+ override arch:=32e
+ override mic:=yes
+else
+ override mic:=no
+endif
+
+ifeq (,$(wildcard $(omp_root)/tools/$(omp_os).inc))
+ $(error "$(omp_os)" is not supported. Add tools/$(omp_os).inc file with os-specific settings )
+endif
+
+# detect arch and runtime versions, provide common host-specific definitions
+include $(omp_root)/tools/$(omp_os).inc
+
+ifeq ($(arch),)
+ $(error Architecture not detected)
+endif
+
+# Setting defaults
+mode?=release
+
+ifeq "$(omp_os)" "windows"
+ compiler?=icl
+else
+ compiler?=icc
+endif
+
+ifneq "$(mic)" "no"
+ ifeq "$(compiler)" "gcc"
+ $(error Compiling the runtime with gcc is not supported on Intel\(R\) Many Integrated Core Architecture)
+ endif
+
+ # Magic flags for the build script!
+ build_args += --os=lrb --mic-arch=knc --mic-os=lin --mic-comp=offload
+
+ # Check that the binutils for Intel(R) Many Integrated Core Architecture are available
+ # First we see whether the objdump on the user's path supports the k1om architecture.
+ hask1om = $(shell if (objdump --help | grep -s k1om); then echo OK; else echo KO; fi)
+ ifneq "$(hask1om)" "OK"
+ # Appropriate binutils are not already set up, so try to add them from the default place.
+ micBinPath = /usr/linux-k1om-4.7/x86_64-k1om-linux/bin
+ micBinPresent = $(shell if test -d $(micBinPath); then echo OK; else echo KO; fi)
+ ifneq "$(micBinPresent)" "OK"
+ # We can't find them in the normal place, so complain.
+ $(error Compiling for Intel\(R\) Many Integrated Core Architecture requires that the cross-hosted binutils are available in $(micBinPath).\
+See the Tools tab at http://software.intel.com/mic-developer)
+ endif
+ export PATH := $(micBinPath):${PATH}
+ endif
+endif
+
+export BUILD_COMPILER := $(compiler)
+
+
diff --git a/runtime/tools/expand-vars.pl b/runtime/tools/expand-vars.pl
new file mode 100755
index 0000000..57a3ec9
--- /dev/null
+++ b/runtime/tools/expand-vars.pl
@@ -0,0 +1,306 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+
+our $VERSION = "0.005";
+
+my $name_rexp = qr{[A-Za-z_]+[A-Za-z0-9_]*};
+my $keyword_rexp = qr{if|else|end|omp};
+
+sub error($$$) {
+ my ( $input, $msg, $bulk ) = @_;
+ my $pos = pos( $$bulk );
+ $$bulk =~ m{^(.*?)\G(.*?)$}m or die "Internal error";
+ my ( $pre, $post ) = ( $1, $2 );
+ my $n = scalar( @{ [ substr( $$bulk, 0, $pos ) =~ m{\n}g ] } ) + 1;
+ runtime_error( "\"$input\" line $n: $msg:", ">>> " . $pre . "--[HERE]-->" . $post );
+}; # sub error
+
+sub evaluate($$$\$) {
+ my ( $expr, $strict, $input, $bulk ) = @_;
+ my $value;
+ { # Signal handler will be restored on exit from this block.
+ # In case of "use strict; use warnings" eval issues warnings to stderr. This direct
+ # output may confuse user, so we need to catch it and prepend with our info.
+ local $SIG{ __WARN__ } = sub { die @_; };
+ $value =
+ eval(
+ "package __EXPAND_VARS__;\n" .
+ ( $strict ? "use strict; use warnings;\n" : "no strict; no warnings;\n" ) .
+ $expr
+ );
+ };
+ if ( $@ ) {
+ # Drop location information -- increasing eval number and constant "line 3"
+ # is useless for the user.
+ $@ =~ s{ at \(eval \d+\) line \d+}{}g;
+ $@ =~ s{\s*\z}{};
+ error( $input, "Cannot evaluate expression \"\${{$expr}}\": $@", $bulk );
+ }; # if
+ if ( $strict and not defined( $value ) ) {
+ error( $input, "Substitution value is undefined", $bulk );
+ }; # if
+ return $value;
+}; # sub evaluate
+
+#
+# Parse command line.
+#
+
+my ( @defines, $input, $output, $strict );
+get_options(
+ "D|define=s" => \@defines,
+ "strict!" => \$strict,
+);
+if ( @ARGV < 2 ) {
+ cmdline_error( "Not enough argument" );
+}; # if
+if ( @ARGV > 2 ) {
+ cmdline_error( "Too many argument(s)" );
+}; # if
+( $input, $output ) = @ARGV;
+
+foreach my $define ( @defines ) {
+ my ( $equal, $name, $value );
+ $equal = index( $define, "=" );
+ if ( $equal < 0 ) {
+ $name = $define;
+ $value = "";
+ } else {
+ $name = substr( $define, 0, $equal );
+ $value = substr( $define, $equal + 1 );
+ }; # if
+ if ( $name eq "" ) {
+ cmdline_error( "Illegal definition: \"$define\": variable name should not be empty." );
+ }; # if
+ if ( $name !~ m{\A$name_rexp\z} ) {
+ cmdline_error(
+ "Illegal definition: \"$define\": " .
+ "variable name should consist of alphanumeric characters."
+ );
+ }; # if
+ eval( "\$__EXPAND_VARS__::$name = \$value;" );
+ if ( $@ ) {
+ die( "Internal error: $@" );
+ }; # if
+}; # foreach $define
+
+#
+# Do the work.
+#
+
+my $bulk;
+
+# Read input file.
+$bulk = read_file( $input );
+
+# Do the replacements.
+$bulk =~
+ s{(?:\$($keyword_rexp)|\$($name_rexp)|\${{(.*?)}})}
+ {
+ my $value;
+ if ( defined( $1 ) ) {
+ # Keyword. Leave it as is.
+ $value = "\$$1";
+ } elsif ( defined( $2 ) ) {
+ # Variable to expand.
+ my $name = $2;
+ $value = eval( "\$__EXPAND_VARS__::$name" );
+ if ( $@ ) {
+ die( "Internal error" );
+ }; # if
+ if ( $strict and not defined( $value ) ) {
+ error( $input, "Variable \"\$$name\" not defined", \$bulk );
+ }; # if
+ } else {
+ # Perl code to evaluate.
+ my $expr = $3;
+ $value = evaluate( $expr, $strict, $input, $bulk );
+ }; # if
+ $value;
+ }ges;
+
+# Process conditionals.
+# Dirty patch! Nested conditionals not supported!
+# TODO: Implement nested constructs.
+$bulk =~
+ s{^\$if +([^\n]*) *\n(.*\n)\$else *\n(.*\n)\$end *\n}
+ {
+ my ( $expr, $then_part, $else_part ) = ( $1, $2, $3 );
+ my $value = evaluate( $expr, $strict, $input, $bulk );
+ if ( $value ) {
+ $value = $then_part;
+ } else {
+ $value = $else_part;
+ }; # if
+ }gesm;
+
+# Write output.
+write_file( $output, \$bulk );
+
+exit( 0 );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<expand-vars.pl> -- Simple text preprocessor.
+
+=head1 SYNOPSIS
+
+B<expand-vars.pl> I<OPTION>... I<input> I<output>
+
+=head1 OPTIONS
+
+=over
+
+=item B<-D> I<name>[B<=>I<value>]
+
+=item B<--define=>I<name>[B<=>I<value>]
+
+Define variable.
+
+=item B<--strict>
+
+In strict mode, the script issues error on using undefined variables and executes Perl code
+with C<use strict; use warnings;> pragmas.
+
+=back
+
+=head2 Standard Options
+
+=over
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full help message and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--usage>
+
+Print very short usage message and exit.
+
+=item B<--verbose>
+
+Do print informational messages.
+
+=item B<--version>
+
+Print version and exit.
+
+=item B<--quiet>
+
+Work quiet, do not print informational messages.
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<input>
+
+Input file name.
+
+=item I<output>
+
+Output file name.
+
+=back
+
+=head1 DESCRIPTION
+
+This script reads input file, makes substitutes and writes output file.
+
+There are two form of substitutes:
+
+=over
+
+=item Variables
+
+Variables are referenced in input file in form:
+
+ $name
+
+Name of variable should consist of alphanumeric characters (Latin letters, digits, and underscores).
+Variables are defined in command line with C<-D> or C<--define> options.
+
+=item Perl Code
+
+Perl code is specified in input file in form:
+
+ ${{ ...code... }}
+
+The code is evaluated, and is replaced with its result. Note: in strict mode, you should declare
+variable before use. See examples.
+
+=back
+
+=head1 EXAMPLES
+
+Replace occurences of C<$year>, C<$month>, and C<$day> in C<input.txt> file with C<2007>, C<09>, C<01>
+respectively and write result to C<output.txt> file:
+
+ $ cat input.var
+ Today is $year-$month-$day.
+ $ expand-vars.pl -D year=2007 -D month=09 -D day=01 input.var output.txt && cat output.txt
+ Today is 2007-09-01.
+
+Using Perl code:
+
+ $ cat input.var
+ ${{ localtime(); }}
+ $ expand-vars.pl -D year=2007 -D month=09 -D day=01 input.var output.txt && cat output.txt
+ Now Tue May 5 20:54:13 2009
+
+Using strict mode for catching bugs:
+
+ $ cat input.var
+ ${{ "year : " . substr( $date, 0, 4 ); }}
+ $ expand-vars.pl input.var output.txt && cat output.txt
+ year :
+
+Oops, why it does not print year? Let us use strict mode:
+
+ $ expand-vars.pl --strict input.var output.txt && cat output.txt
+ expand-vars.pl: (x) "test.var": Cannot evaluate expression "${{ "year : " . substr( $date, 0, 4 ); }}": Global symbol "$date" requires explicit package name
+
+Ok, variable is not defined. Let us define it:
+
+ $ expand-vars.pl --strict -D date=20090501 input.var output.txt && cat output.txt
+ expand-vars.pl: (x) "test.var": Cannot evaluate expression "${{ "year : " . substr( $date, 0, 4 ); }}": Variable "$date" is not imported
+
+What is wrong? Variable should be declared:
+
+ $ cat input.var
+ ${{ our $date; "year : " . substr( $date, 0, 4 ); }}
+ $ expand-vars.pl --strict -D date=20090501 input.var output.txt && cat output.txt
+ year : 2009
+
+=cut
+
+# end of file #
diff --git a/runtime/tools/extract-objects.pl b/runtime/tools/extract-objects.pl
new file mode 100755
index 0000000..04be6ea
--- /dev/null
+++ b/runtime/tools/extract-objects.pl
@@ -0,0 +1,258 @@
+#!/usr/bin/env perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use File::Glob ":glob";
+use File::Temp;
+use Cwd;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+use Uname;
+use Platform ":vars";
+
+our $VERSION = "0.005";
+
+# --------------------------------------------------------------------------------------------------
+# Subroutines.
+# --------------------------------------------------------------------------------------------------
+
+sub windows {
+ my ( $arch, $output, @args ) = @_;
+ my %files;
+ # TODO: Check the archives are of specified architecture.
+ foreach my $arg ( @args ) {
+ foreach my $archive ( bsd_glob( $arg ) ) {
+ info( "Processing \"$archive\"..." );
+ my $bulk;
+ execute( [ "lib.exe", "/nologo", "/list", $archive ], -stdout => \$bulk );
+ my @members = split( "\n", $bulk );
+ foreach my $member ( @members ) {
+ my $file = get_file( $member );
+ my $path = cat_file( $output, $file );
+ if ( exists( $files{ $file } ) ) {
+ runtime_error(
+ "Extraction \"$file\" member from \"$archive\" archive failed:",
+ "\"$file\" member has already been extracted from \"$files{ $file }\" archive"
+ );
+ }; # if
+ $files{ $file } = $archive;
+ info( " Writing \"$path\"..." );
+ execute( [ "lib.exe", "/nologo", "/extract:" . $member, "/out:" . $path, $archive ] );
+ }; # foreach $member
+ }; # foreach $archive
+ }; # foreach $arg
+}; # sub windows
+
+sub linux {
+ my ( $arch, $output, @archives ) = @_;
+ # TODO: Check the archives are of specified architecture.
+ my $cwd = Cwd::cwd();
+ change_dir( $output );
+ foreach my $archive ( @archives ) {
+ info( "Processing \"$archive\"..." );
+ my $path = abs_path( $archive, $cwd );
+ execute( [ "ar", "xo", $path ] );
+ }; # foreach $archive
+ change_dir( $cwd );
+}; # sub linux
+
+my %mac_arch = (
+ "32" => "i386",
+ "32e" => "x86_64"
+);
+
+sub darwin {
+ my ( $arch, $output, @archives ) = @_;
+ my $cwd = getcwd();
+ change_dir( $output );
+ if ( defined( $arch ) ) {
+ if ( not defined( $mac_arch{ $arch } ) ) {
+ runtime_error( "Architecture \"$arch\" is not a valid one for OS X*" );
+ }; # if
+ $arch = $mac_arch{ $arch };
+ }; # if
+ foreach my $archive ( @archives ) {
+ info( "Processing \"$archive\"..." );
+ my $path = abs_path( $archive, $cwd );
+ my $temp;
+ # Whether archive is a fat or thin?
+ my $bulk;
+ execute( [ "file", $path ], -stdout => \$bulk );
+ if ( $bulk =~ m{Mach-O universal binary} ) {
+ # Archive is fat, extracy thin archive first.
+ if ( not defined( $arch ) ) {
+ runtime_error(
+ "\"$archive\" archive is universal binary, " .
+ "please specify architecture to work with"
+ );
+ }; # if
+ ( undef, $temp ) = File::Temp::tempfile();
+ execute( [ "libtool", "-static", "-arch_only", $arch, "-o", $temp, $path ] );
+ $path = $temp;
+ }; # if
+ execute( [ "ar", "xo", $path ] ); # Extract members.
+ if ( defined( $temp ) ) { # Delete temp file, if any.
+ del_file( $temp );
+ }; # if
+ }; # foreach $archive
+ change_dir( $cwd );
+}; # sub darwin
+
+
+# --------------------------------------------------------------------------------------------------
+# Main.
+# --------------------------------------------------------------------------------------------------
+
+# Parse command line.
+
+my $output = ".";
+my @args;
+
+get_options(
+ Platform::target_options(),
+ "o|output-directory=s" => \$output,
+);
+@args = @ARGV;
+
+if ( not -e $output ) {
+ runtime_error( "Output directory \"$output\" does not exist" );
+}; # if
+if ( not -d $output ) {
+ runtime_error( "\"$output\" is not a directory" );
+}; # if
+if ( not -w $output ) {
+ runtime_error( "Output directory \"$output\" is not writable" );
+}; # if
+
+if ( $target_os eq "win" ) {
+ *process = \&windows;
+} elsif ( $target_os eq "lin" or $target_os eq "lrb" ) {
+ *process = \&linux;
+} elsif ( $target_os eq "mac" ) {
+ *process = \&darwin;
+} else {
+ runtime_error( "OS \"$target_os\" not supported" );
+}; # if
+
+
+# Do the work.
+process( $target_arch, $output, @args );
+exit( 0 );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<extract-objects.pl> -- Extract all object files from static library.
+
+=head1 SYNOPSIS
+
+B<extract-objects.pl> I<option>... I<archive>...
+
+=head1 OPTIONS
+
+=over
+
+=item B<--architecture=>I<arch>
+
+Specify architecture to work with. The option is mandatory on OS X* in case of universal archive.
+In other cases the option should not be used. I<arch> may be one of C<32> or C<32e>.
+
+=item B<--os=>I<str>
+
+Specify OS name. By default OS is autodetected.
+
+Depending on OS, B<extract-objects.pl> uses different external tools for handling static
+libraries: F<ar> (in case of "lin" and "mac") or F<lib.exe> (in case of "win").
+
+=item B<--output-directory=>I<dir>
+
+Specify directory to write extracted members to. Current directory is used by default.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full documentation and exit.
+
+=item B<--quiet>
+
+Do not print information messages.
+
+=item B<--version>
+
+Print version and exit.
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<archive>
+
+A name of archive file (static library). Multiple archives may be specified.
+
+=back
+
+=head1 DESCRIPTION
+
+The script extracts all the members (object files) from archive (static library) to specified
+directory. Commands to perform this action differ on different OSes. On Linux* OS, simple command
+
+ ar xo libfile.a
+
+is enough (in case of extracting files to current directory).
+
+On OS X*, it is a bit compilicated with universal ("fat") binaries -- C<ar> cannot
+operate on fat archives, so "thin" archive should be extracted from the universal binary first.
+
+On Windows* OS, library manager (C<lib.exe>) can extract only one object file, so operation should be
+repeated for every object file in the library.
+
+B<extract-objects.pl> detects OS automatically. But detection can be overrided with B<--os> option.
+It may be helpful in cross-build environments.
+
+B<extract-objects.pl> effectively encapsulates all these details and provides uniform way for
+extracting object files from static libraries, which helps to keep makefiles simple and clean.
+
+=head1 EXAMPLES
+
+Extract object files from library F<libirc.lib>, and put them into F<obj/> directory:
+
+ $ extract-objects.pl --output=obj libirc.lib
+
+Extract object files from library F<libirc.a>. Use Linux* OS tools (F<ar>), even if run on another OS:
+
+ $ extract-objects.pl --os=lin libirc.a
+
+Extract object files from library F<libirc.a>, if it is a OS X* universal binary, use i386
+architecture. Be quiet:
+
+ $ extract-objects.pl --quiet --arch=i386 libirc.a
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/generate-def.pl b/runtime/tools/generate-def.pl
new file mode 100755
index 0000000..1ebc424
--- /dev/null
+++ b/runtime/tools/generate-def.pl
@@ -0,0 +1,321 @@
+#!/usr/bin/env perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+# Some pragmas.
+use strict; # Restrict unsafe constructs.
+use warnings; # Enable all warnings.
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+
+our $VERSION = "0.004";
+
+#
+# Subroutines.
+#
+
+sub parse_input($\%) {
+
+ my ( $input, $defs ) = @_;
+ my @bulk = read_file( $input );
+ my %entries;
+ my %ordinals;
+ my @dirs;
+ my $value = 1;
+
+ my $error =
+ sub {
+ my ( $msg, $l, $line ) = @_;
+ runtime_error(
+ "Error parsing file \"$input\" line $l:\n" .
+ " $line" .
+ ( $msg ? $msg . "\n" : () )
+ );
+ }; # sub
+
+ my $n = 0; # Line number.
+ foreach my $line ( @bulk ) {
+ ++ $n;
+ if ( 0 ) {
+ } elsif ( $line =~ m{^\s*(?:#|\n)} ) {
+ # Empty line or comment. Skip it.
+ } elsif ( $line =~ m{^\s*%} ) {
+ # A directive.
+ if ( 0 ) {
+ } elsif ( $line =~ m{^\s*%\s*if(n)?def\s+([A-Za-z0-9_]+)\s*(?:#|\n)} ) {
+ my ( $negation, $name ) = ( $1, $2 );
+ my $dir = { n => $n, line => $line, name => $name, value => $value };
+ push( @dirs, $dir );
+ $value = ( $value and ( $negation xor $defs->{ $name } ) );
+ } elsif ( $line =~ m{^\s*%\s*endif\s*(?:#|\n)} ) {
+ if ( not @dirs ) {
+ $error->( "Orphan %endif directive.", $n, $line );
+ }; # if
+ my $dir = pop( @dirs );
+ $value = $dir->{ value };
+ } else {
+ $error->( "Bad directive.", $n, $line );
+ }; # if
+ } elsif ( $line =~ m{^\s*(-)?\s*([A-Za-z0-9_]+)(?:\s+(\d+|DATA))?\s*(?:#|\n)} ) {
+ my ( $obsolete, $entry, $ordinal ) = ( $1, $2, $3 );
+ if ( $value ) {
+ if ( exists( $entries{ $entry } ) ) {
+ $error->( "Entry \"$entry\" has already been specified.", $n, $line );
+ }; # if
+ $entries{ $entry } = { ordinal => $ordinal, obsolete => defined( $obsolete ) };
+ if ( defined( $ordinal ) and $ordinal ne "DATA" ) {
+ if ( $ordinal >= 1000 and $entry =~ m{\A[ok]mp_} ) {
+ $error->( "Ordinal of user-callable entry must be < 1000", $n, $line );
+ }; # if
+ if ( $ordinal >= 1000 and $ordinal < 2000 ) {
+ $error->( "Ordinals between 1000 and 1999 are reserved.", $n, $line );
+ }; # if
+ if ( exists( $ordinals{ $ordinal } ) ) {
+ $error->( "Ordinal $ordinal has already been used.", $n, $line );
+ }; # if
+ $ordinals{ $ordinal } = $entry;
+ }; # if
+ }; # if
+ } else {
+ $error->( "", $n, $line );
+ }; # if
+ }; # foreach
+
+ if ( @dirs ) {
+ my $dir = pop( @dirs );
+ $error->( "Unterminated %if direcive.", $dir->{ n }, $dir->{ line } );
+ }; # while
+
+ return %entries;
+
+}; # sub parse_input
+
+sub process(\%) {
+
+ my ( $entries ) = @_;
+
+ foreach my $entry ( keys( %$entries ) ) {
+ if ( not $entries->{ $entry }->{ obsolete } ) {
+ my $ordinal = $entries->{ $entry }->{ ordinal };
+ if ( $entry =~ m{\A[ok]mp_} ) {
+ if ( not defined( $ordinal ) or $ordinal eq "DATA" ) {
+ runtime_error(
+ "Bad entry \"$entry\": ordinal number is not specified."
+ );
+ }; # if
+ $entries->{ uc( $entry ) } = { ordinal => 1000 + $ordinal };
+ }; # if
+ }; # if
+ }; # foreach
+
+ return %$entries;
+
+}; # sub process
+
+sub generate_output(\%$) {
+
+ my ( $entries, $output ) = @_;
+ my $bulk;
+
+ $bulk = "EXPORTS\n";
+ foreach my $entry ( sort( keys( %$entries ) ) ) {
+ if ( not $entries->{ $entry }->{ obsolete } ) {
+ $bulk .= sprintf( " %-40s ", $entry );
+ my $ordinal = $entries->{ $entry }->{ ordinal };
+ if ( defined( $ordinal ) ) {
+ if ( $ordinal eq "DATA" ) {
+ $bulk .= "DATA";
+ } else {
+ $bulk .= "\@" . $ordinal;
+ }; # if
+ }; # if
+ $bulk .= "\n";
+ }; # if
+ }; # foreach
+ if ( defined( $output ) ) {
+ write_file( $output, \$bulk );
+ } else {
+ print( $bulk );
+ }; # if
+
+}; # sub generate_ouput
+
+#
+# Parse command line.
+#
+
+my $input; # The name of input file.
+my $output; # The name of output file.
+my %defs;
+
+get_options(
+ "output=s" => \$output,
+ "D|define=s" =>
+ sub {
+ my ( $opt_name, $opt_value ) = @_;
+ my ( $def_name, $def_value );
+ if ( $opt_value =~ m{\A(.*?)=(.*)\z} ) {
+ ( $def_name, $def_value ) = ( $1, $2 );
+ } else {
+ ( $def_name, $def_value ) = ( $opt_value, 1 );
+ }; # if
+ $defs{ $def_name } = $def_value;
+ },
+);
+
+if ( @ARGV == 0 ) {
+ cmdline_error( "Not enough arguments." );
+}; # if
+if ( @ARGV > 1 ) {
+ cmdline_error( "Too many arguments." );
+}; # if
+$input = shift( @ARGV );
+
+#
+# Work.
+#
+
+my %data = parse_input( $input, %defs );
+%data = process( %data );
+generate_output( %data, $output );
+exit( 0 );
+
+__END__
+
+#
+# Embedded documentation.
+#
+
+=pod
+
+=head1 NAME
+
+B<generate-def.pl> -- Generate def file for OpenMP RTL.
+
+=head1 SYNOPSIS
+
+B<generate-def.pl> I<OPTION>... I<file>
+
+=head1 OPTIONS
+
+=over
+
+=item B<--define=>I<name>[=I<value>]
+
+=item B<-D> I<name>[=I<value>]
+
+Define specified name. If I<value> is ommitted, I<name> is defined to 1. If I<value> is 0 or empty,
+name is B<not> defined.
+
+=item B<--output=>I<file>
+
+=item B<-o> I<file>
+
+Specify output file name. If option is not present, result is printed to stdout.
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full help message and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--usage>
+
+Print very short usage message and exit.
+
+=item B<--verbose>
+
+Do print informational messages.
+
+=item B<--version>
+
+Print version and exit.
+
+=item B<--quiet>
+
+Work quiet, do not print informational messages.
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<file>
+
+A name of input file.
+
+=back
+
+=head1 DESCRIPTION
+
+The script reads input file, process conditional directives, checks content for consistency, and
+generates ouptput file suitable for linker.
+
+=head2 Input File Format
+
+=over
+
+=item Comments
+
+ # It's a comment.
+
+Comments start with C<#> symbol and continue to the end of line.
+
+=item Conditional Directives
+
+ %ifdef name
+ %ifndef name
+ %endif
+
+A part of file surronded by C<%ifdef I<name>> and C<%endif> directives is a conditional part -- it
+has effect only if I<name> is defined in the comman line by B<--define> option. C<%ifndef> is a
+negated version of C<%ifdef> -- conditional part has an effect only if I<name> is B<not> defined.
+
+Conditional parts may be nested.
+
+=item Export Definitions
+
+ symbol
+ symbol ordinal
+ symbol DATA
+
+Symbols starting with C<omp_> or C<kmp_> must have ordinal specified. They are subjects for special
+processing: each symbol generates two output lines: original one and upper case version. The ordinal
+number of the second is original ordinal increased by 1000.
+
+=item Obsolete Symbols
+
+ - symbol
+ - symbol ordinal
+ - symbol DATA
+
+Obsolete symbols look like export definitions prefixed with minus sign. Obsolete symbols do not
+affect the output, but obsolete symbols and their ordinals cannot be (re)used in export definitions.
+
+=back
+
+=head1 EXAMPLES
+
+ $ generate-def.pl -D stub -D USE_TCHECK=0 -o libguide.def dllexport
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/lib/Build.pm b/runtime/tools/lib/Build.pm
new file mode 100644
index 0000000..cf67156
--- /dev/null
+++ b/runtime/tools/lib/Build.pm
@@ -0,0 +1,264 @@
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+package Build;
+
+use strict;
+use warnings;
+
+use Cwd qw{};
+
+use LibOMP;
+use tools;
+use Uname;
+use Platform ":vars";
+
+my $host = Uname::host_name();
+my $root = $ENV{ LIBOMP_WORK };
+my $tmp = $ENV{ LIBOMP_TMP };
+my $out = $ENV{ LIBOMP_EXPORTS };
+
+my @jobs;
+our $start = time();
+
+# --------------------------------------------------------------------------------------------------
+# Helper functions.
+# --------------------------------------------------------------------------------------------------
+
+# tstr -- Time string. Returns string "yyyy-dd-mm hh:mm:ss UTC".
+sub tstr(;$) {
+ my ( $time ) = @_;
+ if ( not defined( $time ) ) {
+ $time = time();
+ }; # if
+ my ( $sec, $min, $hour, $day, $month, $year ) = gmtime( $time );
+ $month += 1;
+ $year += 1900;
+ my $str = sprintf( "%04d-%02d-%02d %02d:%02d:%02d UTC", $year, $month, $day, $hour, $min, $sec );
+ return $str;
+}; # sub tstr
+
+# dstr -- Duration string. Returns string "hh:mm:ss".
+sub dstr($) {
+ # Get time in seconds and format it as time in hours, minutes, seconds.
+ my ( $sec ) = @_;
+ my ( $h, $m, $s );
+ $h = int( $sec / 3600 );
+ $sec = $sec - $h * 3600;
+ $m = int( $sec / 60 );
+ $sec = $sec - $m * 60;
+ $s = int( $sec );
+ $sec = $sec - $s;
+ return sprintf( "%02d:%02d:%02d", $h, $m, $s );
+}; # sub dstr
+
+# rstr -- Result string.
+sub rstr($) {
+ my ( $rc ) = @_;
+ return ( $rc == 0 ? "+++ Success +++" : "--- Failure ---" );
+}; # sub rstr
+
+sub shorter($;$) {
+ # Return shorter variant of path -- either absolute or relative.
+ my ( $path, $base ) = @_;
+ my $abs = abs_path( $path );
+ my $rel = rel_path( $path, $base );
+ if ( $rel eq "" ) {
+ $rel = ".";
+ }; # if
+ $path = ( length( $rel ) < length( $abs ) ? $rel : $abs );
+ if ( $target_os eq "win" ) {
+ $path =~ s{\\}{/}g;
+ }; # if
+ return $path;
+}; # sub shorter
+
+sub tee($$) {
+
+ my ( $action, $file ) = @_;
+ my $pid = 0;
+
+ my $save_stdout = Symbol::gensym();
+ my $save_stderr = Symbol::gensym();
+
+ # --- redirect stdout ---
+ STDOUT->flush();
+ # Save stdout in $save_stdout.
+ open( $save_stdout, ">&" . STDOUT->fileno() )
+ or die( "Cannot dup filehandle: $!; stopped" );
+ # Redirect stdout to tee or to file.
+ if ( $tools::verbose ) {
+ $pid = open( STDOUT, "| tee -a \"$file\"" )
+ or die "Cannot open pipe to \"tee\": $!; stopped";
+ } else {
+ open( STDOUT, ">>$file" )
+ or die "Cannot open file \"$file\" for writing: $!; stopped";
+ }; # if
+
+ # --- redirect stderr ---
+ STDERR->flush();
+ # Save stderr in $save_stderr.
+ open( $save_stderr, ">&" . STDERR->fileno() )
+ or die( "Cannot dup filehandle: $!; stopped" );
+ # Redirect stderr to stdout.
+ open( STDERR, ">&" . STDOUT->fileno() )
+ or die( "Cannot dup filehandle: $!; stopped" );
+
+ # Perform actions.
+ $action->();
+
+ # --- restore stderr ---
+ STDERR->flush();
+ # Restore stderr from $save_stderr.
+ open( STDERR, ">&" . $save_stderr->fileno() )
+ or die( "Cannot dup filehandle: $!; stopped" );
+ # Close $save_stderr.
+ $save_stderr->close() or die ( "Cannot close filehandle: $!; stopped" );
+
+ # --- restore stdout ---
+ STDOUT->flush();
+ # Restore stdout from $save_stdout.
+ open( STDOUT, ">&" . $save_stdout->fileno() )
+ or die( "Cannot dup filehandle: $!; stopped" );
+ # Close $save_stdout.
+ $save_stdout->close() or die ( "Cannot close filehandle: $!; stopped" );
+
+ # Wait for the child tee process, otherwise output of make and build.pl interleaves.
+ if ( $pid != 0 ) {
+ waitpid( $pid, 0 );
+ }; # if
+
+}; # sub tee
+
+sub log_it($$@) {
+ my ( $title, $format, @args ) = @_;
+ my $message = sprintf( $format, @args );
+ my $progress = cat_file( $tmp, sprintf( "%s-%s.log", $target_platform, Uname::host_name() ) );
+ if ( $title ne "" and $message ne "" ) {
+ my $line = sprintf( "%-15s : %s\n", $title, $message );
+ info( $line );
+ write_file( $progress, tstr() . ": " . $line, -append => 1 );
+ } else {
+ write_file( $progress, "\n", -append => 1 );
+ }; # if
+}; # sub log_it
+
+sub progress($$@) {
+ my ( $title, $format, @args ) = @_;
+ log_it( $title, $format, @args );
+}; # sub progress
+
+sub summary() {
+ my $total = @jobs;
+ my $success = 0;
+ my $finish = time();
+ foreach my $job ( @jobs ) {
+ my ( $build_dir, $rc ) = ( $job->{ build_dir }, $job->{ rc } );
+ progress( rstr( $rc ), "%s", $build_dir );
+ if ( $rc == 0 ) {
+ ++ $success;
+ }; # if
+ }; # foreach $job
+ my $failure = $total - $success;
+ progress( "Successes", "%3d of %3d", $success, $total );
+ progress( "Failures", "%3d of %3d", $failure, $total );
+ progress( "Time elapsed", " %s", dstr( $finish - $start ) );
+ progress( "Overall result", "%s", rstr( $failure ) );
+ return $failure;
+}; # sub summary
+
+# --------------------------------------------------------------------------------------------------
+# Worker functions.
+# --------------------------------------------------------------------------------------------------
+
+sub init() {
+ make_dir( $tmp );
+}; # sub init
+
+sub clean(@) {
+ # Clean directories.
+ my ( @dirs ) = @_;
+ my $exit = 0;
+ # Mimisc makefile -- print a command.
+ print( "rm -f -r " . join( " ", map( shorter( $_ ) . "/*", @dirs ) ) . "\n" );
+ $exit =
+ execute(
+ [ $^X, cat_file( $ENV{ LIBOMP_WORK }, "tools", "clean-dir.pl" ), @dirs ],
+ -ignore_status => 1,
+ ( $tools::verbose ? () : ( -stdout => undef, -stderr => "" ) ),
+ );
+ return $exit;
+}; # sub clean
+
+sub make($$$) {
+ # Change dir to build one and run make.
+ my ( $job, $clean, $marker ) = @_;
+ my $dir = $job->{ build_dir };
+ my $makefile = $job->{ makefile };
+ my $args = $job->{ make_args };
+ my $cwd = Cwd::cwd();
+ my $width = -10;
+
+ my $exit;
+ $dir = cat_dir( $tmp, $dir );
+ make_dir( $dir );
+ change_dir( $dir );
+
+ my $actions =
+ sub {
+ my $start = time();
+ $makefile = shorter( $makefile );
+ print( "-" x 79, "\n" );
+ printf( "%${width}s: %s\n", "Started", tstr( $start ) );
+ printf( "%${width}s: %s\n", "Root dir", $root );
+ printf( "%${width}s: %s\n", "Build dir", shorter( $dir, $root ) );
+ printf( "%${width}s: %s\n", "Makefile", $makefile );
+ print( "-" x 79, "\n" );
+ {
+ # Use shorter LIBOMP_WORK to have shorter command lines.
+ # Note: Some tools may not work if current dir is changed.
+ local $ENV{ LIBOMP_WORK } = shorter( $ENV{ LIBOMP_WORK } );
+ $exit =
+ execute(
+ [
+ "make",
+ "-r",
+ "-f", $makefile,
+ "arch=" . $target_arch,
+ "marker=$marker",
+ @$args
+ ],
+ -ignore_status => 1
+ );
+ if ( $clean and $exit == 0 ) {
+ $exit = clean( $dir );
+ }; # if
+ }
+ my $finish = time();
+ print( "-" x 79, "\n" );
+ printf( "%${width}s: %s\n", "Finished", tstr( $finish ) );
+ printf( "%${width}s: %s\n", "Elapsed", dstr( $finish - $start ) );
+ printf( "%${width}s: %s\n", "Result", rstr( $exit ) );
+ print( "-" x 79, "\n" );
+ print( "\n" );
+ }; # sub
+ tee( $actions, "build.log" );
+
+ change_dir( $cwd );
+
+ # Save completed job to be able print summary later.
+ $job->{ rc } = $exit;
+ push( @jobs, $job );
+
+ return $exit;
+
+}; # sub make
+
+1;
diff --git a/runtime/tools/lib/LibOMP.pm b/runtime/tools/lib/LibOMP.pm
new file mode 100644
index 0000000..06a371f
--- /dev/null
+++ b/runtime/tools/lib/LibOMP.pm
@@ -0,0 +1,85 @@
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+package LibOMP;
+
+use strict;
+use warnings;
+
+use tools;
+
+sub empty($) {
+ my ( $var ) = @_;
+ return not exists( $ENV{ $var } ) or not defined( $ENV{ $var } ) or $ENV{ $var } eq "";
+}; # sub empty
+
+my ( $base, $out, $tmp );
+if ( empty( "LIBOMP_WORK" ) ) {
+ # $FindBin::Bin is not used intentionally because it gives real path. I want to use absolute,
+ # but not real one (real path does not contain symlinks while absolute path may contain
+ # symlinks).
+ $base = get_dir( get_dir( abs_path( $0 ) ) );
+} else {
+ $base = abs_path( $ENV{ LIBOMP_WORK } );
+}; # if
+
+if ( empty( "LIBOMP_EXPORTS" ) ) {
+ $out = cat_dir( $base, "exports" );
+} else {
+ $out = abs_path( $ENV{ LIBOMP_EXPORTS } );
+}; # if
+
+if ( empty( "LIBOMP_TMP" ) ) {
+ $tmp = cat_dir( $base, "tmp" );
+} else {
+ $tmp = abs_path( $ENV{ LIBOMP_TMP } );
+}; # if
+
+$ENV{ LIBOMP_WORK } = $base;
+$ENV{ LIBOMP_EXPORTS } = $out;
+$ENV{ LIBOMP_TMP } = $tmp;
+
+return 1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<LibOMP.pm> --
+
+=head1 SYNOPSIS
+
+ use FindBin;
+ use lib "$FindBin::Bin/lib";
+ use LibOMP;
+
+ $ENV{ LIBOMP_WORK }
+ $ENV{ LIBOMP_TMP }
+ $ENV{ LIBOMP_EXPORTS }
+
+=head1 DESCRIPTION
+
+The module checks C<LIBOMP_WORK>, C<LIBOMP_EXPORTS>, and C<LIBOMP_TMP> environments variables.
+If a variable set, the module makes sure it is absolute. If a variable does not exist, the module
+sets it to default value.
+
+Default value for C<LIBOMP_EXPORTS> is C<$LIBOMP_WORK/exports>, for C<LIBOMP_TMP> --
+C<$LIBOMP_WORK/tmp>.
+
+Value for C<LIBOMP_WORK> is guessed. The module assumes the script (which uses the module) is
+located in C<tools/> directory of libomp directory tree, and uses path of the script to calculate
+C<LIBOMP_WORK>,
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/lib/Platform.pm b/runtime/tools/lib/Platform.pm
new file mode 100644
index 0000000..584eeb7
--- /dev/null
+++ b/runtime/tools/lib/Platform.pm
@@ -0,0 +1,386 @@
+#
+# This is not a runnable script, it is a Perl module, a collection of variables, subroutines, etc.
+# to be used in Perl scripts.
+#
+# To get help about exported variables and subroutines, execute the following command:
+#
+# perldoc Platform.pm
+#
+# or see POD (Plain Old Documentation) imbedded to the source...
+#
+#
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+package Platform;
+
+use strict;
+use warnings;
+
+use base "Exporter";
+
+use Uname;
+
+my @vars;
+
+BEGIN {
+ @vars = qw{ $host_arch $host_os $host_platform $target_arch $target_os $target_platform };
+}
+
+our $VERSION = "0.014";
+our @EXPORT = qw{};
+our @EXPORT_OK = ( qw{ canon_arch canon_os legal_arch arch_opt }, @vars );
+our %EXPORT_TAGS = ( all => [ @EXPORT_OK ], vars => \@vars );
+
+# Canonize architecture name.
+sub canon_arch($) {
+ my ( $arch ) = @_;
+ if ( defined( $arch ) ) {
+ if ( $arch =~ m{\A\s*(?:32|IA-?32|IA-?32 architecture|i[3456]86|x86)\s*\z}i ) {
+ $arch = "32";
+ } elsif ( $arch =~ m{\A\s*(?:48|(?:ia)?32e|Intel\s*64|Intel\(R\)\s*64|x86[_-]64|x64|AMD64)\s*\z}i ) {
+ $arch = "32e";
+ } else {
+ $arch = undef;
+ }; # if
+ }; # if
+ return $arch;
+}; # sub canon_arch
+
+{ # Return legal approved architecture name.
+ my %legal = (
+ "32" => "IA-32 architecture",
+ "32e" => "Intel(R) 64",
+ );
+
+ sub legal_arch($) {
+ my ( $arch ) = @_;
+ $arch = canon_arch( $arch );
+ if ( defined( $arch ) ) {
+ $arch = $legal{ $arch };
+ }; # if
+ return $arch;
+ }; # sub legal_arch
+}
+
+{ # Return architecture name suitable for Intel compiler setup scripts.
+ my %option = (
+ "32" => "ia32",
+ "32e" => "intel64",
+ "64" => "ia64",
+ );
+
+ sub arch_opt($) {
+ my ( $arch ) = @_;
+ $arch = canon_arch( $arch );
+ if ( defined( $arch ) ) {
+ $arch = $option{ $arch };
+ }; # if
+ return $arch;
+ }; # sub arch_opt
+}
+
+# Canonize OS name.
+sub canon_os($) {
+ my ( $os ) = @_;
+ if ( defined( $os ) ) {
+ if ( $os =~ m{\A\s*(?:Linux|lin|l)\s*\z}i ) {
+ $os = "lin";
+ } elsif ( $os =~ m{\A\s*(?:lrb)\s*\z}i ) {
+ $os = "lrb";
+ } elsif ( $os =~ m{\A\s*(?:Mac(?:\s*OS(?:\s*X)?)?|mac|m|Darwin)\s*\z}i ) {
+ $os = "mac";
+ } elsif ( $os =~ m{\A\s*(?:Win(?:dows)?(?:(?:_|\s*)?(?:NT|XP|95|98|2003))?|w)\s*\z}i ) {
+ $os = "win";
+ } else {
+ $os = undef;
+ }; # if
+ }; # if
+ return $os;
+}; # sub canon_os
+
+my ( $_host_os, $_host_arch, $_target_os, $_target_arch );
+
+sub set_target_arch($) {
+ my ( $arch ) = canon_arch( $_[ 0 ] );
+ if ( defined( $arch ) ) {
+ $_target_arch = $arch;
+ $ENV{ LIBOMP_ARCH } = $arch;
+ }; # if
+ return $arch;
+}; # sub set_target_arch
+
+sub set_target_os($) {
+ my ( $os ) = canon_os( $_[ 0 ] );
+ if ( defined( $os ) ) {
+ $_target_os = $os;
+ $ENV{ LIBOMP_OS } = $os;
+ }; # if
+ return $os;
+}; # sub set_target_os
+
+sub target_options() {
+ my @options = (
+ "target-os|os=s" =>
+ sub {
+ set_target_os( $_[ 1 ] ) or
+ die "Bad value of --target-os option: \"$_[ 1 ]\"\n";
+ },
+ "target-architecture|targert-arch|architecture|arch=s" =>
+ sub {
+ set_target_arch( $_[ 1 ] ) or
+ die "Bad value of --target-architecture option: \"$_[ 1 ]\"\n";
+ },
+ );
+ return @options;
+}; # sub target_options
+
+# Detect host arch.
+{
+ my $hardware_platform = Uname::hardware_platform();
+ if ( 0 ) {
+ } elsif ( $hardware_platform eq "i386" ) {
+ $_host_arch = "32";
+ } elsif ( $hardware_platform eq "ia64" ) {
+ $_host_arch = "64";
+ } elsif ( $hardware_platform eq "x86_64" ) {
+ $_host_arch = "32e";
+ } else {
+ die "Unsupported host hardware platform: \"$hardware_platform\"; stopped";
+ }; # if
+}
+
+# Detect host OS.
+{
+ my $operating_system = Uname::operating_system();
+ if ( 0 ) {
+ } elsif ( $operating_system eq "GNU/Linux" ) {
+ $_host_os = "lin";
+ } elsif ( $operating_system eq "Darwin" ) {
+ $_host_os = "mac";
+ } elsif ( $operating_system eq "MS Windows" ) {
+ $_host_os = "win";
+ } else {
+ die "Unsupported host operating system: \"$operating_system\"; stopped";
+ }; # if
+}
+
+# Detect target arch.
+if ( defined( $ENV{ LIBOMP_ARCH } ) ) {
+ # Use arch specified in LIBOMP_ARCH.
+ $_target_arch = canon_arch( $ENV{ LIBOMP_ARCH } );
+ if ( not defined( $_target_arch ) ) {
+ die "Uknown architecture specified in LIBOMP_ARCH environment variable: \"$ENV{ LIBOMP_ARCH }\"";
+ }; # if
+} else {
+ # Otherwise use host architecture.
+ $_target_arch = $_host_arch;
+}; # if
+$ENV{ LIBOMP_ARCH } = $_target_arch;
+
+# Detect target OS.
+if ( defined( $ENV{ LIBOMP_OS } ) ) {
+ # Use OS specified in LIBOMP_OS.
+ $_target_os = canon_os( $ENV{ LIBOMP_OS } );
+ if ( not defined( $_target_os ) ) {
+ die "Uknown OS specified in LIBOMP_OS environment variable: \"$ENV{ LIBOMP_OS }\"";
+ }; # if
+} else {
+ # Otherwise use host OS.
+ $_target_os = $_host_os;
+}; # if
+$ENV{ LIBOMP_OS } = $_target_os;
+
+use vars @vars;
+
+tie( $host_arch, "Platform::host_arch" );
+tie( $host_os, "Platform::host_os" );
+tie( $host_platform, "Platform::host_platform" );
+tie( $target_arch, "Platform::target_arch" );
+tie( $target_os, "Platform::target_os" );
+tie( $target_platform, "Platform::target_platform" );
+
+{ package Platform::base;
+
+ use Carp;
+
+ use Tie::Scalar;
+ use base "Tie::StdScalar";
+
+ sub STORE {
+ my $self = shift( @_ );
+ croak( "Modifying \$" . ref( $self ) . " is not allowed; stopped" );
+ }; # sub STORE
+
+} # package Platform::base
+
+{ package Platform::host_arch;
+ use base "Platform::base";
+ sub FETCH {
+ return $_host_arch;
+ }; # sub FETCH
+} # package Platform::host_arch
+
+{ package Platform::host_os;
+ use base "Platform::base";
+ sub FETCH {
+ return $_host_os;
+ }; # sub FETCH
+} # package Platform::host_os
+
+{ package Platform::host_platform;
+ use base "Platform::base";
+ sub FETCH {
+ return "${_host_os}_${_host_arch}";
+ }; # sub FETCH
+} # package Platform::host_platform
+
+{ package Platform::target_arch;
+ use base "Platform::base";
+ sub FETCH {
+ return $_target_arch;
+ }; # sub FETCH
+} # package Platform::target_arch
+
+{ package Platform::target_os;
+ use base "Platform::base";
+ sub FETCH {
+ return $_target_os;
+ }; # sub FETCH
+} # package Platform::target_os
+
+{ package Platform::target_platform;
+ use base "Platform::base";
+ sub FETCH {
+ return "${_target_os}_${_target_arch}";
+ }; # sub FETCH
+} # package Platform::target_platform
+
+
+return 1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<Platform.pm> -- Few subroutines to get OS, architecture and platform name in form suitable for
+naming files, directories, macros, etc.
+
+=head1 SYNOPSIS
+
+ use Platform ":all";
+ use tools;
+
+ my $arch = canon_arch( "em64T" ); # Returns "32e".
+ my $legal = legal_arch( "em64t" ); # Returns "Intel(R) 64".
+ my $option = arch_opt( "em64t" ); # Returns "intel64".
+ my $os = canon_os( "Windows NT" ); # Returns "win".
+
+ print( $host_arch, $host_os, $host_platform );
+ print( $taregt_arch, $target_os, $target_platform );
+
+ tools::get_options(
+ Platform::target_options(),
+ ...
+ );
+
+
+=head1 DESCRIPTION
+
+Environment variable LIBOMP_OS specifies target OS to report. If LIBOMP_OS id not defined,
+the script assumes host OS is target OS.
+
+Environment variable LIBOMP_ARCH specifies target architecture to report. If LIBOMP_ARCH is not defined,
+the script assumes host architecture is target one.
+
+=head2 Functions.
+
+=over
+
+=item B<canon_arch( $arch )>
+
+Input string is an architecture name to canonize. The function recognizes many variants, for example:
+C<32e>, C<Intel64>, C<Intel(R) 64>, etc. Returned string is a canononized architecture name,
+one of: C<32>, C<32e>, C<64>, or C<undef> is input string is not recognized.
+
+=item B<legal_arch( $arch )>
+
+Input string is architecture name. The function recognizes the same variants as C<arch_canon()> does.
+Returned string is a name approved by Intel Legal, one of: C<IA-32 architecture>, C<Intel(R) 64>
+or C<undef> if input string is not recognized.
+
+=item B<arch_opt( $arch )>
+
+Input string is architecture name. The function recognizes the same variants as C<arch_canon()> does.
+Returned string is an architecture name suitable for passing to compiler setup scripts
+(e. g. C<iccvars.sh>), one of: C<IA-32 architecture>, C<Intel(R) 64> or C<undef> if input string is not
+recognized.
+
+=item B<canon_os( $os )>
+
+Input string is OS name to canonize. The function recognizes many variants, for example: C<mac>, C<OS X>, etc. Returned string is a canonized OS name, one of: C<lin>, C<lrb>,
+C<mac>, C<win>, or C<undef> is input string is not recognized.
+
+=item B<target_options()>
+
+Returns array suitable for passing to C<tools::get_options()> to let a script recognize
+C<--target-architecture=I<str>> and C<--target-os=I<str>> options. Typical usage is:
+
+ use tools;
+ use Platform;
+
+ my ( $os, $arch, $platform ); # Global variables, not initialized.
+
+ ...
+
+ get_options(
+ Platform::target_options(), # Let script recognize --target-os and --target-arch options.
+ ...
+ );
+ # Initialize variabls after parsing command line.
+ ( $os, $arch, $platform ) = ( Platform::target_os(), Platform::target_arch(), Platform::target_platform() );
+
+=back
+
+=head2 Variables
+
+=item B<$host_arch>
+
+Canonized name of host architecture.
+
+=item B<$host_os>
+
+Canonized name of host OS.
+
+=item B<$host_platform>
+
+Host platform name (concatenated canonized OS name, underscore, and canonized architecture name).
+
+=item B<$target_arch>
+
+Canonized name of target architecture.
+
+=item B<$target_os>
+
+Canonized name of target OS.
+
+=item B<$target_platform>
+
+Target platform name (concatenated canonized OS name, underscore, and canonized architecture name).
+
+=back
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/lib/Uname.pm b/runtime/tools/lib/Uname.pm
new file mode 100644
index 0000000..f978f8b
--- /dev/null
+++ b/runtime/tools/lib/Uname.pm
@@ -0,0 +1,623 @@
+#
+# This is not a runnable script, it is a Perl module, a collection of variables, subroutines, etc.
+# To get help about exported variables and subroutines, execute the following command:
+#
+# perldoc Uname.pm
+#
+# or see POD (Plain Old Documentation) embedded to the source...
+#
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+package Uname;
+
+use strict;
+use warnings;
+use warnings::register;
+use Exporter;
+
+use POSIX;
+use File::Glob ":glob";
+use Net::Domain qw{};
+
+# Following code does not work with Perl 5.6 on Linux* OS and Windows* OS:
+#
+# use if $^O eq "darwin", tools => qw{};
+#
+# The workaround for Perl 5.6:
+#
+BEGIN {
+ if ( $^O eq "darwin" or $^O eq "linux" ) {
+ require tools;
+ import tools;
+ }; # if
+ if ( $^O eq "MSWin32" ) {
+ require Win32;
+ }; # if
+}; # BEGIN
+
+my $mswin = qr{\A(?:MSWin32|Windows_NT)\z};
+
+my @posix = qw{ kernel_name fqdn kernel_release kernel_version machine };
+ # Properties supported by POSIX::uname().
+my @linux =
+ qw{ processor hardware_platform operating_system };
+ # Properties reported by uname in Linux* OS.
+my @base = ( @posix, @linux );
+ # Base properties.
+my @aux =
+ (
+ qw{ host_name domain_name },
+ map( "operating_system_$_", qw{ name release codename description } )
+ );
+ # Auxillary properties.
+my @all = ( @base, @aux );
+ # All the properties.
+my @meta = qw{ base_names all_names value };
+ # Meta functions.
+
+our $VERSION = "0.07";
+our @ISA = qw{ Exporter };
+our @EXPORT = qw{};
+our @EXPORT_OK = ( @all, @meta );
+our %EXPORT_TAGS =
+ (
+ base => [ @base ],
+ all => [ @all ],
+ meta => [ @meta ],
+ );
+
+my %values;
+ # Hash of values. Some values are strings, some may be references to code which should be
+ # evaluated to get real value. This trick is implemented because call to Net::Domain::hostfqdn()
+ # is relatively slow.
+
+# Get values from POSIX::uname().
+@values{ @posix } = POSIX::uname();
+
+# On some systems POSIX::uname() returns "short" node name (without domain name). To be consistent
+# on all systems, we will get node name from alternative source.
+if ( $^O =~ m/cygwin/i ) {
+ # Function from Net::Domain module works well, but on Cygwin it prints to
+ # stderr "domainname: not found". So we will use environment variables for now.
+ $values{ fqdn } = lc( $ENV{ COMPUTERNAME } . "." . $ENV{ USERDNSDOMAIN } );
+} else {
+ # On systems other than Cygwin, let us use Net::Domain::hostfqdn(), but do it only node name
+ # is really requested.
+ $values{ fqdn } =
+ sub {
+ my $fqdn = Net::Domain::hostfqdn(); # "fqdn" stands for "fully qualified doamain name".
+ # On some systems POSIX::uname() and Net::Domain::hostfqdn() reports different names.
+ # Let us issue a warning if they significantly different. Names are insignificantly
+ # different if POSIX::uname() matches the beginning of Net::Domain::hostfqdn().
+ if (
+ $fqdn eq substr( $fqdn, 0, length( $fqdn ) )
+ &&
+ (
+ length( $fqdn ) == length( $fqdn )
+ ||
+ substr( $fqdn, length( $fqdn ), 1 ) eq "."
+ )
+ ) {
+ # Ok.
+ } else {
+ warnings::warnif(
+ "POSIX::uname() and Net::Domain::hostfqdn() reported different names: " .
+ "\"$values{ fqdn }\" and \"$fqdn\" respectively\n"
+ );
+ }; # if
+ return $fqdn;
+ }; # sub
+}; # if
+
+if ( $^O =~ $mswin ) {
+ if (
+ $values{ machine } =~ m{\A(?:x86|[56]86)\z}
+ and
+ exists( $ENV{ PROCESSOR_ARCHITECTURE } ) and $ENV{ PROCESSOR_ARCHITECTURE } eq "x86"
+ and
+ exists( $ENV{ PROCESSOR_ARCHITEW6432 } )
+ ) {
+ if ( $ENV{ PROCESSOR_ARCHITEW6432 } eq "AMD64" ) {
+ $values{ machine } = "x86_64";
+ }; # if
+ }; # if
+}; # if
+
+# Some values are not returned by POSIX::uname(), let us compute them.
+
+# processor.
+$values{ processor } = $values{ machine };
+
+# hardware_platform.
+if ( 0 ) {
+} elsif ( $^O eq "linux" ) {
+ if ( 0 ) {
+ } elsif ( $values{ machine } =~ m{\Ai[3456]86\z} ) {
+ $values{ hardware_platform } = "i386";
+ } elsif ( $values{ machine } =~ m{\Ax86_64\z} ) {
+ $values{ hardware_platform } = "x86_64";
+ } else {
+ die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
+ }; # if
+} elsif ( $^O eq "darwin" ) {
+ if ( 0 ) {
+ } elsif ( $values{ machine } eq "x86" or $values{ machine } eq "i386" ) {
+ $values{ hardware_platform } =
+ sub {
+ my $platform = "i386";
+ # Some OSes on Intel(R) 64 still reports "i386" machine. Verify it by using
+ # the value returned by 'sysctl -n hw.optional.x86_64'. On Intel(R) 64-bit systems the
+ # value == 1; on 32-bit systems the 'hw.optional.x86_64' property either does not exist
+ # or the value == 0. The path variable does not contain a path to sysctl when
+ # started by crontab.
+ my $sysctl = ( which( "sysctl" ) or "/usr/sbin/sysctl" );
+ my $output;
+ debug( "Executing $sysctl..." );
+ execute( [ $sysctl, "-n", "hw.optional.x86_64" ], -stdout => \$output, -stderr => undef );
+ chomp( $output );
+ if ( 0 ) {
+ } elsif ( "$output" eq "" or "$output" eq "0" ) {
+ $platform = "i386";
+ } elsif ( "$output" eq "1" ) {
+ $platform = "x86_64";
+ } else {
+ die "Unsupported value (\"$output\") returned by \"$sysctl -n hw.optional.x86_64\"; stopped";
+ }; # if
+ return $platform;
+ }; # sub {
+ } elsif ( $values{ machine } eq "x86_64" ) {
+ # Some OS X* versions report "x86_64".
+ $values{ hardware_platform } = "x86_64";
+ } else {
+ die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
+ }; # if
+} elsif ( $^O =~ $mswin ) {
+ if ( 0 ) {
+ } elsif ( $values{ machine } =~ m{\A(?:x86|[56]86)\z} ) {
+ $values{ hardware_platform } = "i386";
+ } elsif ( $values{ machine } eq "x86_64" or $values{ machine } eq "amd64" ) {
+ # ActivePerl for IA-32 architecture returns "x86_64", while ActivePerl for Intel(R) 64 returns "amd64".
+ $values{ hardware_platform } = "x86_64";
+ } else {
+ die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
+ }; # if
+} elsif ( $^O eq "cygwin" ) {
+ if ( 0 ) {
+ } elsif ( $values{ machine } =~ m{\Ai[3456]86\z} ) {
+ $values{ hardware_platform } = "i386";
+ } elsif ( $values{ machine } eq "x86_64" ) {
+ $values{ hardware_platform } = "x86_64";
+ } else {
+ die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
+ }; # if
+} else {
+ die "Unsupported OS (\"$^O\"); stopped";
+}; # if
+
+# operating_system.
+if ( 0 ) {
+} elsif ( $values{ kernel_name } eq "Linux" ) {
+ $values{ operating_system } = "GNU/Linux";
+ my $release; # Name of choosen "*-release" file.
+ my $bulk; # Content of release file.
+ # On Ubuntu, lsb-release is quite informative, e. g.:
+ # DISTRIB_ID=Ubuntu
+ # DISTRIB_RELEASE=9.04
+ # DISTRIB_CODENAME=jaunty
+ # DISTRIB_DESCRIPTION="Ubuntu 9.04"
+ # Try lsb-release first. But on some older systems lsb-release is not informative.
+ # It may contain just one line:
+ # LSB_VERSION="1.3"
+ $release = "/etc/lsb-release";
+ if ( -e $release ) {
+ $bulk = read_file( $release );
+ } else {
+ $bulk = "";
+ }; # if
+ if ( $bulk =~ m{^DISTRIB_} ) {
+ # Ok, this lsb-release is informative.
+ $bulk =~ m{^DISTRIB_ID\s*=\s*(.*?)\s*$}m
+ or runtime_error( "$release: There is no DISTRIB_ID:", $bulk, "(eof)" );
+ $values{ operating_system_name } = $1;
+ $bulk =~ m{^DISTRIB_RELEASE\s*=\s*(.*?)\s*$}m
+ or runtime_error( "$release: There is no DISTRIB_RELEASE:", $bulk, "(eof)" );
+ $values{ operating_system_release } = $1;
+ $bulk =~ m{^DISTRIB_CODENAME\s*=\s*(.*?)\s*$}m
+ or runtime_error( "$release: There is no DISTRIB_CODENAME:", $bulk, "(eof)" );
+ $values{ operating_system_codename } = $1;
+ $bulk =~ m{^DISTRIB_DESCRIPTION\s*="?\s*(.*?)"?\s*$}m
+ or runtime_error( "$release: There is no DISTRIB_DESCRIPTION:", $bulk, "(eof)" );
+ $values{ operating_system_description } = $1;
+ } else {
+ # Oops. lsb-release is missed or not informative. Try other *-release files.
+ $release = "/etc/system-release";
+ if ( not -e $release ) { # Use /etc/system-release" if such file exists.
+ # Otherwise try other "/etc/*-release" files, but ignore "/etc/lsb-release".
+ my @releases = grep( $_ ne "/etc/lsb-release", bsd_glob( "/etc/*-release" ) );
+ # On some Fedora systems there are two files: fedora-release and redhat-release
+ # with identical content. If fedora-release present, ignore redjat-release.
+ if ( grep( $_ eq "/etc/fedora-release", @releases ) ) {
+ @releases = grep( $_ ne "/etc/redhat-release", @releases );
+ }; # if
+ if ( @releases == 1 ) {
+ $release = $releases[ 0 ];
+ } else {
+ if ( @releases == 0 ) {
+ # No *-release files found, try debian_version.
+ $release = "/etc/debian_version";
+ if ( not -e $release ) {
+ $release = undef;
+ warning( "No release files found in \"/etc/\" directory." );
+ }; # if
+ } else {
+ $release = undef;
+ warning( "More than one release files found in \"/etc/\" directory:", @releases );
+ }; # if
+ }; # if
+ }; # if
+ if ( defined( $release ) ) {
+ $bulk = read_file( $release );
+ if ( $release =~ m{system|redhat|fedora} ) {
+ # Red Hat or Fedora. Parse the first line of file.
+ # Typical values of *-release (one of):
+ # Red Hat Enterprise Linux* OS Server release 5.2 (Tikanga)
+ # Red Hat Enterprise Linux* OS AS release 3 (Taroon Update 4)
+ # Fedora release 10 (Cambridge)
+ $bulk =~ m{\A(.*)$}m
+ or runtime_error( "$release: Cannot find the first line:", $bulk, "(eof)" );
+ my $first_line = $1;
+ $values{ operating_system_description } = $first_line;
+ $first_line =~ m{\A(.*?)\s+release\s+(.*?)\s+\((.*?)(?:\s+Update\s+(.*?))?\)\s*$}
+ or runtime_error( "$release:1: Cannot parse line:", $first_line );
+ $values{ operating_system_name } = $1;
+ $values{ operating_system_release } = $2 . ( defined( $4 ) ? ".$4" : "" );
+ $values{ operating_system_codename } = $3;
+ } elsif ( $release =~ m{SuSE} ) {
+ # Typical SuSE-release:
+ # SUSE Linux* OS Enterprise Server 10 (x86_64)
+ # VERSION = 10
+ # PATCHLEVEL = 2
+ $bulk =~ m{\A(.*)$}m
+ or runtime_error( "$release: Cannot find the first line:", $bulk, "(eof)" );
+ my $first_line = $1;
+ $values{ operating_system_description } = $first_line;
+ $first_line =~ m{^(.*?)\s*(\d+)\s*\(.*?\)\s*$}
+ or runtime_error( "$release:1: Cannot parse line:", $first_line );
+ $values{ operating_system_name } = $1;
+ $bulk =~ m{^VERSION\s*=\s*(.*)\s*$}m
+ or runtime_error( "$release: There is no VERSION:", $bulk, "(eof)" );
+ $values{ operating_system_release } = $1;
+ if ( $bulk =~ m{^PATCHLEVEL\s*=\s*(.*)\s*$}m ) {
+ $values{ operating_system_release } .= ".$1";
+ }; # if
+ } elsif ( $release =~ m{debian_version} ) {
+ # Debian. The file debian_version contains just version number, nothing more:
+ # 4.0
+ my $name = "Debian";
+ $bulk =~ m{\A(.*)$}m
+ or runtime_error( "$release: Cannot find the first line:", $bulk, "(eof)" );
+ my $version = $1;
+ $values{ operating_system_name } = $name;
+ $values{ operating_system_release } = $version;
+ $values{ operating_system_codename } = "unknown";
+ $values{ operating_system_description } = sprintf( "%s %s", $name, $version );
+ }; # if
+ }; # if
+ }; # if
+ if ( not defined( $values{ operating_system_name } ) ) {
+ $values{ operating_system_name } = "GNU/Linux";
+ }; # if
+} elsif ( $values{ kernel_name } eq "Darwin" ) {
+ my %codenames = (
+ 10.4 => "Tiger",
+ 10.5 => "Leopard",
+ 10.6 => "Snow Leopard",
+ );
+ my $darwin;
+ my $get_os_info =
+ sub {
+ my ( $name ) = @_;
+ if ( not defined $darwin ) {
+ $darwin->{ operating_system } = "Darwin";
+ # sw_vers prints OS X* version to stdout:
+ # ProductName: OS X*
+ # ProductVersion: 10.4.11
+ # BuildVersion: 8S2167
+ # It does not print codename, so we code OS X* codenames here.
+ my $sw_vers = which( "sw_vers" ) || "/usr/bin/sw_vers";
+ my $output;
+ debug( "Executing $sw_vers..." );
+ execute( [ $sw_vers ], -stdout => \$output, -stderr => undef );
+ $output =~ m{^ProductName:\s*(.*)\s*$}m
+ or runtime_error( "There is no ProductName in sw_vers output:", $output, "(eof)" );
+ my $name = $1;
+ $output =~ m{^ProductVersion:\s*(.*)\s*$}m
+ or runtime_error( "There is no ProductVersion in sw_vers output:", $output, "(eof)" );
+ my $release = $1;
+ # Sometimes release reported as "10.4.11" (3 componentes), sometimes as "10.6".
+ # Handle both variants.
+ $release =~ m{^(\d+.\d+)(?:\.\d+)?(?=\s|$)}
+ or runtime_error( "Cannot parse OS X* version: $release" );
+ my $version = $1;
+ my $codename = ( $codenames{ $version } or "unknown" );
+ $darwin->{ operating_system_name } = $name;
+ $darwin->{ operating_system_release } = $release;
+ $darwin->{ operating_system_codename } = $codename;
+ $darwin->{ operating_system_description } = sprintf( "%s %s (%s)", $name, $release, $codename );
+ }; # if
+ return $darwin->{ $name };
+ }; # sub
+ $values{ operating_system } = sub { $get_os_info->( "operating_system" ); };
+ $values{ operating_system_name } = sub { $get_os_info->( "operating_system_name" ); };
+ $values{ operating_system_release } = sub { $get_os_info->( "operating_system_release" ); };
+ $values{ operating_system_codename } = sub { $get_os_info->( "operating_system_codename" ); };
+ $values{ operating_system_description } = sub { $get_os_info->( "operating_system_description" ); };
+} elsif ( $values{ kernel_name } =~ m{\AWindows[ _]NT\z} ) {
+ $values{ operating_system } = "MS Windows";
+ # my @os_name = Win32::GetOSName();
+ # $values{ operating_system_release } = $os_name[ 0 ];
+ # $values{ operating_system_update } = $os_name[ 1 ];
+} elsif ( $values{ kernel_name } =~ m{\ACYGWIN_NT-} ) {
+ $values{ operating_system } = "MS Windows";
+} else {
+ die "Unsuppoprted kernel_name (\"$values{ kernel_name }\") returned by POSIX::uname(); stopped";
+}; # if
+
+# host_name and domain_name
+$values{ host_name } =
+ sub {
+ my $fqdn = value( "fqdn" );
+ $fqdn =~ m{\A([^.]*)(?:\.(.*))?\z};
+ my $host_name = $1;
+ if ( not defined( $host_name ) or $host_name eq "" ) {
+ die "Unexpected error: undefined or empty host name; stopped";
+ }; # if
+ return $host_name;
+ };
+$values{ domain_name } =
+ sub {
+ my $fqdn = value( "fqdn" );
+ $fqdn =~ m{\A([^.]*)(?:\.(.*))?\z};
+ my $domain_name = $2;
+ if ( not defined( $domain_name ) or $domain_name eq "" ) {
+ die "Unexpected error: undefined or empty domain name; stopped";
+ }; # if
+ return $domain_name;
+ };
+
+# Replace undefined values with "unknown".
+foreach my $name ( @all ) {
+ if ( not defined( $values{ $name } ) ) {
+ $values{ $name } = "unknown";
+ }; # if
+}; # foreach $name
+
+# Export functions reporting properties.
+foreach my $name ( @all ) {
+ no strict "refs";
+ *$name = sub { return value( $name ); };
+}; # foreach $name
+
+# This function returns base names.
+sub base_names {
+ return @base;
+}; # sub base_names
+
+# This function returns all the names.
+sub all_names {
+ return @all;
+}; # sub all_names
+
+# This function returns value by the specified name.
+sub value($) {
+ my $name = shift( @_ );
+ if ( ref( $values{ $name } ) ) {
+ my $value = $values{ $name }->();
+ $values{ $name } = $value;
+ }; # if
+ return $values{ $name };
+}; # sub value
+
+return 1;
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<Uname.pm> -- A few subroutines to get system information usually provided by
+C</bin/uname> and C<POSIX::uname()>.
+
+=head1 SYNOPSIS
+
+ use Uname;
+
+ # Base property functions.
+ $kernel_name = Uname::kernel_name();
+ $fqdn = Uname::fqdn();
+ $kernel_release = Uname::kernel_release();
+ $kernel_version = Uname::kernel_version();
+ $machine = Uname::machine();
+ $processor = Uname::processor();
+ $hardware_platform = Uname::hardware_platform();
+ $operating_system = Uname::operating_system();
+
+ # Auxillary property functions.
+ $host_name = Uname::host_name();
+ $domain_name = Uname::domain_name();
+ $os_name = Uname::operating_system_name();
+ $os_release = Uname::operating_system_release();
+ $os_codename = Uname::operating_system_codename();
+ $os_description = Uname::operating_system_description();
+
+ # Meta functions.
+ @base_names = Uname::base_names();
+ @all_names = Uname::all_names();
+ $kernel_name = Uname::value( "kernel_name" );
+
+=head1 DESCRIPTION
+
+B<Uname.pm> resembles functionality found in C<POSIX::uname()> function or in C<uname> program.
+However, both C<POSIX::uname()> and C</bin/uname> have some disadvantages:
+
+=over
+
+=item *
+
+C<uname> may be not available in some environments, for example, in Windows* OS
+(C<uname> may be found in some third-party software packages, like MKS Toolkit or Cygwin, but it is
+not a part of OS).
+
+=item *
+
+There are many different versions of C<uname>. For example, C<uname> on OS X* does not
+recognize options C<-i>, C<-o>, and any long options.
+
+=item *
+
+Different versions of C<uname> may report the same property differently. For example,
+C<uname> on Linux* OS reports machine as C<i686>, while C<uname> on OS X* reports the same machine as
+C<x86>.
+
+=item *
+
+C<POSIX::uname()> returns list of values. I cannot recall what is the fourth element of the list.
+
+=back
+
+=head2 Base Functions
+
+Base property functions provide the information as C<uname> program.
+
+=over
+
+=item B<kernel_name()>
+
+Returns the kernel name, as reported by C<POSIX::uname()>.
+
+=item B<fqdn()>
+
+Returns the FQDN, fully qualified domain name. On some systems C<POSIX::uname()> reports short node
+name (with no domain name), on others C<POSIX::uname()> reports full node name. This
+function strive to return FQDN always (by refining C<POSIX::uname()> with
+C<Net::Domain::hostfqdn()>).
+
+=item B<kernel_release()>
+
+Returns the kernel release string, as reported by C<POSIX::uname()>. Usually the string consists of
+several numbers, separated by dots and dashes, but may also include some non-numeric substrings like
+"smp".
+
+=item B<kernel_version()>
+
+Returns the kernel version string, as reported by C<POSIX::uname()>. It is B<not> several
+dot-separated numbers but much longer string describing the kernel.
+For example, on Linux* OS it includes build date.
+If you look for something identifying the kernel, look at L<kernel_release>.
+
+=item B<machine()>
+
+Returns the machine hardware name, as reported by POSIX::uname(). Not reliable. Different OSes may
+report the same machine hardware name differently. For example, Linux* OS reports C<i686>, while OS X*
+reports C<x86> on the same machine.
+
+=item B<processor()>
+
+Returns the processor type. Not reliable. Usually the same as C<machine>.
+
+=item B<hardware_platform()>
+
+One of: C<i386> or C<x86_64>.
+
+=item B<operating_system()>
+
+One of: C<GNU/Linux>, C<OS X*>, or C<MS Windows>.
+
+=back
+
+=head2 Auxillary Functions
+
+Auxillary functions extends base functions with information not reported by C<uname> program.
+
+Auxillary functions collect information from different sources. For example, on OS X*, they may
+call C<sw_vers> program to find out OS release; on Linux* OS they may parse C</etc/redhat-release> file,
+etc.
+
+=over
+
+=item B<host_name()>
+
+Returns host name (FQDN with dropped domain part).
+
+=item B<domain_name()>
+
+Returns domain name (FQDN with dropped host part).
+
+=item B<operating_system_name>
+
+Name of operating system or name of Linux* OS distribution, like "Fedora" or
+"Red Hat Enterprise Linux* OS Server".
+
+=item B<operating_system_release>
+
+Release (version) of operating system or Linux* OS distribution. Usually it is a series of
+dot-separated numbers.
+
+=item B<operating_system_codename>
+
+Codename of operating system release or Linux* OS distribution. For example, Fedora 10 is "Cambridge"
+while OS X* 10.4 is "Tiger".
+
+=item B<operating_system_description>
+
+Longer string. Usually it includes all the operating system properting mentioned above -- name,
+release, codename in parentheses.
+
+=back
+
+=head2 Meta Functions
+
+=over
+
+=item B<base_names()>
+
+This function returns the list of base property names.
+
+=item B<all_names()>
+
+This function returns the list of all property names.
+
+=item B<value(> I<name> B<)>
+
+This function returns the value of the property specified by I<name>.
+
+=back
+
+=head1 EXAMPLES
+
+ use Uname;
+
+ print( Uname::string(), "\n" );
+
+ foreach my $name ( Uname::all_names() ) {
+ print( "$name=\"" . Uname::value( $name ) . "\"\n" );
+ }; # foreach $name
+
+=head1 SEE ALSO
+
+L<POSIX::uname>, L<uname>.
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/lib/tools.pm b/runtime/tools/lib/tools.pm
new file mode 100644
index 0000000..23feb50
--- /dev/null
+++ b/runtime/tools/lib/tools.pm
@@ -0,0 +1,1981 @@
+#
+# This is not a runnable script, it is a Perl module, a collection of variables, subroutines, etc.
+# to be used in other scripts.
+#
+# To get help about exported variables and subroutines, please execute the following command:
+#
+# perldoc tools.pm
+#
+# or see POD (Plain Old Documentation) imbedded to the source...
+#
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+=head1 NAME
+
+B<tools.pm> -- A collection of subroutines which are widely used in Perl scripts.
+
+=head1 SYNOPSIS
+
+ use FindBin;
+ use lib "$FindBin::Bin/lib";
+ use tools;
+
+=head1 DESCRIPTION
+
+B<Note:> Because this collection is small and intended for widely using in particular project,
+all variables and functions are exported by default.
+
+B<Note:> I have some ideas how to improve this collection, but it is in my long-term plans.
+Current shape is not ideal, but good enough to use.
+
+=cut
+
+package tools;
+
+use strict;
+use warnings;
+
+use vars qw( @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS );
+require Exporter;
+@ISA = qw( Exporter );
+
+my @vars = qw( $tool );
+my @utils = qw( check_opts validate );
+my @opts = qw( get_options );
+my @print = qw( debug info warning cmdline_error runtime_error question );
+my @name = qw( get_vol get_dir get_file get_name get_ext cat_file cat_dir );
+my @file = qw( which abs_path rel_path real_path make_dir clean_dir copy_dir move_dir del_dir change_dir copy_file move_file del_file );
+my @io = qw( read_file write_file );
+my @exec = qw( execute backticks );
+my @string = qw{ pad };
+@EXPORT = ( @utils, @opts, @vars, @print, @name, @file, @io, @exec, @string );
+
+use UNIVERSAL ();
+
+use FindBin;
+use IO::Handle;
+use IO::File;
+use IO::Dir;
+# Not available on some machines: use IO::Zlib;
+
+use Getopt::Long ();
+use Pod::Usage ();
+use Carp ();
+use File::Copy ();
+use File::Path ();
+use File::Temp ();
+use File::Spec ();
+use POSIX qw{ :fcntl_h :errno_h };
+use Cwd ();
+use Symbol ();
+
+use Data::Dumper;
+
+use vars qw( $tool $verbose $timestamps );
+$tool = $FindBin::Script;
+
+my @warning = ( sub {}, \&warning, \&runtime_error );
+
+
+sub check_opts(\%$;$) {
+
+ my $opts = shift( @_ ); # Referense to hash containing real options and their values.
+ my $good = shift( @_ ); # Reference to an array containing all known option names.
+ my $msg = shift( @_ ); # Optional (non-mandatory) message.
+
+ if ( not defined( $msg ) ) {
+ $msg = "unknown option(s) passed"; # Default value for $msg.
+ }; # if
+
+ # I'll use these hashes as sets of options.
+ my %good = map( ( $_ => 1 ), @$good ); # %good now is filled with all known options.
+ my %bad; # %bad is empty.
+
+ foreach my $opt ( keys( %$opts ) ) { # For each real option...
+ if ( not exists( $good{ $opt } ) ) { # Look its name in the set of known options...
+ $bad{ $opt } = 1; # Add unknown option to %bad set.
+ delete( $opts->{ $opt } ); # And delete original option.
+ }; # if
+ }; # foreach $opt
+ if ( %bad ) { # If %bad set is not empty...
+ my @caller = caller( 1 ); # Issue a warning.
+ local $Carp::CarpLevel = 2;
+ Carp::cluck( $caller[ 3 ] . ": " . $msg . ": " . join( ", ", sort( keys( %bad ) ) ) );
+ }; # if
+
+ return 1;
+
+}; # sub check_opts
+
+
+# --------------------------------------------------------------------------------------------------
+# Purpose:
+# Check subroutine arguments.
+# Synopsis:
+# my %opts = validate( params => \@_, spec => { ... }, caller => n );
+# Arguments:
+# params -- A reference to subroutine's actual arguments.
+# spec -- Specification of expected arguments.
+# caller -- ...
+# Return value:
+# A hash of validated options.
+# Description:
+# I would like to use Params::Validate module, but it is not a part of default Perl
+# distribution, so I cannot rely on it. This subroutine resembles to some extent to
+# Params::Validate::validate_with().
+# Specification of expected arguments:
+# { $opt => { type => $type, default => $default }, ... }
+# $opt -- String, option name.
+# $type -- String, expected type(s). Allowed values are "SCALAR", "UNDEF", "BOOLEAN",
+# "ARRAYREF", "HASHREF", "CODEREF". Multiple types may listed using bar:
+# "SCALAR|ARRAYREF". The type string is case-insensitive.
+# $default -- Default value for an option. Will be used if option is not specified or
+# undefined.
+#
+sub validate(@) {
+
+ my %opts = @_; # Temporary use %opts for parameters of `validate' subroutine.
+ my $params = $opts{ params };
+ my $caller = ( $opts{ caller } or 0 ) + 1;
+ my $spec = $opts{ spec };
+ undef( %opts ); # Ok, Clean %opts, now we will collect result of the subroutine.
+
+ # Find out caller package, filename, line, and subroutine name.
+ my ( $pkg, $file, $line, $subr ) = caller( $caller );
+ my @errors; # We will collect errors in array not to stop on the first found error.
+ my $error =
+ sub ($) {
+ my $msg = shift( @_ );
+ push( @errors, "$msg at $file line $line.\n" );
+ }; # sub
+
+ # Check options.
+ while ( @$params ) {
+ # Check option name.
+ my $opt = shift( @$params );
+ if ( not exists( $spec->{ $opt } ) ) {
+ $error->( "Invalid option `$opt'" );
+ shift( @$params ); # Skip value of unknow option.
+ next;
+ }; # if
+ # Check option value exists.
+ if ( not @$params ) {
+ $error->( "Option `$opt' does not have a value" );
+ next;
+ }; # if
+ my $val = shift( @$params );
+ # Check option value type.
+ if ( exists( $spec->{ $opt }->{ type } ) ) {
+ # Type specification exists. Check option value type.
+ my $actual_type;
+ if ( ref( $val ) ne "" ) {
+ $actual_type = ref( $val ) . "REF";
+ } else {
+ $actual_type = ( defined( $val ) ? "SCALAR" : "UNDEF" );
+ }; # if
+ my @wanted_types = split( m{\|}, lc( $spec->{ $opt }->{ type } ) );
+ my $wanted_types = join( "|", map( $_ eq "boolean" ? "scalar|undef" : quotemeta( $_ ), @wanted_types ) );
+ if ( $actual_type !~ m{\A(?:$wanted_types)\z}i ) {
+ $actual_type = lc( $actual_type );
+ $wanted_types = lc( join( " or ", map( "`$_'", @wanted_types ) ) );
+ $error->( "Option `$opt' value type is `$actual_type' but expected to be $wanted_types" );
+ next;
+ }; # if
+ }; # if
+ if ( exists( $spec->{ $opt }->{ values } ) ) {
+ my $values = $spec->{ $opt }->{ values };
+ if ( not grep( $_ eq $val, @$values ) ) {
+ $values = join( ", ", map( "`$_'", @$values ) );
+ $error->( "Option `$opt' value is `$val' but expected to be one of $values" );
+ next;
+ }; # if
+ }; # if
+ $opts{ $opt } = $val;
+ }; # while
+
+ # Assign default values.
+ foreach my $opt ( keys( %$spec ) ) {
+ if ( not defined( $opts{ $opt } ) and exists( $spec->{ $opt }->{ default } ) ) {
+ $opts{ $opt } = $spec->{ $opt }->{ default };
+ }; # if
+ }; # foreach $opt
+
+ # If we found any errors, raise them.
+ if ( @errors ) {
+ die join( "", @errors );
+ }; # if
+
+ return %opts;
+
+}; # sub validate
+
+# =================================================================================================
+# Get option helpers.
+# =================================================================================================
+
+=head2 Get option helpers.
+
+=cut
+
+# -------------------------------------------------------------------------------------------------
+
+=head3 get_options
+
+B<Synopsis:>
+
+ get_options( @arguments )
+
+B<Description:>
+
+It is very simple wrapper arounf Getopt::Long::GetOptions. It passes all arguments to GetOptions,
+and add definitions for standard help options: --help, --doc, --verbose, and --quiet.
+When GetOptions finihes, this subroutine checks exit code, if it is non-zero, standard error
+message is issued and script terminated.
+
+If --verbose or --quiet option is specified, C<tools.pm_verbose> environment variable is set.
+It is the way to propagate verbose/quiet mode to callee Perl scripts.
+
+=cut
+
+sub get_options {
+
+ Getopt::Long::Configure( "no_ignore_case" );
+ Getopt::Long::GetOptions(
+ "h0|usage" => sub { Pod::Usage::pod2usage( -exitval => 0, -verbose => 0 ); },
+ "h1|h|help" => sub { Pod::Usage::pod2usage( -exitval => 0, -verbose => 1 ); },
+ "h2|doc|manual" => sub { Pod::Usage::pod2usage( -exitval => 0, -verbose => 2 ); },
+ "version" => sub { print( "$tool version $main::VERSION\n" ); exit( 0 ); },
+ "v|verbose" => sub { ++ $verbose; $ENV{ "tools.pm_verbose" } = $verbose; },
+ "quiet" => sub { -- $verbose; $ENV{ "tools.pm_verbose" } = $verbose; },
+ "with-timestamps" => sub { $timestamps = 1; $ENV{ "tools.pm_timestamps" } = $timestamps; },
+ @_, # Caller argumetsa are at the end so caller options overrides standard.
+ ) or cmdline_error();
+
+}; # sub get_options
+
+
+# =================================================================================================
+# Print utilities.
+# =================================================================================================
+
+=pod
+
+=head2 Print utilities.
+
+Each of the print subroutines prepends each line of its output with the name of current script and
+the type of information, for example:
+
+ info( "Writing file..." );
+
+will print
+
+ <script>: (i): Writing file...
+
+while
+
+ warning( "File does not exist!" );
+
+will print
+
+ <script>: (!): File does not exist!
+
+Here are exported items:
+
+=cut
+
+# -------------------------------------------------------------------------------------------------
+
+sub _format_message($\@;$) {
+
+ my $prefix = shift( @_ );
+ my $args = shift( @_ );
+ my $no_eol = shift( @_ ); # Do not append "\n" to the last line.
+ my $message = "";
+
+ my $ts = "";
+ if ( $timestamps ) {
+ my ( $sec, $min, $hour, $day, $month, $year ) = gmtime();
+ $month += 1;
+ $year += 1900;
+ $ts = sprintf( "%04d-%02d-%02d %02d:%02d:%02d UTC: ", $year, $month, $day, $hour, $min, $sec );
+ }; # if
+ for my $i ( 1 .. @$args ) {
+ my @lines = split( "\n", $args->[ $i - 1 ] );
+ for my $j ( 1 .. @lines ) {
+ my $line = $lines[ $j - 1 ];
+ my $last_line = ( ( $i == @$args ) and ( $j == @lines ) );
+ my $eol = ( ( substr( $line, -1 ) eq "\n" ) or defined( $no_eol ) ? "" : "\n" );
+ $message .= "$ts$tool: ($prefix) " . $line . $eol;
+ }; # foreach $j
+ }; # foreach $i
+ return $message;
+
+}; # sub _format_message
+
+#--------------------------------------------------------------------------------------------------
+
+=pod
+
+=head3 $verbose
+
+B<Synopsis:>
+
+ $verbose
+
+B<Description:>
+
+Package variable. It determines verbosity level, which affects C<warning()>, C<info()>, and
+C<debug()> subroutnes .
+
+The variable gets initial value from C<tools.pm_verbose> environment variable if it is exists.
+If the environment variable does not exist, variable is set to 2.
+
+Initial value may be overridden later directly or by C<get_options> function.
+
+=cut
+
+$verbose = exists( $ENV{ "tools.pm_verbose" } ) ? $ENV{ "tools.pm_verbose" } : 2;
+
+#--------------------------------------------------------------------------------------------------
+
+=pod
+
+=head3 $timestamps
+
+B<Synopsis:>
+
+ $timestamps
+
+B<Description:>
+
+Package variable. It determines whether C<debug()>, C<info()>, C<warning()>, C<runtime_error()>
+subroutnes print timestamps or not.
+
+The variable gets initial value from C<tools.pm_timestamps> environment variable if it is exists.
+If the environment variable does not exist, variable is set to false.
+
+Initial value may be overridden later directly or by C<get_options()> function.
+
+=cut
+
+$timestamps = exists( $ENV{ "tools.pm_timestamps" } ) ? $ENV{ "tools.pm_timestamps" } : 0;
+
+# -------------------------------------------------------------------------------------------------
+
+=pod
+
+=head3 debug
+
+B<Synopsis:>
+
+ debug( @messages )
+
+B<Description:>
+
+If verbosity level is 3 or higher, print debug information to the stderr, prepending it with "(#)"
+prefix.
+
+=cut
+
+sub debug(@) {
+
+ if ( $verbose >= 3 ) {
+ STDOUT->flush();
+ STDERR->print( _format_message( "#", @_ ) );
+ }; # if
+ return 1;
+
+}; # sub debug
+
+#--------------------------------------------------------------------------------------------------
+
+=pod
+
+=head3 info
+
+B<Synopsis:>
+
+ info( @messages )
+
+B<Description:>
+
+If verbosity level is 2 or higher, print information to the stderr, prepending it with "(i)" prefix.
+
+=cut
+
+sub info(@) {
+
+ if ( $verbose >= 2 ) {
+ STDOUT->flush();
+ STDERR->print( _format_message( "i", @_ ) );
+ }; # if
+
+}; # sub info
+
+#--------------------------------------------------------------------------------------------------
+
+=head3 warning
+
+B<Synopsis:>
+
+ warning( @messages )
+
+B<Description:>
+
+If verbosity level is 1 or higher, issue a warning, prepending it with "(!)" prefix.
+
+=cut
+
+sub warning(@) {
+
+ if ( $verbose >= 1 ) {
+ STDOUT->flush();
+ warn( _format_message( "!", @_ ) );
+ }; # if
+
+}; # sub warning
+
+# -------------------------------------------------------------------------------------------------
+
+=head3 cmdline_error
+
+B<Synopsis:>
+
+ cmdline_error( @message )
+
+B<Description:>
+
+Print error message and exit the program with status 2.
+
+This function is intended to complain on command line errors, e. g. unknown
+options, invalid arguments, etc.
+
+=cut
+
+sub cmdline_error(;$) {
+
+ my $message = shift( @_ );
+
+ if ( defined( $message ) ) {
+ if ( substr( $message, -1, 1 ) ne "\n" ) {
+ $message .= "\n";
+ }; # if
+ } else {
+ $message = "";
+ }; # if
+ STDOUT->flush();
+ die $message . "Try --help option for more information.\n";
+
+}; # sub cmdline_error
+
+# -------------------------------------------------------------------------------------------------
+
+=head3 runtime_error
+
+B<Synopsis:>
+
+ runtime_error( @message )
+
+B<Description:>
+
+Print error message and exits the program with status 3.
+
+This function is intended to complain on runtime errors, e. g.
+directories which are not found, non-writable files, etc.
+
+=cut
+
+sub runtime_error(@) {
+
+ STDOUT->flush();
+ die _format_message( "x", @_ );
+
+}; # sub runtime_error
+
+#--------------------------------------------------------------------------------------------------
+
+=head3 question
+
+B<Synopsis:>
+
+ question( $prompt; $answer, $choices )
+
+B<Description:>
+
+Print $promp to the stderr, prepending it with "question:" prefix. Read a line from stdin, chop
+"\n" from the end, it is answer.
+
+If $answer is defined, it is treated as first user input.
+
+If $choices is specified, it could be a regexp for validating user input, or a string. In latter
+case it interpreted as list of characters, acceptable (case-insensitive) choices. If user enters
+non-acceptable answer, question continue asking until answer is acceptable.
+If $choices is not specified, any answer is acceptable.
+
+In case of end-of-file (or Ctrl+D pressed by user), $answer is C<undef>.
+
+B<Examples:>
+
+ my $answer;
+ question( "Save file [yn]? ", $answer, "yn" );
+ # We accepts only "y", "Y", "n", or "N".
+ question( "Press enter to continue or Ctrl+C to abort..." );
+ # We are not interested in answer value -- in case of Ctrl+C the script will be terminated,
+ # otherwise we continue execution.
+ question( "File name? ", $answer );
+ # Any answer is acceptable.
+
+=cut
+
+sub question($;\$$) {
+
+ my $prompt = shift( @_ );
+ my $answer = shift( @_ );
+ my $choices = shift( @_ );
+ my $a = ( defined( $answer ) ? $$answer : undef );
+
+ if ( ref( $choices ) eq "Regexp" ) {
+ # It is aready a regular expression, do nothing.
+ } elsif ( defined( $choices ) ) {
+ # Convert string to a regular expression.
+ $choices = qr/[@{ [ quotemeta( $choices ) ] }]/i;
+ }; # if
+
+ for ( ; ; ) {
+ STDERR->print( _format_message( "?", @{ [ $prompt ] }, "no_eol" ) );
+ STDERR->flush();
+ if ( defined( $a ) ) {
+ STDOUT->print( $a . "\n" );
+ } else {
+ $a = <STDIN>;
+ }; # if
+ if ( not defined( $a ) ) {
+ last;
+ }; # if
+ chomp( $a );
+ if ( not defined( $choices ) or ( $a =~ m/^$choices$/ ) ) {
+ last;
+ }; # if
+ $a = undef;
+ }; # forever
+ if ( defined( $answer ) ) {
+ $$answer = $a;
+ }; # if
+
+}; # sub question
+
+# -------------------------------------------------------------------------------------------------
+
+# Returns volume part of path.
+sub get_vol($) {
+
+ my $path = shift( @_ );
+ my ( $vol, undef, undef ) = File::Spec->splitpath( $path );
+ return $vol;
+
+}; # sub get_vol
+
+# Returns directory part of path.
+sub get_dir($) {
+
+ my $path = File::Spec->canonpath( shift( @_ ) );
+ my ( $vol, $dir, undef ) = File::Spec->splitpath( $path );
+ my @dirs = File::Spec->splitdir( $dir );
+ pop( @dirs );
+ $dir = File::Spec->catdir( @dirs );
+ $dir = File::Spec->catpath( $vol, $dir, undef );
+ return $dir;
+
+}; # sub get_dir
+
+# Returns file part of path.
+sub get_file($) {
+
+ my $path = shift( @_ );
+ my ( undef, undef, $file ) = File::Spec->splitpath( $path );
+ return $file;
+
+}; # sub get_file
+
+# Returns file part of path without last suffix.
+sub get_name($) {
+
+ my $path = shift( @_ );
+ my ( undef, undef, $file ) = File::Spec->splitpath( $path );
+ $file =~ s{\.[^.]*\z}{};
+ return $file;
+
+}; # sub get_name
+
+# Returns last suffix of file part of path.
+sub get_ext($) {
+
+ my $path = shift( @_ );
+ my ( undef, undef, $file ) = File::Spec->splitpath( $path );
+ my $ext = "";
+ if ( $file =~ m{(\.[^.]*)\z} ) {
+ $ext = $1;
+ }; # if
+ return $ext;
+
+}; # sub get_ext
+
+sub cat_file(@) {
+
+ my $path = shift( @_ );
+ my $file = pop( @_ );
+ my @dirs = @_;
+
+ my ( $vol, $dirs ) = File::Spec->splitpath( $path, "no_file" );
+ @dirs = ( File::Spec->splitdir( $dirs ), @dirs );
+ $dirs = File::Spec->catdir( @dirs );
+ $path = File::Spec->catpath( $vol, $dirs, $file );
+
+ return $path;
+
+}; # sub cat_file
+
+sub cat_dir(@) {
+
+ my $path = shift( @_ );
+ my @dirs = @_;
+
+ my ( $vol, $dirs ) = File::Spec->splitpath( $path, "no_file" );
+ @dirs = ( File::Spec->splitdir( $dirs ), @dirs );
+ $dirs = File::Spec->catdir( @dirs );
+ $path = File::Spec->catpath( $vol, $dirs, "" );
+
+ return $path;
+
+}; # sub cat_dir
+
+# =================================================================================================
+# File and directory manipulation subroutines.
+# =================================================================================================
+
+=head2 File and directory manipulation subroutines.
+
+=over
+
+=cut
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<which( $file, @options )>
+
+Searches for specified executable file in the (specified) directories.
+Raises a runtime eroror if no executable file found. Returns a full path of found executable(s).
+
+Options:
+
+=over
+
+=item C<-all> =E<gt> I<bool>
+
+Do not stop on the first found file. Note, that list of full paths is returned in this case.
+
+=item C<-dirs> =E<gt> I<ref_to_array>
+
+Specify directory list to search through. If option is not passed, PATH environment variable
+is used for directory list.
+
+=item C<-exec> =E<gt> I<bool>
+
+Whether check for executable files or not. By default, C<which> searches executable files.
+However, on Cygwin executable check never performed.
+
+=back
+
+Examples:
+
+Look for "echo" in the directories specified in PATH:
+
+ my $echo = which( "echo" );
+
+Look for all occurenses of "cp" in the PATH:
+
+ my @cps = which( "cp", -all => 1 );
+
+Look for the first occurence of "icc" in the specified directories:
+
+ my $icc = which( "icc", -dirs => [ ".", "/usr/local/bin", "/usr/bin", "/bin" ] );
+
+Look for the the C<omp_lib.f> file:
+
+ my @omp_lib = which( "omp_lib.f", -all => 1, -exec => 0, -dirs => [ @include ] );
+
+=cut
+
+sub which($@) {
+
+ my $file = shift( @_ );
+ my %opts = @_;
+
+ check_opts( %opts, [ qw( -all -dirs -exec ) ] );
+ if ( $opts{ -all } and not wantarray() ) {
+ local $Carp::CarpLevel = 1;
+ Carp::cluck( "`-all' option passed to `which' but list is not expected" );
+ }; # if
+ if ( not defined( $opts{ -exec } ) ) {
+ $opts{ -exec } = 1;
+ }; # if
+
+ my $dirs = ( exists( $opts{ -dirs } ) ? $opts{ -dirs } : [ File::Spec->path() ] );
+ my @found;
+
+ my @exts = ( "" );
+ if ( $^O eq "MSWin32" and $opts{ -exec } ) {
+ if ( defined( $ENV{ PATHEXT } ) ) {
+ push( @exts, split( ";", $ENV{ PATHEXT } ) );
+ } else {
+ # If PATHEXT does not exist, use default value.
+ push( @exts, qw{ .COM .EXE .BAT .CMD } );
+ }; # if
+ }; # if
+
+ loop:
+ foreach my $dir ( @$dirs ) {
+ foreach my $ext ( @exts ) {
+ my $path = File::Spec->catfile( $dir, $file . $ext );
+ if ( -e $path ) {
+ # Executable bit is not reliable on Cygwin, do not check it.
+ if ( not $opts{ -exec } or -x $path or $^O eq "cygwin" ) {
+ push( @found, $path );
+ if ( not $opts{ -all } ) {
+ last loop;
+ }; # if
+ }; # if
+ }; # if
+ }; # foreach $ext
+ }; # foreach $dir
+
+ if ( not @found ) {
+ # TBD: We need to introduce an option for conditional enabling this error.
+ # runtime_error( "Could not find \"$file\" executable file in PATH." );
+ }; # if
+ if ( @found > 1 ) {
+ # TBD: Issue a warning?
+ }; # if
+
+ if ( $opts{ -all } ) {
+ return @found;
+ } else {
+ return $found[ 0 ];
+ }; # if
+
+}; # sub which
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<abs_path( $path, $base )>
+
+Return absolute path for an argument.
+
+Most of the work is done by C<File::Spec->rel2abs()>. C<abs_path()> additionally collapses
+C<dir1/../dir2> to C<dir2>.
+
+It is not so naive and made intentionally. For example on Linux* OS in Bash if F<link/> is a symbolic
+link to directory F<some_dir/>
+
+ $ cd link
+ $ cd ..
+
+brings you back to F<link/>'s parent, not to parent of F<some_dir/>,
+
+=cut
+
+sub abs_path($;$) {
+
+ my ( $path, $base ) = @_;
+ $path = File::Spec->rel2abs( $path, ( defined( $base ) ? $base : $ENV{ PWD } ) );
+ my ( $vol, $dir, $file ) = File::Spec->splitpath( $path );
+ while ( $dir =~ s{/(?!\.\.)[^/]*/\.\.(?:/|\z)}{/} ) {
+ }; # while
+ $path = File::Spec->canonpath( File::Spec->catpath( $vol, $dir, $file ) );
+ return $path;
+
+}; # sub abs_path
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<rel_path( $path, $base )>
+
+Return relative path for an argument.
+
+=cut
+
+sub rel_path($;$) {
+
+ my ( $path, $base ) = @_;
+ $path = File::Spec->abs2rel( abs_path( $path ), $base );
+ return $path;
+
+}; # sub rel_path
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<real_path( $dir )>
+
+Return real absolute path for an argument. In the result all relative components (F<.> and F<..>)
+and U<symbolic links are resolved>.
+
+In most cases it is not what you want. Consider using C<abs_path> first.
+
+C<abs_path> function from B<Cwd> module works with directories only. This function works with files
+as well. But, if file is a symbolic link, function does not resolve it (yet).
+
+The function uses C<runtime_error> to raise an error if something wrong.
+
+=cut
+
+sub real_path($) {
+
+ my $orig_path = shift( @_ );
+ my $real_path;
+ my $message = "";
+ if ( not -e $orig_path ) {
+ $message = "\"$orig_path\" does not exists";
+ } else {
+ # Cwd::abs_path does not work with files, so in this case we should handle file separately.
+ my $file;
+ if ( not -d $orig_path ) {
+ ( my $vol, my $dir, $file ) = File::Spec->splitpath( File::Spec->rel2abs( $orig_path ) );
+ $orig_path = File::Spec->catpath( $vol, $dir );
+ }; # if
+ {
+ local $SIG{ __WARN__ } = sub { $message = $_[ 0 ]; };
+ $real_path = Cwd::abs_path( $orig_path );
+ };
+ if ( defined( $file ) ) {
+ $real_path = File::Spec->catfile( $real_path, $file );
+ }; # if
+ }; # if
+ if ( not defined( $real_path ) or $message ne "" ) {
+ $message =~ s/^stat\(.*\): (.*)\s+at .*? line \d+\s*\z/$1/;
+ runtime_error( "Could not find real path for \"$orig_path\"" . ( $message ne "" ? ": $message" : "" ) );
+ }; # if
+ return $real_path;
+
+}; # sub real_path
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<make_dir( $dir, @options )>
+
+Make a directory.
+
+This function makes a directory. If necessary, more than one level can be created.
+If directory exists, warning issues (the script behavior depends on value of
+C<-warning_level> option). If directory creation fails or C<$dir> exists but it is not a
+directory, error isssues.
+
+Options:
+
+=over
+
+=item C<-mode>
+
+The numeric mode for new directories, 0750 (rwxr-x---) by default.
+
+=back
+
+=cut
+
+sub make_dir($@) {
+
+ my $dir = shift( @_ );
+ my %opts =
+ validate(
+ params => \@_,
+ spec => {
+ parents => { type => "boolean", default => 1 },
+ mode => { type => "scalar", default => 0777 },
+ },
+ );
+
+ my $prefix = "Could not create directory \"$dir\"";
+
+ if ( -e $dir ) {
+ if ( -d $dir ) {
+ } else {
+ runtime_error( "$prefix: it exists, but not a directory." );
+ }; # if
+ } else {
+ eval {
+ File::Path::mkpath( $dir, 0, $opts{ mode } );
+ }; # eval
+ if ( $@ ) {
+ $@ =~ s{\s+at (?:[a-zA-Z0-9 /_.]*/)?tools\.pm line \d+\s*}{};
+ runtime_error( "$prefix: $@" );
+ }; # if
+ if ( not -d $dir ) { # Just in case, check it one more time...
+ runtime_error( "$prefix." );
+ }; # if
+ }; # if
+
+}; # sub make_dir
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<copy_dir( $src_dir, $dst_dir, @options )>
+
+Copy directory recursively.
+
+This function copies a directory recursively.
+If source directory does not exist or not a directory, error issues.
+
+Options:
+
+=over
+
+=item C<-overwrite>
+
+Overwrite destination directory, if it exists.
+
+=back
+
+=cut
+
+sub copy_dir($$@) {
+
+ my $src = shift( @_ );
+ my $dst = shift( @_ );
+ my %opts = @_;
+ my $prefix = "Could not copy directory \"$src\" to \"$dst\"";
+
+ if ( not -e $src ) {
+ runtime_error( "$prefix: \"$src\" does not exist." );
+ }; # if
+ if ( not -d $src ) {
+ runtime_error( "$prefix: \"$src\" is not a directory." );
+ }; # if
+ if ( -e $dst ) {
+ if ( -d $dst ) {
+ if ( $opts{ -overwrite } ) {
+ del_dir( $dst );
+ } else {
+ runtime_error( "$prefix: \"$dst\" already exists." );
+ }; # if
+ } else {
+ runtime_error( "$prefix: \"$dst\" is not a directory." );
+ }; # if
+ }; # if
+
+ execute( [ "cp", "-R", $src, $dst ] );
+
+}; # sub copy_dir
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<move_dir( $src_dir, $dst_dir, @options )>
+
+Move directory.
+
+Options:
+
+=over
+
+=item C<-overwrite>
+
+Overwrite destination directory, if it exists.
+
+=back
+
+=cut
+
+sub move_dir($$@) {
+
+ my $src = shift( @_ );
+ my $dst = shift( @_ );
+ my %opts = @_;
+ my $prefix = "Could not copy directory \"$src\" to \"$dst\"";
+
+ if ( not -e $src ) {
+ runtime_error( "$prefix: \"$src\" does not exist." );
+ }; # if
+ if ( not -d $src ) {
+ runtime_error( "$prefix: \"$src\" is not a directory." );
+ }; # if
+ if ( -e $dst ) {
+ if ( -d $dst ) {
+ if ( $opts{ -overwrite } ) {
+ del_dir( $dst );
+ } else {
+ runtime_error( "$prefix: \"$dst\" already exists." );
+ }; # if
+ } else {
+ runtime_error( "$prefix: \"$dst\" is not a directory." );
+ }; # if
+ }; # if
+
+ execute( [ "mv", $src, $dst ] );
+
+}; # sub move_dir
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<clean_dir( $dir, @options )>
+
+Clean a directory: delete all the entries (recursively), but leave the directory.
+
+Options:
+
+=over
+
+=item C<-force> => bool
+
+If a directory is not writable, try to change permissions first, then clean it.
+
+=item C<-skip> => regexp
+
+Regexp. If a directory entry mached the regexp, it is skipped, not deleted. (As a subsequence,
+a directory containing skipped entries is not deleted.)
+
+=back
+
+=cut
+
+sub _clean_dir($);
+
+sub _clean_dir($) {
+ our %_clean_dir_opts;
+ my ( $dir ) = @_;
+ my $skip = $_clean_dir_opts{ skip }; # Regexp.
+ my $skipped = 0; # Number of skipped files.
+ my $prefix = "Cleaning `$dir' failed:";
+ my @stat = stat( $dir );
+ my $mode = $stat[ 2 ];
+ if ( not @stat ) {
+ runtime_error( $prefix, "Cannot stat `$dir': $!" );
+ }; # if
+ if ( not -d _ ) {
+ runtime_error( $prefix, "It is not a directory." );
+ }; # if
+ if ( not -w _ ) { # Directory is not writable.
+ if ( not -o _ or not $_clean_dir_opts{ force } ) {
+ runtime_error( $prefix, "Directory is not writable." );
+ }; # if
+ # Directory is not writable but mine. Try to change permissions.
+ chmod( $mode | S_IWUSR, $dir )
+ or runtime_error( $prefix, "Cannot make directory writable: $!" );
+ }; # if
+ my $handle = IO::Dir->new( $dir ) or runtime_error( $prefix, "Cannot read directory: $!" );
+ my @entries = File::Spec->no_upwards( $handle->read() );
+ $handle->close() or runtime_error( $prefix, "Cannot read directory: $!" );
+ foreach my $entry ( @entries ) {
+ my $path = cat_file( $dir, $entry );
+ if ( defined( $skip ) and $entry =~ $skip ) {
+ ++ $skipped;
+ } else {
+ if ( -l $path ) {
+ unlink( $path ) or runtime_error( $prefix, "Cannot delete symlink `$path': $!" );
+ } else {
+ stat( $path ) or runtime_error( $prefix, "Cannot stat `$path': $! " );
+ if ( -f _ ) {
+ del_file( $path );
+ } elsif ( -d _ ) {
+ my $rc = _clean_dir( $path );
+ if ( $rc == 0 ) {
+ rmdir( $path ) or runtime_error( $prefix, "Cannot delete directory `$path': $!" );
+ }; # if
+ $skipped += $rc;
+ } else {
+ runtime_error( $prefix, "`$path' is neither a file nor a directory." );
+ }; # if
+ }; # if
+ }; # if
+ }; # foreach
+ return $skipped;
+}; # sub _clean_dir
+
+
+sub clean_dir($@) {
+ my $dir = shift( @_ );
+ our %_clean_dir_opts;
+ local %_clean_dir_opts =
+ validate(
+ params => \@_,
+ spec => {
+ skip => { type => "regexpref" },
+ force => { type => "boolean" },
+ },
+ );
+ my $skipped = _clean_dir( $dir );
+ return $skipped;
+}; # sub clean_dir
+
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<del_dir( $dir, @options )>
+
+Delete a directory recursively.
+
+This function deletes a directory. If directory can not be deleted or it is not a directory, error
+message issues (and script exists).
+
+Options:
+
+=over
+
+=back
+
+=cut
+
+sub del_dir($@) {
+
+ my $dir = shift( @_ );
+ my %opts = @_;
+ my $prefix = "Deleting directory \"$dir\" failed";
+ our %_clean_dir_opts;
+ local %_clean_dir_opts =
+ validate(
+ params => \@_,
+ spec => {
+ force => { type => "boolean" },
+ },
+ );
+
+ if ( not -e $dir ) {
+ # Nothing to do.
+ return;
+ }; # if
+ if ( not -d $dir ) {
+ runtime_error( "$prefix: it is not a directory." );
+ }; # if
+ _clean_dir( $dir );
+ rmdir( $dir ) or runtime_error( "$prefix." );
+
+}; # sub del_dir
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<change_dir( $dir )>
+
+Change current directory.
+
+If any error occured, error issues and script exits.
+
+=cut
+
+sub change_dir($) {
+
+ my $dir = shift( @_ );
+
+ Cwd::chdir( $dir )
+ or runtime_error( "Could not chdir to \"$dir\": $!" );
+
+}; # sub change_dir
+
+
+# -------------------------------------------------------------------------------------------------
+
+=item C<copy_file( $src_file, $dst_file, @options )>
+
+Copy file.
+
+This function copies a file. If source does not exist or is not a file, error issues.
+
+Options:
+
+=over
+
+=item C<-overwrite>
+
+Overwrite destination file, if it exists.
+
+=back
+
+=cut
+
+sub copy_file($$@) {
+
+ my $src = shift( @_ );
+ my $dst = shift( @_ );
+ my %opts = @_;
+ my $prefix = "Could not copy file \"$src\" to \"$dst\"";
+
+ if ( not -e $src ) {
+ runtime_error( "$prefix: \"$src\" does not exist." );
+ }; # if
+ if ( not -f $src ) {
+ runtime_error( "$prefix: \"$src\" is not a file." );
+ }; # if
+ if ( -e $dst ) {
+ if ( -f $dst ) {
+ if ( $opts{ -overwrite } ) {
+ del_file( $dst );
+ } else {
+ runtime_error( "$prefix: \"$dst\" already exists." );
+ }; # if
+ } else {
+ runtime_error( "$prefix: \"$dst\" is not a file." );
+ }; # if
+ }; # if
+
+ File::Copy::copy( $src, $dst ) or runtime_error( "$prefix: $!" );
+ # On Windows* OS File::Copy preserves file attributes, but on Linux* OS it doesn't.
+ # So we should do it manually...
+ if ( $^O =~ m/^linux\z/ ) {
+ my $mode = ( stat( $src ) )[ 2 ]
+ or runtime_error( "$prefix: cannot get status info for source file." );
+ chmod( $mode, $dst )
+ or runtime_error( "$prefix: cannot change mode of destination file." );
+ }; # if
+
+}; # sub copy_file
+
+# -------------------------------------------------------------------------------------------------
+
+sub move_file($$@) {
+
+ my $src = shift( @_ );
+ my $dst = shift( @_ );
+ my %opts = @_;
+ my $prefix = "Could not move file \"$src\" to \"$dst\"";
+
+ check_opts( %opts, [ qw( -overwrite ) ] );
+
+ if ( not -e $src ) {
+ runtime_error( "$prefix: \"$src\" does not exist." );
+ }; # if
+ if ( not -f $src ) {
+ runtime_error( "$prefix: \"$src\" is not a file." );
+ }; # if
+ if ( -e $dst ) {
+ if ( -f $dst ) {
+ if ( $opts{ -overwrite } ) {
+ #
+ } else {
+ runtime_error( "$prefix: \"$dst\" already exists." );
+ }; # if
+ } else {
+ runtime_error( "$prefix: \"$dst\" is not a file." );
+ }; # if
+ }; # if
+
+ File::Copy::move( $src, $dst ) or runtime_error( "$prefix: $!" );
+
+}; # sub move_file
+
+# -------------------------------------------------------------------------------------------------
+
+sub del_file($) {
+ my $files = shift( @_ );
+ if ( ref( $files ) eq "" ) {
+ $files = [ $files ];
+ }; # if
+ foreach my $file ( @$files ) {
+ debug( "Deleting file `$file'..." );
+ my $rc = unlink( $file );
+ if ( $rc == 0 && $! != ENOENT ) {
+ # Reporn an error, but ignore ENOENT, because the goal is achieved.
+ runtime_error( "Deleting file `$file' failed: $!" );
+ }; # if
+ }; # foreach $file
+}; # sub del_file
+
+# -------------------------------------------------------------------------------------------------
+
+=back
+
+=cut
+
+# =================================================================================================
+# File I/O subroutines.
+# =================================================================================================
+
+=head2 File I/O subroutines.
+
+=cut
+
+#--------------------------------------------------------------------------------------------------
+
+=head3 read_file
+
+B<Synopsis:>
+
+ read_file( $file, @options )
+
+B<Description:>
+
+Read file and return its content. In scalar context function returns a scalar, in list context
+function returns list of lines.
+
+Note: If the last of file does not terminate with newline, function will append it.
+
+B<Arguments:>
+
+=over
+
+=item B<$file>
+
+A name or handle of file to read from.
+
+=back
+
+B<Options:>
+
+=over
+
+=item B<-binary>
+
+If true, file treats as a binary file: no newline conversion, no truncating trailing space, no
+newline removing performed. Entire file returned as a scalar.
+
+=item B<-bulk>
+
+This option is allowed only in binary mode. Option's value should be a reference to a scalar.
+If option present, file content placed to pointee scalar and function returns true (1).
+
+=item B<-chomp>
+
+If true, newline characters are removed from file content. By default newline characters remain.
+This option is not applicable in binary mode.
+
+=item B<-keep_trailing_space>
+
+If true, trainling space remain at the ends of lines. By default all trailing spaces are removed.
+This option is not applicable in binary mode.
+
+=back
+
+B<Examples:>
+
+Return file as single line, remove trailing spaces.
+
+ my $bulk = read_file( "message.txt" );
+
+Return file as list of lines with removed trailing space and
+newline characters.
+
+ my @bulk = read_file( "message.txt", -chomp => 1 );
+
+Read a binary file:
+
+ my $bulk = read_file( "message.txt", -binary => 1 );
+
+Read a big binary file:
+
+ my $bulk;
+ read_file( "big_binary_file", -binary => 1, -bulk => \$bulk );
+
+Read from standard input:
+
+ my @bulk = read_file( \*STDIN );
+
+=cut
+
+sub read_file($@) {
+
+ my $file = shift( @_ ); # The name or handle of file to read from.
+ my %opts = @_; # Options.
+
+ my $name;
+ my $handle;
+ my @bulk;
+ my $error = \&runtime_error;
+
+ my @binopts = qw( -binary -error -bulk ); # Options available in binary mode.
+ my @txtopts = qw( -binary -error -keep_trailing_space -chomp -layer ); # Options available in text (non-binary) mode.
+ check_opts( %opts, [ @binopts, @txtopts ] );
+ if ( $opts{ -binary } ) {
+ check_opts( %opts, [ @binopts ], "these options cannot be used with -binary" );
+ } else {
+ check_opts( %opts, [ @txtopts ], "these options cannot be used without -binary" );
+ }; # if
+ if ( not exists( $opts{ -error } ) ) {
+ $opts{ -error } = "error";
+ }; # if
+ if ( $opts{ -error } eq "warning" ) {
+ $error = \&warning;
+ } elsif( $opts{ -error } eq "ignore" ) {
+ $error = sub {};
+ } elsif ( ref( $opts{ -error } ) eq "ARRAY" ) {
+ $error = sub { push( @{ $opts{ -error } }, $_[ 0 ] ); };
+ }; # if
+
+ if ( ( ref( $file ) eq "GLOB" ) or UNIVERSAL::isa( $file, "IO::Handle" ) ) {
+ $name = "unknown";
+ $handle = $file;
+ } else {
+ $name = $file;
+ if ( get_ext( $file ) eq ".gz" and not $opts{ -binary } ) {
+ $handle = IO::Zlib->new( $name, "rb" );
+ } else {
+ $handle = IO::File->new( $name, "r" );
+ }; # if
+ if ( not defined( $handle ) ) {
+ $error->( "File \"$name\" could not be opened for input: $!" );
+ }; # if
+ }; # if
+ if ( defined( $handle ) ) {
+ if ( $opts{ -binary } ) {
+ binmode( $handle );
+ local $/ = undef; # Set input record separator to undef to read entire file as one line.
+ if ( exists( $opts{ -bulk } ) ) {
+ ${ $opts{ -bulk } } = $handle->getline();
+ } else {
+ $bulk[ 0 ] = $handle->getline();
+ }; # if
+ } else {
+ if ( defined( $opts{ -layer } ) ) {
+ binmode( $handle, $opts{ -layer } );
+ }; # if
+ @bulk = $handle->getlines();
+ # Special trick for UTF-8 files: Delete BOM, if any.
+ if ( defined( $opts{ -layer } ) and $opts{ -layer } eq ":utf8" ) {
+ if ( substr( $bulk[ 0 ], 0, 1 ) eq "\x{FEFF}" ) {
+ substr( $bulk[ 0 ], 0, 1 ) = "";
+ }; # if
+ }; # if
+ }; # if
+ $handle->close()
+ or $error->( "File \"$name\" could not be closed after input: $!" );
+ } else {
+ if ( $opts{ -binary } and exists( $opts{ -bulk } ) ) {
+ ${ $opts{ -bulk } } = "";
+ }; # if
+ }; # if
+ if ( $opts{ -binary } ) {
+ if ( exists( $opts{ -bulk } ) ) {
+ return 1;
+ } else {
+ return $bulk[ 0 ];
+ }; # if
+ } else {
+ if ( ( @bulk > 0 ) and ( substr( $bulk[ -1 ], -1, 1 ) ne "\n" ) ) {
+ $bulk[ -1 ] .= "\n";
+ }; # if
+ if ( not $opts{ -keep_trailing_space } ) {
+ map( $_ =~ s/\s+\n\z/\n/, @bulk );
+ }; # if
+ if ( $opts{ -chomp } ) {
+ chomp( @bulk );
+ }; # if
+ if ( wantarray() ) {
+ return @bulk;
+ } else {
+ return join( "", @bulk );
+ }; # if
+ }; # if
+
+}; # sub read_file
+
+#--------------------------------------------------------------------------------------------------
+
+=head3 write_file
+
+B<Synopsis:>
+
+ write_file( $file, $bulk, @options )
+
+B<Description:>
+
+Write file.
+
+B<Arguments:>
+
+=over
+
+=item B<$file>
+
+The name or handle of file to writte to.
+
+=item B<$bulk>
+
+Bulk to write to a file. Can be a scalar, or a reference to scalar or an array.
+
+=back
+
+B<Options:>
+
+=over
+
+=item B<-backup>
+
+If true, create a backup copy of file overwritten. Backup copy is placed into the same directory.
+The name of backup copy is the same as the name of file with `~' appended. By default backup copy
+is not created.
+
+=item B<-append>
+
+If true, the text will be added to existing file.
+
+=back
+
+B<Examples:>
+
+ write_file( "message.txt", \$bulk );
+ # Write file, take content from a scalar.
+
+ write_file( "message.txt", \@bulk, -backup => 1 );
+ # Write file, take content from an array, create a backup copy.
+
+=cut
+
+sub write_file($$@) {
+
+ my $file = shift( @_ ); # The name or handle of file to write to.
+ my $bulk = shift( @_ ); # The text to write. Can be reference to array or scalar.
+ my %opts = @_; # Options.
+
+ my $name;
+ my $handle;
+
+ check_opts( %opts, [ qw( -append -backup -binary -layer ) ] );
+
+ my $mode = $opts{ -append } ? "a": "w";
+ if ( ( ref( $file ) eq "GLOB" ) or UNIVERSAL::isa( $file, "IO::Handle" ) ) {
+ $name = "unknown";
+ $handle = $file;
+ } else {
+ $name = $file;
+ if ( $opts{ -backup } and ( -f $name ) ) {
+ copy_file( $name, $name . "~", -overwrite => 1 );
+ }; # if
+ $handle = IO::File->new( $name, $mode )
+ or runtime_error( "File \"$name\" could not be opened for output: $!" );
+ }; # if
+ if ( $opts{ -binary } ) {
+ binmode( $handle );
+ } elsif ( $opts{ -layer } ) {
+ binmode( $handle, $opts{ -layer } );
+ }; # if
+ if ( ref( $bulk ) eq "" ) {
+ if ( defined( $bulk ) ) {
+ $handle->print( $bulk );
+ if ( not $opts{ -binary } and ( substr( $bulk, -1 ) ne "\n" ) ) {
+ $handle->print( "\n" );
+ }; # if
+ }; # if
+ } elsif ( ref( $bulk ) eq "SCALAR" ) {
+ if ( defined( $$bulk ) ) {
+ $handle->print( $$bulk );
+ if ( not $opts{ -binary } and ( substr( $$bulk, -1 ) ne "\n" ) ) {
+ $handle->print( "\n" );
+ }; # if
+ }; # if
+ } elsif ( ref( $bulk ) eq "ARRAY" ) {
+ foreach my $line ( @$bulk ) {
+ if ( defined( $line ) ) {
+ $handle->print( $line );
+ if ( not $opts{ -binary } and ( substr( $line, -1 ) ne "\n" ) ) {
+ $handle->print( "\n" );
+ }; # if
+ }; # if
+ }; # foreach
+ } else {
+ Carp::croak( "write_file: \$bulk must be a scalar or reference to (scalar or array)" );
+ }; # if
+ $handle->close()
+ or runtime_error( "File \"$name\" could not be closed after output: $!" );
+
+}; # sub write_file
+
+#--------------------------------------------------------------------------------------------------
+
+=cut
+
+# =================================================================================================
+# Execution subroutines.
+# =================================================================================================
+
+=head2 Execution subroutines.
+
+=over
+
+=cut
+
+#--------------------------------------------------------------------------------------------------
+
+sub _pre {
+
+ my $arg = shift( @_ );
+
+ # If redirection is not required, exit.
+ if ( not exists( $arg->{ redir } ) ) {
+ return 0;
+ }; # if
+
+ # Input parameters.
+ my $mode = $arg->{ mode }; # Mode, "<" (input ) or ">" (output).
+ my $handle = $arg->{ handle }; # Handle to manipulate.
+ my $redir = $arg->{ redir }; # Data, a file name if a scalar, or file contents, if a reference.
+
+ # Output parameters.
+ my $save_handle;
+ my $temp_handle;
+ my $temp_name;
+
+ # Save original handle (by duping it).
+ $save_handle = Symbol::gensym();
+ $handle->flush();
+ open( $save_handle, $mode . "&" . $handle->fileno() )
+ or die( "Cannot dup filehandle: $!" );
+
+ # Prepare a file to IO.
+ if ( UNIVERSAL::isa( $redir, "IO::Handle" ) or ( ref( $redir ) eq "GLOB" ) ) {
+ # $redir is reference to an object of IO::Handle class (or its decedant).
+ $temp_handle = $redir;
+ } elsif ( ref( $redir ) ) {
+ # $redir is a reference to content to be read/written.
+ # Prepare temp file.
+ ( $temp_handle, $temp_name ) =
+ File::Temp::tempfile(
+ "$tool.XXXXXXXX",
+ DIR => File::Spec->tmpdir(),
+ SUFFIX => ".tmp",
+ UNLINK => 1
+ );
+ if ( not defined( $temp_handle ) ) {
+ runtime_error( "Could not create temp file." );
+ }; # if
+ if ( $mode eq "<" ) {
+ # It is a file to be read by child, prepare file content to be read.
+ $temp_handle->print( ref( $redir ) eq "SCALAR" ? ${ $redir } : @{ $redir } );
+ $temp_handle->flush();
+ seek( $temp_handle, 0, 0 );
+ # Unfortunatelly, I could not use OO interface to seek.
+ # ActivePerl 5.6.1 complains on both forms:
+ # $temp_handle->seek( 0 ); # As declared in IO::Seekable.
+ # $temp_handle->setpos( 0 ); # As described in documentation.
+ } elsif ( $mode eq ">" ) {
+ # It is a file for output. Clear output variable.
+ if ( ref( $redir ) eq "SCALAR" ) {
+ ${ $redir } = "";
+ } else {
+ @{ $redir } = ();
+ }; # if
+ }; # if
+ } else {
+ # $redir is a name of file to be read/written.
+ # Just open file.
+ if ( defined( $redir ) ) {
+ $temp_name = $redir;
+ } else {
+ $temp_name = File::Spec->devnull();
+ }; # if
+ $temp_handle = IO::File->new( $temp_name, $mode )
+ or runtime_error( "file \"$temp_name\" could not be opened for " . ( $mode eq "<" ? "input" : "output" ) . ": $!" );
+ }; # if
+
+ # Redirect handle to temp file.
+ open( $handle, $mode . "&" . $temp_handle->fileno() )
+ or die( "Cannot dup filehandle: $!" );
+
+ # Save output parameters.
+ $arg->{ save_handle } = $save_handle;
+ $arg->{ temp_handle } = $temp_handle;
+ $arg->{ temp_name } = $temp_name;
+
+}; # sub _pre
+
+
+sub _post {
+
+ my $arg = shift( @_ );
+
+ # Input parameters.
+ my $mode = $arg->{ mode }; # Mode, "<" or ">".
+ my $handle = $arg->{ handle }; # Handle to save and set.
+ my $redir = $arg->{ redir }; # Data, a file name if a scalar, or file contents, if a reference.
+
+ # Parameters saved during preprocessing.
+ my $save_handle = $arg->{ save_handle };
+ my $temp_handle = $arg->{ temp_handle };
+ my $temp_name = $arg->{ temp_name };
+
+ # If no handle was saved, exit.
+ if ( not $save_handle ) {
+ return 0;
+ }; # if
+
+ # Close handle.
+ $handle->close()
+ or die( "$!" );
+
+ # Read the content of temp file, if necessary, and close temp file.
+ if ( ( $mode ne "<" ) and ref( $redir ) ) {
+ $temp_handle->flush();
+ seek( $temp_handle, 0, 0 );
+ if ( $^O =~ m/MSWin/ ) {
+ binmode( $temp_handle, ":crlf" );
+ }; # if
+ if ( ref( $redir ) eq "SCALAR" ) {
+ ${ $redir } .= join( "", $temp_handle->getlines() );
+ } elsif ( ref( $redir ) eq "ARRAY" ) {
+ push( @{ $redir }, $temp_handle->getlines() );
+ }; # if
+ }; # if
+ if ( not UNIVERSAL::isa( $redir, "IO::Handle" ) ) {
+ $temp_handle->close()
+ or die( "$!" );
+ }; # if
+
+ # Restore handle to original value.
+ $save_handle->flush();
+ open( $handle, $mode . "&" . $save_handle->fileno() )
+ or die( "Cannot dup filehandle: $!" );
+
+ # Close save handle.
+ $save_handle->close()
+ or die( "$!" );
+
+ # Delete parameters saved during preprocessing.
+ delete( $arg->{ save_handle } );
+ delete( $arg->{ temp_handle } );
+ delete( $arg->{ temp_name } );
+
+}; # sub _post
+
+#--------------------------------------------------------------------------------------------------
+
+=item C<execute( [ @command ], @options )>
+
+Execute specified program or shell command.
+
+Program is specified by reference to an array, that array is passed to C<system()> function which
+executes the command. See L<perlfunc> for details how C<system()> interprets various forms of
+C<@command>.
+
+By default, in case of any error error message is issued and script terminated (by runtime_error()).
+Function returns an exit code of program.
+
+Alternatively, he function may return exit status of the program (see C<-ignore_status>) or signal
+(see C<-ignore_signal>) so caller may analyze it and continue execution.
+
+Options:
+
+=over
+
+=item C<-stdin>
+
+Redirect stdin of program. The value of option can be:
+
+=over
+
+=item C<undef>
+
+Stdin of child is attached to null device.
+
+=item a string
+
+Stdin of child is attached to a file with name specified by option.
+
+=item a reference to a scalar
+
+A dereferenced scalar is written to a temp file, and child's stdin is attached to that file.
+
+=item a reference to an array
+
+A dereferenced array is written to a temp file, and child's stdin is attached to that file.
+
+=back
+
+=item C<-stdout>
+
+Redirect stdout. Possible values are the same as for C<-stdin> option. The only difference is
+reference specifies a variable receiving program's output.
+
+=item C<-stderr>
+
+It similar to C<-stdout>, but redirects stderr. There is only one additional value:
+
+=over
+
+=item an empty string
+
+means that stderr should be redirected to the same place where stdout is redirected to.
+
+=back
+
+=item C<-append>
+
+Redirected stream will not overwrite previous content of file (or variable).
+Note, that option affects both stdout and stderr.
+
+=item C<-ignore_status>
+
+By default, subroutine raises an error and exits the script if program returns non-exit status. If
+this options is true, no error is raised. Instead, status is returned as function result (and $@ is
+set to error message).
+
+=item C<-ignore_signal>
+
+By default, subroutine raises an error and exits the script if program die with signal. If
+this options is true, no error is raised in such a case. Instead, signal number is returned (as
+negative value), error message is placed to C<$@> variable.
+
+If command is not even started, -256 is returned.
+
+=back
+
+Examples:
+
+ execute( [ "cmd.exe", "/c", "dir" ] );
+ # Execute NT shell with specified options, no redirections are
+ # made.
+
+ my $output;
+ execute( [ "cvs", "-n", "-q", "update", "." ], -stdout => \$output );
+ # Execute "cvs -n -q update ." command, output is saved
+ # in $output variable.
+
+ my @output;
+ execute( [ qw( cvs -n -q update . ) ], -stdout => \@output, -stderr => undef );
+ # Execute specified command, output is saved in @output
+ # variable, stderr stream is redirected to null device
+ # (/dev/null in Linux* OS an nul in Windows* OS).
+
+=cut
+
+sub execute($@) {
+
+ # !!! Add something to complain on unknown options...
+
+ my $command = shift( @_ );
+ my %opts = @_;
+ my $prefix = "Could not execute $command->[ 0 ]";
+
+ check_opts( %opts, [ qw( -stdin -stdout -stderr -append -ignore_status -ignore_signal ) ] );
+
+ if ( ref( $command ) ne "ARRAY" ) {
+ Carp::croak( "execute: $command must be a reference to array" );
+ }; # if
+
+ my $stdin = { handle => \*STDIN, mode => "<" };
+ my $stdout = { handle => \*STDOUT, mode => ">" };
+ my $stderr = { handle => \*STDERR, mode => ">" };
+ my $streams = {
+ stdin => $stdin,
+ stdout => $stdout,
+ stderr => $stderr
+ }; # $streams
+
+ for my $stream ( qw( stdin stdout stderr ) ) {
+ if ( exists( $opts{ "-$stream" } ) ) {
+ if ( ref( $opts{ "-$stream" } ) !~ m/\A(|SCALAR|ARRAY)\z/ ) {
+ Carp::croak( "execute: -$stream option: must have value of scalar, or reference to (scalar or array)." );
+ }; # if
+ $streams->{ $stream }->{ redir } = $opts{ "-$stream" };
+ }; # if
+ if ( $opts{ -append } and ( $streams->{ $stream }->{ mode } ) eq ">" ) {
+ $streams->{ $stream }->{ mode } = ">>";
+ }; # if
+ }; # foreach $stream
+
+ _pre( $stdin );
+ _pre( $stdout );
+ if ( defined( $stderr->{ redir } ) and not ref( $stderr->{ redir } ) and ( $stderr->{ redir } eq "" ) ) {
+ if ( exists( $stdout->{ redir } ) ) {
+ $stderr->{ redir } = $stdout->{ temp_handle };
+ } else {
+ $stderr->{ redir } = ${ $stdout->{ handle } };
+ }; # if
+ }; # if
+ _pre( $stderr );
+ my $rc = system( @$command );
+ my $errno = $!;
+ my $child = $?;
+ _post( $stderr );
+ _post( $stdout );
+ _post( $stdin );
+
+ my $exit = 0;
+ my $signal_num = $child & 127;
+ my $exit_status = $child >> 8;
+ $@ = "";
+
+ if ( $rc == -1 ) {
+ $@ = "\"$command->[ 0 ]\" failed: $errno";
+ $exit = -256;
+ if ( not $opts{ -ignore_signal } ) {
+ runtime_error( $@ );
+ }; # if
+ } elsif ( $signal_num != 0 ) {
+ $@ = "\"$command->[ 0 ]\" failed due to signal $signal_num.";
+ $exit = - $signal_num;
+ if ( not $opts{ -ignore_signal } ) {
+ runtime_error( $@ );
+ }; # if
+ } elsif ( $exit_status != 0 ) {
+ $@ = "\"$command->[ 0 ]\" returned non-zero status $exit_status.";
+ $exit = $exit_status;
+ if ( not $opts{ -ignore_status } ) {
+ runtime_error( $@ );
+ }; # if
+ }; # if
+
+ return $exit;
+
+}; # sub execute
+
+#--------------------------------------------------------------------------------------------------
+
+=item C<backticks( [ @command ], @options )>
+
+Run specified program or shell command and return output.
+
+In scalar context entire output is returned in a single string. In list context list of strings
+is returned. Function issues an error and exits script if any error occurs.
+
+=cut
+
+
+sub backticks($@) {
+
+ my $command = shift( @_ );
+ my %opts = @_;
+ my @output;
+
+ check_opts( %opts, [ qw( -chomp ) ] );
+
+ execute( $command, -stdout => \@output );
+
+ if ( $opts{ -chomp } ) {
+ chomp( @output );
+ }; # if
+
+ return ( wantarray() ? @output : join( "", @output ) );
+
+}; # sub backticks
+
+#--------------------------------------------------------------------------------------------------
+
+sub pad($$$) {
+ my ( $str, $length, $pad ) = @_;
+ my $lstr = length( $str ); # Length of source string.
+ if ( $lstr < $length ) {
+ my $lpad = length( $pad ); # Length of pad.
+ my $count = int( ( $length - $lstr ) / $lpad ); # Number of pad repetitions.
+ my $tail = $length - ( $lstr + $lpad * $count );
+ $str = $str . ( $pad x $count ) . substr( $pad, 0, $tail );
+ }; # if
+ return $str;
+}; # sub pad
+
+# --------------------------------------------------------------------------------------------------
+
+=back
+
+=cut
+
+#--------------------------------------------------------------------------------------------------
+
+return 1;
+
+#--------------------------------------------------------------------------------------------------
+
+=cut
+
+# End of file.
diff --git a/runtime/tools/linux.inc b/runtime/tools/linux.inc
new file mode 100644
index 0000000..7ad2512
--- /dev/null
+++ b/runtime/tools/linux.inc
@@ -0,0 +1,35 @@
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+####### Detections and Commands ###############################################
+
+ifndef arch
+ uname_m:=$(shell uname -m)
+ ifeq ($(uname_m),i686)
+ export arch:=32
+ endif
+ ifeq ($(uname_m),x86_64)
+ export arch:=32e
+ endif
+ ifndef arch
+ export arch:=$(uname_m)
+ endif
+endif
+
+CMD=sh -c
+CWD=$(shell pwd)
+CP=cp
+RM?=rm -f
+RMR?=rm -rf
+RD?=rmdir
+MD?=mkdir -p
+NUL= /dev/null
+SLASH=/
diff --git a/runtime/tools/macos.inc b/runtime/tools/macos.inc
new file mode 100644
index 0000000..38f86b1
--- /dev/null
+++ b/runtime/tools/macos.inc
@@ -0,0 +1,37 @@
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+####### Detections and Commands ###############################################
+
+ifndef arch
+ ifeq ($(shell /usr/sbin/sysctl -n hw.machine),Power Macintosh)
+ ifeq ($(shell /usr/sbin/sysctl -n hw.optional.64bitops),1)
+ export arch:=ppc64
+ else
+ export arch:=ppc32
+ endif
+ else
+ ifeq ($(shell /usr/sbin/sysctl -n hw.optional.x86_64 2>/dev/null),1)
+ export arch:=intel64
+ else
+ export arch:=ia32
+ endif
+ endif
+endif
+
+CMD=$(SHELL) -c
+CWD=$(shell pwd)
+RM?=rm -f
+RMR?=rm -rf
+RD?=rmdir
+MD?=mkdir -p
+NUL= /dev/null
+SLASH=/
diff --git a/runtime/tools/message-converter.pl b/runtime/tools/message-converter.pl
new file mode 100755
index 0000000..382f42e
--- /dev/null
+++ b/runtime/tools/message-converter.pl
@@ -0,0 +1,775 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use File::Glob ":glob";
+use Encode qw{ encode };
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+use Platform ":vars";
+
+our $VERSION = "0.04";
+my $escape = qr{%};
+my $placeholder = qr{(\d)\$(s|l?[du])};
+
+my $sections =
+ {
+ meta => { short => "prp" }, # "prp" stands for "property".
+ strings => { short => "str" },
+ formats => { short => "fmt" },
+ messages => { short => "msg" },
+ hints => { short => "hnt" },
+ };
+my @sections = qw{ meta strings formats messages hints };
+# Assign section properties: long name, set number, base number.
+map( $sections->{ $sections[ $_ ] }->{ long } = $sections[ $_ ], ( 0 .. @sections - 1 ) );
+map( $sections->{ $sections[ $_ ] }->{ set } = ( $_ + 1 ), ( 0 .. @sections - 1 ) );
+map( $sections->{ $sections[ $_ ] }->{ base } = ( ( $_ + 1 ) << 16 ), ( 0 .. @sections - 1 ) );
+
+# Properties of Meta section.
+my @properties = qw{ Language Country LangId Version Revision };
+
+
+sub _generate_comment($$$) {
+
+ my ( $data, $open, $close ) = @_;
+ my $bulk =
+ $open . " Do not edit this file! " . $close . "\n" .
+ $open . " The file was generated from " . get_file( $data->{ "%meta" }->{ source } ) .
+ " by " . $tool . " on " . localtime() . ". " . $close . "\n";
+ return $bulk;
+
+}; # sub _generate_comment
+
+
+sub msg2sgn($) {
+
+ # Convert message string to signature. Signature is a list of placeholders in sorted order.
+ # For example, signature of "%1$s value \"%2$s\" is invalid." is "%1$s %2$s".
+
+ my ( $msg ) = @_;
+ my @placeholders;
+ pos( $msg ) = 0;
+ while ( $msg =~ m{\G.*?$escape$placeholder}g ) {
+ $placeholders[ $1 - 1 ] = "%$1\$$2";
+ }; # while
+ for ( my $i = 1; $i <= @placeholders; ++ $i ) {
+ if ( not defined( $placeholders[ $i - 1 ] ) ) {
+ $placeholders[ $i - 1 ] = "%$i\$-";
+ }; # if
+ }; # for $i
+ return join( " ", @placeholders );
+
+}; # sub msg2sgn
+
+
+sub msg2src($) {
+
+ # Convert message string to a C string constant.
+
+ my ( $msg ) = @_;
+ if ( $target_os eq "win" ) {
+ $msg =~ s{$escape$placeholder}{\%$1!$2!}g;
+ }; # if
+ return $msg;
+
+}; # sub msg2src
+
+
+my $special =
+ {
+ "n" => "\n",
+ "t" => "\t",
+ };
+
+sub msg2mc($) {
+ my ( $msg ) = @_;
+ $msg = msg2src( $msg ); # Get windows style placeholders.
+ $msg =~ s{\\(.)}{ exists( $special->{ $1 } ) ? $special->{ $1 } : $1 }ge;
+ return $msg;
+}; # sub msg2mc
+
+
+
+sub parse_message($) {
+
+ my ( $msg ) = @_;
+ pos( $msg ) = 0;
+ for ( ; ; ) {
+ if ( $msg !~ m{\G.*?$escape}gc ) {
+ last;
+ }
+ if ( $msg !~ m{\G$placeholder}gc ) {
+ return "Bad %-sequence near \"%" . substr( $msg, pos( $msg ), 7 ) . "\"";
+ }; # if
+ }; # forever
+ return undef;
+
+}; # sub parse_message
+
+
+sub parse_source($) {
+
+ my ( $name ) = @_;
+
+ my @bulk = read_file( $name, -layer => ":utf8" );
+ my $data = {};
+
+ my $line;
+ my $n = 0; # Line number.
+ my $obsolete = 0; # Counter of obsolete entries.
+ my $last_idx;
+ my %idents;
+ my $section;
+
+ my $error =
+ sub {
+ my ( $n, $line, $msg ) = @_;
+ runtime_error( "Error parsing $name line $n: " . "$msg:\n" . " $line" );
+ }; # sub
+
+ foreach $line ( @bulk ) {
+ ++ $n;
+ # Skip empty lines and comments.
+ if ( $line =~ m{\A\s*(\n|#)} ) {
+ $last_idx = undef;
+ next;
+ }; # if
+ # Parse section header.
+ if ( $line =~ m{\A-\*-\s*([A-Z_]*)\s*-\*-\s*\n\z}i ) {
+ $section = ( lc( $1 ) );
+ if ( not grep( $section eq $_, @sections ) ) {
+ $error->( $n, $line, "Unknown section \"$section\" specified" );
+ }; # if
+ if ( exists( $data->{ $section } ) ) {
+ $error->( $n, $line, "Multiple sections of the same type specified" );
+ }; # if
+ %idents = (); # Clean list of known message identifiers.
+ next;
+ }; # if
+ if ( not defined( $section ) ) {
+ $error->( $n, $line, "Section heading expected" );
+ }; # if
+ # Parse section body.
+ if ( $section eq "meta" ) {
+ if ( $line =~ m{\A([A-Z_][A-Z_0-9]*)\s+"(.*)"\s*?\n?\z}i ) {
+ # Parse meta properties (such as Language, Country, and LangId).
+ my ( $property, $value ) = ( $1, $2 );
+ if ( not grep( $_ eq $property , @properties ) ) {
+ $error->( $n, $line, "Unknown property \"$property\" specified" );
+ }; # if
+ if ( exists( $data->{ "%meta" }->{ $property } ) ) {
+ $error->( $n, $line, "Property \"$property\" has already been specified" );
+ }; # if
+ $data->{ "%meta" }->{ $property } = $value;
+ $last_idx = undef;
+ next;
+ }; # if
+ $error->( $n, $line, "Property line expected" );
+ }; # if
+ # Parse message.
+ if ( $line =~ m{\A([A-Z_][A-Z_0-9]*)\s+"(.*)"\s*?\n?\z}i ) {
+ my ( $ident, $message ) = ( $1, $2 );
+ if ( $ident eq "OBSOLETE" ) {
+ # If id is "OBSOLETE", add a unique suffix. It provides convenient way to mark
+ # obsolete messages.
+ ++ $obsolete;
+ $ident .= $obsolete;
+ }; # if
+ if ( exists( $idents{ $ident } ) ) {
+ $error->( $n, $line, "Identifier \"$ident\" is redefined" );
+ }; # if
+ # Check %-sequences.
+ my $err = parse_message( $message );
+ if ( $err ) {
+ $error->( $n, $line, $err );
+ }; # if
+ # Save message.
+ push( @{ $data->{ $section } }, [ $ident, $message ] );
+ $idents{ $ident } = 1;
+ $last_idx = @{ $data->{ $section } } - 1;
+ next;
+ }; # if
+ # Parse continuation line.
+ if ( $line =~ m{\A\s*"(.*)"\s*\z} ) {
+ my $message = $1;
+ if ( not defined( $last_idx ) ) {
+ $error->( $n, $line, "Unexpected continuation line" );
+ }; # if
+ # Check %-sequences.
+ my $err = parse_message( $message );
+ if ( $err ) {
+ $error->( $n, $line, $err );
+ }; # if
+ # Save continuation.
+ $data->{ $section }->[ $last_idx ]->[ 1 ] .= $message;
+ next;
+ }; # if
+ $error->( $n, $line, "Message definition expected" );
+ }; # foreach
+ $data->{ "%meta" }->{ source } = $name;
+ foreach my $section ( @sections ) {
+ if ( not exists( $data->{ $section } ) ) {
+ $data->{ $section } = [];
+ }; # if
+ }; # foreach $section
+
+ foreach my $property ( @properties ) {
+ if ( not defined( $data->{ "%meta" }->{ $property } ) ) {
+ runtime_error(
+ "Error parsing $name: " .
+ "Required \"$property\" property is not specified"
+ );
+ }; # if
+ push( @{ $data->{ meta } }, [ $property, $data->{ "%meta" }->{ $property } ] );
+ }; # foreach
+
+ return $data;
+
+}; # sub parse_source
+
+
+sub generate_enum($$$) {
+
+ my ( $data, $file, $prefix ) = @_;
+ my $bulk = "";
+
+ $bulk =
+ _generate_comment( $data, "//", "//" ) .
+ "\n" .
+ "enum ${prefix}_id {\n\n" .
+ " // A special id for absence of message.\n" .
+ " ${prefix}_null = 0,\n\n";
+
+ foreach my $section ( @sections ) {
+ my $props = $sections->{ $section }; # Section properties.
+ my $short = $props->{ short }; # Short section name, frequently used.
+ $bulk .=
+ " // Set #$props->{ set }, $props->{ long }.\n" .
+ " ${prefix}_${short}_first = $props->{ base },\n";
+ foreach my $item ( @{ $data->{ $section } } ) {
+ my ( $ident, undef ) = @$item;
+ $bulk .= " ${prefix}_${short}_${ident},\n";
+ }; # foreach
+ $bulk .= " ${prefix}_${short}_last,\n\n";
+ }; # foreach $type
+ $bulk .= " ${prefix}_xxx_lastest\n\n";
+
+ $bulk .=
+ "}; // enum ${prefix}_id\n" .
+ "\n" .
+ "typedef enum ${prefix}_id ${prefix}_id_t;\n" .
+ "\n";
+
+ $bulk .=
+ "\n" .
+ "// end of file //\n";
+
+ write_file( $file, \$bulk );
+
+}; # sub generate_enum
+
+
+sub generate_signature($$) {
+
+ my ( $data, $file ) = @_;
+ my $bulk = "";
+
+ $bulk .= "// message catalog signature file //\n\n";
+
+ foreach my $section ( @sections ) {
+ my $props = $sections->{ $section }; # Section properties.
+ my $short = $props->{ short }; # Short section name, frequently used.
+ $bulk .= "-*- " . uc( $props->{ long } ) . "-*-\n\n";
+ foreach my $item ( @{ $data->{ $section } } ) {
+ my ( $ident, $msg ) = @$item;
+ $bulk .= sprintf( "%-40s %s\n", $ident, msg2sgn( $msg ) );
+ }; # foreach
+ $bulk .= "\n";
+ }; # foreach $type
+
+ $bulk .= "// end of file //\n";
+
+ write_file( $file, \$bulk );
+
+}; # sub generate_signature
+
+
+sub generate_default($$$) {
+
+ my ( $data, $file, $prefix ) = @_;
+ my $bulk = "";
+
+ $bulk .=
+ _generate_comment( $data, "//", "//" ) .
+ "\n";
+
+ foreach my $section ( @sections ) {
+ $bulk .=
+ "static char const *\n" .
+ "__${prefix}_default_${section}" . "[] =\n" .
+ " {\n" .
+ " NULL,\n";
+ foreach my $item ( @{ $data->{ $section } } ) {
+ my ( undef, $msg ) = @$item;
+ $bulk .= " \"" . msg2src( $msg ) . "\",\n";
+ }; # while
+ $bulk .=
+ " NULL\n" .
+ " };\n" .
+ "\n";
+ }; # foreach $type
+
+ $bulk .=
+ "struct kmp_i18n_section {\n" .
+ " int size;\n" .
+ " char const ** str;\n" .
+ "}; // struct kmp_i18n_section\n" .
+ "typedef struct kmp_i18n_section kmp_i18n_section_t;\n" .
+ "\n" .
+ "static kmp_i18n_section_t\n" .
+ "__${prefix}_sections[] =\n" .
+ " {\n" .
+ " { 0, NULL },\n";
+ foreach my $section ( @sections ) {
+ $bulk .=
+ " { " . @{ $data->{ $section } } . ", __${prefix}_default_${section} },\n";
+ }; # foreach $type
+ $bulk .=
+ " { 0, NULL }\n" .
+ " };\n" .
+ "\n";
+
+ $bulk .=
+ "struct kmp_i18n_table {\n" .
+ " int size;\n" .
+ " kmp_i18n_section_t * sect;\n" .
+ "}; // struct kmp_i18n_table\n" .
+ "typedef struct kmp_i18n_table kmp_i18n_table_t;\n" .
+ "\n" .
+ "static kmp_i18n_table_t __kmp_i18n_default_table =\n" .
+ " {\n" .
+ " " . @sections . ",\n" .
+ " __kmp_i18n_sections\n" .
+ " };\n" .
+ "\n" .
+ "// end of file //\n";
+
+ write_file( $file, \$bulk );
+
+}; # sub generate_default
+
+
+sub generate_message_unix($$) {
+
+ my ( $data, $file ) = @_;
+ my $bulk = "";
+
+ $bulk .=
+ _generate_comment( $data, "\$", "\$" ) .
+ "\n" .
+ "\$quote \"\n\n";
+
+ foreach my $section ( @sections ) {
+ $bulk .=
+ "\$ " . ( "-" x 78 ) . "\n\$ $section\n\$ " . ( "-" x 78 ) . "\n\n" .
+ "\$set $sections->{ $section }->{ set }\n" .
+ "\n";
+ my $n = 0;
+ foreach my $item ( @{ $data->{ $section } } ) {
+ my ( undef, $msg ) = @$item;
+ ++ $n;
+ $bulk .= "$n \"" . msg2src( $msg ) . "\"\n";
+ }; # foreach
+ $bulk .= "\n";
+ }; # foreach $type
+
+ $bulk .=
+ "\n" .
+ "\$ end of file \$\n";
+
+ write_file( $file, \$bulk, -layer => ":utf8" );
+
+}; # sub generate_message_linux
+
+
+sub generate_message_windows($$) {
+
+ my ( $data, $file ) = @_;
+ my $bulk = "";
+ my $language = $data->{ "%meta" }->{ Language };
+ my $langid = $data->{ "%meta" }->{ LangId };
+
+ $bulk .=
+ _generate_comment( $data, ";", ";" ) .
+ "\n" .
+ "LanguageNames = ($language=$langid:msg_$langid)\n" .
+ "\n";
+
+ $bulk .=
+ "FacilityNames=(\n";
+ foreach my $section ( @sections ) {
+ my $props = $sections->{ $section }; # Section properties.
+ $bulk .=
+ " $props->{ short }=" . $props->{ set } ."\n";
+ }; # foreach $section
+ $bulk .=
+ ")\n\n";
+
+ foreach my $section ( @sections ) {
+ my $short = $sections->{ $section }->{ short };
+ my $n = 0;
+ foreach my $item ( @{ $data->{ $section } } ) {
+ my ( undef, $msg ) = @$item;
+ ++ $n;
+ $bulk .=
+ "MessageId=$n\n" .
+ "Facility=$short\n" .
+ "Language=$language\n" .
+ msg2mc( $msg ) . "\n.\n\n";
+ }; # foreach $item
+ }; # foreach $section
+
+ $bulk .=
+ "\n" .
+ "; end of file ;\n";
+
+ $bulk = encode( "UTF-16LE", $bulk ); # Convert text to UTF-16LE used in Windows* OS.
+ write_file( $file, \$bulk, -binary => 1 );
+
+}; # sub generate_message_windows
+
+
+#
+# Parse command line.
+#
+
+my $input_file;
+my $enum_file;
+my $signature_file;
+my $default_file;
+my $message_file;
+my $id;
+my $prefix = "";
+get_options(
+ Platform::target_options(),
+ "enum-file=s" => \$enum_file,
+ "signature-file=s" => \$signature_file,
+ "default-file=s" => \$default_file,
+ "message-file=s" => \$message_file,
+ "id|lang-id" => \$id,
+ "prefix=s" => \$prefix,
+);
+if ( @ARGV == 0 ) {
+ cmdline_error( "No source file specified -- nothing to do" );
+}; # if
+if ( @ARGV > 1 ) {
+ cmdline_error( "Too many source files specified" );
+}; # if
+$input_file = $ARGV[ 0 ];
+
+
+my $generate_message;
+if ( $target_os =~ m{\A(?:lin|lrb|mac)\z} ) {
+ $generate_message = \&generate_message_unix;
+} elsif ( $target_os eq "win" ) {
+ $generate_message = \&generate_message_windows;
+} else {
+ runtime_error( "OS \"$target_os\" is not supported" );
+}; # if
+
+
+#
+# Do the work.
+#
+
+my $data = parse_source( $input_file );
+if ( defined( $id ) ) {
+ print( $data->{ "%meta" }->{ LangId }, "\n" );
+}; # if
+if ( defined( $enum_file ) ) {
+ generate_enum( $data, $enum_file, $prefix );
+}; # if
+if ( defined( $signature_file ) ) {
+ generate_signature( $data, $signature_file );
+}; # if
+if ( defined( $default_file ) ) {
+ generate_default( $data, $default_file, $prefix );
+}; # if
+if ( defined( $message_file ) ) {
+ $generate_message->( $data, $message_file );
+}; # if
+
+exit( 0 );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<message-converter.pl> -- Convert message catalog source file into another text forms.
+
+=head1 SYNOPSIS
+
+B<message-converter.pl> I<option>... <file>
+
+=head1 OPTIONS
+
+=over
+
+=item B<--enum-file=>I<file>
+
+Generate enum file named I<file>.
+
+=item B<--default-file=>I<file>
+
+Generate default messages file named I<file>.
+
+=item B<--lang-id>
+
+Print language identifier of the message catalog source file.
+
+=item B<--message-file=>I<file>
+
+Generate message file.
+
+=item B<--signature-file=>I<file>
+
+Generate signature file.
+
+Signatures are used for checking compatibility. For example, to check a primary
+catalog and its translation to another language, signatures of both catalogs should be generated
+and compared. If signatures are identical, catalogs are compatible.
+
+=item B<--prefix=>I<prefix>
+
+Prefix to be used for all C identifiers (type and variable names) in enum and default messages
+files.
+
+=item B<--os=>I<str>
+
+Specify OS name the message formats to be converted for. If not specified expolicitly, value of
+LIBOMP_OS environment variable is used. If LIBOMP_OS is not defined, host OS is detected.
+
+Depending on OS, B<message-converter.pl> converts message formats to GNU style or MS style.
+
+=item Standard Options
+
+=over
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full documentation and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--version>
+
+Print version string and exit.
+
+=back
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<file>
+
+A name of input file.
+
+=back
+
+=head1 DESCRIPTION
+
+=head2 Message Catalog File Format
+
+It is plain text file in UTF-8 encoding. Empty lines and lines beginning with sharp sign (C<#>) are
+ignored. EBNF syntax of content:
+
+ catalog = { section };
+ section = header body;
+ header = "-*- " section-id " -*-" "\n";
+ body = { message };
+ message = message-id string "\n" { string "\n" };
+ section-id = identifier;
+ message-id = "OBSOLETE" | identifier;
+ identifier = letter { letter | digit | "_" };
+ string = """ { character } """;
+
+Identifier starts with letter, with following letters, digits, and underscores. Identifiers are
+case-sensitive. Setion identifiers are fixed: C<META>, C<STRINGS>, C<FORMATS>, C<MESSAGES> and
+C<HINTS>. Message identifiers must be unique within section. Special C<OBSOLETE> pseudo-identifier
+may be used many times.
+
+String is a C string literal which must not cross line boundaries.
+Long messages may occupy multiple lines, a string per line.
+
+Message may include printf-like GNU-style placeholders for arguments: C<%I<n>$I<t>>,
+where I<n> is argument number (C<1>, C<2>, ...),
+I<t> -- argument type, C<s> (string) or C<d> (32-bit integer).
+
+See also comments in F<i18n/en_US.txt>.
+
+=head2 Output Files
+
+This script can generate 3 different text files from single source:
+
+=over
+
+=item Enum file.
+
+Enum file is a C include file, containing definitions of message identifiers, e. g.:
+
+ enum kmp_i18n_id {
+
+ // Set #1, meta.
+ kmp_i18n_prp_first = 65536,
+ kmp_i18n_prp_Language,
+ kmp_i18n_prp_Country,
+ kmp_i18n_prp_LangId,
+ kmp_i18n_prp_Version,
+ kmp_i18n_prp_Revision,
+ kmp_i18n_prp_last,
+
+ // Set #2, strings.
+ kmp_i18n_str_first = 131072,
+ kmp_i18n_str_Error,
+ kmp_i18n_str_UnknownFile,
+ kmp_i18n_str_NotANumber,
+ ...
+
+ // Set #3, fotrmats.
+ ...
+
+ kmp_i18n_xxx_lastest
+
+ }; // enum kmp_i18n_id
+
+ typedef enum kmp_i18n_id kmp_i18n_id_t;
+
+=item Default messages file.
+
+Default messages file is a C include file containing default messages to be embedded into
+application (and used if external message catalog does not exist or could not be open):
+
+ static char const *
+ __kmp_i18n_default_meta[] =
+ {
+ NULL,
+ "English",
+ "USA",
+ "1033",
+ "2",
+ "20090806",
+ NULL
+ };
+
+ static char const *
+ __kmp_i18n_default_strings[] =
+ {
+ "Error",
+ "(unknown file)",
+ "not a number",
+ ...
+ NULL
+ };
+
+ ...
+
+=item Message file.
+
+Message file is an input for message compiler, F<gencat> on Linux* OS and OS X*, or F<mc.exe> on
+Windows* OS.
+
+Here is the example of Linux* OS message file:
+
+ $quote "
+ 1 "Japanese"
+ 2 "Japan"
+ 3 "1041"
+ 4 "2"
+ 5 "Based on Enlish message catalog revision 20090806"
+ ...
+
+Example of Windows* OS message file:
+
+ LanguageNames = (Japanese=10041:msg_1041)
+
+ FacilityNames = (
+ prp=1
+ str=2
+ fmt=3
+ ...
+ )
+
+ MessageId=1
+ Facility=prp
+ Language=Japanese
+ Japanese
+ .
+
+ ...
+
+=item Signature.
+
+Signature is a processed source file: comments stripped, strings deleted, but placeholders kept and
+sorted.
+
+ -*- FORMATS-*-
+
+ Info %1$d %2$s
+ Warning %1$d %2$s
+ Fatal %1$d %2$s
+ SysErr %1$d %2$s
+ Hint %1$- %2$s
+ Pragma %1$s %2$s %3$s %4$s
+
+The purpose of signatures -- compare two message source files for compatibility. If signatures of
+two message sources are the same, binary message catalogs will be compatible.
+
+=back
+
+=head1 EXAMPLES
+
+Generate include file containing message identifiers:
+
+ $ message-converter.pl --enum-file=kmp_i18n_id.inc en_US.txt
+
+Generate include file contating default messages:
+
+ $ message-converter.pl --default-file=kmp_i18n_default.inc en_US.txt
+
+Generate input file for message compiler, Linux* OS example:
+
+ $ message-converter.pl --message-file=ru_RU.UTF-8.msg ru_RU.txt
+
+Generate input file for message compiler, Windows* OS example:
+
+ > message-converter.pl --message-file=ru_RU.UTF-8.mc ru_RU.txt
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/objcopy.cpp b/runtime/tools/objcopy.cpp
new file mode 100644
index 0000000..afd18b0
--- /dev/null
+++ b/runtime/tools/objcopy.cpp
@@ -0,0 +1,522 @@
+/*
+ * $Revision: 42181 $
+ * $Date: 2013-03-26 15:04:45 -0500 (Tue, 26 Mar 2013) $
+ */
+
+
+//===----------------------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.txt for details.
+//
+//===----------------------------------------------------------------------===//
+
+
+#include <stdlib.h>
+#include <iostream>
+#include <strstream>
+#include <fstream>
+#include <string>
+#include <set>
+#include <map>
+#include <vector>
+#include <cstring>
+
+using namespace std;
+
+typedef std::string string_t;
+typedef std::vector< string_t > strings_t;
+typedef std::map< string_t, string_t > str_hash_t;
+typedef std::pair< string_t, string_t > str_pair_t;
+#ifdef _WIN32
+ typedef long long int64_t;
+#endif
+
+string_t
+shift( strings_t & strs ) {
+ string_t first = strs.front();
+ strs.erase( strs.begin() );
+ return first;
+} // shift
+
+string_t
+find(
+ str_hash_t const & hash,
+ string_t const & key
+) {
+ string_t value;
+ str_hash_t::const_iterator it = hash.find( key );
+ if ( it != hash.end() ) {
+ value = it->second;
+ }; // if
+ return value;
+} // find
+
+void die( string_t const & message ) {
+ std::cerr << message << std::endl;
+ exit( 1 );
+} // die
+
+void stop( string_t const & message ) {
+ printf( "%s\n", message.c_str() );
+ exit( 1 );
+}
+
+// An entry in the symbol table of a .obj file.
+struct symbol_t {
+ long long name;
+ unsigned value;
+ unsigned short section_num;
+ unsigned short type;
+ char storage_class;
+ char nAux;
+}; // struct symbol_t
+
+
+class _rstream_t : public std::istrstream {
+
+ private:
+
+ const char * buf;
+
+ protected:
+
+ _rstream_t( pair< const char *, streamsize > p )
+ : istrstream( p.first, p.second ), buf( p.first )
+ {
+ }
+
+ ~_rstream_t() {
+ delete [] buf;
+ }
+
+}; // class _rstream_t
+
+/* A stream encapuslating the content of a file or the content of a string, overriding the
+ >> operator to read various integer types in binary form, as well as a symbol table
+ entry.
+*/
+class rstream_t : public _rstream_t {
+private:
+
+ template< typename type_t >
+ inline rstream_t & do_read( type_t & x ) {
+ read( (char*) & x, sizeof( type_t ) );
+ return * this;
+ }
+
+ static pair<const char*, streamsize> getBuf(const char *fileName) {
+ ifstream raw(fileName,ios::binary | ios::in);
+ if(!raw.is_open())
+ stop("rstream.getBuf: Error opening file");
+ raw.seekg(0,ios::end);
+ streampos fileSize = raw.tellg();
+ if(fileSize < 0)
+ stop("rstream.getBuf: Error reading file");
+ char *buf = new char[fileSize];
+ raw.seekg(0,ios::beg);
+ raw.read(buf, fileSize);
+ return pair<const char*, streamsize>(buf,fileSize);
+ }
+public:
+ // construct from a string
+ rstream_t( const char * buf, streamsize size ) :
+ _rstream_t( pair< const char *, streamsize >( buf, size ) )
+ {}
+ /* construct from a file whole content is fully read once to initialize the content of
+ this stream
+ */
+ rstream_t( string_t const & fileName )
+ : _rstream_t( getBuf( fileName.c_str() ) )
+ {
+ }
+
+ rstream_t & operator >>( int & x ) {
+ return do_read(x);
+ }
+ rstream_t & operator >>(unsigned &x) {
+ return do_read(x);
+ }
+ rstream_t & operator>>(short &x) {
+ return do_read(x);
+ }
+ rstream_t & operator>>(unsigned short &x) {
+ return do_read(x);
+ }
+ rstream_t & operator>>( symbol_t & e ) {
+ read((char*)&e, 18);
+ return *this;
+ }
+}; // class rstream_t
+
+// string table in a .OBJ file
+class StringTable {
+private:
+ map<string, unsigned> directory;
+ size_t length;
+ char *data;
+
+ // make <directory> from <length> bytes in <data>
+ void makeDirectory(void) {
+ unsigned i = 4;
+ while(i < length) {
+ string s = string(data + i);
+ directory.insert(make_pair(s, i));
+ i += s.size() + 1;
+ }
+ }
+ // initialize <length> and <data> with contents specified by the arguments
+ void init(const char *_data) {
+ unsigned _length = *(unsigned*)_data;
+
+ if(_length < sizeof(unsigned) || _length != *(unsigned*)_data)
+ stop("StringTable.init: Invalid symbol table");
+ if(_data[_length - 1]) {
+ // to prevent runaway strings, make sure the data ends with a zero
+ data = new char[length = _length + 1];
+ data[_length] = 0;
+ } else {
+ data = new char[length = _length];
+ }
+ *(unsigned*)data = length;
+ memcpy( data + sizeof(unsigned), _data + sizeof(unsigned), length - sizeof(unsigned) );
+ makeDirectory();
+ }
+public:
+ StringTable( rstream_t & f ) {
+ /* Construct string table by reading from f.
+ */
+ streampos s;
+ unsigned strSize;
+ char *strData;
+
+ s = f.tellg();
+ f>>strSize;
+ if(strSize < sizeof(unsigned))
+ stop("StringTable: Invalid string table");
+ strData = new char[strSize];
+ *(unsigned*)strData = strSize;
+ // read the raw data into <strData>
+ f.read(strData + sizeof(unsigned), strSize - sizeof(unsigned));
+ s = f.tellg() - s;
+ if(s < strSize)
+ stop("StringTable: Unexpected EOF");
+ init(strData);
+ delete[]strData;
+ }
+ StringTable(const set<string> &strings) {
+ /* Construct string table from given strings.
+ */
+ char *p;
+ set<string>::const_iterator it;
+ size_t s;
+
+ // count required size for data
+ for(length = sizeof(unsigned), it = strings.begin(); it != strings.end(); ++it) {
+ size_t l = (*it).size();
+
+ if(l > (unsigned) 0xFFFFFFFF)
+ stop("StringTable: String too long");
+ if(l > 8) {
+ length += l + 1;
+ if(length > (unsigned) 0xFFFFFFFF)
+ stop("StringTable: Symbol table too long");
+ }
+ }
+ data = new char[length];
+ *(unsigned*)data = length;
+ // populate data and directory
+ for(p = data + sizeof(unsigned), it = strings.begin(); it != strings.end(); ++it) {
+ const string &str = *it;
+ size_t l = str.size();
+ if(l > 8) {
+ directory.insert(make_pair(str, p - data));
+ memcpy(p, str.c_str(), l);
+ p[l] = 0;
+ p += l + 1;
+ }
+ }
+ }
+ ~StringTable() {
+ delete[] data;
+ }
+ /* Returns encoding for given string based on this string table.
+ Error if string length is greater than 8 but string is not in
+ the string table--returns 0.
+ */
+ int64_t encode(const string &str) {
+ int64_t r;
+
+ if(str.size() <= 8) {
+ // encoded directly
+ ((char*)&r)[7] = 0;
+ strncpy((char*)&r, str.c_str(), 8);
+ return r;
+ } else {
+ // represented as index into table
+ map<string,unsigned>::const_iterator it = directory.find(str);
+ if(it == directory.end())
+ stop("StringTable::encode: String now found in string table");
+ ((unsigned*)&r)[0] = 0;
+ ((unsigned*)&r)[1] = (*it).second;
+ return r;
+ }
+ }
+ /* Returns string represented by x based on this string table.
+ Error if x references an invalid position in the table--returns
+ the empty string.
+ */
+ string decode(int64_t x) const {
+ if(*(unsigned*)&x == 0) {
+ // represented as index into table
+ unsigned &p = ((unsigned*)&x)[1];
+ if(p >= length)
+ stop("StringTable::decode: Invalid string table lookup");
+ return string(data + p);
+ } else {
+ // encoded directly
+ char *p = (char*)&x;
+ int i;
+
+ for(i = 0; i < 8 && p[i]; ++i);
+ return string(p, i);
+ }
+ }
+ void write(ostream &os) {
+ os.write(data, length);
+ }
+};
+
+
+void
+obj_copy(
+ string_t const & src, // Name of source file.
+ string_t const & dst, // Name of destination file.
+ str_hash_t const & redefs // List of redefinititions.
+) {
+
+ set< string > strings; // set of all occurring symbols, appropriately prefixed
+ streampos fileSize;
+ size_t strTabStart;
+ unsigned symTabStart;
+ unsigned symNEntries;
+ int i;
+
+
+ string const error_reading = "Error reading \"" + src + "\" file: ";
+
+ rstream_t in( src );
+
+ in.seekg( 0, ios::end );
+ fileSize = in.tellg();
+
+ in.seekg( 8 );
+ in >> symTabStart >> symNEntries;
+ strTabStart = symTabStart + 18 * size_t( symNEntries );
+ in.seekg( strTabStart );
+ if ( in.eof() ) {
+ stop( error_reading + "Unexpected end of file" );
+ }
+ StringTable stringTableOld( in ); // Read original string table.
+
+ if ( in.tellg() != fileSize ) {
+ stop( error_reading + "Unexpected data after string table" );
+ }
+
+ // compute set of occurring strings with prefix added
+ for ( i = 0; i < symNEntries; ++ i ) {
+
+ symbol_t e;
+
+ in.seekg( symTabStart + i * 18 );
+ if ( in.eof() ) {
+ stop("hideSymbols: Unexpected EOF");
+ }
+ in >> e;
+ if ( in.fail() ) {
+ stop("hideSymbols: File read error");
+ }
+ if ( e.nAux ) {
+ i += e.nAux;
+ }
+ const string & s = stringTableOld.decode( e.name );
+ // if symbol is extern and found in <hide>, prefix and insert into strings,
+ // otherwise, just insert into strings without prefix
+ string_t name = find( redefs, s );
+ strings.insert( name != "" && e.storage_class == 2 ? name : s );
+ }
+
+ ofstream out( dst.c_str(), ios::trunc | ios::out | ios::binary );
+ if ( ! out.is_open() ) {
+ stop("hideSymbols: Error opening output file");
+ }
+
+ // make new string table from string set
+ StringTable stringTableNew = StringTable( strings );
+
+ // copy input file to output file up to just before the symbol table
+ in.seekg( 0 );
+ char * buf = new char[ symTabStart ];
+ in.read( buf, symTabStart );
+ out.write( buf, symTabStart );
+ delete [] buf;
+
+ // copy input symbol table to output symbol table with name translation
+ for ( i = 0; i < symNEntries; ++ i ) {
+ symbol_t e;
+
+ in.seekg( symTabStart + i * 18 );
+ if ( in.eof() ) {
+ stop("hideSymbols: Unexpected EOF");
+ }
+ in >> e;
+ if ( in.fail() ) {
+ stop("hideSymbols: File read error");
+ }
+ const string & s = stringTableOld.decode( e.name );
+ out.seekp( symTabStart + i * 18 );
+ string_t name = find( redefs, s );
+ e.name = stringTableNew.encode( ( e.storage_class == 2 && name != "" ) ? name : s );
+ out.write( (char*) & e, 18 );
+ if ( out.fail() ) {
+ stop( "hideSymbols: File write error" );
+ }
+ if ( e.nAux ) {
+ // copy auxiliary symbol table entries
+ int nAux = e.nAux;
+ for (int j = 1; j <= nAux; ++j ) {
+ in >> e;
+ out.seekp( symTabStart + ( i + j ) * 18 );
+ out.write( (char*) & e, 18 );
+ }
+ i += nAux;
+ }
+ }
+ // output string table
+ stringTableNew.write( out );
+}
+
+
+void
+split( string_t const & str, char ch, string_t & head, string_t & tail ) {
+ string_t::size_type pos = str.find( ch );
+ if ( pos == string_t::npos ) {
+ head = str;
+ tail = "";
+ } else {
+ head = str.substr( 0, pos );
+ tail = str.substr( pos + 1 );
+ }; // if
+} // split
+
+
+void help() {
+ std::cout
+ << "NAME\n"
+ << " objcopy -- copy and translate object files\n"
+ << "\n"
+ << "SYNOPSIS\n"
+ << " objcopy options... source destination\n"
+ << "\n"
+ << "OPTIONS\n"
+ << " --help Print this help and exit.\n"
+ << " --redefine-sym old=new\n"
+ << " Rename \"old\" symbol in source object file to \"new\" symbol in\n"
+ << " destination object file.\n"
+ << " --redefine-syms sym_file\n"
+ << " For each pair \"old new\" in sym_file rename \"old\" symbol in \n"
+ << " source object file to \"new\" symbol in destination object file.\n"
+ << "\n"
+ << "ARGUMENTS\n"
+ << " source The name of source object file.\n"
+ << " destination\n"
+ << " The name of destination object file.\n"
+ << "\n"
+ << "DESCRIPTION\n"
+ << " This program implements a minor bit of Linux* OS's objcopy utility on Windows* OS.\n"
+ << " It can copy object files and edit its symbol table.\n"
+ << "\n"
+ << "EXAMPLES\n"
+ << " \n"
+ << " > objcopy --redefine-sym fastcpy=__xxx_fastcpy a.obj b.obj\n"
+ << "\n";
+} // help
+
+
+int
+main( int argc, char const * argv[] ) {
+
+ strings_t args( argc - 1 );
+ str_hash_t redefs;
+ strings_t files;
+
+ std::copy( argv + 1, argv + argc, args.begin() );
+
+ while ( args.size() > 0 ) {
+ string_t arg = shift( args );
+ if ( arg.substr( 0, 2 ) == "--" ) {
+ // An option.
+ if ( 0 ) {
+ } else if ( arg == "--help" ) {
+ help();
+ return 0;
+ } else if ( arg == "--redefine-sym" ) {
+ if ( args.size() == 0 ) {
+ die( "\"" + arg + "\" option requires an argument" );
+ }; // if
+ // read list of symbol pairs "old new" from command line.
+ string_t redef = shift( args );
+ string_t old_sym;
+ string_t new_sym;
+ split( redef, '=', old_sym, new_sym );
+ if ( old_sym.length() == 0 || new_sym.length() == 0 ) {
+ die( "Illegal redefinition: \"" + redef + "\"; neither old symbol nor new symbol may be empty" );
+ }; // if
+ redefs.insert( str_pair_t( old_sym, new_sym ) );
+ } else if ( arg == "--redefine-syms" ) {
+ if ( args.size() == 0 ) {
+ die( "\"" + arg + "\" option requires an argument" );
+ }; // if
+ // read list of symbol pairs "old new" from file.
+ string_t fname = shift( args );
+ string_t redef;
+ ifstream ifs( fname.c_str() );
+ while ( ifs.good() ) {
+ getline( ifs, redef );// get pair of old/new symbols separated by space
+ string_t old_sym;
+ string_t new_sym;
+ // AC: gcount() does not work here (always return 0), so comment it
+ //if ( ifs.gcount() ) { // skip empty lines
+ split( redef, ' ', old_sym, new_sym );
+ if ( old_sym.length() == 0 || new_sym.length() == 0 ) {
+ break; // end of file reached (last empty line)
+ //die( "Illegal redefinition: \"" + redef + "\"; neither old symbol nor new symbol may be empty" );
+ }; // if
+ redefs.insert( str_pair_t( old_sym, new_sym ) );
+ //}
+ }
+ } else {
+ die( "Illegal option: \"" + arg + "\"" );
+ }; // if
+ } else {
+ // Not an option, a file name.
+ if ( files.size() >= 2 ) {
+ die( "Too many files specified; two files required (use --help option for help)" );
+ }; // if
+ files.push_back( arg );
+ }; // if
+ }; // while
+ if ( files.size() < 2 ) {
+ die( "Not enough files specified; two files required (use --help option for help)" );
+ }; // if
+
+ obj_copy( files[ 0 ], files[ 1 ], redefs );
+
+ return 0;
+
+} // main
+
+
+// end of file //
diff --git a/runtime/tools/required-objects.pl b/runtime/tools/required-objects.pl
new file mode 100755
index 0000000..c5d75b0
--- /dev/null
+++ b/runtime/tools/required-objects.pl
@@ -0,0 +1,629 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use File::Glob ":glob";
+use Data::Dumper;
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+use Platform ":vars";
+
+our $VERSION = "0.004";
+
+# --------------------------------------------------------------------------------------------------
+# Set of objects: # Ref to hash, keys are names of objects.
+# object0: # Ref to hash of two elements with keys "defined" and "undefined".
+# defined: # Ref to array of symbols defined in object0.
+# - symbol0 # Symbol name.
+# - ...
+# undefined: # Ref to array of symbols referenced in object0.
+# - symbol0
+# - ...
+# object1:
+# ...
+# ...
+# --------------------------------------------------------------------------------------------------
+
+# --------------------------------------------------------------------------------------------------
+# Set of symbols: # Ref to hash, keys are names of symbols.
+# symbol0: # Ref to array of object names where the symbol0 is defined.
+# - object0 # Object file name.
+# - ...
+# symbol1:
+# ...
+# ...
+# --------------------------------------------------------------------------------------------------
+
+sub dump_objects($$$) {
+
+ my ( $title, $objects, $dump ) = @_;
+
+ if ( $dump > 0 ) {
+ STDERR->print( $title, "\n" );
+ foreach my $object ( sort( keys( %$objects ) ) ) {
+ STDERR->print( " $object\n" );
+ if ( $dump > 1 ) {
+ STDERR->print( " Defined symbols:\n" );
+ foreach my $symbol ( sort( @{ $objects->{ $object }->{ defined } } ) ) {
+ STDERR->print( " $symbol\n" );
+ }; # foreach $symbol
+ STDERR->print( " Undefined symbols:\n" );
+ foreach my $symbol ( sort( @{ $objects->{ $object }->{ undefined } } ) ) {
+ STDERR->print( " $symbol\n" );
+ }; # foreach $symbol
+ }; # if
+ }; # foreach $object
+ }; # if
+
+}; # sub dump_objects
+
+sub dump_symbols($$$) {
+
+ my ( $title, $symbols, $dump ) = @_;
+
+ if ( $dump > 0 ) {
+ STDERR->print( $title, "\n" );
+ foreach my $symbol ( sort( keys( %$symbols ) ) ) {
+ STDERR->print( " $symbol\n" );
+ if ( $dump > 1 ) {
+ foreach my $object ( sort( @{ $symbols->{ $symbol } } ) ) {
+ STDERR->print( " $object\n" );
+ }; # foreach
+ }; # if
+ }; # foreach $object
+ }; # if
+
+}; # sub dump_symbols
+
+# --------------------------------------------------------------------------------------------------
+# Name:
+# load_symbols -- Fullfill objects data structure with symbol names.
+# Synopsis:
+# load_symbols( $objects );
+# Arguments:
+# $objects (in/out) -- Set of objects. On enter, it is expected that top-level hash has filled
+# with object names only. On exit, it is completely fullfilled with lists of symbols
+# defined or referenced in each object file.
+# Returns:
+# Nothing.
+# Example:
+# my $objects = { foo.o => {} };
+# load_symbols( $objects );
+# # Now $objects is { goo.o => { defined => [ ... ], undefined => [ ... ] } }.
+#
+# --------------------------------------------------------------------------------------------------
+# This version of load_symbols parses output of nm command and works on Linux* OS and OS X*.
+#
+sub _load_symbols_nm($) {
+
+ my $objects = shift( @_ );
+ # It is a ref to hash. Keys are object names, values are empty hashes (for now).
+ my @bulk;
+
+ if ( %$objects ) {
+ # Do not run nm if a set of objects is empty -- nm will try to open a.out in this case.
+ execute(
+ [
+ "nm",
+ "-g", # Display only external (global) symbols.
+ "-o", # Precede each symbol by the name of the input file.
+ keys( %$objects )
+ # Running nm once (rather than once per object) improves performance
+ # drastically.
+ ],
+ -stdout => \@bulk
+ );
+ }; # if
+
+ foreach my $line ( @bulk ) {
+ if ( $line !~ m{^(.*):(?: ?[0-9a-f]*| *) ([A-Za-z]) (.*)$} ) {
+ die "Cannot parse nm output, line:\n $line\n";
+ }; # if
+ my ( $file, $tag, $symbol ) = ( $1, $2, $3 );
+ if ( not exists( $objects->{ $file } ) ) {
+ die "nm reported unknown object file:\n $line\n";
+ }; # if
+ # AC: exclude some libc symbols from renaming, otherwise we have problems
+ # in tests for gfortran + static libiomp on Lin_32.
+ # These symbols came from libtbbmalloc.a
+ if ( $target_os eq "lin" ) {
+ if ( $symbol =~ m{__i686} ) {
+ next;
+ }
+ }
+ # AC: added "w" to tags of undefined symbols, e.g. malloc is weak in libirc v12.1.
+ if ( $tag eq "U" or $tag eq "w" ) { # Symbol not defined.
+ push( @{ $objects->{ $file }->{ undefined } }, $symbol );
+ } else { # Symbol defined.
+ push( @{ $objects->{ $file }->{ defined } }, $symbol );
+ }; # if
+ }; # foreach
+
+ return undef;
+
+}; # sub _load_symbols_nm
+
+# --------------------------------------------------------------------------------------------------
+# This version of load_symbols parses output of link command and works on Windows* OS.
+#
+sub _load_symbols_link($) {
+
+ my $objects = shift( @_ );
+ # It is a ref to hash. Keys are object names, values are empty hashes (for now).
+ my @bulk;
+
+ if ( %$objects ) {
+ # Do not run nm if a set of objects is empty -- nm will try to open a.out in this case.
+ execute(
+ [
+ "link",
+ "/dump",
+ "/symbols",
+ keys( %$objects )
+ # Running nm once (rather than once per object) improves performance
+ # drastically.
+ ],
+ -stdout => \@bulk
+ );
+ }; # if
+
+ my $num_re = qr{[0-9A-F]{3,4}};
+ my $addr_re = qr{[0-9A-F]{8}};
+ my $tag_re = qr{DEBUG|ABS|UNDEF|SECT[0-9A-F]+};
+ my $class_re = qr{Static|External|Filename|Label|BeginFunction|EndFunction|WeakExternal|\.bf or\.ef};
+
+ my $file;
+ foreach my $line ( @bulk ) {
+ if ( $line =~ m{\ADump of file (.*?)\n\z} ) {
+ $file = $1;
+ if ( not exists( $objects->{ $file } ) ) {
+ die "link reported unknown object file:\n $line\n";
+ }; # if
+ } elsif ( $line =~ m{\A$num_re } ) {
+ if ( not defined( $file ) ) {
+ die "link reported symbol of unknown object file:\n $line\n";
+ }; # if
+ if ( $line !~ m{\A$num_re $addr_re ($tag_re)\s+notype(?: \(\))?\s+($class_re)\s+\| (.*?)\n\z} ) {
+ die "Cannot parse link output, line:\n $line\n";
+ }; # if
+ my ( $tag, $class, $symbol ) = ( $1, $2, $3 );
+ # link.exe /dump sometimes prints comments for symbols, e. g.:
+ # ".?0_memcopyA ([Entry] ?0_memcopyA)", or "??_C@_01A@r?$AA@ (`string')".
+ # Strip these comments.
+ $symbol =~ s{ \(.*\)\z}{};
+ if ( $class eq "External" ) {
+ if ( $tag eq "UNDEF" ) { # Symbol not defined.
+ push( @{ $objects->{ $file }->{ undefined } }, $symbol );
+ } else { # Symbol defined.
+ push( @{ $objects->{ $file }->{ defined } }, $symbol );
+ }; # if
+ }; # if
+ } else {
+ # Ignore all other lines.
+ }; # if
+ }; # foreach
+
+ return undef;
+
+}; # sub _load_symbols_link
+
+# --------------------------------------------------------------------------------------------------
+# Name:
+# symbols -- Construct set of symbols with specified tag in the specified set of objects.
+# Synopsis:
+# my $symbols = defined_symbols( $objects, $tag );
+# Arguments:
+# $objects (in) -- Set of objects.
+# $tag (in) -- A tag, "defined" or "undefined".
+# Returns:
+# Set of symbols with the specified tag.
+#
+sub symbols($$) {
+
+ my $objects = shift( @_ );
+ my $tag = shift( @_ );
+
+ my $symbols = {};
+
+ foreach my $object ( keys( %$objects ) ) {
+ foreach my $symbol ( @{ $objects->{ $object }->{ $tag } } ) {
+ push( @{ $symbols->{ $symbol } }, $object );
+ }; # foreach $symbol
+ }; # foreach $object
+
+ return $symbols;
+
+}; # sub symbols
+
+sub defined_symbols($) {
+
+ my $objects = shift( @_ );
+ my $defined = symbols( $objects, "defined" );
+ return $defined;
+
+}; # sub defined_symbols
+
+sub undefined_symbols($) {
+
+ my $objects = shift( @_ );
+ my $defined = symbols( $objects, "defined" );
+ my $undefined = symbols( $objects, "undefined" );
+ foreach my $symbol ( keys( %$defined ) ) {
+ delete( $undefined->{ $symbol } );
+ }; # foreach symbol
+ return $undefined;
+
+}; # sub undefined_symbols
+
+# --------------------------------------------------------------------------------------------------
+# Name:
+# _required_extra_objects -- Select a subset of extra objects required to resolve undefined
+# symbols in a set of objects. It is a helper sub for required_extra_objects().
+# Synopsis:
+# my $required = _required_extra_objects( $objects, $extra, $symbols );
+# Arguments:
+# $objects (in) -- A set of objects to be searched for undefined symbols.
+# $extra (in) -- A set of extra objects to be searched for defined symbols to resolve undefined
+# symbols in objects.
+# $symbols (in/out) -- Set of symbols defined in the set of external objects. At the first call
+# it should consist of all the symbols defined in all the extra objects. Symbols defined in
+# the selected subset of extra objects are removed from set of defined symbols, because
+# they are out of interest for subsequent calls.
+# Returns:
+# A subset of extra objects required by the specified set of objects.
+#
+sub _required_extra_objects($$$$) {
+
+ my $objects = shift( @_ );
+ my $extra = shift( @_ );
+ my $symbols = shift( @_ );
+ my $dump = shift( @_ );
+
+ my $required = {};
+
+ if ( $dump > 0 ) {
+ STDERR->print( "Required extra objects:\n" );
+ }; # if
+ foreach my $object ( keys( %$objects ) ) {
+ foreach my $symbol ( @{ $objects->{ $object }->{ undefined } } ) {
+ if ( exists( $symbols->{ $symbol } ) ) {
+ # Add all objects where the symbol is defined to the required objects.
+ foreach my $req_obj ( @{ $symbols->{ $symbol } } ) {
+ if ( $dump > 0 ) {
+ STDERR->print( " $req_obj\n" );
+ if ( $dump > 1 ) {
+ STDERR->print( " by $object\n" );
+ STDERR->print( " due to $symbol\n" );
+ }; # if
+ }; # if
+ $required->{ $req_obj } = $extra->{ $req_obj };
+ }; # foreach $req_obj
+ # Delete the symbol from list of defined symbols.
+ delete( $symbols->{ $symbol } );
+ }; # if
+ }; # foreach $symbol
+ }; # foreach $object
+
+ return $required;
+
+}; # sub _required_extra_objects
+
+
+# --------------------------------------------------------------------------------------------------
+# Name:
+# required_extra_objects -- Select a subset of extra objects required to resolve undefined
+# symbols in a set of base objects and selected extra objects.
+# Synopsis:
+# my $required = required_extra_objects( $base, $extra );
+# Arguments:
+# $base (in/out) -- A set of base objects to be searched for undefined symbols. On enter, it is
+# expected that top-level hash has filled with object names only. On exit, it is completely
+# fullfilled with lists of symbols defined and/or referenced in each object file.
+# $extra (in/out) -- A set of extra objects to be searched for defined symbols required to
+# resolve undefined symbols in a set of base objects. Usage is similar to base objects.
+# Returns:
+# A subset of extra object files.
+#
+sub required_extra_objects($$$) {
+
+ my $base = shift( @_ );
+ my $extra = shift( @_ );
+ my $dump = shift( @_ );
+
+ # Load symbols for each object.
+ load_symbols( $base );
+ load_symbols( $extra );
+ if ( $dump ) {
+ dump_objects( "Base objects:", $base, $dump );
+ dump_objects( "Extra objects:", $extra, $dump );
+ }; # if
+
+ # Collect symbols defined in extra objects.
+ my $symbols = defined_symbols( $extra );
+
+ my $required = {};
+ # Select extra objects required by base objects.
+ my $delta = _required_extra_objects( $base, $extra, $symbols, $dump );
+ while ( %$delta ) {
+ %$required = ( %$required, %$delta );
+ # Probably, just selected objects require some more objects.
+ $delta = _required_extra_objects( $delta, $extra, $symbols, $dump );
+ }; # while
+
+ if ( $dump ) {
+ my $base_undefined = undefined_symbols( $base );
+ my $req_undefined = undefined_symbols( $required );
+ dump_symbols( "Symbols undefined in base objects:", $base_undefined, $dump );
+ dump_symbols( "Symbols undefined in required objects:", $req_undefined, $dump );
+ }; # if
+
+ return $required;
+
+}; # sub required_extra_objects
+
+
+# --------------------------------------------------------------------------------------------------
+# Name:
+# copy_objects -- Copy (and optionally edit) object files to specified directory.
+# Synopsis:
+# copy_objects( $objects, $target, $prefix, @symbols );
+# Arguments:
+# $objects (in) -- A set of object files.
+# $target (in) -- A name of target directory. Directory must exist.
+# $prefix (in) -- A prefix to add to all the symbols listed in @symbols. If prefix is undefined,
+# object files are just copied.
+# @symbols (in) -- List of symbol names to be renamed.
+# Returns:
+# None.
+#
+sub copy_objects($$;$\@) {
+
+ my $objects = shift( @_ );
+ my $target = shift( @_ );
+ my $prefix = shift( @_ );
+ my $symbols = shift( @_ );
+ my @redefine;
+ my @redefine_;
+ my $syms_file = "__kmp_sym_pairs.log";
+
+ if ( not -e $target ) {
+ die "\"$target\" directory does not exist\n";
+ }; # if
+ if ( not -d $target ) {
+ die "\"$target\" is not a directory\n";
+ }; # if
+
+ if ( defined( $prefix ) and @$symbols ) {
+ my %a = map ( ( "$_ $prefix$_" => 1 ), @$symbols );
+ @redefine_ = keys( %a );
+ }; # if
+ foreach my $line ( @redefine_ ) {
+ $line =~ s{$prefix(\W+)}{$1$prefix};
+ push( @redefine, $line );
+ }
+ write_file( $syms_file, \@redefine );
+ foreach my $src ( sort( keys( %$objects ) ) ) {
+ my $dst = cat_file( $target, get_file( $src ) );
+ if ( @redefine ) {
+ execute( [ "objcopy", "--redefine-syms", $syms_file, $src, $dst ] );
+ } else {
+ copy_file( $src, $dst, -overwrite => 1 );
+ }; # if
+ }; # foreach $object
+
+}; # sub copy_objects
+
+
+# --------------------------------------------------------------------------------------------------
+# Main.
+# --------------------------------------------------------------------------------------------------
+
+my $base = {};
+my $extra = {};
+my $switcher = $base;
+my $dump = 0;
+my $print_base;
+my $print_extra;
+my $copy_base;
+my $copy_extra;
+my $prefix;
+
+# Parse command line.
+
+Getopt::Long::Configure( "permute" );
+get_options(
+ Platform::target_options(),
+ "base" => sub { $switcher = $base; },
+ "extra" => sub { $switcher = $extra; },
+ "print-base" => \$print_base,
+ "print-extra" => \$print_extra,
+ "print-all" => sub { $print_base = 1; $print_extra = 1; },
+ "copy-base=s" => \$copy_base,
+ "copy-extra=s" => \$copy_extra,
+ "copy-all=s" => sub { $copy_base = $_[ 1 ]; $copy_extra = $_[ 1 ]; },
+ "dump" => sub { ++ $dump; },
+ "prefix=s" => \$prefix,
+ "<>" =>
+ sub {
+ my $arg = $_[ 0 ];
+ my @args;
+ if ( $^O eq "MSWin32" ) {
+ # Windows* OS does not expand wildcards. Do it...
+ @args = bsd_glob( $arg );
+ } else {
+ @args = ( $arg );
+ }; # if
+ foreach my $object ( @args ) {
+ if ( exists( $base->{ $object } ) or exists( $extra->{ $object } ) ) {
+ die "Object \"$object\" has already been specified.\n";
+ }; # if
+ $switcher->{ $object } = { defined => [], undefined => [] };
+ }; # foreach
+ },
+);
+if ( not %$base ) {
+ cmdline_error( "No base objects specified" );
+}; # if
+
+if ( $target_os eq "win" ) {
+ *load_symbols = \&_load_symbols_link;
+} elsif ( $target_os eq "lin" or $target_os eq "lrb" ) {
+ *load_symbols = \&_load_symbols_nm;
+} elsif ( $target_os eq "mac" ) {
+ *load_symbols = \&_load_symbols_nm;
+} else {
+ runtime_error( "OS \"$target_os\" not supported" );
+}; # if
+
+# Do the work.
+
+my $required = required_extra_objects( $base, $extra, $dump );
+if ( $print_base ) {
+ print( map( "$_\n", sort( keys( %$base ) ) ) );
+}; # if
+if ( $print_extra ) {
+ print( map( "$_\n", sort( keys( %$required ) ) ) );
+}; # if
+my @symbols;
+if ( defined( $prefix ) ) {
+ foreach my $object ( sort( keys( %$required ) ) ) {
+ push( @symbols, @{ $required->{ $object }->{ defined } } );
+ }; # foreach $objects
+}; # if
+if ( $copy_base ) {
+ copy_objects( $base, $copy_base, $prefix, @symbols );
+}; # if
+if ( $copy_extra ) {
+ copy_objects( $required, $copy_extra, $prefix, @symbols );
+}; # if
+
+exit( 0 );
+
+__END__
+
+=pod
+
+=head1 NAME
+
+B<required-objects.pl> -- Select a required extra object files.
+
+=head1 SYNOPSIS
+
+B<required-objects.pl> I<option>... [--base] I<file>... --extra I<file>...
+
+=head1 DESCRIPTION
+
+B<required-objects.pl> works with two sets of object files -- a set of I<base> objects
+and a set of I<extra> objects, and selects those extra objects which are required for resolving
+undefined symbols in base objects I<and> selected extra objects.
+
+Selected object files may be copied to specified location or their names may be printed to stdout,
+a name per line. Additionally, symbols defined in selected extra objects may be renamed.
+
+Depending on OS, different external tools may be used. For example, B<required-objects.pl> uses
+F<link.exe> on "win" and F<nm> on "lin" and "mac" OSes. Normally OS is autodetected, but
+detection can be overrided with B<--os> option. It may be helpful in cross-build environments.
+
+=head1 OPTIONS
+
+=over
+
+=item B<--base>
+
+The list of base objects follows this option.
+
+=item B<--extra>
+
+List of extra objects follows this option.
+
+=item B<--print-all>
+
+Print list of base objects and list of required extra objects.
+
+=item B<--print-base>
+
+Print list of base objects.
+
+=item B<--print-extra>
+
+Print list of selected extra objects.
+
+=item B<--copy-all=>I<dir>
+
+Copy all base and selected extra objects to specified directory. The directory must exist. Existing
+files are overwritten.
+
+=item B<--copy-base=>I<dir>
+
+Copy all base objects to specified directory.
+
+=item B<--copy-extra=>I<dir>
+
+Copy selected extra objects to specified directory.
+
+=item B<--prefix=>I<str>
+
+If prefix is specified, copied object files are edited -- symbols defined in selected extra
+object files are renamed (in all the copied object files) by adding this prefix.
+
+F<objcopy> program should be available for performing this operation.
+
+=item B<--os=>I<str>
+
+Specify OS name. By default OS is autodetected.
+
+Depending on OS, B<required-objects.pl> uses different external tools.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full documentation and exit.
+
+=item B<--version>
+
+Print version and exit.
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<file>
+
+A name of object file.
+
+=back
+
+=head1 EXAMPLES
+
+ $ required-objects.pl --base obj/*.o --extra ../lib/obj/*.o --print-extra > required.lst
+ $ ar cr libx.a obj/*.o $(cat required.lst)
+
+ $ required-objects.pl --base internal/*.o --extra external/*.o --prefix=__xyz_ --copy-all=obj
+ $ ar cr xyz.a obj/*.o
+
+=cut
+
+# end of file #
+
diff --git a/runtime/tools/src/common-checks.mk b/runtime/tools/src/common-checks.mk
new file mode 100644
index 0000000..08c246f
--- /dev/null
+++ b/runtime/tools/src/common-checks.mk
@@ -0,0 +1,85 @@
+# common-checks.mk #
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+# --------------------------------------------------------------------------------------------------
+# This file contains really common definitions used by multiple makefiles. Modify it carefully!
+# --------------------------------------------------------------------------------------------------
+
+#
+# Check tools versions.
+#
+ifeq "$(clean)" "" # Do not check tools if clean goal specified.
+ ifeq "$(c)" "gcc"
+ curr_tools := $(strip $(shell $(perl) $(tools_dir)check-tools.pl $(oa-opts) --no-intel --gnu-fortran --make))
+ ifneq "$(findstring N/A,$(curr_tools))" ""
+ curr_tools := $(strip $(shell $(perl) $(tools_dir)check-tools.pl $(oa-opts) --make))
+ fort = ifort
+ else
+ fort = gfortran
+ endif
+ else
+ curr_tools := $(strip $(shell $(perl) $(tools_dir)check-tools.pl $(oa-opts) --make))
+ endif
+ ifeq "$(curr_tools)" ""
+ $(error check-tools.pl failed)
+ endif
+ ifneq "$(findstring N/A,$(curr_tools))" ""
+ missed_tools := $(filter %---_N/A_---,$(curr_tools))
+ missed_tools := $(subst =---_N/A_---,,$(missed_tools))
+ missed_tools := $(subst $(space),$(comma)$(space),$(missed_tools))
+ $(error Development tools not found: $(missed_tools))
+ endif
+ prev_tools := $(strip $(shell [ -e tools.cfg ] && cat tools.cfg))
+ $(call say,Tools : $(curr_tools))
+ ifeq "$(prev_tools)" ""
+ # No saved config file, let us create it.
+ dummy := $(shell echo "$(curr_tools)" > tools.cfg)
+ else
+ # Check the saved config file matches current configuration.
+ ifneq "$(curr_tools)" "$(prev_tools)"
+ # Show the differtence between previous and current tools.
+ $(call say,Old tools : $(filter-out $(curr_tools),$(prev_tools)))
+ $(call say,New tools : $(filter-out $(prev_tools),$(curr_tools)))
+ # And initiate rebuild.
+ $(call say,Tools changed$(comma) rebuilding...)
+ dummy := $(shell $(rm) .rebuild && echo "$(curr_tools)" > tools.cfg)
+ endif
+ endif
+endif
+
+# Check config.
+ifeq "$(curr_config)" ""
+ $(error makefile must define `curr_config' variable)
+endif
+prev_config := $(shell [ -e build.cfg ] && cat build.cfg)
+curr_config := $(strip $(curr_config))
+ifeq "$(clean)" "" # Do not check config if clean goal specified.
+ $(call say,Config : $(curr_config))
+ ifeq "$(prev_config)" ""
+ # No saved config file, let us create it.
+ dummy := $(shell echo "$(curr_config)" > build.cfg)
+ else
+ # Check saved config file matches current configuration.
+ ifneq "$(curr_config)" "$(prev_config)"
+ # Show the differtence between previous and current configurations.
+ $(call say,Old config : $(filter-out $(curr_config),$(prev_config)))
+ $(call say,New config : $(filter-out $(prev_config),$(curr_config)))
+ # And initiate rebuild.
+ $(call say,Configuration changed$(comma) rebuilding...)
+ dummy := $(shell $(rm) .rebuild && echo "$(curr_config)" > build.cfg)
+ endif
+ endif
+endif
+
+# end of file #
+
diff --git a/runtime/tools/src/common-defs.mk b/runtime/tools/src/common-defs.mk
new file mode 100644
index 0000000..1c164bc
--- /dev/null
+++ b/runtime/tools/src/common-defs.mk
@@ -0,0 +1,228 @@
+# common-defs.mk #
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+# --------------------------------------------------------------------------------------------------
+# This file contains really common definitions used by multiple makefiles. Modify it carefully!
+# --------------------------------------------------------------------------------------------------
+
+# --------------------------------------------------------------------------------------------------
+# Some tricky variables.
+# --------------------------------------------------------------------------------------------------
+empty :=
+space := $(empty) $(empty)
+comma := ,
+ifeq "$(date)" ""
+ $(error Caller should specify "date" variable.)
+endif
+
+# --------------------------------------------------------------------------------------------------
+# Helper finctions.
+# --------------------------------------------------------------------------------------------------
+
+# Synopsis:
+# $(call say,text-to-print-to-the-screen)
+# Description:
+# The function prints its argument to the screen. In case of older makes it is analog of
+# $(warning), starting from make 3.81 is is analog of $(info).
+#
+say = $(warning $(1))
+ifneq "$(filter 3.81,$(MAKE_VERSION))" ""
+ say = $(info $(1))
+endif
+
+# Synopsis:
+# architecture = $(call legal_arch,32)
+# Description:
+# The function return printable name of specified architecture, IA-32 architecture or Intel(R) 64.
+#
+legal_arch = $(if $(filter 32,$(1)),IA-32,$(if $(filter 32e,$(1)),Intel(R) 64,$(if $(filter l1,$(1)),L1OM,$(error Bad architecture specified: $(1))))))
+
+# Synopsis:
+# var_name = $(call check_variable,var,list)
+# Description:
+# The function verifies the value of var varibale. If it is empty, the first word from the list
+# is assigned to var as default value. Otherwise the var value must match one of words in the
+# list, or error is issued.
+# Example:
+# LINK_TYPE = $(call check_variable,LINK_TYPE,static dynamic)
+#
+check_variable = $(call _check_variable_words,$(1))$(call _check_variable_value,$(1),$(2))
+
+# Synopsis:
+# $(call _check_variable_words,var)
+# Description:
+# Checks that variable var is empty or single word. In case of multiple words an error is
+# issued. It is helper function for check_variable.
+#
+_check_variable_words = $(if $(filter 0 1,$(words $($(1)))),,\
+ $(error Illegal value of $(1): "$($(1))"))
+
+# Synopsis:
+# $(call _check_variable_value,var)
+# Description:
+# If variable var is empty, the function returns the first word from the list. If variable is
+# not empty and match one of words in the list, variable's value returned. Otherwise, error is
+# issued. It is helper function for check_variable.
+#
+_check_variable_value = $(if $($(1)),$(if $(filter $(2),$($(1))),$($(1)),\
+ $(error Illegal value of $(1): "$($(1))")),$(firstword $(2)))
+
+# Synopsis:
+# $(call debug,var)
+# Description:
+# If LIBOMP_MAKE_DEBUG is not empty, var name and value printed. Use this for debug purpose.
+#
+ifeq "$(LIBOMP_MAKE_DEBUG)" ""
+ debug =
+else
+ debug = $(call say,debug: $(1)="$($(1))")
+endif
+
+# Synopsis:
+# $(call header,target)
+# Description:
+# Returns a string to print to show build progress.
+#
+header = ----- $(marker) --- $(1) -----
+
+# --------------------------------------------------------------------------------------------------
+# Global make settings.
+# --------------------------------------------------------------------------------------------------
+
+# Non-empty CDPATH may lead to problems on some platforms: simple "cd dir" (where "dir" is an
+# existing directory in current one) fails. Clearing CDPATH solves the problem.
+CDPATH =
+.SUFFIXES : # Clean default list of suffixes.
+.DELETE_ON_ERROR : # Delete target file in case of error.
+
+$(call say,$(call header,making $(if $(MAKECMDGOALS),$(MAKECMDGOALS),all)))
+
+# --------------------------------------------------------------------------------------------------
+# Check clean and clobber goals.
+# --------------------------------------------------------------------------------------------------
+
+# "clean" goal must be specified alone, otherwise we have troubles with dependency files.
+clean := $(filter clean%,$(MAKECMDGOALS))
+ifneq "$(clean)" "" # "clean" goal is present in command line.
+ ifneq "$(filter-out clean%,$(MAKECMDGOALS))" "" # there are non-clean goals.
+ $(error "clean" goals must not be mixed with other goals)
+ endif
+endif
+# Issue error on "clobber" target.
+ifneq "$(filter clobber,$(MAKECMDGOALS))" ""
+ $(error There is no clobber goal in makefile)
+endif
+
+# --------------------------------------------------------------------------------------------------
+# Mandatory variables passed from build.pl.
+# --------------------------------------------------------------------------------------------------
+
+os := $(call check_variable,os,lin lrb mac win)
+arch := $(call check_variable,arch,32 32e 64)
+platform := $(os)_$(arch)
+platform := $(call check_variable,platform,lin_32 lin_32e lin_64 lrb_32e mac_32 mac_32e win_32 win_32e win_64)
+# oa-opts means "os and arch options". They are passed to almost all perl scripts.
+oa-opts := --os=$(os) --arch=$(arch)
+
+# --------------------------------------------------------------------------------------------------
+# Directories.
+# --------------------------------------------------------------------------------------------------
+
+ifeq "$(LIBOMP_WORK)" ""
+ $(error Internal error: LIBOMP_WORK variable must be set in makefile.mk)
+endif
+tools_dir = $(LIBOMP_WORK)tools/
+# We do not define src/ and other directories here because they depends on target (RTL, DSL, tools).
+
+# --------------------------------------------------------------------------------------------------
+# File suffixes.
+# --------------------------------------------------------------------------------------------------
+
+ifeq "$(os)" "win" # win
+ asm = .asm
+ obj = .obj
+ lib = .lib
+ dll = .dll
+ exe = .exe
+ cat = $(dll)
+else # lin, lrb or mac
+ asm = .s
+ obj = .o
+ lib = .a
+ ifeq "$(os)" "mac"
+ dll = .dylib
+ else
+ dll = .so
+ endif
+ exe = $(empty)
+ cat = .cat
+endif
+
+# --------------------------------------------------------------------------------------------------
+# File manipulation and misc commands.
+# --------------------------------------------------------------------------------------------------
+
+target = @echo "$(call header,$@)"
+ifeq "$(os)" "win"
+ cp = cp -f
+ rm = rm -f
+ mkdir = mkdir -p
+ touch = touch
+ perl = perl
+ slash = \\
+else # lin, lrb or mac
+ cp = cp -f
+ rm = rm -f
+ mkdir = mkdir -p
+ touch = touch
+ perl = perl
+ slash = /
+endif
+
+# --------------------------------------------------------------------------------------------------
+# Common non-configuration options.
+# --------------------------------------------------------------------------------------------------
+# They may affect build process but does not affect result.
+
+# If TEST_DEPS is "off", test deps is still performed, but its result is ignored.
+TEST_DEPS := $(call check_variable,TEST_DEPS,on off)
+# The same for test touch.
+TEST_TOUCH := $(call check_variable,TEST_TOUCH,on off)
+td-i = $(if $(filter off,$(TEST_DEPS)),-)
+tt-i = $(if $(filter off,$(TEST_TOUCH)),-)
+
+# --------------------------------------------------------------------------------------------------
+# Common targets.
+# --------------------------------------------------------------------------------------------------
+
+# All common targets are defined as phony. It allows "buil.pl --all test-xxx".
+# Makefile can define actions for a particiular test or leave it no-op.
+
+# all, the default target, should be the first one.
+.PHONY : all
+all :
+
+.PHONY : common clean clean-common fat inc l10n lib
+
+.PHONY : force-tests tests
+.PHONY : force-test-touch test-touch
+.PHONY : force-test-relo test-relo
+.PHONY : force-test-execstack test-execstack
+.PHONY : force-test-instr test-instr
+.PHONY : force-test-deps test-deps
+
+tests = touch relo execstack instr deps
+tests : $(addprefix test-,$(tests))
+force-tests : $(addprefix force-test-,$(tests))
+
+# end of file #
diff --git a/runtime/tools/src/common-rules.mk b/runtime/tools/src/common-rules.mk
new file mode 100644
index 0000000..c289beb
--- /dev/null
+++ b/runtime/tools/src/common-rules.mk
@@ -0,0 +1,188 @@
+# common-rules.mk #
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+# --------------------------------------------------------------------------------------------------
+# This file contains really common definitions used by multiple makefiles. Modify it carefully!
+# --------------------------------------------------------------------------------------------------
+
+# --- Creating a directory ---
+# A directory cannot be a target, because in Linux* OS directory's timestamp is updated each time a
+# file is created or deleted in the directory. We use ".dir" file in place of directory. If such
+# file exists, it means directory exists also.
+
+.PRECIOUS : %/.dir # Do not delete automatically created files.
+
+%/.dir :
+ $(target)
+ $(mkdir) $(dir $@)
+ $(touch) $@
+
+# --- Rebuilding ---
+# Removing or touching .rebuild file causes rebuild.
+# To let it work, .rebuild should be added as prerequisite to every rule (dependency with commands)
+# except clean* and force*, in this and other makefiles.
+.rebuild :
+ $(target)
+ $(touch) $@
+
+# -- Creating dependency file for C/C++ ---
+
+%.d : %.c .rebuild
+ $(target)
+ $(c) $(cpp-flags) $(c-flags) $(c-flags-m) $< > $@
+
+%.d : %.cpp .rebuild
+ $(target)
+ $(cxx) $(cpp-flags) $(cxx-flags) $(cxx-flags-m) $< > $@
+
+# -- Creating preprocessed file for C/C++ ---
+
+%.i : %.c .rebuild
+ $(target)
+ $(c) $(cpp-flags) $(c-flags) -P $(c-out)$@ $<
+
+%.i : %.cpp .rebuild
+ $(target)
+ $(cxx) $(cpp-flags) $(cxx-flags) -P $(cxx-out)$@ $<
+
+# -- Compiling C/C++ files ---
+
+%$(obj) : %.c .rebuild
+ $(target)
+ $(c) $(cpp-flags) $(c-flags) $(c-out)$@ $<
+
+%$(obj) : %.cpp .rebuild
+ $(target)
+ $(cxx) $(cpp-flags) $(cxx-flags) $(cxx-out)$@ $<
+
+# -- Generate assembly files ---
+
+%$(asm) : %.c .rebuild
+ $(target)
+ $(c) $(cpp-flags) $(c-flags) -S $(c-out)$@ $<
+
+%$(asm) : %.cpp .rebuild
+ $(target)
+ $(cxx) $(cpp-flags) $(cxx-flags) -S $(cxx-out)$@ $<
+
+# -- Compiling asm files ---
+
+%$(obj) : %$(asm) .rebuild
+ $(target)
+ # There is a bug on lrb: icc does not work with "-x assembler-with-cpp" option, so we have
+ # to preprocess file manually and then assembly it.
+ ifeq "$(os)" "lrb"
+ $(c) -E $(cpp-flags) $< > $@.tmp
+ $(as) $(as-flags) -x assembler $(as-out)$@ $@.tmp
+ else
+ $(as) $(as-flags) $(as-out)$@ $<
+ endif
+
+# -- Expanding variables in template files ---
+
+# General rule "% : %.var" does not work good, so we have to write more specific rules:
+# "%.h : %.h.var", etc.
+
+.PRECIOUS : %.h %.f %.rc # Do not delete automatically created files.
+
+expand-vars = $(perl) $(tools_dir)expand-vars.pl --strict $(ev-flags) $< $@
+
+# Any generated file depends on kmp_version.c, because we extract build number from that file.
+
+%.h : %.h.var \
+ kmp_version.c $(tools_dir)expand-vars.pl .rebuild
+ $(target)
+ $(expand-vars)
+
+%.f : %.f.var \
+ kmp_version.c $(tools_dir)expand-vars.pl .rebuild
+ $(target)
+ $(expand-vars)
+
+%.f90 : %.f90.var \
+ kmp_version.c $(tools_dir)expand-vars.pl .rebuild
+ $(target)
+ $(expand-vars)
+
+%.rc : %.rc.var \
+ kmp_version.c $(tools_dir)expand-vars.pl .rebuild
+ $(target)
+ $(expand-vars)
+
+# -- Making static library ---
+
+.PRECIOUS : %$(lib) # Do not delete automatically created files.
+
+%$(lib) : %$(lib).lst .rebuild
+ $(target)
+ $(rm) $@
+ $(ar) $(ar-flags) $(ar-out)$@ $$(cat $<)
+ # strip debug info in case it is requested (works for Linux* OS only)
+ ifneq "$(dbg_strip)" ""
+ ifeq "$(DEBUG_INFO)" "off"
+ objcopy --strip-debug $@
+ endif
+ endif
+
+# -- Making dynamic library ---
+
+.PRECIOUS : %$(dll) # Do not delete automatically created files.
+
+# makefile.mk should properly define imp_file, def_file, res_file, and pdb_file:
+# lin and mac: def_file and res_file should be empty, imp_file and pdb_file do not matter.
+# win: all the variabe may be empty; if a variable specified, it affects ld-flags.
+# Note: imp_file and pdb_file are side effect of building this target.
+# Note: to workaround CQ215229 $ld-flags-extra introduced to keep options be placed after objects
+%$(dll) : %$(dll).lst $(def_file) $(res_file) .rebuild
+ $(target)
+ $(ld) $(ld-flags-dll) $(ld-flags) $(ld-out)$@ $$(cat $<) $(ld-flags-extra) $(res_file)
+ # If stripped pdb exist, rename it to normal pdb name. See devtools.mk for explanation.
+ ifneq "$(pdb_file)" ""
+ ifeq "$(DEBUG_INFO)" "off"
+ mv $(pdb_file) $(pdb_file).nonstripped
+ mv $(pdb_file).stripped $(pdb_file)
+ endif
+ endif
+
+%.dbg : %$(dll) .rebuild
+ $(target)
+ objcopy --only-keep-debug $< $@
+
+
+.PRECIOUS: %.res # Do not delete automatically created files.
+
+%.res : %.rc .rebuild
+ $(target)
+ rc -fo$@ $<
+
+# --- Building helper tools from sources ---
+
+.PRECIOUS: %$(exe) # Do not delete automatically created files.
+
+%$(exe) : $(tools_dir)%.cpp .rebuild
+ $(target)
+ $(cxx) $(cxx-out)$@ $<
+
+# --- Forcing a test ---
+
+test-%/.force : test-%/.dir
+ $(target)
+ $(rm) $(dir $@).{test,force}
+
+# --- Removing a file in build directory ---
+
+rm-% :
+ $(target)
+ $(rm) $(patsubst rm-%,%,$@)
+
+# end of file #
diff --git a/runtime/tools/src/common-tools.mk b/runtime/tools/src/common-tools.mk
new file mode 100644
index 0000000..65bc92e
--- /dev/null
+++ b/runtime/tools/src/common-tools.mk
@@ -0,0 +1,410 @@
+# common-tools.mk #
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+# --------------------------------------------------------------------------------------------------
+# Dev tools and general options (like -fpic, -O2 or -g).
+# --------------------------------------------------------------------------------------------------
+
+# c -- C compiler.
+# cxx -- C++ compiler.
+# cpp -- C preprocessor.
+# fort -- Fortran compiler.
+# as -- Assembler.
+# ar -- Librarian (static library maker).
+# ld -- Linker (dynamic library maker).
+# *-out -- Flag denoting output file. If space between flag and file name required, add explicit
+# space to variable, e. g.: "c-out = -o$(space)".
+# *-flags -- Flags to appropriate program, e. g. c-flags -- flags for C compiler, etc.
+
+# --- Common definitions ---
+
+# Add current directory (it contains generated files).
+# Note: It is important to specify current dir as "./" (not just "."). Otherwise Intel compiler
+# on Windows* OS generates such a dependency: "kmp_runtime.obj: .\kmp_i18n.inc", and make complains
+# "No rule to build .\kmp_i18n.inc". Using "./" solves the problem.
+cpp-flags += -I ./
+# Add all VPATH directories to path for searching include files.
+cpp-flags += $(foreach i,$(VPATH),-I $(i))
+
+
+# Shouldn't this be being set from the command line somehow?
+cpp-flags += -D USE_ITT_BUILD
+
+ifeq "$(OPTIMIZATION)" "on"
+ cpp-flags += -D NDEBUG
+else
+ cpp-flags += -D _DEBUG -D BUILD_DEBUG
+endif
+
+# --- Linux* OS, Intel(R) Many Integrated Core Architecture and OS X* definitions ---
+
+ifneq "$(filter lin lrb mac,$(os))" ""
+ # --- C/C++ ---
+ ifeq "$(c)" ""
+ c = icc
+ endif
+ # C++ compiler is a complement to C compiler.
+ ifeq "$(c)" "icc"
+ cxx = icpc
+ endif
+ ifeq "$(c)" "gcc"
+ cxx = g++
+ endif
+ # Output file flag.
+ c-out = -o$(space)
+ cxx-out = -o$(space)
+ # Compile only, no link.
+ c-flags += -c
+ cxx-flags += -c
+ # Generating dependecy file.
+ c-flags-m += -M -MG
+ cxx-flags-m += -M -MG
+ # Enable C99 language.
+ c-flags += -std=c99
+ # Generate position-independent code (a must for shared objects).
+ ifeq "$(LINK_TYPE)" "dyna"
+ c-flags += -fPIC
+ cxx-flags += -fPIC
+ endif
+ # Emit debugging information.
+ ifeq "$(DEBUG_INFO)" "on"
+ c-flags += -g
+ cxx-flags += -g
+ endif
+ # Instrument program for profiling, gather extra information.
+ ifeq "$(COVERAGE)" "on"
+ ifeq "$(c)" "icc"
+ c-flags += -prof_genx
+ endif
+ ifeq "$(cxx)" "icpc"
+ cxx-flags += -prof_genx
+ endif
+ endif
+ # Turn optimization on or off.
+ ifeq "$(OPTIMIZATION)" "on"
+ # -inline-min-size=1 improves performance of PARALLEL EPCC up to 10% on fxi64lin01,
+ # doesn't change performance on fxe64lin01.
+ # Presence of the -inline-min-size=1 switch should only help
+ # to promote performance stability between changes,
+ # even if it has no observable impact right now.
+ ifneq "$(filter icl icl.exe,$(c))" ""
+ c-flags += -O2 -inline-min-size=1
+ else
+ c-flags += -O2
+ endif
+ ifneq "$(filter icl icl.exe,$(cxx))" ""
+ cxx-flags += -O2 -inline-min-size=1
+ else
+ cxx-flags += -O2
+ endif
+ else
+ c-flags += -O0
+ cxx-flags += -O0
+ endif
+ # --- Assembler ---
+ ifeq "$(c)" "icc"
+ as = icc
+ endif
+ ifeq "$(c)" "gcc"
+ as = gcc
+ endif
+ as-out = -o$(space)
+ as-flags += $(cpp-flags)
+ # Compile only, no link.
+ as-flags += -c
+ as-flags += -x assembler-with-cpp
+ # --- Fortran ---
+ ifeq "$(fort)" ""
+ fort = ifort
+ endif
+ fort-out = -o$(space)
+ fort-flags += -c
+endif
+
+# --- Linux* OS definitions ---
+
+ifeq "$(os)" "lin"
+ # --- C/C++ ---
+ # On lin_32, we want to maintain stack alignment to be conpatible with GNU binaries built with
+ # compiler.
+ ifeq "$(c)" "icc"
+ ifeq "$(arch)" "32"
+ c-flags += -falign-stack=maintain-16-byte
+ cxx-flags += -falign-stack=maintain-16-byte
+ endif
+ # Generate code that will run on any Pentium or later processor.
+ ifeq "$(arch)" "32"
+ c-flags += -mia32
+ cxx-flags += -mia32
+ endif
+ endif
+ # --- Librarian ---
+ ar = ar
+ ar-out = $(empty)
+ ar-flags += cr
+ # --- Linker ---
+ # Use ld by default, however, makefile may specify ld=$(c) before including devtools.mk.
+ ifeq "$(ld)" ""
+ ld = $(c)
+ endif
+ ld-flags-dll += -shared
+ ifeq "$(ld)" "ld"
+ ld-out = -o$(space)
+ ifeq "$(arch)" "32"
+ ld-flags += -m elf_i386
+ endif
+ ifeq "$(arch)" "32e"
+ ld-flags += -m elf_x86_64
+ endif
+ ld-flags += -x -lc -ldl
+ ld-flags += -z noexecstack
+ ld-flags-dll += -soname=$(@F)
+ endif
+ ifeq "$(ld)" "$(c)"
+ ld-out = $(c-out)
+ ld-flags += -Wl,-z,noexecstack
+ ld-flags-dll += -Wl,-soname=$(@F)
+ endif
+ ifeq "$(ld)" "$(cxx)"
+ ld-out = $(cxx-out)
+ ld-flags += -Wl,-z,noexecstack
+ ld-flags-dll += -Wl,-soname=$(@F)
+ endif
+endif
+
+# --- Intel(R) Many Integrated Core Architecture definitions ---
+
+ifeq "$(os)" "lrb"
+ # --- C/C++ ---
+ # Intel(R) Many Integrated Core Architecture specific options, need clarification for purpose:
+ #c-flags += -mmic -mP2OPT_intrin_disable_name=memcpy -mP2OPT_intrin_disable_name=memset -mGLOB_freestanding -mGLOB_nonstandard_lib -nostdlib -fno-builtin
+ #cxx-flags += -mmic -mP2OPT_intrin_disable_name=memcpy -mP2OPT_intrin_disable_name=memset -mGLOB_freestanding -mGLOB_nonstandard_lib -nostdlib -fno-builtin
+ # icc for lrb has a bug: it generates dependencies for target like file.obj, while real object
+ # files are named file.o. -MT is a workaround for the problem.
+ c-flags-m += -MT $(basename $@).o
+ cxx-flags-m += -MT $(basename $@).o
+ # --- Librarian ---
+ ar = ar
+ ar-out = $(empty)
+ ar-flags += cr
+ # --- Linker ---
+ # Use $(c) by default, however, makefile may specify another linker (e.g. ld=ld) before including devtools.mk.
+ ifeq "$(ld)" ""
+ ld = $(c)
+ endif
+ ifeq "$(ld)" "ld"
+ ld-out = -o$(space)
+ ld-flags += -m elf_l1om_fbsd
+ ld-flags-dll += -shared -x -lc
+ ld-flags-dll += -soname=$(@F)
+ # Now find out path to libraries.
+ ld-flags-L := $(shell $(c) -Wl,-v -\# 2>&1 | grep -e "-L")
+ $(call debug,ld-flags-L)
+ # Remove continuation characters; first add a space to the end (" -Lpath1 /" -> "-Lpath1 / ")
+ ld-flags-L := $(filter-out \,$(ld-flags-L))
+ $(call debug,ld-flags-L)
+ # Linker treats backslash ('\') as an escape symbol, so replace it with forward slash.
+ ld-flags-L := $(subst \,/,$(ld-flags-L))
+ $(call debug,ld-flags-L)
+ ld-flags += $(ld-flags-L)
+ endif
+ ifeq "$(ld)" "$(c)"
+ ld-out = $(c-out)
+ ld-flags-dll += -shared -Wl,-x -Wl,-soname=$(@F)
+ endif
+ ifeq "$(ld)" "$(cxx)"
+ ld-out = $(cxx-out)
+ ld-flags-dll += -shared -Wl,-x -Wl,-soname=$(@F)
+ endif
+endif
+
+# --- OS X* definitions ---
+
+ifeq "$(os)" "mac"
+ # --- Librarian ---
+ ar = libtool
+ ar-out = -o$(space)
+ ar-flags += -static
+ # --- Linker ---
+ # Use C compiler as linker by default, however, makefile may specify ld=$(libtool) before
+ # including devtools.mk.
+ ifeq "$(ld)" ""
+ ld = $(c)
+ endif
+ ifeq "$(ld)" "libtool"
+ ld-out = -o$(space)
+ ld-flags-dll += -dynamic
+ ld-flags += -lc -ldl
+ endif
+ ifeq "$(ld)" "$(c)"
+ ld-out = $(c-out)
+ ld-flags-dll += -dynamiclib
+ endif
+ ifeq "$(ld)" "$(cxx)"
+ ld-out = $(cxx-out)
+ ld-flags-dll += -dynamiclib
+ endif
+ # These options suitable for any linker, either C compiler or libtool.
+ ld-flags-dll += -headerpad_max_install_names
+ ld-flags-dll += -install_name $(@F)
+endif
+
+# --- Windows* OS definitions ---
+
+ifeq "$(os)" "win"
+ # Disable warning "function "..." (declared at line ... of ...) was declared deprecated...".
+ cpp-flags += -D_CRT_SECURE_NO_WARNINGS -D_CRT_SECURE_NO_DEPRECATE
+ # --- C/C++ ---
+ ifeq "$(c)" ""
+ c = icl.exe
+ endif
+ cxx = $(c)
+ # Often default icl.cfg file in compiler bin/ directory contains options -Qvc and
+ # -Qlocation,link. Setting ICLCFG (and IFORTCFG) to specially prepared empty config file
+ # overrides default config.
+ ICLCFG = $(tools_dir)icc.cfg
+ IFORTCFG = $(tools_dir)icc.cfg
+ export ICLCFG
+ export IFORTCFG
+ # Output file.
+ c-out = -o$(space)
+ cxx-out = -o$(space)
+ # Disable annoying compiler logo.
+ c-flags += -nologo
+ cxx-flags += -nologo
+ # Generate code that will run on any Pentium or later processor.
+ ifeq "$(arch)" "32"
+ c-flags += -arch:ia32
+ cxx-flags += -arch:ia32
+ endif
+ # Compile only, no link.
+ c-flags += -c
+ cxx-flags += -c
+ # -QM -- Generate dependency file.
+ # -QMM -- do not include system headers. On Windows* OS, system headers may be located in
+ # "C:\Program Files\...", but path with space confuses make, so we exclude system
+ # headers.
+ # -QMG -- Treat missed headers as generated. We do have some generated include files.
+ c-flags-m += -QM -QMM -QMG
+ cxx-flags-m += -QM -QMM -QMG
+ # Enable C99 language.
+ c-flags += -Qstd=c99
+ # Enable C++ exception handling.
+ # ??? Why we disable it on Linux* OS?
+ cxx-flags += -EHsc
+ ifeq "$(arch)" "32"
+ ifneq "$(filter icl icl.exe,$(c))" ""
+ c-flags += -Qsafeseh
+ endif
+ ifneq "$(filter icl icl.exe,$(cxx))" ""
+ cxx-flags += -Qsafeseh
+ endif
+ endif
+ # Emit debugging information.
+ ifeq "$(DEBUG_INFO)" "on"
+ c-flags += -Zi
+ cxx-flags += -Zi
+ endif
+ # Instrument program for profiling, gather extra information.
+ ifeq "$(COVERAGE)" "on"
+ c-flags += -Qprof_genx
+ cxx-flags += -Qprof_genx
+ endif
+ # Turn optimization on or off.
+ ifeq "$(OPTIMIZATION)" "on"
+ # Presence of the -inline-min-size=1 switch should only help
+ # to promote performance stability between changes,
+ # even if it has no observable impact right now.
+ # See the Linux* OS section above.
+ ifneq "$(filter icl icl.exe,$(c))" ""
+ c-flags += -O2 -Qinline-min-size=1
+ else
+ c-flags += -O2
+ endif
+ ifneq "$(filter icl icl.exe,$(cxx))" ""
+ cxx-flags += -O2 -Qinline-min-size=1
+ else
+ cxx-flags += -O2
+ endif
+ else
+ c-flags += -Od
+ cxx-flags += -Od
+ # Enable stack frame runtime error checking.
+ # !!! 0Obsolete option. Should use /RTC instead.
+ c-flags += -RTC1
+ cxx-flags += -RTC1
+ endif
+ # --- Assembler ---
+ ifeq "$(arch)" "32"
+ as = ml
+ endif
+ ifeq "$(arch)" "32e"
+ as = ml64
+ endif
+ ifeq "$(as)" "ias"
+ as-out = -o$(space)
+ endif
+ ifneq "$(filter ml ml64,$(as))" ""
+ as-out = -Fo
+ as-flags += -nologo -c
+ endif
+ # --- Fortran ---
+ fort = ifort
+ fort-out = -o$(space)
+ fort-flags += -nologo
+ fort-flags += -c
+ # --- Librarian ---
+ ar = link.exe
+ ar-out = -out:
+ # Generate static library. Must be the first option.
+ ar-flags += -lib
+ # Turn off tool banner.
+ ar-flags += -nologo
+ # --- Linker ---
+ ld = link.exe
+ ld-out = -out:
+ # Generate dynamic library.
+ ld-flags-dll += -dll
+ # Turn off tool banner.
+ ld-flags += -nologo
+ # Generate pdb (Program DataBase, debug information) file.
+ # If DEBUG_INFO is on, generate normal (full-featured) pdb file. Otherwise, we need only
+ # stripped pdb. But stripped pdb cannot be generated alone, we have to generate normal *and*
+ # stripped pdb. After generating both pdb files we rename stripped pdb to normal pdb name (see
+ # rules.mk).
+ ifeq "$(DEBUG_INFO)" "on"
+ ld-flags += $(if $(pdb_file),-debug -pdb:$(pdb_file))
+ else
+ ld-flags += $(if $(pdb_file),-debug -pdb:$(pdb_file) -pdbstripped:$(pdb_file).stripped)
+ endif
+ # Use def file, if $(def_file) is specified.
+ ld-flags += $(if $(def_file),-def:$(def_file))
+ # Generate import library, if $(imp_file) is specified.
+ ld-flags += $(if $(imp_file),-implib:$(imp_file))
+ # Specify architecture.
+ ifeq "$(arch)" "32"
+ ar-flags += -machine:i386
+ ld-flags += -machine:i386
+ endif
+ ifeq "$(arch)" "32e"
+ ar-flags += -machine:amd64
+ ld-flags += -machine:amd64
+ endif
+ # SAFESEH
+ ifeq "$(arch)" "32"
+ as-flags += -safeseh
+ ld-flags += -safeseh
+ endif
+endif
+
+# end of file #
diff --git a/runtime/tools/windows.inc b/runtime/tools/windows.inc
new file mode 100644
index 0000000..3d2e070
--- /dev/null
+++ b/runtime/tools/windows.inc
@@ -0,0 +1,27 @@
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+export SHELL = cmd
+
+# TODO give an error if archs doesn't match
+ifndef arch
+ $(error Could not detect arch: please specify on command line.)
+endif
+
+
+CMD=cmd /C
+CWD=$(shell cmd /C echo %CD%)
+RM=cmd /C del /Q /F
+RD=cmd /C rmdir
+MD=cmd /c mkdir
+SLASH=\\
+NUL = nul
+
diff --git a/runtime/tools/wipe-string.pl b/runtime/tools/wipe-string.pl
new file mode 100755
index 0000000..3d879c8
--- /dev/null
+++ b/runtime/tools/wipe-string.pl
@@ -0,0 +1,183 @@
+#!/usr/bin/perl
+
+#
+#//===----------------------------------------------------------------------===//
+#//
+#// The LLVM Compiler Infrastructure
+#//
+#// This file is dual licensed under the MIT and the University of Illinois Open
+#// Source Licenses. See LICENSE.txt for details.
+#//
+#//===----------------------------------------------------------------------===//
+#
+
+use strict;
+use warnings;
+
+use File::Glob ":glob";
+
+use FindBin;
+use lib "$FindBin::Bin/lib";
+
+use tools;
+
+our $VERSION = "0.02";
+
+sub wipe($$$) {
+
+ my ( $input, $output, $wipe ) = @_;
+ my $bulk = read_file( $input, -binary => 1 );
+ $bulk =~ s{($wipe)}{ " " x length( $1 ) }ge;
+ write_file( $output, \$bulk, -binary => 1 );
+ return undef;
+
+}; # sub wipe
+
+my @wipe;
+my $target = ".";
+get_options(
+ "wipe-literal=s" =>
+ sub { my $arg = $_[ 1 ]; push( @wipe, qr{@{ [ quotemeta( $arg ) ] }} ); },
+ "wipe-regexp=s" =>
+ sub { my $arg = $_[ 1 ]; push( @wipe, qr{$arg} ); },
+ "target-directory=s" => \$target,
+);
+
+# Convert strings to regular expression.
+my $wipe = qr{@{ [ join( "|", @wipe ) ] }};
+
+my %jobs;
+
+# Collect files to process.
+# jobs: output -> input.
+foreach my $arg ( @ARGV ) {
+ my @inputs = ( $^O eq "MSWin32" ? bsd_glob( $arg ) : ( $arg ) );
+ foreach my $input ( @inputs ) {
+ my $file = get_file( $input );
+ my $output = cat_file( $target, $file );
+ if ( exists( $jobs{ $output } ) ) {
+ runtime_error(
+ "\"$jobs{ $output }\" and \"$input\" input files tend to be written " .
+ "to the same output file \"$output\""
+ );
+ }; # if
+ $jobs{ $output } = $input;
+ }; # foreach
+}; # foreach $file
+
+# Process files.
+%jobs = reverse( %jobs ); # jobs: input -> output.
+foreach my $input ( sort( keys( %jobs ) ) ) {
+ my $output = $jobs{ $input };
+ info( "\"$input\" -> \"$output\"" );
+ wipe( $input, $output, $wipe );
+}; # foreach $input
+
+exit( 0 );
+
+__END__
+
+#
+# Embedded documentation.
+#
+
+=pod
+
+=head1 NAME
+
+B<wipe-string.pl> -- Wipe string in text or binary files.
+
+=head1 SYNOPSIS
+
+B<wipe-string.pl> I<OPTION>... I<FILE>...
+
+=head1 OPTIONS
+
+=over
+
+=item B<--doc>
+
+=item B<--manual>
+
+Print full help message and exit.
+
+=item B<--help>
+
+Print short help message and exit.
+
+=item B<--target-directory=>I<dir>
+
+Directory to put result files to. By default result files are written in the current working
+directory.
+
+=item B<--usage>
+
+Print very short usage message and exit.
+
+=item B<--version>
+
+Print version and exit.
+
+=item B<--wipe-literal=>I<str>
+
+Specify literal string to wipe. Multiple literals are allowed.
+
+=item B<--wipe-regexp=>I<str>
+
+Specify Perl regular expression to wipe. Multiple regular expressions may be specified.
+
+Be careful. Protect special characters from beign interpreted by shell.
+
+=back
+
+=head1 ARGUMENTS
+
+=over
+
+=item I<file>
+
+File name to wipe string in.
+
+=back
+
+=head1 DESCRIPTION
+
+The script wipes strings in files. String may be specified literally or by Perl regular expression.
+Strings are wiped by replacing characters with spaces, so size of file remains the same. The script
+may be applied to both text and binary files.
+
+Result files are written by default to current directory, or to directory specified by
+B<--target-directory> option, if any. If multiple input files tend to be written to the same output
+file (e. g. identically named input files located in different directories), the script generates an
+error.
+
+The script reads entire file, process it, and the writes to disk. Therefore it is (almost) safe to
+update files in-place (see examples).
+
+=head1 EXAMPLES
+
+Wipe "Copyright" word in all the files with "txt" suffix in current directory, overwrite original
+files (update them in-place):
+
+ wipe-string.pl --wipe-literal="Copyright" *.txt
+
+Wipe "Copyright" and "Copyleft" words in all the files with "txt" suffix in current directory,
+write result files to ../wiped directory:
+
+ wipe-string.pl --wipe-literal=Copyright --wipe-literal=Copyleft --target-dir=../wiped *.txt
+
+Wipe "Copyright" and "Copyleft" words in files from "doc" directory, write result files to current
+directory;
+
+ wipe-string.pl --wipe-regexp="Copyright|Copyleft" doc/*
+
+Wipe "defaultlib" directive in all the library files:
+
+ wipe-string.pl --wipe-regexp="-defaultlib:[A-Za-z0-9_.]+" *.lib
+
+(Be careful: the script does not analyze structure of library and object files, it just wipes
+U<strings>, so it wipes all the occurences of strings matching to specified regular expression.)
+
+=cut
+
+# end of file #