diff options
-rw-r--r-- | INSTALL.md | 8 | ||||
-rw-r--r-- | MAKEHELP.md | 2 | ||||
-rwxr-xr-x | boot | 385 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rwxr-xr-x | validate | 4 |
5 files changed, 236 insertions, 165 deletions
diff --git a/INSTALL.md b/INSTALL.md index 42bbc1e63d..3ae9186874 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -18,7 +18,7 @@ XeLaTex (only for PDF output). Quick start: the following gives you a default build: - $ python3 boot + $ perl boot $ ./configure $ make $ make install @@ -31,9 +31,9 @@ Quick start: the following gives you a default build: You can use Make's `-jN` option to parallelize the build. It's generally best to set `N` somewhere around the core count of the build machine. -The `python3 boot` step is only necessary if this is a tree checked out from -git. For source distributions downloaded from GHC's web site, this step has -already been performed. +The `perl boot` step is only necessary if this is a tree checked out +from git. For source distributions downloaded from GHC's web site, +this step has already been performed. These steps give you the default build, which includes everything optimised and built in various ways (eg. profiling libs are built). diff --git a/MAKEHELP.md b/MAKEHELP.md index 819da9754b..233298478e 100644 --- a/MAKEHELP.md +++ b/MAKEHELP.md @@ -48,7 +48,7 @@ Common commands: Various levels of cleaning: "clean" restores the tree to the state after "./configure", "distclean" restores to the state - after "python3 boot", and maintainer-clean restores the tree to the + after "perl boot", and maintainer-clean restores the tree to the completely clean checked-out state. Using `make` in subdirectories @@ -1,172 +1,243 @@ -#!/usr/bin/env python3 - -import glob -import os -import os.path -import sys -import argparse -from textwrap import dedent -import subprocess - -cwd = os.getcwd() - -parser = argparse.ArgumentParser() -parser.add_argument('--validate', action='store_true', help='Run in validate mode') -parser.add_argument('--required-tag', type=str, action='append', default=set()) -args = parser.parse_args() - -def print_err(s): - print(dedent(s), file=sys.stderr) - -def die(mesg): - print_err(mesg) - sys.exit(1) - -def check_for_url_rewrites(): - if os.path.isdir('.git') and \ - subprocess.check_output('git config remote.origin.url'.split()).find(b'github.com') != -1 and \ - subprocess.call(['git', 'config', '--get-regexp', '^url.*github.com/.*/packages-.insteadOf']) != 0: +#!/usr/bin/env perl + +use warnings; +use strict; + +use Cwd; +use File::Path 'rmtree'; +use File::Basename; + +my %required_tag; +my $validate; +my $curdir; + +# See Trac #11530 +$ENV{GREP_OPTIONS} = ''; + +$required_tag{"-"} = 1; +$validate = 0; + +$curdir = &cwd() + or die "Can't find current directory: $!"; + +while ($#ARGV ne -1) { + my $arg = shift @ARGV; + + if ($arg =~ /^--required-tag=(.*)/) { + $required_tag{$1} = 1; + } + elsif ($arg =~ /^--validate$/) { + $validate = 1; + } + else { + die "Bad arg: $arg"; + } +} + +sub sanity_check_line_endings { + local $/ = undef; + open FILE, "packages" or die "Couldn't open file: $!"; + binmode FILE; + my $string = <FILE>; + close FILE; + + if ($string =~ /\r/) { + print STDERR <<EOF; +Found ^M in packages. +Perhaps you need to run + git config --global core.autocrlf false +and re-check out the tree? +EOF + exit 1; + } +} + +sub sanity_check_tree { + my $tag; + my $dir; + + if (-d ".git" && + system("git config remote.origin.url | grep github.com > /dev/null") == 0 && + system("git config --get-regexp '^url.*github.com/.*/packages-.insteadOf' > /dev/null") != 0) { # If we cloned from github, make sure the url rewrites are set. # Otherwise 'git submodule update --init' prints confusing errors. - die("""\ - It seems you cloned this repository from GitHub. But your git config files - don't contain the url rewrites that are needed to make this work (GitHub - doesn't support '/' in repository names, so we use a different naming scheme - for the submodule repositories there). + die <<EOF; +It seems you cloned this repository from GitHub. But your git config files +don't contain the url rewrites that are needed to make this work (GitHub +doesn't support '/' in repository names, so we use a different naming scheme +for the submodule repositories there). - Please run the following commands first: +Please run the following commands first: - git config --global url."git://github.com/ghc/packages-".insteadOf git://github.com/ghc/packages/ - git config --global url."http://github.com/ghc/packages-".insteadOf http://github.com/ghc/packages/ - git config --global url."https://github.com/ghc/packages-".insteadOf https://github.com/ghc/packages/ - git config --global url."ssh://git\@github.com/ghc/packages-".insteadOf ssh://git\@github.com/ghc/packages/ - git config --global url."git\@github.com:/ghc/packages-".insteadOf git\@github.com:/ghc/packages/ + git config --global url."git://github.com/ghc/packages-".insteadOf git://github.com/ghc/packages/ + git config --global url."http://github.com/ghc/packages-".insteadOf http://github.com/ghc/packages/ + git config --global url."https://github.com/ghc/packages-".insteadOf https://github.com/ghc/packages/ + git config --global url."ssh://git\@github.com/ghc/packages-".insteadOf ssh://git\@github.com/ghc/packages/ + git config --global url."git\@github.com:/ghc/packages-".insteadOf git\@github.com:/ghc/packages/ - And then: +And then: - git submodule update --init - ./boot + git submodule update --init + ./boot - Or start over, and clone the GHC repository from the haskell server: +Or start over, and clone the GHC repository from the haskell server: - git clone --recursive git://git.haskell.org/ghc.git + git clone --recursive git://git.haskell.org/ghc.git - For more information, see: - * https://ghc.haskell.org/trac/ghc/wiki/Newcomers or - * https://ghc.haskell.org/trac/ghc/wiki/Building/GettingTheSources#CloningfromGitHub - """) +For more information, see: + * https://ghc.haskell.org/trac/ghc/wiki/Newcomers or + * https://ghc.haskell.org/trac/ghc/wiki/Building/GettingTheSources#CloningfromGitHub +EOF + } -def check_boot_packages(): # Check that we have all boot packages. - import re - for l in open('packages', 'r'): - if l.startswith('#'): - continue - - parts = l.split(' ') - if len(parts) != 4: - die("Error: Bad line in packages file: " + l) - - dir_ = parts[0] - tag = parts[1] - - # If $tag is not "-" then it is an optional repository, so its - # absence isn't an error. - if tag in args.required_tag: - # We would like to just check for a .git directory here, - # but in an lndir tree we avoid making .git directories, - # so it doesn't exist. We therefore require that every repo - # has a LICENSE file instead. - license_path = os.path.join(dir_, 'LICENSE') - if not os.path.isfile(license_path): - die("""\ - Error: %s doesn't exist" % license_path) - Maybe you haven't run 'git submodule update --init'? - """) + open PACKAGES, "< packages"; + while (<PACKAGES>) { + if (/^#/) { + # Comment; do nothing + } + elsif (/^([a-zA-Z0-9\/.-]+) +([^ ]+) +[^ ]+ +[^ ]+$/) { + $dir = $1; + $tag = $2; + + # If $tag is not "-" then it is an optional repository, so its + # absence isn't an error. + if (defined($required_tag{$tag})) { + # We would like to just check for a .git directory here, + # but in an lndir tree we avoid making .git directories, + # so it doesn't exist. We therefore require that every repo + # has a LICENSE file instead. + if (! -f "$dir/LICENSE") { + print STDERR "Error: $dir/LICENSE doesn't exist.\n"; + die "Maybe you haven't done 'git submodule update --init'?"; + } + } + } + else { + die "Bad line in packages file: $_"; + } + } + close PACKAGES; +} # Create libraries/*/{ghc.mk,GNUmakefile} -def boot_pkgs(): - library_dirs = [] - - for package in glob.glob("libraries/*/"): - packages_file = os.path.join(package, 'ghc-packages') - if os.path.isfile(packages_file): - for subpkg in open(packages_file, 'r'): - library_dirs.append(os.path.join(package, subpkg.strip())) - else: - library_dirs.append(package) - - print(library_dirs) - for package in library_dirs: - if package[-1] == '/': - # drop trailing '/' - package = package[:-1] - - dir_ = os.path.relpath(package, 'libraries') - cabals = glob.glob(os.path.join(package, '*.cabal.in')) - if len(cabals) == 0: - cabals = glob.glob(os.path.join(package, '*.cabal')) - - if len(cabals) > 1: - die('Too many .cabal files in %s' % package) - elif len(cabals) == 1: - cabal = cabals[0] - - if os.path.isfile(cabal): - # strip both .cabal and .in - pkg = os.path.splitext(os.path.splitext(os.path.basename(cabal))[0])[0] - top = package - - ghc_mk = os.path.join(package, 'ghc.mk') - print('Creating %s' % ghc_mk) - with open(ghc_mk, 'w') as f: - f.write(dedent( - """\ - {package}_PACKAGE = {pkg} - {package}_dist-install_GROUP = libraries - $(if $(filter {dir},$(PACKAGES_STAGE0)),$(eval $(call build-package,{package},dist-boot,0))) - $(if $(filter {dir},$(PACKAGES_STAGE1)),$(eval $(call build-package,{package},dist-install,1))) - $(if $(filter {dir},$(PACKAGES_STAGE2)),$(eval $(call build-package,{package},dist-install,2))) - """.format(package = package, - pkg = pkg, - dir = dir_))) - - -def autoreconf(): - # Run autoreconf on everything that needs it. - processes = {} - for dir_ in ['.'] + glob.glob('libraries/*/'): - if os.path.isfile(os.path.join(dir_, 'configure.ac')): - print("Booting %s" % dir_) - processes[dir_] = subprocess.Popen('autoreconf', cwd=dir_) +sub boot_pkgs { + my @library_dirs = (); + + my $package; + + for $package (glob "libraries/*/") { + $package =~ s/\/$//; + my $pkgs = "$package/ghc-packages"; + if (-f $pkgs) { + open PKGS, "< $pkgs" + or die "Failed to open $pkgs: $!"; + while (<PKGS>) { + chomp; + s/\r//g; + if (/.+/) { + push @library_dirs, "$package/$_"; + } + } + } + else { + push @library_dirs, $package; + } + } + + for $package (@library_dirs) { + my $dir = &basename($package); + my @cabals = glob("$package/*.cabal.in"); + if ($#cabals < 0) { + @cabals = glob("$package/*.cabal"); + } + if ($#cabals > 0) { + die "Too many .cabal file in $package\n"; + } + if ($#cabals eq 0) { + my $cabal = $cabals[0]; + my $pkg; + my $top; + if (-f $cabal) { + $pkg = $cabal; + $pkg =~ s#.*/##; + $pkg =~ s/\.cabal.in$//; + $pkg =~ s/\.cabal$//; + $top = $package; + $top =~ s#[^/]+#..#g; + $dir = $package; + $dir =~ s#^libraries/##g; + + print "Creating $package/ghc.mk\n"; + open GHCMK, "> $package/ghc.mk" + or die "Opening $package/ghc.mk failed: $!"; + print GHCMK "${package}_PACKAGE = ${pkg}\n"; + print GHCMK "${package}_dist-install_GROUP = libraries\n"; + print GHCMK "\$(if \$(filter ${dir},\$(PACKAGES_STAGE0)),\$(eval \$(call build-package,${package},dist-boot,0)))\n"; + print GHCMK "\$(if \$(filter ${dir},\$(PACKAGES_STAGE1)),\$(eval \$(call build-package,${package},dist-install,1)))\n"; + print GHCMK "\$(if \$(filter ${dir},\$(PACKAGES_STAGE2)),\$(eval \$(call build-package,${package},dist-install,2)))\n"; + close GHCMK + or die "Closing $package/ghc.mk failed: $!"; + + print "Creating $package/GNUmakefile\n"; + open GNUMAKEFILE, "> $package/GNUmakefile" + or die "Opening $package/GNUmakefile failed: $!"; + print GNUMAKEFILE "dir = ${package}\n"; + print GNUMAKEFILE "TOP = ${top}\n"; + print GNUMAKEFILE "include \$(TOP)/mk/sub-makefile.mk\n"; + print GNUMAKEFILE "FAST_MAKE_OPTS += stage=0\n"; + close GNUMAKEFILE + or die "Closing $package/GNUmakefile failed: $!"; + } + } + } +} + +# autoreconf everything that needs it. +sub autoreconf { + my $dir; + my $fail; + + foreach $dir (".", glob("libraries/*/")) { + if (-f "$dir/configure.ac") { + next if (my $pid = fork); + die "fork failed: $!" if (! defined $pid); + print "Booting $dir\n"; + chdir $dir or die "can't change to $dir: $!"; + exec("autoreconf"); + exit 1; + } + } # Wait for all child processes to finish. - fail = False - for k,v in processes.items(): - code = v.wait() - if code != 0: - print_err('autoreconf in %s failed with exit code %d' % (k, code)) - fail = True - - if fail: - sys.exit(1) - -def check_build_mk(): - if not args.validate and not os.path.isfile("mk/build.mk"): - print(dedent( - """ - WARNING: You don't have a mk/build.mk file. - - By default a standard GHC build will be done, which uses optimisation - and builds the profiling libraries. This will take a long time, so may - not be what you want if you are developing GHC or the libraries, rather - than simply building it to use it. - - For information on creating a mk/build.mk file, please see: - http://ghc.haskell.org/trac/ghc/wiki/Building/Using#Buildconfiguration - """)) - -check_for_url_rewrites() -boot_pkgs() -autoreconf() -check_build_mk() + while (wait() != -1) { + $fail = 1 if $?; + } + + die "Running autoreconf failed" if $fail; +} + +sub checkBuildMk { + if ($validate eq 0 && ! -f "mk/build.mk") { + print <<EOF; + +WARNING: You don't have a mk/build.mk file. + +By default a standard GHC build will be done, which uses optimisation +and builds the profiling libraries. This will take a long time, so may +not be what you want if you are developing GHC or the libraries, rather +than simply building it to use it. + +For information on creating a mk/build.mk file, please see: + http://ghc.haskell.org/trac/ghc/wiki/Building/Using#Buildconfiguration + +EOF + } +} + +&sanity_check_line_endings(); +&sanity_check_tree(); +&boot_pkgs(); +&autoreconf(); +&checkBuildMk(); + diff --git a/configure.ac b/configure.ac index 9f742d8c75..45b2ab3b62 100644 --- a/configure.ac +++ b/configure.ac @@ -27,7 +27,7 @@ AC_INIT([The Glorious Glasgow Haskell Compilation System], [8.3], [glasgow-haske # when the source distribution was created. if test ! -f mk/config.h.in; then - echo "mk/config.h.in doesn't exist: perhaps you haven't run 'python3 boot'?" + echo "mk/config.h.in doesn't exist: perhaps you haven't run 'perl boot'?" exit 1 fi @@ -181,9 +181,9 @@ if [ $no_clean -eq 0 ]; then INSTDIR="$thisdir/inst" if [ $use_dph -eq 1 ]; then - python3 ./boot --validate --required-tag=dph + perl -w boot --validate --required-tag=dph else - python3 ./boot --validate + perl -w boot --validate fi ./configure --prefix="$INSTDIR" $config_args fi |