diff --git a/.gitattributes b/.gitattributes index 1ab590f4048..9d3747cd63a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -6,6 +6,7 @@ *.zsh text eol=lf configure text eol=lf configure.ac text eol=lf +msvs-detect text eol=lf check_linker text eol=lf *.m4 text eol=lf diff --git a/.gitignore b/.gitignore index 885ea173088..c00344afd54 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,7 @@ src_ext/*.tbz src_ext/*.tar.gz src_ext/archives/* src_ext/*.download +src_ext/jbuild-ignore *.tar.bz2 *.annot *.tar.gz diff --git a/Makefile b/Makefile index a824bb8764a..41f21789af8 100644 --- a/Makefile +++ b/Makefile @@ -70,7 +70,7 @@ OPAMINSTALLER_FLAGS += --mandir "$(DESTDIR)$(mandir)" ifdef OCAMLFIND ifndef DESTDIR ifneq ($(OCAMLFIND),no) - LIBINSTALL_DIR ?= $(shell $(OCAMLFIND) printconf destdir) + LIBINSTALL_DIR ?= $(shell PATH="$(PATH)" $(OCAMLFIND) printconf destdir) endif endif endif @@ -156,11 +156,26 @@ configure: configure.ac m4/*.m4 release-%: $(MAKE) -C release TAG="$*" -cold: - env MAKE=$(MAKE) ./shell/bootstrap-ocaml.sh +ifeq ($(OCAML_PORT),) +ifneq ($(COMSPEC),) +ifeq ($(shell which gcc 2>/dev/null),) +OCAML_PORT=auto +endif +endif +endif + +.PHONY: compiler cold +compiler: + env MAKE=$(MAKE) ./shell/bootstrap-ocaml.sh $(OCAML_PORT) + +cold: compiler env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" ./configure $(CONFIGURE_ARGS) env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" $(MAKE) lib-ext env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" $(MAKE) cold-%: env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" $(MAKE) $* + +.PHONY: run-appveyor-test +run-appveyor-test: + env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" ./appveyor_test.sh diff --git a/Makefile.config.in b/Makefile.config.in index 37a30a912cf..e0a4875703c 100644 --- a/Makefile.config.in +++ b/Makefile.config.in @@ -8,7 +8,9 @@ PACKS = $(filter-out no,@OCAML_PKG_unix@ @OCAML_PKG_bigarray@ @OCAML_PKG_extlib@ CONF_OCAMLFLAGS = @CONF_OCAMLFLAGS@ -MCCS_DISABLED = @MCCS_DISABLED@ +MCCS_ENABLED = @MCCS_ENABLED@ + +OCAMLLIB = @OCAMLLIB@ OCAMLFIND = @OCAMLFIND@ OCAML = @OCAML@ @@ -21,9 +23,11 @@ EXE = @EXE@ WIN32 = @WIN32@ PATH:=@PATH_PREPEND@$(PATH) +INCLUDE:=@INC_PREPEND@$(INCLUDE) +LIB:=@LIB_PREPEND@$(LIB) LIB_PREFIX = @LIB_PREFIX@ CPATH = @CPATH@ LIBRARY_PATH = @LIBRARY_PATH@ -export OCAMLVERSION OCAMLFIND OCAML OCAMLC OCAMLOPT EXE PATH CPATH LIBRARY_PATH +export OCAMLVERSION OCAMLFIND OCAML OCAMLC OCAMLOPT EXE PATH INCLUDE LIB CPATH LIBRARY_PATH OCAMLLIB diff --git a/README.md b/README.md index 13eb2589aa9..fd4b8196bca 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,77 @@ it as a second step. If you are developing OPAM, you may enable developer features by including the `--enable-developer-mode` parameter with `./configure`. +## Compiling on Native Windows + +``` +BUILDING ON WINDOWS IS A WORK-IN-PROGRESS AND THESE INSTRUCTIONS WILL EVOLVE! +``` + +Cygwin (https://www.cygwin.com/setup-x86_64.exe) is always required to build opam on +Windows. Both the 64-bit and 32-bit versions of Cygwin may be used (you can build +32-bit opam using 64-bit Cygwin and vice versa though note that you must be running +64-bit Windows in order to build the 64-bit version). + +The following Cygwin packages are required: +* From Devel - `make` +* From Devel - `patch` (not required if OCaml and all required packages are + pre-installed) +* From Devel - `mingw64-i686-gcc-core` & `mingw64-x86_64-gcc-core` (not required if + building with MSVC) + +Alternatively, having downloaded Cygwin's setup program, Cygwin can be installed +using the following command line: + +`setup-x86_64 --root=C:\cygwin64 --quiet-mode --no-desktop --no-startmenu --packages=make,mingw64-i686-gcc-core,mingw64-x86_64-gcc-core,patch` + +The `--no-desktop` and `--no-startmenu` switches may be omitted in order to create +shortcuts on the Desktop and Start Menu respectively. Executed this way, setup will +still be interactive, but the packages will have been pre-selected. To make setup +fully unattended, choose a mirror URL from https://cygwin.com/mirrors.lst and add +the --site switch to the command line +(e.g. `--site=http://www.mirrorservice.org/sites/sourceware.org/pub/cygwin/`). + +It is recommended that you set the `CYGWIN` environment variable to +`nodosfilewarning winsymlinks:native`. + +Cygwin is started either from a shortcut or by running: + +``` +C:\cygwin64\bin\mintty - +``` + +It is recommended that opam be built outside Cygwin's root +(so in `/cygdrive/c/...`). From an elevated Cygwin shell, edit `/etc/fstab` and +ensure that the file's content is exactly: + +``` +none /cygdrive cygdrive noacl,binary,posix=0,user 0 0 +``` + +The change is the addition of the `noacl` option to the mount instructions for +`/cygdrive` and this stops from Cygwin from attempting to emulate POSIX permissions +over NTFS (which can result in strange and unnecessary permissions showing up in +Windows Explorer). It is necessary to close and restart all Cygwin terminal windows +after changing `/etc/fstab`. + +opam is able to be built **without** a pre-installed OCaml compiler. For the MSVC +ports of OCaml, the Microsoft Windows SDK 7 or later or Microsoft Visual Studio is +required (https://www.microsoft.com/en-gb/download/details.aspx?id=8442 - either x86 +or x64 may be installed, as appropriate to your system). It is not necessary to +modify PATH, INCLUDE or LIB - opam's build system will automatically detect the +required changes. + +If OCaml is not pre-installed, run: +``` +make compiler [OCAML_PORT=mingw64|mingw|msvc64|msvc|auto] +``` +The `OCAML_PORT` variable determines which flavour of Windows OCaml is compiled - +`auto` will attempt to guess. As long as `gcc` is **not** installed in Cygwin +(i.e. the native C compiler *for Cygwin*), `OCAML_PORT` does not need to be +specified and `auto` will be assumed. + +You can then `configure` and build opam as above. + ## Compiling without OCaml `make cold` is provided as a facility to compile OCaml, then bootstrap opam. diff --git a/appveyor.patch b/appveyor.patch new file mode 100644 index 00000000000..186199af69f --- /dev/null +++ b/appveyor.patch @@ -0,0 +1,49 @@ +From 083bae674ad77b6b5b5fafbff25b20473655cb2a Mon Sep 17 00:00:00 2001 +From: David Allsopp +Date: Wed, 27 Dec 2017 10:37:12 +0200 +Subject: [PATCH] Use dra27 jbuilder/ocaml-mccs/flexdll + + - Need unreleased ocaml-mccs 1.1+5 to build mccs on Windows. + - Need unreleased jbuilder beta17 to build mccs on MSVC. + - Need unreleased flexdll 0.38 to build mccs on mingw. +--- + src_ext/Makefile | 12 ++++++------ + 1 file changed, 6 insertions(+), 6 deletions(-) + +diff --git a/src_ext/Makefile b/src_ext/Makefile +index 64fdd8af..d1457b5f 100644 +--- a/src_ext/Makefile ++++ b/src_ext/Makefile +@@ -28,8 +28,8 @@ MD5_cudf = a4c0e652e56e74c7b388a43f9258d119 + URL_dose3 = https://gforge.inria.fr/frs/download.php/file/36063/dose3-5.0.1.tar.gz + MD5_dose3 = e7d4b1840383c6732f29a47c08ba5650 + +-URL_mccs = https://github.com/AltGr/ocaml-mccs/archive/1.1+4.tar.gz +-MD5_mccs = 1ff2d73ab5c69e1fb58160213fc2d4c9 ++URL_mccs = https://github.com/dra27/ocaml-mccs/archive/1.1+4-dra27.tar.gz ++MD5_mccs = 08e80e39a0d960c01955a970d13afb29 + + URL_opam-file-format = https://github.com/ocaml/opam-file-format/archive/2.0.0-beta5.tar.gz + MD5_opam-file-format = 5408798ca3af6e36379dd34b1b618b9c +@@ -37,14 +37,14 @@ MD5_opam-file-format = 5408798ca3af6e36379dd34b1b618b9c + URL_result = https://github.com/janestreet/result/archive/1.2.tar.gz + MD5_result = 3d5b66c5526918f0f2ca9d6811ef09c8 + +-URL_jbuilder = https://github.com/janestreet/jbuilder/archive/08050696fb701dafcd1372aadfaa800a50bc01ca.tar.gz +-MD5_jbuilder = 53b65232ebb5fd319acf90922342615d ++URL_jbuilder = https://github.com/dra27/jbuilder/archive/fix-copy.tar.gz ++MD5_jbuilder = cbf9d2e783feab1f4a61fea35ec96118 + + URL_ocaml = http://caml.inria.fr/pub/distrib/ocaml-4.06/ocaml-4.06.0.tar.gz + MD5_ocaml = 66e5439eb63dbb8b8224cba5d1b20947 + +-URL_flexdll = https://github.com/alainfrisch/flexdll/archive/0.37.tar.gz +-MD5_flexdll = cc456a89382e60d84130cddd53977486 ++URL_flexdll = https://github.com/dra27/flexdll/archive/linking-c++.tar.gz ++MD5_flexdll = 75bd0efc328ad9f8f2e01414464d217b + + ifndef FETCH + ifneq ($(shell command -v curl 2>/dev/null),) +-- +2.12.0.windows.1 + diff --git a/appveyor.yml b/appveyor.yml index 17333451a21..80109a5eca5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,25 +1,37 @@ platform: - - x86 + - x64 + +image: Visual Studio 2017 environment: global: - CYG_ROOT: C:/cygwin + CYG_ROOT: cygwin64 + CYG_ARCH: x86_64 + OCAML_PORT: CYG_CACHE: C:/cygwin/var/cache/setup CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/ matrix: - - + - CYG_ROOT: cygwin CYG_ARCH: x86 + - CYG_ROOT: cygwin64 + CYG_ARCH: x86_64 + - OCAML_PORT: msvc + - OCAML_PORT: msvc64 + - OCAML_PORT: mingw + - OCAML_PORT: mingw64 + +cache: + - C:\projects\opam\bootstrap + - C:\projects\opam\src_ext\archives init: - - 'echo System architecture: %PLATFORM%' + - "echo System architecture: %PLATFORM%" install: - - 'appveyor DownloadFile http://cygwin.com/setup-%CYG_ARCH%.exe -FileName setup.exe' - - 'setup.exe -gqnNdO -R "%CYG_ROOT%" -s "%CYG_MIRROR%" -l "%CYG_CACHE%" -P make,git,gcc-core,gcc-g++,ocaml,ocaml-camlp4,ocaml-compiler-libs,libncurses-devel,unzip,libmpfr-devel,patch,flexdll,libglpk-devel >NUL' - - '%CYG_ROOT%/bin/bash -lc "cygcheck -dc cygwin gcc-core"' + - call "%APPVEYOR_BUILD_FOLDER%\appveyor_build.cmd" install build_script: - - '%CYG_ROOT%/bin/bash -lc "cd \"$OLDPWD\" && env DJDIR="workaround" ./configure && make lib-ext && make && make install"' - - '%CYG_ROOT%/bin/bash -lc "opam init -y -a"' - - '%CYG_ROOT%/bin/bash -lc "opam config env"' - - '%CYG_ROOT%/bin/bash -lc "opam install -y -v ocamlfind"' + - call "%APPVEYOR_BUILD_FOLDER%\appveyor_build.cmd" build + +test_script: + - call "%APPVEYOR_BUILD_FOLDER%\appveyor_build.cmd" test diff --git a/appveyor_build.cmd b/appveyor_build.cmd new file mode 100644 index 00000000000..450b658b895 --- /dev/null +++ b/appveyor_build.cmd @@ -0,0 +1,131 @@ +@rem *********************************************************************** +@rem * * +@rem * opam * +@rem * * +@rem * David Allsopp, OCaml Labs, Cambridge. * +@rem * * +@rem * Copyright 2018 MetaStack Solutions Ltd. * +@rem * * +@rem * All rights reserved. This file is distributed under the terms of * +@rem * the GNU Lesser General Public License version 2.1, with the * +@rem * special exception on linking described in the file LICENSE. * +@rem * * +@rem *********************************************************************** + +@rem BE CAREFUL ALTERING THIS FILE TO ENSURE THAT ERRORS PROPAGATE +@rem IF A COMMAND SHOULD FAIL IT PROBABLY NEEDS TO END WITH +@rem || exit /b 1 +@rem BASICALLY, DO THE TESTING IN BASH... + +@rem Do not call setlocal! +@echo off + +goto %1 + +goto :EOF + +:CheckPackage +"%CYG_ROOT%\bin\bash.exe" -lc "cygcheck -dc %1" | findstr %1 > nul +if %ERRORLEVEL% equ 1 ( + echo Cygwin package %1 will be installed + set CYGWIN_INSTALL_PACKAGES=%CYGWIN_INSTALL_PACKAGES%,%1 +) +goto :EOF + +:UpgradeCygwin +if "%CYGWIN_INSTALL_PACKAGES%" neq "" "%CYG_ROOT%\setup-%CYG_ARCH%.exe" --quiet-mode --no-shortcuts --no-startmenu --no-desktop --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --packages %CYGWIN_INSTALL_PACKAGES:~1% > nul +for %%P in (%CYGWIN_COMMANDS%) do "%CYG_ROOT%\bin\bash.exe" -lc "%%P --help" > nul || set CYGWIN_UPGRADE_REQUIRED=1 +"%CYG_ROOT%\bin\bash.exe" -lc "cygcheck -dc %CYGWIN_PACKAGES%" +if %CYGWIN_UPGRADE_REQUIRED% equ 1 ( + echo Cygwin package upgrade required - please go and drink coffee + "%CYG_ROOT%\setup-%CYG_ARCH%.exe" --quiet-mode --no-shortcuts --no-startmenu --no-desktop --only-site --root "%CYG_ROOT%" --site "%CYG_MIRROR%" --local-package-dir "%CYG_CACHE%" --upgrade-also > nul + "%CYG_ROOT%\bin\bash.exe" -lc "cygcheck -dc %CYGWIN_PACKAGES%" +) +goto :EOF + +:install +set CYG_ROOT=C:\%CYG_ROOT% + +cd "%APPVEYOR_BUILD_FOLDER%" + +rem CYGWIN_PACKAGES is the list of required Cygwin packages (cygwin is included +rem in the list just so that the Cygwin version is always displayed on the log). +rem CYGWIN_COMMANDS is a corresponding command to run with --version to test +rem whether the package works. This is used to verify whether the installation +rem needs upgrading. +set CYGWIN_PACKAGES=cygwin make patch curl diffutils tar unzip +set CYGWIN_COMMANDS=cygcheck make patch curl diff tar unzip + +if "%OCAML_PORT%" equ "mingw" ( + set CYGWIN_PACKAGES=%CYGWIN_PACKAGES% mingw64-i686-gcc-g++ + set CYGWIN_COMMANDS=%CYGWIN_COMMANDS% i686-w64-mingw32-g++ +) +if "%OCAML_PORT%" equ "mingw64" ( + set CYGWIN_PACKAGES=%CYGWIN_PACKAGES% mingw64-x86_64-gcc-g++ + set CYGWIN_COMMANDS=%CYGWIN_COMMANDS% x86_64-w64-mingw32-g++ +) +if "%OCAML_PORT%" equ "" ( + set CYGWIN_PACKAGES=%CYGWIN_PACKAGES% gcc-g++ flexdll + set CYGWIN_COMMANDS=%CYGWIN_COMMANDS% g++ flexlink +) + +set CYGWIN_INSTALL_PACKAGES= +set CYGWIN_UPGRADE_REQUIRED=0 + +for %%P in (%CYGWIN_PACKAGES%) do call :CheckPackage %%P +call :UpgradeCygwin + +rem Use dra27 jbuilder/ocaml-mccs/flexdll for native ports +if "%OCAML_PORT%" neq "" git apply appveyor.patch + +set INSTALLED_URL= +for /f "tokens=3" %%U in ('findstr /C:"URL_ocaml = " src_ext\Makefile') do set OCAML_URL=%%U +for /f "tokens=3" %%U in ('findstr /C:"URL_flexdll = " src_ext\Makefile') do set FLEXDLL_URL=%%U +if exist bootstrap\installed-tarball for /f "delims=" %%U in ('type bootstrap\installed-tarball') do set INSTALLED_URL=%%U +if "%INSTALLED_URL%" neq "%OCAML_URL% %FLEXDLL_URL%" if exist bootstrap\nul ( + echo Required: %OCAML_URL% %FLEXDLL_URL% + echo Compiled: %INSTALLED_URL% + echo Re-building bootstrap compiling + rd /s/q bootstrap + if exist src_ext\archives\nul rd /s/q src_ext\archives +) + +"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make -C src_ext cache-archives" || exit /b 1 + +if not exist bootstrap\nul ( + "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make compiler" || exit /b 1 + if "%CYG_ARCH%%OCAML_PORT%" equ "x86_64" ( + "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && rebase -b 0x7cd20000 bootstrap/ocaml/lib/ocaml/stublibs/dllunix.so" || exit /b 1 + "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && rebase -b 0x7cd20000 bootstrap/ocaml/lib/ocaml/stublibs/dllthreads.so" || exit /b 1 + ) + if exist bootstrap\ocaml-*.tar.gz del bootstrap\ocaml-*.tar.gz + if "%OCAML_PORT%" neq "" if exist bootstrap\flexdll-*.tar.gz del bootstrap\flexdll-*.tar.gz + del bootstrap\ocaml\bin\*.byte.exe + if "%OCAML_PORT%" equ "" ( + del bootstrap\ocaml\lib\ocaml\expunge.exe + ) else ( + del bootstrap\ocaml\lib\expunge.exe + ) + for /f %%D in ('dir /b/ad bootstrap\ocaml-*') do ( + rd /s/q bootstrap\%%D + if "%OCAML_PORT%" equ "" ( + rem Directory needs to exist, as the Cygwin bootstraps OCAMLLIB refers to it + md bootstrap\%%D + ) + ) +) + +goto :EOF + +:build +if "%OCAML_PORT%" equ "" ( + rem make install doesn't yet work for the native Windows builds + set POST_COMMAND=^&^& make opam-installer install +) +"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && ./configure && make lib-ext && make opam %POST_COMMAND%" || exit /b 1 +goto :EOF + +:test +rem Can't yet do an opam init with the native Windows builds +if "%OCAML_PORT%" equ "" "%CYG_ROOT%\bin\bash.exe" -lc "make -C $APPVEYOR_BUILD_FOLDER run-appveyor-test" || exit /b 1 +goto :EOF diff --git a/appveyor_test.sh b/appveyor_test.sh new file mode 100644 index 00000000000..5a6af18e198 --- /dev/null +++ b/appveyor_test.sh @@ -0,0 +1,5 @@ +#!/bin/bash + +opam init -y -a +eval $(opam config env) +opam install -y -v ocamlfind diff --git a/configure b/configure index 66218b4cbac..785cb01f95a 100755 --- a/configure +++ b/configure @@ -605,11 +605,13 @@ JBUILDER FETCH OCAMLFIND OCAMLOBJINFO +INC_PREPEND +LIB_PREPEND PATH_PREPEND EXE WIN32 CONF_OCAMLFLAGS -MCCS_DISABLED +MCCS_ENABLED DEVELOPER OBJEXT EXEEXT @@ -1815,6 +1817,18 @@ ac_compiler_gnu=$ac_cv_c_compiler_gnu +# If a bootstrap compiler has been built, always use it +PATH_PREPEND= +PRE_BOOTSTRAP_PATH="$PATH" +if test -x bootstrap/ocaml/bin/ocamlc -o -x bootstrap/ocaml/bin/ocamlopt ; then : + + echo Bootstrap compiler found -- activating + unset OCAMLLIB + export PATH_PREPEND=`pwd`/bootstrap/ocaml/bin: + export PATH="$PATH_PREPEND:$PATH" + +fi + # checking for ocamlc if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlc", so it can be a program name with args. @@ -1915,7 +1929,7 @@ fi $as_echo "OCaml version is $OCAMLVERSION" >&6; } # If OCAMLLIB is set, use it if test "$OCAMLLIB" = ""; then - OCAMLLIB=`$OCAMLC -where 2>/dev/null || $OCAMLC -v|tail -1|cut -d ' ' -f 4` + OCAMLLIB=`$OCAMLC -where 2>/dev/null | tr -d '\015' || $OCAMLC -v|tail -1|cut -d ' ' -f 4` else { $as_echo "$as_me:${as_lineno-$LINENO}: result: OCAMLLIB previously set; preserving it." >&5 $as_echo "OCAMLLIB previously set; preserving it." >&6; } @@ -3169,11 +3183,78 @@ x$ax_compare_version_B" | sed 's/^ *//' | sort -r | sed "s/x${ax_compare_version fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler type" >&5 +$as_echo_n "checking for compiler type... " >&6; } +CCOMP_TYPE=`$OCAML shell/print_config.ml ccomp_type 2>/dev/null | fgrep -v "Cannot find" || $OCAMLC -config | tr -d '\r' | sed -n -e "s/ccomp_type: //p"` +if test "$?" -eq 0 ; then : + +else + as_fn_error $? "failed" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CCOMP_TYPE" >&5 +$as_echo "$CCOMP_TYPE" >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for compiler architecture" >&5 +$as_echo_n "checking for compiler architecture... " >&6; } +ARCH=`$OCAML shell/print_config.ml arch 2>/dev/null | fgrep -v "Cannot find" || $OCAMLC -config | tr -d '\r' | sed -n -e "s/architecture: //p"` +if test "$?" -eq 0 ; then : + +else + as_fn_error $? "failed" "$LINENO" 5 +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ARCH" >&5 +$as_echo "$ARCH" >&6; } + +LIB_PREPEND= +INC_PREPEND= # Need the C compiler used for OCaml (important on Windows, as both x86 and x64 are used) if test "$OCAMLBEST" = "opt"; then : OCAMLBESTCC=$OCAMLOPT else OCAMLBESTCC=$OCAMLC +fi +OCAML_CC="$($OCAMLBESTCC -config | sed -n -e "s/native_c_compiler: \(.*\) .*/\1/p")" +set dummy ${OCAML_CC}; OCAML_TEST_CC=$2 +if test ! -x ${OCAML_TEST_CC}; then : + + if test "x${CCOMP_TYPE}" = "xmsvc"; then : + + if test "${ARCH}" = "i386"; then : + SDK_ARCH=x86 +else + SDK_ARCH=x64 +fi + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for an installed Microsoft C Compiler for ${SDK_ARCH}" >&5 +$as_echo_n "checking for an installed Microsoft C Compiler for ${SDK_ARCH}... " >&6; } + eval `PATH="$PRE_BOOTSTRAP_PATH" ./shell/msvs-detect --arch=$SDK_ARCH; echo RESULT=$?` + if test "x$MSVS_NAME" = "x" ; then : + + if test ${RESULT} -eq 0 ; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: from PATH" >&5 +$as_echo "from PATH" >&6; } + +else + + as_fn_error $? "no" "$LINENO" 5 + +fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: from $MSVS_NAME" >&5 +$as_echo "from $MSVS_NAME" >&6; } + export PATH_PREPEND="${MSVS_PATH}:${PATH_PREPEND}" + # Note that we put ${MSVS_PATH} here NOT ${PATH_PREPEND} so that the bootstrap path isn't repeated + export PATH="${MSVS_PATH}:$PATH" + LIB_PREPEND="${MSVS_LIB};" + INC_PREPEND="${MSVS_INC};" + export Lib="${MSVS_LIB};$LIB" + export Include="${MSVS_INC};$INCLUDE" + +fi + +fi + fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' @@ -3181,7 +3262,7 @@ ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then - for ac_prog in $($OCAMLBESTCC -config | sed -n -e "s/native_c_compiler: \(.*\) .*/\1/p") gcc cl cc + for ac_prog in "${OCAML_CC}" gcc cc do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 @@ -3225,7 +3306,7 @@ fi fi if test -z "$CC"; then ac_ct_CC=$CC - for ac_prog in $($OCAMLBESTCC -config | sed -n -e "s/native_c_compiler: \(.*\) .*/\1/p") gcc cl cc + for ac_prog in "${OCAML_CC}" gcc cc do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 @@ -3782,10 +3863,10 @@ else fi if test "x${with_mccs}" = "xno"; then : - MCCS_DISABLED=true + MCCS_ENABLED=false else - MCCS_DISABLED=false + MCCS_ENABLED=true fi @@ -3819,12 +3900,18 @@ if test ${WIN32} -eq 1 -a "$GCC" != "yes" ; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether Microsoft Linker needs a PATH shim" >&5 $as_echo_n "checking whether Microsoft Linker needs a PATH shim... " >&6; } + PREV_PREPEND="$PATH_PREPEND" PATH_PREPEND=$(bash ./shell/check_linker) - if test "x${PATH_PREPEND}" = "x" ; then : + if test "x${PATH_PREPEND}" = "x${PREV_PREPEND}" ; then : PATH_PREPEND_RESULT=no else PATH_PREPEND_RESULT=yes fi + PATH_PREPEND=`echo "${PATH_PREPEND}" | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` + LIB_PREPEND=`echo ${LIB_PREPEND} | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` + INC_PREPEND=`echo ${INC_PREPEND} | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $PATH_PREPEND_RESULT" >&5 $as_echo "$PATH_PREPEND_RESULT" >&6; } @@ -5715,7 +5802,7 @@ bindir="`eval echo ${bindir}`" mandir="`eval echo ${mandir}`" mandir="`eval echo ${mandir}`" -if { test "x$MCCS_DISABLED" = "xtrue" && test "x${hasalldeps}" = "x"; } || +if { test "x$MCCS_ENABLED" = "xfalse" && test "x${hasalldeps}" = "x"; } || { test "x$OCAML_PKG_mccs" = "xno" && test "x${hasalldeps}" = "xtrue"; }; then : echo "Opam will be built WITHOUT a built-in solver" else diff --git a/configure.ac b/configure.ac index 6dfc59fc1af..0e04e379ecf 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,16 @@ AC_COPYRIGHT(Copyright 2012-2017 OcamlPro SAS) AC_CONFIG_MACRO_DIR([m4]) +# If a bootstrap compiler has been built, always use it +PATH_PREPEND= +PRE_BOOTSTRAP_PATH="$PATH" +AS_IF([ test -x bootstrap/ocaml/bin/ocamlc -o -x bootstrap/ocaml/bin/ocamlopt ],[ + echo Bootstrap compiler found -- activating + unset OCAMLLIB + export PATH_PREPEND=`pwd`/bootstrap/ocaml/bin: + export PATH="$PATH_PREPEND:$PATH" +]) + AC_PROG_OCAML if test "x$OCAMLC" = "xno"; then AC_MSG_ERROR([You must install the OCaml compiler]) @@ -50,13 +60,49 @@ AS_IF([test "x${enable_version_check}" != "xno"], [ AC_MSG_ERROR([Your version of OCaml: $OCAMLVERSION is not supported])) ]) +AC_MSG_CHECKING([for compiler type]) +CCOMP_TYPE=`$OCAML shell/print_config.ml ccomp_type 2>/dev/null | fgrep -v "Cannot find" || $OCAMLC -config | tr -d '\r' | sed -n -e "s/ccomp_type: //p"` +AS_IF([ test "$?" -eq 0 ],,[AC_MSG_ERROR([failed])] ) +AC_MSG_RESULT([$CCOMP_TYPE]) +AC_MSG_CHECKING([for compiler architecture]) +ARCH=`$OCAML shell/print_config.ml arch 2>/dev/null | fgrep -v "Cannot find" || $OCAMLC -config | tr -d '\r' | sed -n -e "s/architecture: //p"` +AS_IF([ test "$?" -eq 0 ],,[AC_MSG_ERROR([failed])] ) +AC_MSG_RESULT([$ARCH]) + +LIB_PREPEND= +INC_PREPEND= # Need the C compiler used for OCaml (important on Windows, as both x86 and x64 are used) AS_IF([test "$OCAMLBEST" = "opt"],[OCAMLBESTCC=$OCAMLOPT],[OCAMLBESTCC=$OCAMLC]) -AC_PROG_CC([$($OCAMLBESTCC -config | sed -n -e "s/native_c_compiler: \(.*\) .*/\1/p") gcc cl cc]) +OCAML_CC="$($OCAMLBESTCC -config | sed -n -e "s/native_c_compiler: \(.*\) .*/\1/p")" +set dummy ${OCAML_CC}; OCAML_TEST_CC=$2 +AS_IF([test ! -x ${OCAML_TEST_CC}],[ + AS_IF([test "x${CCOMP_TYPE}" = "xmsvc"],[ + AS_IF([test "${ARCH}" = "i386"],[SDK_ARCH=x86],[SDK_ARCH=x64]) + AC_MSG_CHECKING([for an installed Microsoft C Compiler for ${SDK_ARCH}]) + eval `PATH="$PRE_BOOTSTRAP_PATH" ./shell/msvs-detect --arch=$SDK_ARCH; echo RESULT=$?` + AS_IF([ test "x$MSVS_NAME" = "x" ], [ + AS_IF([ test ${RESULT} -eq 0 ], [ + AC_MSG_RESULT([from PATH]) + ],[ + AC_MSG_ERROR([no]) + ]) + ],[ + AC_MSG_RESULT([from $MSVS_NAME]) + export PATH_PREPEND="${MSVS_PATH}:${PATH_PREPEND}" + # Note that we put ${MSVS_PATH} here NOT ${PATH_PREPEND} so that the bootstrap path isn't repeated + export PATH="${MSVS_PATH}:$PATH" + LIB_PREPEND="${MSVS_LIB};" + INC_PREPEND="${MSVS_INC};" + export Lib="${MSVS_LIB};$LIB" + export Include="${MSVS_INC};$INCLUDE" + ]) + ]) +]) +AC_PROG_CC(["${OCAML_CC}" gcc cc]) AS_IF([test "x${enable_developer_mode}" = "xyes"], [AC_SUBST(DEVELOPER,true)], [AC_SUBST(DEVELOPER,false)]) -AS_IF([test "x${with_mccs}" = "xno"], [AC_SUBST(MCCS_DISABLED,true)], [AC_SUBST(MCCS_DISABLED,false)]) +AS_IF([test "x${with_mccs}" = "xno"], [AC_SUBST(MCCS_ENABLED,false)], [AC_SUBST(MCCS_ENABLED,true)]) AS_IF([test "x${CI}" != "x" -o "x${enable_developer_mode}" = "xyes"], [ CONF_OCAMLFLAGS="-warn-error A" @@ -78,9 +124,15 @@ AC_SUBST(EXE) AS_IF([ test ${WIN32} -eq 1 -a "$GCC" != "yes" ],[ AC_MSG_CHECKING([whether Microsoft Linker needs a PATH shim]) + PREV_PREPEND="$PATH_PREPEND" PATH_PREPEND=$(bash ./shell/check_linker) - AS_IF([test "x${PATH_PREPEND}" = "x" ],[PATH_PREPEND_RESULT=no],[PATH_PREPEND_RESULT=yes]) + AS_IF([test "x${PATH_PREPEND}" = "x${PREV_PREPEND}" ],[PATH_PREPEND_RESULT=no],[PATH_PREPEND_RESULT=yes]) + PATH_PREPEND=`echo "${PATH_PREPEND}" | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` + LIB_PREPEND=`echo ${LIB_PREPEND} | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` + INC_PREPEND=`echo ${INC_PREPEND} | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` AC_SUBST(PATH_PREPEND) + AC_SUBST(LIB_PREPEND) + AC_SUBST(INC_PREPEND) AC_MSG_RESULT([$PATH_PREPEND_RESULT]) ]) @@ -189,7 +241,7 @@ bindir="`eval echo ${bindir}`" mandir="`eval echo ${mandir}`" mandir="`eval echo ${mandir}`" -AS_IF([{ test "x$MCCS_DISABLED" = "xtrue" && test "x${hasalldeps}" = "x"; } || +AS_IF([{ test "x$MCCS_ENABLED" = "xfalse" && test "x${hasalldeps}" = "x"; } || { test "x$OCAML_PKG_mccs" = "xno" && test "x${hasalldeps}" = "xtrue"; }], [echo "Opam will be built WITHOUT a built-in solver"], [echo "Opam will be built WITH a built-in solver"]) diff --git a/jbuild-ignore b/jbuild-ignore index b793755791a..b7c200acbff 100644 --- a/jbuild-ignore +++ b/jbuild-ignore @@ -1,3 +1 @@ bootstrap -src_ext/jbuilder -tests/packages diff --git a/m4/ocaml.m4 b/m4/ocaml.m4 index 1c9efddb3f8..6b5da59a1f4 100644 --- a/m4/ocaml.m4 +++ b/m4/ocaml.m4 @@ -18,7 +18,7 @@ AC_DEFUN([AC_PROG_OCAML], AC_MSG_RESULT([OCaml version is $OCAMLVERSION]) # If OCAMLLIB is set, use it if test "$OCAMLLIB" = ""; then - OCAMLLIB=`$OCAMLC -where 2>/dev/null || $OCAMLC -v|tail -1|cut -d ' ' -f 4` + OCAMLLIB=`$OCAMLC -where 2>/dev/null | tr -d '\015' || $OCAMLC -v|tail -1|cut -d ' ' -f 4` else AC_MSG_RESULT([OCAMLLIB previously set; preserving it.]) fi diff --git a/shell/bootstrap-ocaml.sh b/shell/bootstrap-ocaml.sh index 64f8e0feb94..7739abf37e7 100755 --- a/shell/bootstrap-ocaml.sh +++ b/shell/bootstrap-ocaml.sh @@ -1,21 +1,114 @@ #!/bin/sh -e -V=ocaml-4.06.0 -URL=http://caml.inria.fr/pub/distrib/ocaml-4.06/${V}.tar.gz +if command -v curl > /dev/null; then + CURL="curl -OLSs" +elif command -v wget > /dev/null; then + CURL=wget +else + echo "This script requires curl or wget" + exit 1 +fi mkdir -p bootstrap cd bootstrap +URL=`sed -ne 's/URL_ocaml *= *//p' ../src_ext/Makefile | tr -d '\r'` +V=`echo ${URL}| sed -e 's/.*\/\([^\/]\+\)\.tar\.gz/\1/'` +FV_URL=`sed -ne 's/URL_flexdll *= *//p' ../src_ext/Makefile | tr -d '\r'` +FLEXDLL=`echo ${FV_URL}| sed -e 's/.*\/\([^\/]*\)/\1/'` if [ ! -e ${V}.tar.gz ]; then - if command -v curl > /dev/null; then - curl -OLSs ${URL} - elif command -v wget > /dev/null; then - wget ${URL} - else - echo "This script requires curl or wget" - exit 1 - fi + cp ../src_ext/archives/${V}.tar.gz . 2>/dev/null || ${CURL} ${URL} fi tar -zxf ${V}.tar.gz cd ${V} -./configure -prefix "`pwd`/../ocaml" -${MAKE:-make} world opt.opt -${MAKE:-make} install +PATH_PREPEND= +if [ -n "$1" -a -n "${COMSPEC}" -a -x "${COMSPEC}" ] ; then + LIB_PREPEND= + INC_PREPEND= + + case "$1" in + "mingw"|"mingw64") + BUILD=$1 + ;; + "msvc") + BUILD=$1 + if ! command -v ml > /dev/null ; then + eval `../../shell/msvs-detect --arch=x86` + if [ -n "${MSVS_NAME}" ] ; then + PATH_PREPEND="${MSVS_PATH}" + LIB_PREPEND="${MSVS_LIB};" + INC_PREPEND="${MSVS_INC};" + fi + fi + ;; + "msvc64") + BUILD=$1 + if ! command -v ml64 > /dev/null ; then + eval `../../shell/msvs-detect --arch=x64` + if [ -n "${MSVS_NAME}" ] ; then + PATH_PREPEND="${MSVS_PATH}" + LIB_PREPEND="${MSVS_LIB};" + INC_PREPEND="${MSVS_INC};" + fi + fi + ;; + *) + if [ "$1" != "auto" ] ; then + echo "Compiler architecture $1 not recognised -- mingw64, mingw, msvc64, msvc (or auto)" + fi + if [ -n "${PROCESSOR_ARCHITEW6432}" -o "${PROCESSOR_ARCHITECTURE}" = "AMD64" ] ; then + TRY64=1 + else + TRY64=0 + fi + + if [ ${TRY64} -eq 1 ] && command -v x86_64-w64-mingw32-gcc > /dev/null ; then + BUILD=mingw64 + elif command -v i686-w64-mingw32-gcc > /dev/null ; then + BUILD=mingw + elif [ ${TRY64} -eq 1 ] && command -v ml64 > /dev/null ; then + BUILD=msvc64 + PATH_PREPEND=`bash ../../shell/check_linker` + elif command -v ml > /dev/null ; then + BUILD=msvc + PATH_PREPEND=`bash ../../shell/check_linker` + else + if [ ${TRY64} -eq 1 ] ; then + BUILD=msvc64 + BUILD_ARCH=x64 + else + BUILD=msvc + BUILD_ARCH=x86 + fi + eval `../../shell/msvs-detect --arch=${BUILD_ARCH}` + if [ -z "${MSVS_NAME}" ] ; then + echo "No appropriate C compiler was found -- unable to build OCaml" + exit 1 + else + PATH_PREPEND="${MSVS_PATH}" + LIB_PREPEND="${MSVS_LIB};" + INC_PREPEND="${MSVS_INC};" + fi + fi + ;; + esac + if [ -n "${PATH_PREPEND}" ] ; then + PATH_PREPEND="${PATH_PREPEND}:" + fi + PREFIX=`cd .. ; pwd | cygpath -f - -m`/ocaml + sed -e "s|^PREFIX=.*|PREFIX=${PREFIX}|" config/Makefile.${BUILD} > config/Makefile + cp config/s-nt.h byterun/caml/s.h + cp config/m-nt.h byterun/caml/m.h + cd .. + if [ ! -e ${FLEXDLL} ]; then + cp ../src_ext/archives/${FLEXDLL} . 2>/dev/null || ${CURL} ${FV_URL} + fi + cd ${V} + tar -xzf ../${FLEXDLL} + rm -rf flexdll + mv flexdll-* flexdll + PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" Lib="${LIB_PREPEND}${Lib}" Include="${INC_PREPEND}${Include}" make flexdll world.opt install +else + ./configure -prefix "`pwd`/../ocaml" + ${MAKE:-make} world opt.opt + ${MAKE:-make} install +fi +echo "${URL} ${FV_URL}" > ../installed-tarball diff --git a/shell/check_linker b/shell/check_linker index 173899080a5..8f475d0dd6f 100644 --- a/shell/check_linker +++ b/shell/check_linker @@ -5,7 +5,7 @@ FIRST=1 FAULT=0 PREPEND= while IFS= read -r line; do - OUTPUT=$("$line" --version | head -1 | fgrep "Microsoft (R) Incremental Linker") + OUTPUT=$("$line" --version 2>/dev/null | head -1 | fgrep "Microsoft (R) Incremental Linker") if [ "x$OUTPUT" = "x" -a $FIRST -eq 1 ] ; then FAULT=1 elif [ $FAULT -eq 1 ] ; then diff --git a/shell/msvs-detect b/shell/msvs-detect new file mode 100644 index 00000000000..fdbc5ab0293 --- /dev/null +++ b/shell/msvs-detect @@ -0,0 +1,1085 @@ +#!/bin/bash +# ################################################################################################ # +# MetaStack Solutions Ltd. # +# ################################################################################################ # +# Microsoft C Compiler Environment Detection Script # +# ################################################################################################ # +# Copyright (c) 2016, 2017, 2018 MetaStack Solutions Ltd. # +# ################################################################################################ # +# Author: David Allsopp # +# 16-Feb-2016 # +# ################################################################################################ # +# Redistribution and use in source and binary forms, with or without modification, are permitted # +# provided that the following two conditions are met: # +# 1. Redistributions of source code must retain the above copyright notice, this list of # +# conditions and the following disclaimer. # +# 2. Neither the name of MetaStack Solutions Ltd. nor the names of its contributors may be # +# used to endorse or promote products derived from this software without specific prior # +# written permission. # +# # +# This software is provided by the Copyright Holder 'as is' and any express or implied warranties # +# including, but not limited to, the implied warranties of merchantability and fitness for a # +# particular purpose are disclaimed. In no event shall the Copyright Holder be liable for any # +# direct, indirect, incidental, special, exemplary, or consequential damages (including, but not # +# limited to, procurement of substitute goods or services; loss of use, data, or profits; or # +# business interruption) however caused and on any theory of liability, whether in contract, # +# strict liability, or tort (including negligence or otherwise) arising in any way out of the use # +# use of this software, even if advised of the possibility of such damage. # +# ################################################################################################ # + +VERSION=0.3.2 + +# debug [level=2] message +debug () +{ + if [[ -z ${2+x} ]] ; then + DEBUG_LEVEL=2 + else + DEBUG_LEVEL=$1 + shift + fi + + if [[ $DEBUG -ge $DEBUG_LEVEL ]] ; then + echo "$1">&2 + fi +} + +# warning message +warning () +{ + if [[ $DEBUG -gt 0 ]] ; then + echo "Warning: $1">&2 + fi +} + +# reg_string key value +# Retrieves a REG_SZ value from the registry (redirected on WOW64) +reg_string () +{ + reg query "$1" /v "$2" 2>/dev/null | tr -d '\r' | sed -ne "s/ *$2 *REG_SZ *//p" +} + +# reg64_string key value +# As reg_string, but without WOW64 redirection (i.e. guaranteed access to 64-bit registry) +reg64_string () +{ + $REG64 query "$1" /v "$2" 2>/dev/null | tr -d '\r' | sed -ne "s/ *$2 *REG_SZ *//p" +} + +# find_in list file +# Increments $RET if file does not exist in any of the directories in the *-separated list +find_in () +{ + debug 4 "Looking for $2" + if [[ -z $1 ]] ; then + STATUS=1 + else + IFS=* + STATUS=1 + for f in $1; do + if [[ -e "$f/$2" ]] ; then + STATUS=0 + break + fi + done + unset IFS + fi + if [[ $STATUS -eq 1 ]] ; then + debug 4 "$2 not found" + fi + ((RET+=$STATUS)) +} + +# check_environment PATH INC LIB name arch +# By checking for the presence of various files, verifies that PATH, INC and LIB provide a complete +# compiler and indicates this in its return status. RET is assumed to be zero on entry. $ASSEMBLER +# will contain the name of assembler for this compiler series (ml.exe or ml64.exe). +# The following files are checked: +# cl.exe PATH Microsoft C compiler +# kernel32.lib LIB Implies Windows SDK present +# link.exe PATH Microsoft Linker +# ml[64].exe PATH Microsoft Assembler (ml.exe or ml64.exe) +# msvcrt.lib LIB Implies C Runtime Libraries present +# mt.exe PATH Microsoft Manifest Tool +# oldnames.lib LIB Implies C Runtime Libraries present +# rc.exe PATH Microsoft Resource Compiler (implies tools present) +# stdlib.h INC Implies Microsoft C Runtime Libraries present +# windows.h INC Implies Windows SDK present +# oldnames.lib is included, because certain SDKs and older versions don't correctly install the +# entire runtime if only some options (e.g. Dynamic Runtime and not Static) are selected. +check_environment () +{ + for tool in cl rc link ; do + find_in "$1" $tool.exe + done + + if [[ $RET -gt 0 ]] ; then + warning "Microsoft C Compiler tools not all found - $4 ($5) excluded" + return 1 + fi + + RET=0 + find_in "$2" windows.h + find_in "$3" kernel32.lib + if [[ $RET -gt 0 ]] ; then + warning "Windows SDK not all found - $4 ($5) excluded" + return 1 + fi + + RET=0 + find_in "$2" stdlib.h + find_in "$3" msvcrt.lib + find_in "$3" oldnames.lib + if [[ $RET -gt 0 ]] ; then + warning "Microsoft C runtime library not all found - $4 ($5) excluded" + return 1 + fi + + ASSEMBLER=ml${5#x} + ASSEMBLER=${ASSEMBLER%86}.exe + if [[ $ML_REQUIRED -eq 1 ]] ; then + RET=0 + find_in "$1" $ASSEMBLER + if [[ $RET -gt 0 ]] ; then + warning "Microsoft Assembler ($ASSEMBLER) not found - $4 ($5)" + return 1 + fi + fi + + if [[ $MT_REQUIRED -eq 1 ]] ; then + RET=0 + find_in "$1" mt.exe + if [[ $RET -gt 0 ]] ; then + warning "Microsoft Manifest Tool not found - $4 ($5)" + return 1 + fi + fi + + return 0 +} + +# output VAR value arch +# Outputs a command for setting VAR to value based on $OUTPUT. If $ENV_ARCH is arch, then an empty +# value (i.e. no change) is output. +output () +{ + if [[ $3 = $ENV_ARCH ]] ; then + VALUE= + else + VALUE=$2 + fi + case "$OUTPUT" in + 0) + echo "$1='${VALUE//\'/\'\"\'\"\'}'";; + 1) + VALUE=${VALUE//#/\\\#} + echo "$1=${VALUE//\$/\$\$}";; + esac +} + +# DEBUG Debugging level +# MODE Operation mode +# 0 - Normal +# 1 - --all +# 2 - --help +# 3 - --version +# OUTPUT --output option +# 0 - =shell +# 1 - =make +# MT_REQUIRED --with-mt +# ML_REQUIRED --with-assembler +# TARGET_ARCH Normalised --arch (x86, x64 or blank for both) +# LEFT_ARCH \ If $TARGET_ARCH is blank, these will be x86 and x64 respectively, otherwise they +# RIGHT_ARCH / equal $TARGET_ARCH +# SCAN_ENV Controls from parsing whether the environment should be queried for a compiler +DEBUG=0 +MODE=0 +OUTPUT=0 +MT_REQUIRED=0 +ML_REQUIRED=0 +TARGET_ARCH= +SCAN_ENV=0 + +# Parse command-line. At the moment, the short option which usefully combines with anything is -d, +# so for the time being, combining short options is not permitted, as the loop becomes even less +# clear with getopts. GNU getopt isn't installed by default on Cygwin... +if [[ $@ != "" ]] ; then + while true ; do + case "$1" in + # Mode settings ($MODE) + -a|--all) + MODE=1 + shift 1;; + -h|--help) + MODE=2 + shift;; + -v|--version) + MODE=3 + shift;; + + # Simple flags ($MT_REQUIRED and $ML_REQUIRED) + --with-mt) + MT_REQUIRED=1 + shift;; + --with-assembler) + ML_REQUIRED=1 + shift;; + + # -o, --output ($OUTPUT) + -o|--output) + case "$2" in + shell) + ;; + make) + OUTPUT=1;; + *) + echo "$0: unrecognised option for $1: '$2'">&2 + exit 2;; + esac + shift 2;; + -oshell|--output=shell) + shift;; + -omake|--output=make) + OUTPUT=1 + shift;; + -o*) + echo "$0: unrecognised option for -o: '${1#-o}'">&2 + exit 2;; + --output=*) + echo "$0: unrecognised option for --output: '${1#--output=}'">&2 + exit 2;; + + # -x, --arch ($TARGET_ARCH) + -x|--arch) + case "$2" in + 86|x86) + TARGET_ARCH=x86;; + 64|x64) + TARGET_ARCH=x64;; + *) + echo "$0: unrecognised option for $1: '$2'">&2 + exit 2 + esac + shift 2;; + -x86|-xx86|--arch=x86|--arch=86) + TARGET_ARCH=x86 + shift;; + -x64|-xx64|--arch=x64|--arch=64) + TARGET_ARCH=x64 + shift;; + -x*) + echo "$0: unrecognised option for -x: '${1#-x}'">&2 + exit 2;; + --arch=*) + echo "$0: unrecognised option for --arch: '${1#--arch}'">&2 + exit 2;; + + # -d, --debug ($DEBUG) + -d*) + DEBUG=${1#-d} + if [[ -z $DEBUG ]] ; then + DEBUG=1 + fi + shift;; + --debug=*) + DEBUG=${1#*=} + shift;; + --debug) + DEBUG=1 + shift;; + + # End of option marker + --) + shift + break;; + + # Invalid options + --*) + echo "$0: unrecognised option: '${1%%=*}'">&2 + exit 2;; + -*) + echo "$0: unrecognised option: '${1:1:1}'">&2 + exit 2;; + + # MSVS_PREFERENCE (without end-of-option marker) + *) + break;; + esac + done + + if [[ -n ${1+x} ]] ; then + if [[ $MODE -eq 1 ]] ; then + echo "$0: cannot specify MSVS_PREFERENCE and --all">&2 + exit 2 + else + MSVS_PREFERENCE="$@" + fi + fi +fi + +# Options sanitising +if [[ $MODE -eq 1 ]] ; then + if [[ -n $TARGET_ARCH ]] ; then + echo "$0: --all and --arch are mutually exclusive">&2 + exit 2 + fi + MSVS_PREFERENCE= + SCAN_ENV=1 +elif [[ -z ${MSVS_PREFERENCE+x} ]] ; then + MSVS_PREFERENCE='@;VS15.*;VS14.0;VS12.0;VS11.0;10.0;9.0;8.0;7.1;7.0' +fi + +MSVS_PREFERENCE=${MSVS_PREFERENCE//;/ } + +if [[ -z $TARGET_ARCH ]] ; then + LEFT_ARCH=x86 + RIGHT_ARCH=x64 +else + LEFT_ARCH=$TARGET_ARCH + RIGHT_ARCH=$TARGET_ARCH +fi + +# Command line parsing complete (MSVS_PREFERENCE pending) + +NAME="Microsoft C Compiler Environment Detection Script" +case $MODE in + 2) + echo "$NAME" + echo "Queries the environment and registry to locate Visual Studio / Windows SDK" + echo "installations and uses their initialisation scripts (SetEnv.cmd, vcvarsall.bat," + echo "etc.) to determine INCLUDE, LIB and PATH alterations." + echo + echo "Usage:" + echo " $0 [OPTIONS] [--] [MSVS_PREFERENCE]" + echo + echo "Options:" + echo " -a, --all Display all available compiler packages" + echo " -x, --arch=ARCH Only consider packages for ARCH (x86 or x64). Default is" + echo " to return packages containing both architectures" + echo " -d, --debug[=LEVEL] Set debug messages level" + echo " -h, --help Display this help screen" + echo " -o, --output=OUTPUT Set final output. Default is shell. Valid values:" + echo " shell - shell assignments, for use with eval" + echo " make - make assignments, for inclusion in a Makefile" + echo " -v, --version Display the version" + echo " --with-mt Only consider packages including the Manifest Tool" + echo " --with-assembler Only consider packages including an assembler" + echo + echo "If MSVS_PREFERENCE is not given, then the environment variable MSVS_PREFERENCE" + echo "is read. MSVS_PREFERENCE is a semicolon separated list of preferred versions." + echo "Three kinds of version notation are supported:" + echo " 1. @ - which refers to the C compiler found in PATH (if it can be identified)" + echo " (this allows the C compiler corresponding to the opposite architecture to" + echo " be selected, if possible)." + echo " 2. mm.n - which refers to a Visual Studio version (e.g. 14.0, 7.1) but which" + echo " also allows an SDK to provide the compiler (e.g. Windows SDK 7.1 provides" + echo " 10.0). Visual Studio packages are always preferred ahead of SDKs." + echo " 3. SPEC - an actual package specification. Visual Studio packages are VSmm.n" + echo " (e.g. VS14.0, VS7.1) and SDK packages are SDKm.n (e.g. SDK7.1)." + echo " Any Visual Studio 2017 update can be selected with VS15.*" + echo "The default behaviour is to match the environment compiler followed by the most" + echo "recent version of the compiler." + exit 0;; + 3) + echo "$NAME" + echo "Version $VERSION" + exit 0;; +esac + +# Known compiler packages. Visual Studio .NET 2002 onwards. Detection is in place for Visual Studio +# 2005 Express, but because it doesn't include a Windows SDK, it can only ever be detected if the +# script has been launched from within a Platform SDK command prompt (this provides the Windows +# Headers and Libraries which allows this script to detect the rest). +# Each element is either a Visual Studio or SDK package and the value is the syntax for a bash +# associative array to be eval'd. Each of these contains the following properties: +# NAME - the friendly name of the package +# ENV - (VS only) the version-specific portion of the VSCOMNTOOLS environment variable +# VERSION - (VS only) version number of the package +# ARCH - Lists the architectures available in this version +# ARCH_SWITCHES - The script is assumed to accept x86 and x64 to indicate architecture. This key +# contains another eval'd associative array allowing alternate values to be given +# SETENV_RELEASE - (SDK only) script switch necessary to select release than debugging versions +# EXPRESS - (VS only) the prefix to the registry key to detect the Express edition +# EXPRESS_ARCH - (VS only) overrides ARCH if Express edition is detected +# EXPRESS_ARCH_SWITCHES - (VS only) overrides ARCH_SWITCHES if Express edition is detected +# VC_VER - (SDK only) specifies the version of the C Compilers included in the SDK (SDK +# equivalent of the VERSION key) +# REG_KEY - (SDK only) registry key to open to identify this package installation +# REG_VALUE - (SDK only) registry value to query to identify this package installation +# VSWHERE - (VS 2017+) is 1 if the compiler can only be detected using vswhere +# For a while, Windows SDKs followed a standard pattern which is stored in the SDK element and +# copied to the appropriate version. SDKs after 7.1 do not include compilers, and so are not +# captured (as of Visual Studio 2015, the Windows SDK is official part of Visual Studio). +declare -A COMPILERS +SDK52_KEY='HKLM\SOFTWARE\Microsoft\MicrosoftSDK\InstalledSDKs\8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3' +COMPILERS=( + ["VS7.0"]='( + ["NAME"]="Visual Studio .NET 2002" + ["ENV"]="" + ["VERSION"]="7.0" + ["ARCH"]="x86")' + ["VS7.1"]='( + ["NAME"]="Visual Studio .NET 2003" + ["ENV"]="71" + ["VERSION"]="7.1" + ["ARCH"]="x86")' + ["VS8.0"]='( + ["NAME"]="Visual Studio 2005" + ["ENV"]="80" + ["VERSION"]="8.0" + ["EXPRESS"]="VC" + ["ARCH"]="x86 x64" + ["EXPRESS_ARCH"]="x86")' + ["VS9.0"]='( + ["NAME"]="Visual Studio 2008" + ["ENV"]="90" + ["VERSION"]="9.0" + ["EXPRESS"]="VC" + ["ARCH"]="x86 x64" + ["EXPRESS_ARCH"]="x86")' + ["VS10.0"]='( + ["NAME"]="Visual Studio 2010" + ["ENV"]="100" + ["VERSION"]="10.0" + ["EXPRESS"]="VC" + ["ARCH"]="x86 x64" + ["EXPRESS_ARCH"]="x86")' + ["VS11.0"]='( + ["NAME"]="Visual Studio 2012" + ["ENV"]="110" + ["VERSION"]="11.0" + ["EXPRESS"]="WD" + ["ARCH"]="x86 x64" + ["EXPRESS_ARCH_SWITCHES"]="([\"x64\"]=\"x86_amd64\")")' + ["VS12.0"]='( + ["NAME"]="Visual Studio 2013" + ["ENV"]="120" + ["VERSION"]="12.0" + ["EXPRESS"]="WD" + ["ARCH"]="x86 x64" + ["EXPRESS_ARCH_SWITCHES"]="([\"x64\"]=\"x86_amd64\")")' + ["VS14.0"]='( + ["NAME"]="Visual Studio 2015" + ["ENV"]="140" + ["VERSION"]="14.0" + ["ARCH"]="x86 x64")' + ["VS15.*"]='( + ["NAME"]="Visual Studio 2017" + ["VSWHERE"]="1")' + ["SDK5.2"]='( + ["NAME"]="Windows Server 2003 SP1 SDK" + ["VC_VER"]="8.0" + ["REG_KEY"]="$SDK52_KEY" + ["REG_VALUE"]="Install Dir" + ["SETENV_RELEASE"]="/RETAIL" + ["ARCH"]="x64" + ["ARCH_SWITCHES"]="([\"x64\"]=\"/X64\")")' + ["SDK"]='( + ["NAME"]="Generalised Windows SDK" + ["SETENV_RELEASE"]="/Release" + ["ARCH"]="x86 x64" + ["ARCH_SWITCHES"]="([\"x86\"]=\"/x86\" [\"x64\"]=\"/x64\")")' + ["SDK6.1"]='( + ["NAME"]="Windows Server 2008 with .NET 3.5 SDK" + ["VC_VER"]="9.0")' + ["SDK7.0"]='( + ["NAME"]="Windows 7 with .NET 3.5 SP1 SDK" + ["VC_VER"]="9.0")' + ["SDK7.1"]='( + ["NAME"]="Windows 7 with .NET 4 SDK" + ["VC_VER"]="10.0")' +) + +# FOUND is ultimately an associative array containing installed compiler packages. It's +# hijacked here as part of MSVS_PREFERENCE validation. +# Ultimately, it contains a copy of the value from COMPILERS with the following extra keys: +# IS_EXPRESS - (VS only) indicates whether the Express edition was located +# SETENV - (SDK only) the full location of the SetEnv.cmd script +# ASSEMBLER - the name of the assembler (ml or ml64) +# MSVS_PATH \ +# MSVS_INC > prefix values for PATH, INCLUDE and LIB determined by running the scripts. +# MSVS_LIB / +declare -A FOUND + +# Check that MSVS_PREFERENCE is valid and contains no repetitions. +for v in $MSVS_PREFERENCE ; do + if [[ -n ${FOUND[$v]+x} ]] ; then + echo "$0: corrupt MSVS_PREFERENCE: repeated '$v'">&2 + exit 2 + fi + if [[ $v != "@" ]] ; then + if [[ -z ${COMPILERS[$v]+x} && -z ${COMPILERS["VS$v"]+x} && -z ${COMPILERS[${v%.*}.*]+x} ]] ; then + echo "$0: corrupt MSVS_PREFERENCE: unknown compiler '$v'">&2 + exit 2 + fi + else + SCAN_ENV=1 + fi + FOUND["$v"]="" +done + +# Reset FOUND for later use. +FOUND=() + +# Scan the environment for a C compiler, and check that it's valid. Throughout the rest of the +# script, it is assumed that if ENV_ARCH is set then there is a valid environment compiler. +if [[ $SCAN_ENV -eq 1 ]] ; then + if which cl >/dev/null 2>&1 ; then + # Determine its architecture from the Microsoft Logo line. + ENV_ARCH=$(cl 2>&1 | head -1 | tr -d '\r') + case "${ENV_ARCH#* for }" in + x64|AMD64) + ENV_ARCH=x64;; + 80x86|x86) + ENV_ARCH=x86;; + *) + echo "Unable to identify C compiler architecture from '${ENV_ARCH#* for }'">&2 + echo "Environment C compiler discarded">&2 + unset ENV_ARCH;; + esac + + # Environment variable names are a bit of a nightmare on Windows - they are actually case + # sensitive (at the kernel level) but not at the user level! To compound the misery is that SDKs + # use Include and Lib where vcvars32 tends to use INCLUDE and LIB. Windows versions also contain + # a mix of Path and PATH, but fortunately Cygwin normalises that to PATH for us! For this + # reason, use env to determine the actual case of the LIB and INCLUDE variables. + if [[ -n ${ENV_ARCH+x} ]] ; then + RET=0 + ENV_INC=$(env | sed -ne 's/^\(INCLUDE\)=.*/\1/pi') + ENV_LIB=$(env | sed -ne 's/^\(LIB\)=.*/\1/pi') + if check_environment "${PATH//:/*}" \ + "${!ENV_INC//;/*}" \ + "${!ENV_LIB//;/*}" \ + "Environment C compiler" \ + "$ENV_ARCH" ; then + ENV_CL=$(which cl) + ENV_cl=${ENV_CL,,} + ENV_cl=${ENV_cl/bin\/*_/bin\/} + debug "Environment appears to include a compiler at $ENV_CL" + if [[ -n $TARGET_ARCH && $TARGET_ARCH != $ENV_ARCH ]] ; then + debug "But architecture doesn't match required value" + unset ENV_ARCH + fi + else + unset ENV_ARCH + fi + fi + fi +fi + +# Even if launched from a 64-bit Command Prompt, Cygwin is usually 32-bit and so the scripts +# executed will inherit that fact. This is a problem when querying the registry, but fortunately +# WOW64 provides a mechanism to break out of the 32-bit environment by mapping $WINDIR/sysnative to +# the real 64-bit programs. +# Thus: +# MS_ROOT is the 32-bit Microsoft Registry key (all Visual Studio keys are located there) +# REG64 is the processor native version of the reg utility (allowing 64-bit keys to be read for +# the SDKs) +if [[ -n ${PROCESSOR_ARCHITEW6432+x} ]] ; then + debug "WOW64 detected" + MS_ROOT='HKLM\SOFTWARE\Microsoft' + REG64=$WINDIR/sysnative/reg +else + MS_ROOT='HKLM\SOFTWARE\Wow6432Node\Microsoft' + REG64=reg +fi + +# COMPILER contains each eval'd element from COMPILERS +declare -A COMPILER + +# Scan the registry for compiler package (vswhere is later) +for i in "${!COMPILERS[@]}" ; do + eval COMPILER=${COMPILERS[$i]} + + if [[ -n ${COMPILER["ENV"]+x} ]] ; then + # Visual Studio package - test for its environment variable + ENV=VS${COMPILER["ENV"]}COMNTOOLS + if [[ -n ${!ENV+x} ]] ; then + debug "$ENV is a candidate" + TEST_PATH=${!ENV%\"} + TEST_PATH=$(cygpath -u -f - <<< ${TEST_PATH#\"}) + if [[ -e $TEST_PATH/vsvars32.bat ]] ; then + debug "Directory pointed to by $ENV contains vsvars32.bat" + EXPRESS=0 + # Check for the primary Visual Studio registry value indicating installation + INSTALL_DIR=$(reg_string "$MS_ROOT\\VisualStudio\\${COMPILER["VERSION"]}" InstallDir) + if [[ -z $INSTALL_DIR ]] ; then + if [[ -n ${COMPILER["EXPRESS"]+x} ]] ; then + TEST_KEY="$MS_ROOT\\${COMPILER["EXPRESS"]}Express\\${COMPILER["VERSION"]}" + INSTALL_DIR=$(reg_string "$TEST_KEY" InstallDir) + # Exception for Visual Studio 2005 Express, which doesn't set the registry correctly, so + # set INSTALL_DIR to a fake value to pass the next test. + if [[ ${COMPILER["VERSION"]} = "8.0" ]] ; then + INSTALL_DIR=$(cygpath -w "$TEST_PATH") + EXPRESS=1 + else + if [[ -z $INSTALL_DIR ]] ; then + warning "vsvars32.bat found, but registry value not located (Exp or Pro)" + else + EXPRESS=1 + fi + fi + else + warning "vsvars32.bat found, but registry value not located" + fi + fi + + if [[ -n $INSTALL_DIR ]] ; then + if [[ ${TEST_PATH%/} = $(cygpath -u "$INSTALL_DIR\\..\\Tools") ]] ; then + RESULT=${COMPILERS[$i]%)} + DISPLAY=${COMPILER["NAME"]} + if [[ $EXPRESS -eq 1 ]] ; then + DISPLAY="$DISPLAY Express" + fi + FOUND+=(["$i"]="$RESULT [\"DISPLAY\"]=\"$DISPLAY\" [\"IS_EXPRESS\"]=\"$EXPRESS\")") + debug "${COMPILER["NAME"]} accepted for further detection" + else + warning "$ENV doesn't agree with registry" + fi + else + warning "vsvars32.bat found, but registry settings not found" + fi + else + warning "$ENV set, but vsvars32.bat not found" + fi + fi + elif [[ -n ${COMPILER["REG_KEY"]+x} ]] ; then + # SDK with explicit registry detection value + INSTALL_DIR=$(reg64_string "${COMPILER["REG_KEY"]}" "${COMPILER["REG_VALUE"]}") + if [[ -n $INSTALL_DIR ]] ; then + TEST_PATH=$(cygpath -u "$INSTALL_DIR") + if [[ -e $TEST_PATH/SetEnv.cmd ]] ; then + RESULT=${COMPILERS[$i]%)} + FOUND+=(["$i"]="$RESULT [\"DISPLAY\"]=\"${COMPILER["NAME"]}\" [\"SETENV\"]=\"$INSTALL_DIR\\SetEnv.cmd\")") + debug "${COMPILER["NAME"]} accepted for further detection" + else + warning "Registry set for Windows Server 2003 SDK, but SetEnv.cmd not found" + fi + fi + fi +done + +# Now enumerate installed SDKs for v6.0+ +SDK_ROOT='HKLM\SOFTWARE\Microsoft\Microsoft SDKs\Windows' +for i in $(reg query "$SDK_ROOT" 2>/dev/null | tr -d '\r' | sed -ne '/Windows\\v/s/.*\\//p') ; do + debug "Analysing SDK key $SDK_ROOT\\$i" + INSTALL_DIR=$(reg_string "$SDK_ROOT\\$i" InstallationFolder) + if [[ -n $INSTALL_DIR ]] ; then + TEST_PATH=$(cygpath -u "$INSTALL_DIR") + if [[ -e $TEST_PATH/Bin/SetEnv.cmd ]] ; then + if [[ -z ${COMPILERS["SDK${i#v}"]+x} ]] ; then + warning "SDK $i is not known to this script - assuming compatibility" + DISPLAY="Windows SDK $i" + else + eval COMPILER=${COMPILERS["SDK${i#v}"]} + DISPLAY=${COMPILER['NAME']} + fi + RESULT=${COMPILERS['SDK']%)} + FOUND+=(["SDK${i/v/}"]="$RESULT [\"DISPLAY\"]=\"$DISPLAY\" [\"SETENV\"]=\"$INSTALL_DIR\\Bin\\SetEnv.cmd\")") + else + if [[ -n ${COMPILERS["SDK${i#v}"]+x} ]] ; then + warning "Registry set for Windows SDK $i, but SetEnv.cmd not found" + fi + fi + else + warning "Registry key for Windows SDK $i doesn't contain expected InstallationFolder value" + fi +done + +# Now enumerate Visual Studio 2017+ instances +VSWHERE=$(dirname $(realpath $0))/vswhere.exe +if [[ ! -x $VSWHERE ]] ; then + VSWHERE="$(printenv 'ProgramFiles(x86)')\\Microsoft Visual Studio\\Installer\\vswhere.exe" + VSWHERE=$(echo $VSWHERE| cygpath -f -) +fi +if [[ -x $VSWHERE ]] ; then + debug "$VSWHERE found" + while IFS= read -r line; do + case ${line%: *} in + instanceId) + INSTANCE=${line#*: };; + installationPath) + INSTANCE_PATH=${line#*: };; + installationVersion) + INSTANCE_VER=${line#*: } + INSTANCE_VER=${INSTANCE_VER%.*} + INSTANCE_VER=${INSTANCE_VER%.*};; + displayName) + INSTANCE_NAME=${line#*: } + debug "Looking at $INSTANCE in $INSTANCE_PATH ($INSTANCE_VER $INSTANCE_NAME)" + if [[ -e "$(echo $INSTANCE_PATH| cygpath -f -)/VC/Auxiliary/Build/vcvarsall.bat" ]] ; then + debug "vcvarsall.bat found" + FOUND+=(["VS$INSTANCE_VER"]="([\"DISPLAY\"]=\"$INSTANCE_NAME\" [\"ARCH\"]=\"x86 x64\" [\"SETENV\"]=\"$INSTANCE_PATH\\VC\\Auxiliary\\Build\\vcvarsall.bat\" [\"SETENV_RELEASE\"]=\"\")") + else + warning "vcvarsall.bat not found for $INSTANCE" + fi;; + esac + done < <("$VSWHERE" -all -nologo | tr -d '\r') +fi + +if [[ $DEBUG -gt 1 ]] ; then + for i in "${!FOUND[@]}" ; do + echo "Inspect $i">&2 + done +fi + +# Basic scanning is complete, now interrogate the packages which seem to be installed and ensure +# that they pass the check_environment tests. + +# CANDIDATES is a hash table of the keys of FOUND. The result of the next piece of processing is to +# derive two arrays PREFERENCE and TEST. TEST will contain a list of the keys of FOUND in the order +# in which they should be evaluated. PREFERENCE contains a parsed version of MSVS_PREFERENCE but +# filtered on the basis of the compiler packages already identified. The current "hoped for" +# preference is stored in $pref (the index into PREFERENCE) and $PREF (which is +# ${PREFERENCE[$pref]}). These two arrays together allow testing to complete quickly if the desired +# version is found (note that often this won't be possible as the @ environment option requires all +# packages to be tested in order to be sure that the environment compiler is not ambiguous). +declare -A CANDIDATES +for i in "${!FOUND[@]}" ; do + CANDIDATES[$i]=""; +done + +# For --all, act as though MSVS_PREFERENCE were "@" because this causes all packages to be tested. +if [[ $MODE -eq 1 ]] ; then + PREFER_ENV=1 + PREFERENCE=("@") +else + PREFER_ENV=0 + PREFERENCE=() +fi + +TEST=() +for i in $MSVS_PREFERENCE ; do + if [[ $i = "@" ]] ; then + if [[ -n ${ENV_ARCH+x} ]] ; then + PREFERENCE+=("@") + PREFER_ENV=1 + else + debug "Preference @ ignored since no environment compiler selected" + fi + else + if [[ -n ${COMPILERS[$i]+x} || -n ${COMPILERS[${i%.*}.*]+x} ]] ; then + if [[ -n ${CANDIDATES[$i]+x} ]] ; then + unset CANDIDATES[$i] + TEST+=($i) + PREFERENCE+=($i) + elif [[ ${i#*.} = "*" ]] ; then + INSTANCES= + for j in "${!CANDIDATES[@]}" ; do + if [[ "${j%.*}.*" = $i ]] ; then + unset CANDIDATES[$j] + INSTANCES="$INSTANCES $j" + fi + done + INSTANCES="$(sort -r <<< "${INSTANCES// /$'\n'}")" + eval TEST+=($INSTANCES) + eval PREFERENCE+=($INSTANCES) + fi + else + if [[ -n ${CANDIDATES["VS$i"]+x} ]] ; then + unset CANDIDATES["VS$i"] + TEST+=("VS$i") + PREFERENCE+=("VS$i") + fi + SDKS= + for j in "${!COMPILERS[@]}" ; do + eval COMPILER=${COMPILERS[$j]} + if [[ -n ${COMPILER["VC_VER"]}+x ]] ; then + if [[ $i = ${COMPILER["VC_VER"]} && -n ${CANDIDATES[$j]+x} ]] ; then + unset CANDIDATES[$j] + SDKS="$j $SDKS" + fi + fi + done + SDKS=${SDKS% } + SDKS="$(sort -r <<< "${SDKS// /$'\n'}")" + SDKS=${SDKS//$'\n'/ } + eval TEST+=($SDKS) + eval PREFERENCE+=($SDKS) + fi + fi +done + +# If MSVS_PREFERENCE includes @, add any remaining items from CANDIDATES to TEST, otherwise remove +# them from FOUND so that they don't accidentally get reported on later. +for i in "${!CANDIDATES[@]}" ; do + if [[ $PREFER_ENV -eq 1 ]] ; then + TEST+=($i) + else + unset FOUND[$i] + fi +done + +# Initialise pref and PREF to ${PREFERENCE[0]} +pref=0 +PREF=${PREFERENCE[0]} + +if [[ $DEBUG -gt 1 ]] ; then + for i in "${!TEST[@]}" ; do + echo "Test ${TEST[$i]}">&2 + done +fi + + +# Now run each compiler's environment script and then test whether it is suitable. During this loop, +# attempt to identify the environment C compiler (if one was found). The environment C compiler is +# strongly identified if the full location of cl matches the one in PATH and both LIB and INCLUDE +# contain the strings returned by the script in an otherwise empty environment (if one or both of +# the LIB and INCLUDE variables do not contain the string returned, then the compiler is weakly +# identified). If the environment compiler is strongly identified by more than one package, then it +# is not identified at all; if it is strongly identified by no packages but weakly identified by +# exactly 1, then we grudgingly accept that that's probably the one. +ENV_COMPILER= +WEAK_ENV= + +# ARCHINFO contains the appropriate ARCH_SWITCHES associative array for each compiler. +declare -A ARCHINFO + +for i in "${TEST[@]}" ; do + CURRENT=${FOUND[$i]} + eval COMPILER=$CURRENT + # At the end of this process, the keys of FOUND will be augmented with the architecture found in + # each case (so if "VS14.0" was in FOUND from the scan and both the x86 and x64 compilers are + # valid, then at the end of this loop FOUND will contain "VS14.0-x86" and "VS14.0-x64"). + unset FOUND[$i] + + if [[ ${COMPILER["IS_EXPRESS"]}0 -gt 0 && -n ${COMPILER["EXPRESS_ARCH_SWITCHES"]+x} ]] ; then + eval ARCHINFO=${COMPILER["EXPRESS_ARCH_SWITCHES"]} + elif [[ -n ${COMPILER["ARCH_SWITCHES"]+x} ]] ; then + eval ARCHINFO=${COMPILER["ARCH_SWITCHES"]} + else + ARCHINFO=() + fi + + # Determine the script to be executed and any non-architecture specific switches needed. + # $ENV is will contain the value of the environment variable for the compiler (empty for an SDK) + # which is required for Visual Studio 7.x shim later. + if [[ -n ${COMPILER["ENV"]+x} ]] ; then + ENV=VS${COMPILER["ENV"]}COMNTOOLS + ENV=${!ENV%\"} + ENV=${ENV#\"} + if [[ ${COMPILER["ENV"]}0 -ge 800 ]] ; then + SCRIPT="$(cygpath -d -f - <<< $ENV)\\..\\..\\VC\\vcvarsall.bat" + SCRIPT_SWITCHES= + else + SCRIPT="$(cygpath -d -f - <<< $ENV)\\vsvars32.bat" + SCRIPT_SWITCHES= + fi + else + ENV= + SCRIPT=${COMPILER["SETENV"]} + SCRIPT_SWITCHES=${COMPILER["SETENV_RELEASE"]} + fi + # For reasons of escaping, the script is executed using its basename so the directory needs + # prepending to PATH. + DIR=$(dirname "$SCRIPT" | cygpath -u -f -) + + if [[ ${COMPILER["IS_EXPRESS"]} -gt 0 && -n ${COMPILER["EXPRESS_ARCH"]+x} ]] ; then + ARCHS=${COMPILER["EXPRESS_ARCH"]} + else + ARCHS=${COMPILER["ARCH"]} + fi + + for arch in $ARCHS ; do + # Determine the command line switch for this architecture + if [[ -n ${ARCHINFO[$arch]+x} ]] ; then + ARCH_SWITCHES=${ARCHINFO[$arch]} + else + ARCH_SWITCHES=$arch + fi + + # Run the script in order to determine changes made to PATH, INCLUDE and LIB. These scripts + # always prepend changes to the environment variables. + MSVS_PATH= + MSVS_LIB= + MSVS_INC= + + COMMAND='%EXEC_SCRIPT% && echo XMARKER && echo !PATH! && echo !LIB! && echo !INCLUDE!' + + # Note that EXEC_SCRIPT must have ARCH_SWITCHES first for older Platform SDKs (newer ones parse + # arguments properly) + if [[ $DEBUG -gt 3 ]] ; then + printf "Scanning %s... " "$(basename "$SCRIPT") $ARCH_SWITCHES $SCRIPT_SWITCHES">&2 + fi + num=0 + while IFS= read -r line; do + case $num in + 0) + MSVS_PATH=${line%% };; + 1) + MSVS_LIB=${line%% };; + 2) + MSVS_INC=${line%% };; + esac + ((num++)) + done < <(INCLUDE= LIB= PATH="$DIR:$PATH" \ + EXEC_SCRIPT="$(basename "$SCRIPT") $ARCH_SWITCHES $SCRIPT_SWITCHES" \ + cmd /v:on /c $COMMAND 2>/dev/null | fgrep XMARKER -A 3 | tr -d '\015' | tail -3) + if [[ $DEBUG -gt 3 ]] ; then + echo done>&2 + fi + + if [[ -n $MSVS_PATH ]] ; then + # Translate MSVS_PATH back to Cygwin notation (/cygdrive, etc. and colon-separated) + MSVS_PATH=$(cygpath "$MSVS_PATH" -p) + # Remove the actual PATH (and the extra $DIR added to run the script) + MSVS_PATH=${MSVS_PATH%:$DIR:$PATH} + # Guarantee that MSVS_PATH ends with a single : + MSVS_PATH="${MSVS_PATH%%:}:" + fi + # Ensure that both variables end with a semi-colon (it doesn't matter if for some erroneous + # reason they have come back blank, because check_environment will shortly fail) + MSVS_LIB="${MSVS_LIB%%;};" + MSVS_INC="${MSVS_INC%%;};" + + # Visual Studio .NET 2002 and 2003 do not include mt in PATH, for not entirely clear reasons. + # This shim detects that scenario and adds the winnt folder to MSVS_PATH. + RET=0 + if [[ ${i/.*/} = "VS7" ]] ; then + find_in "${MSVS_PATH//:/*}" mt.exe + if [[ $RET -eq 1 ]] ; then + MSVS_PATH="$MSVS_PATH$(cygpath -u -f - <<< $ENV\\Bin\\winnt):" + RET=0 + fi + fi + + # Ensure that these derived values give a valid compiler. + if check_environment "${MSVS_PATH//:/*}" "${MSVS_INC//;/*}" "${MSVS_LIB//;/*}" "$i" $arch ; then + # Put the package back into FOUND, but augmented with the architecture name and with the + # derived values. + FOUND["$i-$arch"]="${CURRENT%)} [\"MSVS_PATH\"]=\"$MSVS_PATH\" \ + [\"MSVS_INC\"]=\"$MSVS_INC\" \ + [\"MSVS_LIB\"]=\"$MSVS_LIB\" \ + [\"ASSEMBLER\"]=\"$ASSEMBLER\")" #"# fixes vim syn match error + + # Check to see if this is a match for the environment C compiler. + if [[ -n ${ENV_ARCH+x} ]] ; then + TEST_cl=$(PATH="$MSVS_PATH:$PATH" which cl) + TEST_cl=${TEST_cl,,} + TEST_cl=${TEST_cl/bin\/*_/bin\/} + if [[ $TEST_cl = $ENV_cl ]] ; then + if [[ ${!ENV_INC/"$MSVS_INC"/} != "${!ENV_INC}" && \ + ${!ENV_LIB/"$MSVS_LIB"/} != "${!ENV_LIB}" ]] ; then + debug "$i-$arch is a strong candidate for the Environment C compiler" + if [[ -n ${ENV_COMPILER+x} ]] ; then + if [[ -z ${ENV_COMPILER} ]] ; then + ENV_COMPILER=$i-$arch + unset WEAK_ENV + else + # More than one strong candidate - no fall back available + unset ENV_COMPILER + unset WEAK_ENV + fi + fi + else + debug "$i-$arch is a weak candidate for the Environment C compiler" + if [[ -n ${WEAK_ENV+x} ]] ; then + if [[ -z ${WEAK_ENV} ]] ; then + WEAK_ENV=$i-$arch + else + # More than one weak candidate - no fall back available + unset WEAK_ENV + fi + fi + fi + fi + fi + fi + done + + # Does this package match the current preference? Note that PREFERENCE and TEST are constructed in + # a cunning (and hopefully not too "You are not expected to understand this" way) such that $PREF + # will always equal $i, unless $PREF = "@". + if [[ $PREF = $i ]] ; then + # In which case, check that the architecture(s)s were found + if [[ -n ${FOUND["$i-$LEFT_ARCH"]+x} && -n ${FOUND["$i-$RIGHT_ARCH"]+x} ]] ; then + debug "Solved TARGET_ARCH=$TARGET_ARCH with $i" + SOLUTION=$i + break + fi + fi + + if [[ $PREF != "@" ]] ; then + ((pref++)) + PREF=${PREFERENCE[$pref]} + fi +done + +# If we got this far, then either we failed to find a compiler at all, or we were looking for the +# environment compiler (or --all was specified). + +# Adopt a weak match for the environment compiler, if that's the best we can do. +if [[ -n ${ENV_COMPILER+x} && -z ${ENV_COMPILER} && -n ${WEAK_ENV} ]] ; then + warning "Assuming Environment C compiler is $WEAK_ENV" + ENV_COMPILER=$WEAK_ENV +fi + +declare -A FLIP +FLIP=(["x86"]="x64" ["x64"]="x86") + +if [[ $MODE -eq 0 ]] ; then + if [[ $PREF = "@" && -n ${ENV_COMPILER} ]] ; then + SOLUTION=${ENV_COMPILER%-$ENV_ARCH} + # If --arch wasn't specified, then ensure that the other architecture was also found. If --arch + # was specified, then validate that the compiler was valid. This should always happen, unless + # something went wrong running the script to get MSVS_PATH, MSVS_LIB and MSVS_INC. + if [[ -n ${FOUND["$SOLUTION-${FLIP[$ENV_ARCH]}"]+x} || + -n ${FOUND["$SOLUTION-$TARGET_ARCH"]+x} ]] ; then + debug "Solved with $SOLUTION" + else + unset SOLUTION + unset ENV_ARCH + fi + fi + + if [[ -z ${SOLUTION+x} ]] ; then + ((pref++)) + debug "Search remaining: ${PREFERENCE[*]}" + TEST_ARCH=$TARGET_ARCH + for i in "${PREFERENCE[@]:$pref}" ; do + if [[ -n ${FOUND["$i-$LEFT_ARCH"]+x} && -n ${FOUND["$i-$RIGHT_ARCH"]+x} ]] ; then + debug "Solved TARGET_ARCH='$TARGET_ARCH' with $i" + SOLUTION=$i + break + fi + done + fi +fi + +debug "Solution: $SOLUTION" + +if [[ -n ${ENV_COMPILER} && $MODE -eq 1 ]] ; then + echo "Identified Environment C compiler as $ENV_COMPILER" +fi + +if [[ $MODE -eq 1 ]] ; then + echo "Installed and usable packages:" + for i in "${!FOUND[@]}" ; do + echo " $i" + done | sort + exit 0 +fi + +if [[ -n $SOLUTION ]] ; then + eval COMPILER=${FOUND[$SOLUTION-$LEFT_ARCH]} + output MSVS_NAME "${COMPILER["DISPLAY"]}" $LEFT_ARCH + output MSVS_PATH "${COMPILER["MSVS_PATH"]}" $LEFT_ARCH + output MSVS_INC "${COMPILER["MSVS_INC"]}" $LEFT_ARCH + output MSVS_LIB "${COMPILER["MSVS_LIB"]}" $LEFT_ARCH + if [[ $ML_REQUIRED -eq 1 ]] ; then + output MSVS_ML "${COMPILER["ASSEMBLER"]%.exe}" always + fi + if [[ -z $TARGET_ARCH ]] ; then + eval COMPILER=${FOUND[$SOLUTION-$RIGHT_ARCH]} + output MSVS64_PATH "${COMPILER["MSVS_PATH"]}" $RIGHT_ARCH + output MSVS64_INC "${COMPILER["MSVS_INC"]}" $RIGHT_ARCH + output MSVS64_LIB "${COMPILER["MSVS_LIB"]}" $RIGHT_ARCH + if [[ $ML_REQUIRED -eq 1 ]] ; then + output MSVS64_ML "${COMPILER["ASSEMBLER"]%.exe}" always + fi + fi + exit 0 +else + exit 1 +fi + diff --git a/src_ext/Makefile b/src_ext/Makefile index f616e444832..c3bfa4dc90a 100644 --- a/src_ext/Makefile +++ b/src_ext/Makefile @@ -3,7 +3,7 @@ ifneq ($(filter-out archives cache-archives,$(MAKECMDGOALS)),) endif SRC_EXTS = cppo extlib re cmdliner ocamlgraph cudf dose3 opam-file-format result -ifneq ($(MCCS_DISABLED),true) +ifeq ($(MCCS_ENABLED),true) SRC_EXTS := $(SRC_EXTS) mccs endif @@ -40,6 +40,12 @@ MD5_result = 3d5b66c5526918f0f2ca9d6811ef09c8 URL_jbuilder = https://github.com/janestreet/jbuilder/archive/08050696fb701dafcd1372aadfaa800a50bc01ca.tar.gz MD5_jbuilder = 53b65232ebb5fd319acf90922342615d +URL_ocaml = http://caml.inria.fr/pub/distrib/ocaml-4.06/ocaml-4.06.0.tar.gz +MD5_ocaml = 66e5439eb63dbb8b8224cba5d1b20947 + +URL_flexdll = https://github.com/alainfrisch/flexdll/archive/0.37.tar.gz +MD5_flexdll = cc456a89382e60d84130cddd53977486 + ifndef FETCH ifneq ($(shell command -v curl 2>/dev/null),) FETCH = curl -OLSs @@ -77,14 +83,19 @@ endif jbuilder/_build/install/default/bin/jbuilder$(EXE): $(JBUILDER_CLONE) cd jbuilder && ocaml bootstrap.ml && ./boot.exe -clone: $(JBUILDER_CLONE) $(SRC_EXTS:=.stamp) +.PHONY: ext-ignore +ext-ignore: + @echo "This file is automatically generated" > jbuild-ignore + @echo jbuilder >> jbuild-ignore + +clone: $(JBUILDER_CLONE) $(SRC_EXTS:=.stamp) | ext-ignore @ .PHONY: archives archives: $(SRC_EXTS:=.download) @ -cache-archives: $(SRC_EXTS:=.cache) jbuilder.cache +cache-archives: $(SRC_EXTS:=.cache) jbuilder.cache ocaml.cache flexdll.cache @ %.cache: @@ -132,5 +143,5 @@ clean: distclean: clean rm -rf jbuilder $(SRC_EXTS) mccs - rm -f *.tar.gz *.tbz *.stamp *.download + rm -f *.tar.gz *.tbz *.stamp *.download jbuild-ignore [ -d archives ] && ([ "$$(find archives -maxdepth 0 -type d -empty)" != "" ] && rmdir archives || echo "WARNING! $$(pwd)/archives/ not empty so left") || true diff --git a/tests/jbuild-ignore b/tests/jbuild-ignore new file mode 100644 index 00000000000..f9ced93c2fe --- /dev/null +++ b/tests/jbuild-ignore @@ -0,0 +1 @@ +packages