@echo off

rem file      : build-clang.bat.in
rem license   : MIT; see accompanying LICENSE file

setlocal EnableDelayedExpansion
goto start

:usage
echo.
echo Usage: %0 [/?] [^<options^>] [--] [^<clang++-compiler^>] [^<compile-options^>] [-- ^<link-options^>]
echo Options:
echo   --local                Don't build from packages, only from local source.
echo   --no-bpkg              Don't install bpkg nor bdep ^(requires --local^).
echo   --no-bdep              Don't install bdep.
echo   --no-modules           Don't install standard build system modules.
echo   --modules "<list>"     Install only specified standard build system modules.
echo   --install-dir ^<dir^>    Alternative installation directory.
echo   --exe-prefix ^<pfx^>     Toolchain executables name prefix.
echo   --exe-suffix ^<sfx^>     Toolchain executables name suffix.
echo   --stage-suffix ^<sfx^>   Staged executables name suffix ^('-stage' by default^).
echo   --jobs^|-j ^<num^>        Number of jobs to perform in parallel.
echo   --repo ^<loc^>           Alternative package repository location.
echo   --trust ^<fp^>           Repository certificate fingerprint to trust.
echo   --timeout ^<sec^>        Network operations timeout in seconds.
echo   --make ^<arg^>           Bootstrap using GNU make instead of batch file.
echo   --verbose ^<level^>      Diagnostics verbosity level between 0 and 6.
echo.
echo By default the batch file will use clang++.exe as the C++ compiler and
echo install into C:\build2. It also expects to find the base utilities in the
echo bin\ subdirectory of the installation directory ^(C:\build2\bin\ by
echo default^).
echo.
echo If --jobs^|-j is unspecified, then the bootstrap step is performed
echo serially with the rest of the process using the number of available
echo hardware threads.
echo.
echo The --trust option recognizes two special values: 'yes' ^(trust everything^)
echo and 'no' ^(trust nothing^).
echo.
echo The --make option can be used to bootstrap using GNU make. The first
echo --make value should specify the make executable optionally followed by
echo additional make options, for example:
echo.
echo %0 --make mingw32-make --make -j8
echo.
echo If --jobs^|-j is specified, then its value is passed to make before
echo any additional options.
echo.
echo The script by default installs the following standard build system
echo modules:
echo.
echo %standard_modules%
echo.
echo Use --no-modules to suppress installing build system modules or
echo --modules "<list>" to specify a comma-separated subset to install.
echo.
echo See the BOOTSTRAP-WINDOWS-CLANG file for details.
echo.
goto end

:start

set "owd=%CD%"

rem Package repository URL (or path).
rem
if "_%BUILD2_REPO%_" == "__" (
  set "BUILD2_REPO=https://pkg.cppget.org/1/alpha"
)

rem Package versions.
rem
set "build2_ver=0.16.0"
set "bpkg_ver=0.16.0"
set "bdep_ver=0.16.0"

rem Standard modules comma-separated list and versions.
rem
rem NOTE: we currently print the list as a single line and will need to
rem somehow change that when it becomes too long.
rem
set "standard_modules=autoconf, kconfig"
set "autoconf_ver=0.2.0"
set "kconfig_ver=0.3.0"

rem The bpkg configuration directory.
rem
set "cver=0.16"
set "cdir=build2-toolchain-%cver%"

rem Parse options.
rem
set "local="
set "bpkg_install=true"
set "bdep_install=true"
set "modules=%standard_modules%"
set "idir=C:\build2"
set "exe_prefix="
set "exe_suffix="
set "stage_suffix=-stage"
set "jobs="
set "trust="
set "timeout="
set "make="
set "make_options="
set "verbose="

:options
if "_%~1_" == "_/?_"     goto usage
if "_%~1_" == "_-h_"     goto usage
if "_%~1_" == "_--help_" goto usage

if "_%~1_" == "_--local_" (
  set "local=true"
  shift
  goto options
)

if "_%~1_" == "_--no-bpkg_" (
  set "bpkg_install="
  shift
  goto options
)

if "_%~1_" == "_--no-bdep_" (
  set "bdep_install="
  shift
  goto options
)

if "_%~1_" == "_--no-modules_" (
  set "modules="
  shift
  goto options
)

if "_%~1_" == "_--modules_" (
  set "modules=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--install-dir_" (
  if "_%~2_" == "__" (
    echo error: installation directory expected after --install-dir
    goto error
  )
  set "idir=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--exe-prefix_" (
  if "_%~2_" == "__" (
    echo error: executables name prefix expected after --exe-prefix
    goto error
  )
  set "exe_prefix=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--exe-suffix_" (
  if "_%~2_" == "__" (
    echo error: executables name suffix expected after --exe-suffix
    goto error
  )
  set "exe_suffix=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--stage-suffix_" (
  if "_%~2_" == "__" (
    echo error: staged executables name suffix expected after --stage-suffix
    goto error
  )
  set "stage_suffix=%~2"
  shift
  shift
  goto options
)

set "jo="
if "_%~1_" == "_-j_"     set "jo=true"
if "_%~1_" == "_--jobs_" set "jo=true"

if "_%jo%_" == "_true_" (
  if "_%~2_" == "__" (
    echo error: number of jobs expected after --jobs^|-j
    goto error
  )
  set "jobs=-j %~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--trust_" (
  if "_%~2_" == "__" (
    echo error: certificate fingerprint expected after --trust
    goto error
  )
  set "trust=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--repo_" (
  if "_%~2_" == "__" (
    echo error: repository location expected after --repo
    goto error
  )
  set "BUILD2_REPO=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--timeout_" (
  if "_%~2_" == "__" (
    echo error: value in seconds expected after --timeout
    goto error
  )
  set "timeout=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--make_" (
  if "_%~2_" == "__" (
    echo error: argument expected after --make
    goto error
  )

  if "_%make%_" == "__" (
    set "make=%~2"
  ) else (
    set "make_options=%make_options% %~2"
  )
  shift
  shift
  goto options
)

if "_%~1_" == "_--verbose_" (
  if "_%~2_" == "__" (
    echo error: diagnostics level between 0 and 6 expected after --verbose
    goto error
  )
  set "verbose=%~2"
  shift
  shift
  goto options
)

if "_%~1_" == "_--_" shift

rem Compiler.
rem
if "_%1_" == "__" (
  set "cxx=clang++"
) else (
  set "cxx=%~1"
  shift
)

rem Compile and link options.
rem
set "compile_ops="
set "link_ops="

:compile_options
if not "_%1_" == "__" (
  if not "_%~1_" == "_--_" (
    set "compile_ops=%compile_ops% %~1"
    shift
    goto compile_options
  ) else (
    shift
  )
)

:link_options
if not "_%1_" == "__" (
  set "link_ops=%link_ops% %~1"
  shift
  goto link_options
)

if not "_%compile_ops%_" == "__" (
  set coptions="config.cc.coptions=%compile_ops:~1%"
) else (
  set coptions="config.cc.coptions=-O2"
)

if not "_%link_ops%_" == "__" (
  set loptions="config.cc.loptions=%link_ops:~1%"
) else (
  set "loptions="
)

rem Merge jobs and make_options into make.
rem
if not "_%make%_" == "__" (
  if not "_%jobs%_" == "__" set "make=%make% %jobs%"

  rem Already has leading space.
  rem
  if not "_%make_options%_" == "__" set "make=!make!%make_options%"
)

rem Validate options and arguments.
rem

rem If --no-bpkg is specified, then we require --local to also be specified
rem since it won't be possible to build things from packages without bpkg.
rem Also imply --no-bdep in this case, since bdep is pretty much useless
rem without bpkg.
rem
if "_%bpkg_install%_" == "__" (
  if "_%local%_" == "__" (
    echo error: --no-bpkg can only be used for local installation
    echo   info: additionally specify --local
    goto error
  )

  set "bdep_install="
)

for %%m in (%modules%) do (
  if "_!%%m_ver!_" == "__" (
    echo error: unknown standard build system module '%%m'
    echo   info: available standard modules: %standard_modules%
    goto error
  )
)

rem Convert a relative path to an absolute.
rem
for /F "delims=|" %%D in ("%idir%") do set "idir=%%~dpnxD"

rem Certificate to trust.
rem
if not "_%trust%_" == "__" (
          if "_%trust%_" == "_yes_" (set "trust=--trust-yes"
  ) else (if "_%trust%_" == "_no_"  (set "trust=--trust-no"
  ) else                            (set "trust=--trust %trust%"))
)

rem Network timeout.
rem
if not "_%timeout%_" == "__" (
  set "timeout=--fetch-timeout %timeout%"
)

rem Diagnostics verbosity.
rem
if not "_%verbose%_" == "__" (
  set "verbose=--verbose %verbose%"
)

if not exist %idir%\bin\ (
  echo error: %idir%\bin\ does not exist
  goto error
)

rem Derive the to be installed executables names based on
rem --exe-{prefix,suffix}. Unless the installation is local, also derive the
rem staged executables names based on --stage-suffix and verify that they
rem don't clash with existing filesystem entries as well as the executables
rem being installed.
rem
set "b=%exe_prefix%b%exe_suffix%"
set "bpkg=%exe_prefix%bpkg%exe_suffix%"
set "bdep=%exe_prefix%bdep%exe_suffix%"

if "_%local%_" == "__" (
  set "b_stage=b%stage_suffix%"
  set "bpkg_stage=bpkg%stage_suffix%"

  if exist %idir%\bin\!b_stage!.exe (
    echo error: staged executable name '!b_stage!' clashes with existing %idir%\bin\!b_stage!.exe
    echo   info: specify alternative staged executables name suffix with --stage-suffix
    goto error
  )

  if exist %idir%\bin\!bpkg_stage!.exe (
    echo error: staged executable name '!bpkg_stage!' clashes with existing %idir%\bin\!bpkg_stage!.exe
    echo   info: specify alternative staged executables name suffix with --stage-suffix
    goto error
  )

  if "_%stage_suffix%_" == "_%exe_suffix%_" (
    if "_%exe_prefix%_" == "__" (
      echo error: suffix '%exe_suffix%' is used for both final and staged executables
      echo   info: specify alternative staged executables name suffix with --stage-suffix
      goto error
    )
  )
)

set "conf_exe_affixes="

if not "_%exe_prefix%_" == "__" (
  set "conf_exe_affixes=config.bin.exe.prefix=%exe_prefix%"
)

if not "_%exe_suffix%_" == "__" (
  set "conf_exe_affixes=%conf_exe_affixes% config.bin.exe.suffix=%exe_suffix%"
)

if exist build\config.build (
  echo error: current directory already configured, start with clean source
  goto error
)

if "_%local%_" == "__" (
  if exist ..\%cdir%\ (
    echo error: ..\%cdir%\ bpkg configuration directory already exists, remove it
    goto error
  )
)

set "PATH=%idir%\bin;%PATH%"

rem Show the steps we are performing.
rem
@echo on

@rem Verify the compiler works.
@rem
%cxx% --version
@if errorlevel 1 goto error

@rem Suppress loading of default options files.
@rem
set "BUILD2_DEF_OPT=0"
set "BPKG_DEF_OPT=0"
set "BDEP_DEF_OPT=0"

@rem Bootstrap.
@rem
@rem Note: disable all warnings since we cannot do anything more granular
@rem during bootstrap stage 1.
@rem
cd build2

@if "_%make%_" == "__" (
  goto batchfile
) else (
  goto makefile
)

:batchfile
@rem Execute in a separate cmd.exe to preserve the echo mode.
@rem
cmd /C bootstrap-clang.bat %cxx% -m64 -w
@if errorlevel 1 goto error
@goto endfile

:makefile
%make% -f bootstrap.gmake CXX=%cxx% "CXXFLAGS=-m64 -w"
@if errorlevel 1 goto error
@goto endfile

:endfile
build2\b-boot --version
@if errorlevel 1 goto error

build2\b-boot %verbose% %jobs% "config.cxx=%cxx% -m64" config.bin.lib=static build2\exe{b}
@if errorlevel 1 goto error

move /y build2\b.exe build2\b-boot.exe
@if errorlevel 1 goto error

build2\b-boot --version
@if errorlevel 1 goto error

cd ..

@rem Local installation early return.
@rem
@if "_%local%_" == "__" goto stage

build2\build2\b-boot %verbose% configure^
 config.config.hermetic=true^
 "config.cxx=%cxx% -m64"^
 %coptions%^
 %loptions%^
 config.bin.lib=shared^
 config.install.root=%idir%^
 %conf_exe_affixes%
@if errorlevel 1 goto error

@rem Install toolchain.
@rem
@set "projects=build2\"

@if "_%bpkg_install%_" == "_true_" (
  set "projects=%projects% bpkg\"
)

@if "_%bdep_install%_" == "_true_" (
  set "projects=%projects% bdep\"
)

build2\build2\b-boot %verbose% %jobs% install: %projects%
@if errorlevel 1 goto error

where %b%
@if errorlevel 1 goto error

%b% --version
@if errorlevel 1 goto error

@if "_%bpkg_install%_" == "__" goto bpkg_vle

where %bpkg%
@if errorlevel 1 goto error

%bpkg% --version
@if errorlevel 1 goto error

:bpkg_vle

@if "_%bdep_install%_" == "__" goto bdep_vle

where %bdep%
@if errorlevel 1 goto error

%bdep% --version
@if errorlevel 1 goto error

:bdep_vle

@rem Install modules.
@rem
@set "projects="
@set "tests="

@for %%m in (%modules%) do @(
  set "projects=!projects! libbuild2-%%m\"
  set "tests=!tests! tests\libbuild2-%%m-tests\"
)

@if "_%projects%_" == "__" goto mods_ile

%b% %verbose% %jobs% install: ^^!config.install.scope=project %projects:~1%
@if errorlevel 1 goto error

%b% %verbose% noop: %tests:~1%
@if errorlevel 1 goto error

:mods_ile

@echo off

echo.
echo Toolchain installation: %idir%\bin
echo Build configuration:    %owd%
echo.

goto end

@rem Build and stage the build system and the package manager.
@rem
:stage

build2\build2\b-boot %verbose% configure^
 "config.cxx=%cxx% -m64"^
 config.bin.lib=shared^
 config.bin.suffix=%stage_suffix%^
 config.install.root=%idir%^
 config.install.data_root=root\stage
@if errorlevel 1 goto error

build2\build2\b-boot %verbose% %jobs% install: build2\ bpkg\
@if errorlevel 1 goto error

where %b_stage%
@if errorlevel 1 goto error

%b_stage% --version
@if errorlevel 1 goto error

where %bpkg_stage%
@if errorlevel 1 goto error

%bpkg_stage% --version
@if errorlevel 1 goto error

@rem Build the entire toolchain from packages.
@rem
cd ..

md %cdir%
@if errorlevel 1 goto error

cd %cdir%

@rem Save full path for later.
@rem
@set "cdir=%CD%"

%bpkg_stage% %verbose% create^
 cc^
 config.config.hermetic=true^
 "config.cxx=%cxx% -m64"^
 %coptions%^
 %loptions%^
 config.bin.lib=shared^
 config.install.root=%idir%^
 %conf_exe_affixes%
@if errorlevel 1 goto error

@set "packages=build2/%build2_ver% bpkg/%bpkg_ver%"

@if "_%bdep_install%_" == "_true_" (
  set "packages=%packages% bdep/%bdep_ver%"
)

%bpkg_stage% %verbose% add %BUILD2_REPO%
@if errorlevel 1 goto error

%bpkg_stage% %verbose% %timeout% %trust% fetch
@if errorlevel 1 goto error

%bpkg_stage% %verbose% %jobs% %timeout% build --for install --yes --plan= %packages%
@if errorlevel 1 goto error

%bpkg_stage% %verbose% %jobs% install --all
@if errorlevel 1 goto error

where %b%
@if errorlevel 1 goto error

%b% --version
@if errorlevel 1 goto error

where %bpkg%
@if errorlevel 1 goto error

%bpkg% --version
@if errorlevel 1 goto error

@if "_%bdep_install%_" == "__" goto bdep_vpe

where %bdep%
@if errorlevel 1 goto error

%bdep% --version
@if errorlevel 1 goto error

:bdep_vpe

@rem Build, install, and verify the build system modules.
@rem
@set "packages="
@set "tests="

@for %%m in (%modules%) do @(
  set "packages=!packages! libbuild2-%%m/!%%m_ver!"
  set "tests=!tests! tests\libbuild2-%%m-tests\"
)

@if "_%packages%_" == "__" goto mods_ipe

%bpkg% %verbose% %jobs% %timeout% build --for install %packages:~1%
@if errorlevel 1 goto error

%bpkg% %verbose% %jobs% install ^^!config.install.scope=project --all-pattern=libbuild2-*
@if errorlevel 1 goto error

:mods_ipe

cd %owd%

@if "_%tests%_" == "__" goto mods_lpe

%b% %verbose% noop: %tests:~1%
@if errorlevel 1 goto error

:mods_lpe

@rem Clean up stage.
@rem
%b% %verbose% %jobs% uninstall: build2\ bpkg\
@if errorlevel 1 goto error

@echo off

echo.
echo Toolchain installation: %idir%\bin
echo Build configuration:    %cdir%
echo.

goto end

:error
@echo off
cd %owd%
endlocal
exit /b 1

:end
endlocal
