#!/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 one or more configurations of OMP RTL libraries. =head1 SYNOPSIS B I