diff -Nru opam-2.0.10/admin-scripts/compilers-to-packages.ml opam-2.1.2/admin-scripts/compilers-to-packages.ml --- opam-2.0.10/admin-scripts/compilers-to-packages.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/admin-scripts/compilers-to-packages.ml 2021-12-07 16:09:27.000000000 +0000 @@ -5,7 +5,7 @@ (**************************************************************************) (* *) -(* Copyright 2013 OCamlPro *) +(* Copyright 2013-2016 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -94,8 +94,15 @@ in OpamFilename.with_tmp_dir_job @@ fun dir -> try + (* Download to package.patch, rather than allowing the name to be + guessed since, on Windows, some of the characters which are + valid in URLs are not valid in filenames *) + let f = + let base = OpamFilename.Base.of_string "package.patch" in + OpamFilename.create dir base + in OpamProcess.Job.catch err - (OpamDownload.download ~overwrite:false url dir @@| fun f -> + (OpamDownload.download_as ~overwrite:false url f @@| fun () -> Some (url, OpamFilename.digest f, None)) with e -> err e) (OpamFile.Comp.patches comp) diff -Nru opam-2.0.10/admin-scripts/couverture.ml opam-2.1.2/admin-scripts/couverture.ml --- opam-2.0.10/admin-scripts/couverture.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/admin-scripts/couverture.ml 2021-12-07 16:09:27.000000000 +0000 @@ -4,7 +4,7 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -97,7 +97,7 @@ OpamFormatConfig.init (); if not (OpamStateConfig.load_defaults root) then failwith "Opam root not found"; - OpamStd.Config.init (); + OpamCoreConfig.init (); OpamSolverConfig.init (); OpamStateConfig.init (); let t = diff -Nru opam-2.0.10/appveyor_build.cmd opam-2.1.2/appveyor_build.cmd --- opam-2.0.10/appveyor_build.cmd 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/appveyor_build.cmd 2021-12-07 16:09:27.000000000 +0000 @@ -1,161 +1,208 @@ -@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 cmdliner 1.0.4 + #111 in order to build manpages -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\ocaml\lib\stdlib.cmxa ( - echo Deleting out-of-date bootstrap compiler - rd /s/q bootstrap -) -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% %DEP_MODE%" if exist bootstrap\nul ( - echo Required: %OCAML_URL% %FLEXDLL_URL% %DEP_MODE% - echo Compiled: %INSTALLED_URL% - echo Re-building bootstrap compiler - rd /s/q bootstrap - if exist src_ext\archives\nul rd /s/q src_ext\archives -) - -if "%DEP_MODE%" equ "lib-pkg" "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -C src_ext lib-pkg-urls | head -n -1 | sort | uniq" > current-lib-pkg-list -if not exist bootstrap\installed-packages goto SkipCheck - -fc bootstrap\installed-packages current-lib-pkg-list > nul -if %ERRORLEVEL% equ 1 ( - echo lib-pkg packages changed: - "%CYG_ROOT%\bin\diff.exe" bootstrap/installed-packages current-lib-pkg-list | "%CYG_ROOT%\bin\sed.exe" -ne "s//Add/p" | "%CYG_ROOT%\bin\gawk.exe" "BEGIN{FS="" ""}$2!=k{if(k!="""")print o==f?w:o;w=$0;k=$2;f=o=$2"" ""$3;next}{o=""Switch ""o"" --> ""$3}END{print o==f?w:o}" - echo lib-pkg will be re-built - "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -C src_ext reset-lib-pkg" - del bootstrap\installed-packages -) else ( - del current-lib-pkg-list -) - -:SkipCheck - -"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -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 - for /f "delims=" %%U in ('type bootstrap\installed-tarball') do echo %%U %DEP_MODE%> bootstrap\installed-tarball - 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 - del bootstrap\ocaml\lib\ocaml\expunge.exe - for /f %%D in ('dir /b/ad bootstrap\ocaml-*') do ( - rd /s/q bootstrap\%%D - rem Directory needs to exist, as the Cygwin bootstraps OCAMLLIB refers to it - rem and bootstrap-ocaml.sh assumes it will exist even when regenerating the - rem config. - md bootstrap\%%D - ) -) else ( - if not exist bootstrap\installed-packages "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -C src_ext reset-lib-pkg" - if exist current-lib-pkg-list "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && GEN_CONFIG_ONLY=1 shell/bootstrap-ocaml.sh %OCAML_PORT%" || exit /b 1 -) - -if exist current-lib-pkg-list ( - "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make lib-pkg" || exit /b 1 - move current-lib-pkg-list bootstrap\installed-packages -) - -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 -) -set LIB_EXT= -if "%DEP_MODE%" equ "lib-ext" set LIB_EXT=^&^& make lib-ext -set PRIVATE_RUNTIME= -if "%OCAML_PORT:~0,5%" equ "mingw" set PRIVATE_RUNTIME=--with-private-runtime -"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER %LIB_PKG% && ./configure %PRIVATE_RUNTIME% %LIB_EXT% && make opam %POST_COMMAND%" || exit /b 1 -goto :EOF - -:test -if "%OCAML_PORT%" neq "" ( - echo Running the opam command... - opam || exit /b 1 -) -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 +@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_UPGRADE_REQUIRED% equ 1 ( + echo Cygwin package upgrade required - please go and drink coffee + set CYGWIN_UPGRADE_FLAG=--upgrade-also + SET CYGWIN_UPGRADE_REQUIRED=0 +) else ( + set CYGWIN_UPGRADE_FLAG= +) +if "%CYGWIN_INSTALL_PACKAGES%" neq "" set CYGWIN_INSTALL_PACKAGES=--packages %CYGWIN_INSTALL_PACKAGES:~1% +if "%CYGWIN_INSTALL_PACKAGES%%FLAG%" equ "" goto UpgradeCygwin_next +"%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%" %CYGWIN_INSTALL_PACKAGES% %CYGWIN_UPGRADE_FLAG% > nul +set CYGWIN_INSTALL_PACKAGES= +:UpgradeCygwin_next +if "%CYGWIN_UPGRADE_FLAG%" equ "" 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%%CYGWIN_UPGRADE_FLAG%" equ "1" call :UpgradeCygwin +goto :EOF + +:install +echo Build Worker Image: %APPVEYOR_BUILD_WORKER_IMAGE% +systeminfo 2>nul | findstr /B /C:"OS Name" /C:"OS Version" +echo System architecture: %PLATFORM% +set CYG_ROOT=C:\%CYG_ROOT% + +cd "%APPVEYOR_BUILD_FOLDER%" + +:: if "%OCAML_PORT%" equ "" ( +:: rem Need unreleased Cygwin 3.1.7 for bugfix in acl_get_tag_type and acl_get_permset +:: appveyor DownloadFile "https://cygwin.com/snapshots/x86/cygwin1-20200710.dll.xz" -FileName "cygwin1.dll.xz" || exit /b 1 +:: "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER ; unxz cygwin1.dll.xz ; chmod +x cygwin1.dll" +:: move cygwin1.dll %CYG_ROOT%\bin\cygwin1.dll +:: ) + +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 git +set CYGWIN_COMMANDS=cygcheck make patch curl diff tar unzip git + +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 + +rem Check that all packages are installed +for %%P in (%CYGWIN_PACKAGES%) do call :CheckPackage %%P + +rem Check that Cygwin is at least 3.1.7 +for /f "tokens=2,3,4 delims=-. " %%a in ('%CYG_ROOT%\bin\bash.exe -lc "cygcheck -dc cygwin" ^| findstr cygwin') do ( + set CYG_MAJOR=%%a + set CYG_MINOR=%%b + set CYG_REV=%%c +) +set /a CYG_VER=%CYG_MAJOR%*10000+%CYG_MINOR%*100+%CYG_REV% +if %CYG_VER% lss 30107 ( + if "%OCAML_PORT%" equ "" ( + echo Cygwin version %CYG_MAJOR%.%CYG_MINOR%.%CYG_REV% installed; opam requires 3.1.7 or later + set CYGWIN_UPGRADE_REQUIRED=1 + ) +) + +rem Upgrade/install packages as necessary +call :UpgradeCygwin + +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\ocaml\lib\stdlib.cmxa ( + echo Deleting out-of-date bootstrap compiler + rd /s/q bootstrap +) +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% %DEP_MODE%" if exist bootstrap\nul ( + echo Required: %OCAML_URL% %FLEXDLL_URL% %DEP_MODE% + echo Compiled: %INSTALLED_URL% + echo Re-building bootstrap compiler + rd /s/q bootstrap + if exist src_ext\archives\nul rd /s/q src_ext\archives +) + +if "%DEP_MODE%" equ "lib-pkg" "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -C src_ext lib-pkg-urls | head -n -1 | sort | uniq" > current-lib-pkg-list +if not exist bootstrap\installed-packages goto SkipCheck + +fc bootstrap\installed-packages current-lib-pkg-list > nul +if %ERRORLEVEL% equ 1 ( + echo lib-pkg packages changed: + "%CYG_ROOT%\bin\diff.exe" bootstrap/installed-packages current-lib-pkg-list | "%CYG_ROOT%\bin\sed.exe" -ne "s//Add/p" | "%CYG_ROOT%\bin\gawk.exe" "BEGIN{FS="" ""}$2!=k{if(k!="""")print o==f?w:o;w=$0;k=$2;f=o=$2"" ""$3;next}{o=""Switch ""o"" --> ""$3}END{print o==f?w:o}" + echo lib-pkg will be re-built + "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -C src_ext reset-lib-pkg" + del bootstrap\installed-packages +) else ( + del current-lib-pkg-list +) + +:SkipCheck + +"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -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 + for /f "delims=" %%U in ('type bootstrap\installed-tarball') do echo %%U %DEP_MODE%> bootstrap\installed-tarball + 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 + del bootstrap\ocaml\lib\ocaml\expunge.exe + for /f %%D in ('dir /b/ad bootstrap\ocaml-*') do ( + rd /s/q bootstrap\%%D + rem Directory needs to exist, as the Cygwin bootstraps OCAMLLIB refers to it + rem and bootstrap-ocaml.sh assumes it will exist even when regenerating the + rem config. + md bootstrap\%%D + ) +) else ( + if not exist bootstrap\installed-packages "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make --no-print-directory -C src_ext reset-lib-pkg" + if exist current-lib-pkg-list "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && GEN_CONFIG_ONLY=1 shell/bootstrap-ocaml.sh %OCAML_PORT%" || exit /b 1 +) + +if exist current-lib-pkg-list ( + "%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make lib-pkg" || exit /b 1 + move current-lib-pkg-list bootstrap\installed-packages +) + +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 +) +set LIB_EXT= +if "%DEP_MODE%" equ "lib-ext" set LIB_EXT=^&^& make lib-ext +set PRIVATE_RUNTIME= +if "%OCAML_PORT:~0,5%" equ "mingw" set PRIVATE_RUNTIME=--with-private-runtime +set WITH_MCCS=--with-mccs +if "%DEP_MODE%" equ "lib-pkg" set WITH_MCCS= +"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER %LIB_PKG% && ./configure %PRIVATE_RUNTIME% %WITH_MCCS% %LIB_EXT%" || exit /b 1 +"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && echo DUNE_PROFILE=dev >> Makefile.config" || exit /b 1 +"%CYG_ROOT%\bin\bash.exe" -lc "cd $APPVEYOR_BUILD_FOLDER && make opam %POST_COMMAND%" || exit /b 1 +goto :EOF + +:test +rem Configure Git for Windows (for the testsuite, this isn't strictly necessary +rem as Git-for-Windows will pick up $HOME/.gitconfig for Cygwin's git) +git config --global user.email travis@example.com +git config --global user.name Travis +rem Configure Cygwin's Git +"%CYG_ROOT%\bin\bash.exe" -lc "git config --global user.email travis@example.com" +"%CYG_ROOT%\bin\bash.exe" -lc "git config --global user.name Travis" +set OPAMCOLOR=always +set PATH_SHIM= +if "%OCAML_PORT%" neq "" if "%GIT_FOR_WINDOWS%" equ "1" ( + set PATH_SHIM=PATH=/cygdrive/c/Program\ Files/Git/cmd:$PATH + "C:\Program Files\Git\cmd\git.exe" config --global core.autocrlf + "C:\Program Files\Git\cmd\git.exe" config --global core.autocrlf true + "C:\Program Files\Git\cmd\git.exe" config --global core.autocrlf +) +"%CYG_ROOT%\bin\bash.exe" -lc "%PATH_SHIM% make -C $APPVEYOR_BUILD_FOLDER tests" || exit /b 1 +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 -Nru opam-2.0.10/appveyor.patch opam-2.1.2/appveyor.patch --- opam-2.0.10/appveyor.patch 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/appveyor.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ -From c4d7bfa9bee7b62f6121a7993c6be2071d6bde34 Mon Sep 17 00:00:00 2001 -From: David Allsopp -Date: Fri, 19 Jul 2019 13:14:44 +0100 -Subject: [PATCH] AppVeyor src_ext patches - -- Need unreleased cmdliner 1.0.5 to build man pages on Windows. ---- - src_ext/Makefile.sources | 4 ++-- - src_ext/dune-cmdliner-src | 5 ----- - .../0001-Escape-default-values.patch | Bin 0 -> 920 bytes - 3 files changed, 2 insertions(+), 7 deletions(-) - delete mode 100644 src_ext/dune-cmdliner-src - create mode 100644 src_ext/patches/cmdliner.common/0001-Escape-default-values.patch - -diff --git a/src_ext/Makefile.sources b/src_ext/Makefile.sources -index f21ca4b3..e9830a7b 100644 ---- a/src_ext/Makefile.sources -+++ b/src_ext/Makefile.sources -@@ -13,8 +13,8 @@ MD5_re = 765f6f8d3e6ab200866e719ed7e5178d - - $(call PKG_SAME,re) - --URL_cmdliner = http://erratique.ch/software/cmdliner/releases/cmdliner-1.0.2.tbz --MD5_cmdliner = ab2f0130e88e8dcd723ac6154c98a881 -+URL_cmdliner = http://erratique.ch/software/cmdliner/releases/cmdliner-1.0.4.tbz -+MD5_cmdliner = fe2213d0bc63b1e10a2d0aa66d2fc8d9 - - $(call PKG_SAME,cmdliner) - -diff --git a/src_ext/dune-cmdliner-src b/src_ext/dune-cmdliner-src -deleted file mode 100644 -index 0bdeb4e4..00000000 ---- a/src_ext/dune-cmdliner-src -+++ /dev/null -@@ -1,5 +0,0 @@ --(library -- (name cmdliner) -- (public_name cmdliner) -- (libraries result) -- (wrapped false)) -diff --git a/src_ext/patches/cmdliner.common/0001-Escape-default-values.patch b/src_ext/patches/cmdliner.common/0001-Escape-default-values.patch -new file mode 100644 -index 0000000000000000000000000000000000000000..0f351f96db7f93511854788e960909fb578db9e7 -GIT binary patch -literal 920 -zcmb7CU2EJh5Pa9K7!CqC96OS+or`HgLefx5OA9IOLn()>kBb^xIxN{~O1R%%`Eq$^ -z;3zdVW;Hv*&a7^F*P-OBqKiVD(y~}^UI?|2JTF*TEz0t=$XO}NMoWs1&f-?B!A?MF -zy?v2W%E(QWsd3FmBXQZb-mTYoBd0Rs&*ep@0{4N7`%Jj*oLuvuYJ5}j2ziC~gN;J$ -z6l_t`vd$MsD5I3z4$V&`LXGdAFTY&9`+>J!@U?ngc-DH6~rC4t7&2eW4-EwarQ8oPqkF7+Lq~@ -z5NWhVnyyR$Ki(tV>;Z_eQvE{Q1gi?2WvXDDR}?XgswhaDcCRK$k_3BoFD{U>qB=T3 -z^8AYjac1y-wG3|Aal14@b=auB_t^NM@d2A=(BL>DKn&yRB_U(A%f9j!BhvE;o1Tsb -zafc85_hY7AFH{5{Ot^c2pD>%nZurn^%ntkvhpt~LJ7TllVy$`W75JQ_&w5W=^<6)2 -zyt3i!z)!^SH8u3ZmYMvEd8oX=IN!&QbYrK1r(oPVjPb+S&e`AYr`RI8ypQ{e3B5Rd -k9nz=RvQ?Rq+IiVs-e@LTi5msUvz%rudx%=@!Doz+KYk7!^#A|> - -literal 0 -HcmV?d00001 - --- -2.19.1 - diff -Nru opam-2.0.10/appveyor_test.sh opam-2.1.2/appveyor_test.sh --- opam-2.0.10/appveyor_test.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/appveyor_test.sh 2021-12-07 16:09:27.000000000 +0000 @@ -1,5 +1,5 @@ #!/bin/bash -opam init -y -a +opam init -y -a --compiler=ocaml.4.12.0 git+https://github.com/ocaml/opam-repository#$OPAM_TEST_REPO_SHA eval $(opam config env) opam install -y -v ocamlfind diff -Nru opam-2.0.10/appveyor.yml opam-2.1.2/appveyor.yml --- opam-2.0.10/appveyor.yml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/appveyor.yml 2021-12-07 16:09:27.000000000 +0000 @@ -1,7 +1,7 @@ platform: - x64 -image: Visual Studio 2017 +image: Visual Studio 2019 environment: global: @@ -11,27 +11,30 @@ CYG_CACHE: C:/cygwin/var/cache/setup CYG_MIRROR: http://mirrors.kernel.org/sourceware/cygwin/ DEP_MODE: lib-ext + # This should be identical to the value in .travis.yml + OPAM_REPO_SHA: 38e8f54c584fa3cfe779890f7a56fe88ee38be78 + OPAM_TEST_REPO_SHA: 38e8f54c584fa3cfe779890f7a56fe88ee38be78 matrix: - CYG_ROOT: cygwin CYG_ARCH: x86 -# - CYG_ROOT: cygwin64 -# CYG_ARCH: x86_64 -# DEP_MODE: lib-pkg - - OCAML_PORT: msvc + - CYG_ROOT: cygwin64 + CYG_ARCH: x86_64 DEP_MODE: lib-pkg - - OCAML_PORT: msvc64 + - appveyor_build_worker_image: Visual Studio 2017 + OCAML_PORT: msvc + DEP_MODE: lib-pkg + GIT_FOR_WINDOWS: 1 + - appveyor_build_worker_image: Visual Studio 2017 + OCAML_PORT: msvc64 - OCAML_PORT: mingw - OCAML_PORT: mingw64 DEP_MODE: lib-pkg + GIT_FOR_WINDOWS: 1 cache: - C:\projects\opam\bootstrap - C:\projects\opam\src_ext\archives -init: - - systeminfo 2>nul | findstr /B /C:"OS Name" /C:"OS Version" - - "echo System architecture: %PLATFORM%" - install: - call "%APPVEYOR_BUILD_FOLDER%\appveyor_build.cmd" install diff -Nru opam-2.0.10/CHANGES opam-2.1.2/CHANGES --- opam-2.0.10/CHANGES 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/CHANGES 2021-12-07 16:09:27.000000000 +0000 @@ -1,178 +1,760 @@ Changes prefixed with "(*)" are potentially breaking to scripts or existing repositories (changes that are automatically handled by the format upgrade tools -are not marked). +are not marked). Those prefixed with "(+)" are new command/option (since +2.1.0~alpha2). -2.0.10: +2.1.2: +* Fallback on dnf if yum does not exist on RHEL-based systems [#4825 @kit-ty-kate] +* Use --no-depexts in CLI 2.0 mode [#4908 @dra27] +* bootstrap: update ocaml version (fixes the compilation of opam with mingw) [#4927 @kit-ty-kate] + +2.1.1: +* Fix typo in error message for opam var [#4786 @kit-ty-kate - fix #4785] +* Run the sandbox check in the temporary directory [#4787 @dra27 - fix #4783] +* OpamSystem: avoid calling Unix.environment at top level [#4789 @hannesm] +* Homebrew: Add support for casks and full-names [#4801 @kit-ty-kate] +* Fix the cold target in presence of an older OCaml compiler version on macOS + [#4802 @kit-ty-kate - fix #4801] +* Archlinux: handle virtual package detection [#4833 @rjbou - partial fix #4759] +* Disable the detection of available packages on RHEL-based distributions. + This fixes an issue on RHEL-based distributions where yum list used to detect + available and installed packages would wait for user input without showing + any output and/or fail in some cases [#4791 @kit-ty-kate - fixes #4790] +* Handle empty environment variable updates - missed cherry-pick from 2.0 + [#4840 @dra27] +* Fix vendored build on mingw-w64 with g++ 11.2 [#4835 @dra27] +* Put back support for switch creation with packages argument and + `--packages` option with cli 2.0, and a specific error message for cli 2.1 + [#4853 @rjbou - fix #4843] * Fix reverting environment additions to PATH-like variables when several dirs added at once [#4861 @dra27] +* Fix dose3 download url since gforge is gone [#4870 @avsm] * Ensure setenv can use package variables defined during the build [#4841 @dra27] +* Fix `set-invariant: default repos were loaded instead of switch repos + [#4866 @rjbou] -2.0.9: -* Fix the conflict with the environment variable name used by dune - [#4535 @smorimoto - fix ocaml/dune#4166] -* Kill builds on Ctrl-C with bubblewrap [#4530 @kit-ty-kate - fix #4404] -* Linux: mount existing TMPDIR read-only, re-bind `$TMPDIR` to a separate tmpfs - [#4589 @AltGr] -* Fix the sandbox check [#4589 @AltGr] -* Fix sandbox script shell mistake that made `PWD` read-write on remove actions - [#4589 @AltGr] -* Port bwrap improvements to sandbox_exec [#4589 #4609 @AltGr] -* Fix W59 & E60 with conf flag handling (no url required) [#4550 @rjbou - fix #4549] -* Fix temporary file with a too long name causing errors on Windows - [#4590 @AltGr] -* Switch to newer version of MCCS (based on newer GLPK) for src_ext - [#4559 @AltGr] -* Fix version pin source retrieving: don't error if archive opam file is - malformed [#4580 @rjbou] -* Release scripts: switch to OCaml 4.10.2 by default, add macos/arm64 builds by - default [#4559 @AltGr] -* Fix opam-devel's tests on platforms without openssl, GNU-diff and a - system-wide ocaml [#4500 @kit-ty-kate] -* Untag dune as a build dependency [#4229 @kit-ty-kate] -* Fix configure check in github actions [#4593 @rjbou] -* Add missing shell quoting to support space and special shell characters in - switch directory path [#4707 @kit-ty-kate] +2.1.0: +* Set DEBIAN_FRONTEND=noninteractive for unsafe-yes confirmation level + [#4735 @dra27 - partially fix #4731] +* Fix 2.1~alpha2 to 2.1 format upgrade with reinit [#4750 #4756 @rjbou - fix #4748] +* Fix bypass-check handling on reinit [#4750 @rjbou] +* fish: fix deprecated redirection syntax `^` [#4736 @vzaliva] +* Bump src_exts and fix build compat with Dune 2.9.0 [#4754 @dra27] +* Fix depext alpine tagged repositories handling [#4758 @rjbou] + +2.1.0~rc2: +* Remove OPAMZ3DEBUG evironment variable [#4720 @rjbou - fix #4717] +* Fix format upgrade when there is missing local switches in the config file + [#4715 @rjbou - fix #4713] +* Fix not recorded local switch handling, with format upgrade [#4715 @rjbou] +* Set opam root version to 2.1 [#4715 @rjbou] +* Improved and extended tests [#4715 @rjbou] + +2.1.0~rc: +* (*) Environment variables initialised only at opam client launch, no more via + libraries [#4606 #4703 @rjbou] +* (*) Deprecated `build-doc`, `build-test`, `make` flags [#4581 @rjbou] +* (+) Add `--confirm-level` and `OPAMCONFIRMLEVEL` for automatic answering + [#4582 @rjbou - fix #4168; #4683 @dra27 - fix #4682; #4691 @rjbou - fix #4682] +* (+) Add `--no` [#4582 @rjbou] +* (+) Add a `--with-0install-solver` option to the configure script to enable + the 'builtin-0install' solver [#4646 @kit-ty-kate] +* Add default cli mechanism: deprecated options are accepted (in the major + version) if no cli is specified [#4575 @rjbou] +* Add `opam config` deprecated subcommands in the default cli + [#4575 @rjbou - fix #4503] +* Add cli versioning for opam environment variables [#4606 @rjbou] +* Add cli versioning for enums of flags with predefined enums [#4606 @rjbou] +* Clearer messages about using --cli and OPAMCLI [#4655 @dra27] * The options `--root` and `--switch` are now reflected in environment variables when building packages so that calls to `opam` during build access the correct root and switch [#4668 @LasseBlaauwbroek] +* Add cli versioning for enums of flags with predefined enums [#4626 @rjbou] +* Preprocess `--confirm-level` for plugins calls/install [#4694 @rjbou] +* Ensure the symlink for a plugin is maintained on each invocation + [#4621 @dra27 - partially fixes #4619] +* Initialise environment variables for plugins call/install [#4582 @rjbou] +* Expect plugins to end in .exe on Windows [#4709 @dra27] +* Introduce a `default-invariant` config field, restore the 2.0 semantics for + `default-compiler` [#4607 @AltGr] +* Fix default invariant with no system compiler [#4644 @AltGr - fix #4640] +* Perform an hard upgrade on intermediate roots, ie root from `2.1~alpha/beta`, + and keep a light upgrade from `2.0` [#4638 @rjbou] +* Send the 'opam root layout update' message to stderr [#4692 @AltGr] * If opam root is different from the binary, allow reading it and try to read in best effort mode [#4638 @rjbou - fix #4636] -* Differentiate bad format from bad (opam) version with `Bad_version` - exception, raised from `OpamFormat.check_opam_version` [#4638 @rjbou] -* Add `BestEffort` modules with reading functions that don't show errors, given - the `opam_file_format` internal field [#4638 @rjbou - fix -* Require opam-file-format 2.1.3+ in order to enforce opam-version: "2.1" as - first non-comment line [#4639 @dra27 - fix #4394] -* Fix opam switch creation not compatible compiler message - [#4547 @rjbou - fix #4718] -* fish: fix deprecated redirection syntax `^` [#4736 @vzaliva] -* Fix `opam-version' field reading in new roots [#4742 @dra27 @rjbou] - -2.0.8: -* Add colon for fish MANPATH fix. [#4084 @rjbou - fix #4078] -* No error when linked directory doesn't exist (e.g. XDG defined) - [#4278 @kit-ty-kate] -* Add quotes to avoid space unwanted behaviors [#4278 @kit-ty-kate] -* Handle `CCACHE_DIR` environment variable in sandbox script. - [#4087 @rjbou - fix #4079] -* Follow links of `~/.cache` & `~/.cache/dune` for bwrap call. - [#4087 @rjbou - fix #4086] -* Don't overwrite user's sandbow script modification. [#4020 #4092 @rjbou] -* On MacOS sandbox script, always read write mount `/tmp` - [#3742 @rjbou - fix ocaml/opam-repository#13339] -* Use version var in opam file instead of equal current version number in - opamlib dependencies [#4178 @rjbou] -* Opam file build using dune [#4178 @rjbou #4229 @kit-ty-kate - fix #4173] -* Update opam file to 2.0 [#4371 @AltGr] +* Don't check opam system dependencies on reinit after a format upgrade + [#4638 @rjbou] +* Fix `sys-ocaml-cc`, `sys-ocaml-arch` and `sys-ocaml-libc` when no system + compiler installed [#4706 @dra27] +* Fix `Not_found` (config file) in config report [#4570 @rjbou] +* Config report: Print variables of installed compilers and their (installed) + dependencies [#4570 @rjbou] +* Don't patch twice file [#4529 @rjbou] +* With `--deps-only`, set dependencies as root packages + [#4964 @rjbou - fix #4502] +* Keep global lock only if root format upgrade is performed + [#4612 @rjbou - fix #4597] +* Improve installation times by only tracking files listed in `.install` + instead of the whole switch prefix when there are no `install:` instructions + (and no preinstall commands) + [#4494 @kit-ty-kate @rjbou; #4667 @dra27 - fix #4422] +* Scrub OPAM* environment variables added since 2.0 from package builds to + prevent warnings when a package calls opam [#4663 @dra27 - fix #4660] +* Correct the message when more than one depext is missing [#4678 @dra27] +* Only display one conflict message when they are all owing to identical + missing depexts [#4678 @dra27] +* Don't exclude base packages from rebuilds (made some sense in opam 2.0 with + base packages but doesn't make sense with 2.1 switch invariants) [#4569 @dra27] +* Don't refer to base packages in messages any more + [#4623 @dra27 - fixes #4572] +* Give the correct command when demonstrating switch creation + [#4675 @dra27 - fixes #4673] +* On switch loading, if invariant is inferred and a write lock required, write + the file [#4638 @rjbou] +* Don't look for lock files for pin depends [#4511 @rjbou - fix #4505] +* Fetch sources when pinning an already pinned package with a different url + when using working directory [#4542 @rjbou - fix #4484] +* Don't ask for confirmation for pinning base packages (similarly makes no + sense with 2.1 switch invariants) [#4571 @dra27] +* Fix version pin source retrieving: mustn't error if archive opam file is + malformed [#4580 @rjbou] +* `opam list --silent` renamed to `--check` [#4595 @dra27 - fix #4323] +* Include doc field in opam-show [#4567 @dra27 - partially fix #4565] +* Fix `switch` global variable resolving [#4685 @rjbou - fix #4684] +* Fix `hash` package variable resolving [#4687 @rjbou] +* Lint: Fix W59 & E60 for conf packages (no url required) + [#4550 @rjbou - fix #4549] +* Lint: Fix W59 & E60 with VCS urls, don't check upstream if url has VCS + backend [#4635 @rjbou] +* Lint: Add E67 checksum specified with non archive url [#4635 @rjbou] +* Lint: Disable subpath warning E63,W64 [#4638 @rjbou] +* Lint: Fix manpage listing [#4708 @rjbou] +* Don't write lock file with `--read-only', `--safe`, and `--dryrun` + [#4562 @rjbou - fix #4320] +* Make `opam lock` consistent with `opam install`, on local pin always take + last opam file even if uncommitted [#4562 @rjbou - fix #4320] +* Opam file: Fix `features` parser [#4507 @rjbou] +* Opam file: Rename `hidden-version` to `avoid-version` [#4527 @dra27] +* Opam file: Fix rewriting with preserved format empty field error + [#4634 @rjbou - fix #4628] +* Opam file: Switch config: Defined `invariant` field as an option to + differentiate when it is not defined [#4638 @rjbou] +* Opam file: Differentiate bad format from bad (opam) version with + `Bad_version` exception, raised from `OpamFormat.check_opam_version` + [#4638 @rjbou] +* Opam file: Always print the `opam-version` field on files [#4638 @rjbou] +* Opam file: Config: add `opam-root-version` field as a marker for the whole + opam root [#4638 @rjbou - fix #4636] +* Opam file: Add `BestEffort` modules with reading functions that don't show + errors, given the `opam_file_format` internal field [#4638 @rjbou - fix #4636] +* Depext: Handle macport variants [#4509 @rjbou - fix #4297] +* Depext: Always upgrade all the installed packages when installing a new + package on Archlinux [#4556 @kit-ty-kate] +* Depext: Handle some additional environment variables (`OPAMASSUMEDEPEXTS`, + `OPAMNODEPEXTS`) [#4587 @AltGr] +* Depext: Improve messages to hint that answering `no` doesn't abort + installation [#4591 @AltGr] +* Depext: Add support for non-interactive mode in macports [#4676 @kit-ty-kate] +* Depext: Handling of packages of tagged repositories for alpine + [#4700 @rjbou - fix #4670] +* Depext: Clarify some `assume-depexts` related messages + [#4671 @AltGr - partial fix #4662] +* Depext: Warn the user if epel-release is missing and unavailable depexts are + detected [#4679 @dra27 fix #4669] +* Depext: Ignore config yes automatic answering when asking confirmation to run + install commands [#4698 @rjbou - fix #4680] +* Sandbox: Fix the conflict with the environment variable name used by dune + [#4535 @smorimoto - fix ocaml/dune#4166] +* Sandbox: Kill builds on Ctrl-C with bubblewrap + [#4530 @kit-ty-kate - fix #4400] +* Sandbox: Linux: mount existing TMPDIR read-only, re-bind `$TMPDIR` to a + separate tmpfs [#4589 @AltGr] +* Sandbox: Fix the sandbox check [#4589 @AltGr] +* Sandbox: Fix sandbox script shell mistake that made `PWD` read-write on + remove actions [#4589 @AltGr] +* Sandbox: Port bwrap improvements to sandbox_exec [#4589 @AltGr] +* Sandbox: Fix realpath use for macos, partial revert of #4589 [#4609 @AltGr] +* Add missing shell quoting to support space and special shell characters in + switch directory path [#4707 @kit-ty-kate] +* Rename `state.cache` to include the `OpamVersion.magic()` string. All .cache + files are deleted if any cache file is written to, allowing multiple versions + of the library to co-exist without constantly regenerating it + [#4642 @dra27 - fix #4554] +* Fix Cudf preprocessing [#4534 #4627 @AltGr - fix #4624] +* Allow to upgrade to a hidden-version package if a hidden-version package is + already installed [#4525 @kit-ty-kate] +* Add support for a few select criteria useful to CI to the 0install solver: + `+count[version-lag,solution]` to always choose the oldest version available, + `+removed` to not try to keep installed packages [#4631 @kit-ty-kate] +* Fix opam-devel's tests on platforms without openssl, GNU-diff and a + system-wide ocaml [#4500 @kit-ty-kate] +* Use dune to run reftests [#4376 @emillon] +* Restrict `extlib` and `dose` version [#4517 @kit-ty-kate] +* Restrict to `opam-file-format.2.1.2` [#4495 @rjbou] +* Require `opam-file-format.2.1.3+` in order to enforce `opam-version: "2.1"` + as first non-comment line [#4639 @dra27 - fix #4394] +* Switch to newer version of MCCS (based on newer GLPK) for src_ext + [#4559 @AltGr] +* Bump dune version to 2.8.2 [#4592 @AltGr] +* Bump the minimal dune requirement to dune 1.11 [#4437 @dra27 @kit-ty-kate] +* 4.12 compatibility [#4437 @dra27 @kit-ty-kate] +* Cold compiler updated to 4.12 [#4616 @dra27] +* Fix build from source when a dune-project file is presented in the parent + directory [#4545 @kit-ty-kate] +* Fix build from source when a dune-project file is presented in the parent + directory [#4545 @kit-ty-kate - fix #4537] +* Fix opam-devel.install not to install two files called opam [#4664 @dra27] +* Build release tags as non-dev versions, as for release tarballs + [#4665 @dra27 - fix #4656] +* Disable dev version for tests (needed for format upgrade test) [#4638 @rjbou] +* Add a hint for missing `openssl` in `make cold` [#4702 @rjbou] +* Remove test field from opam-devel, they need the network [#4702 @rjbou] +* Update src_ext for Dune and MCCS [#4704 @dra27] +* Release scripts: switch to OCaml 4.10.2 by default, add macos/arm64 builds by + default [#4559 @AltGr] +* Release scripts: add default cli version check on full archive build + [#4575 @rjbou] +* Arg: Generalise `mk_tristate_opt` to `mk_state_opt` [#4575 @rjbou] +* Arg: Fix `mk_state_opt` and rename to `mk_enum_opt` [#4626 @rjbou] +* Arg: Add `mk_enum_opt_all` for state flags that appears more than once + [#4582 @rjbou] +* Fix `opam exec` on native Windows when calling cygwin executables + [#4588 @AltGr] +* Fix temporary file with a too long name causing errors on Windows + [#4590 @AltGr] +* CLI: Add flag deprecation and replacement helper [#4595 @rjbou] +* Win32 Console: fix VT100 support [#3897 #4710 @dra27] +* Tidied the opam files [#4620 @dra27] +* Externalise cli versioning tools from `OpamArg` into `OpamArgTools` + [#4606 @rjbou] +* Each library defines its own environment variables, that fills the config + record [#4606 @rjbou] +* Harden cygpath wrapper [#4625 @dra27] +* Reset the plugin symlinks when the root is upgraded + [#4641 @dra27 - partial fix for #4619] +* Formalise opam dev version detection with `OpamVersion.is_dev_version` + [#4665 @dra27] +* Add `OpamStd.String.is_prefix_of` [#4694 @rjbou @dra27] +* Fix `OpamStd.Format.pretty_list`: `last` argument dropped if list contains + more than 2 elements [#4694 @rjbou] +* Run the shell hooks with closed stdin (bash, zsh) [#4692 @AltGr] +* Improved and extended tests + [#4376 #4504 #4545 #4612 #4668 #4612 #4634 #4672 #4638 #4702 #4697 #4697 + @AltGr @dra27 @emillon @rjbou] +* Improve Github Actions + [#4593 #4575 #4610 #4610 #4618 #4606 #4695 #4695 @AltGr @dra27 @rjbou] +* Improve documentation + [#4496 #4506 #4513 #4637 #4681 #4702 + @dannywillems @eth-arm @kit-ty-kate @rjbou @UnixJunkie] + + +2.1.0~beta4: +* (*) Implemented CLI version compatibility layer [#4385 @rjbou] +* (*) Return code 31 (`Sync_error`) instead of code 40 + (`Package_operation_error`) when all failures happend during fetching + [#4416 @rjbou - fix #4214] +* (+) Add `--download-only` flag [#4071 @Armael @rjbou - fix #4036] +* (+) Provide `opam update --depexts` to request an update of the system package manager databases [#4379 @AltGr - fix #4355] +* Set OPAMCLI=2.0 during package action commands [#4492 @kit-ty-kate] +* Fix sandbox check on first `opam init` [#4370 @rjbou - fix #4368] +* Print shell-appropriate eval command on `opam init` [#4427 @freevoid] +* Fix init script check in csh [#4482 @gahr] +* The stdout of `pre-` and `post-session` hooks is now propagated to the user [#4382 @AltGr - fix #4359] +* `post-install` hooks are now allowed to modify or remove installed files [#4388 @lefessan] +* Add support for switch-specific pre/post sessions hooks [#4476 @rjbou - fix #4472] +* Ensure we don't advertise upgrades to hidden versions [#4477 @AltGr - fix #4432] +* Fix `opam remove --autoremove ` to not autoremove unrelated packages [#4369 @AltGr - fix #4250 #4332] +* Fix cases where `opam remove -a` could trigger conflicts in the presence of orphan packages [#4369 @AltGr - fix #4250 #4332] +* Fix `--update-invariant` when removing or changing package name [#4360 @AltGr - fix #4353] +* Fix updates of the invariant with `--update-invariant` [#4431 @AltGr] +* Fix cleanup of build dirs for version pinned packages [#4436 @rjbou - fix #4255] +* Fix opamfile format upgrade on pinning [#4366 @rjbou - fix #4365] +* Fix `pin --show` actually pinning [#4367 @rjbou - fix #4348] +* When several pins are needed, do their fetching in parallel [#4399 @rjbou - fix #4315] +* Don't cleanup VCS pin source directories [#4399 @rjbou] +* Fix `--working-dir` with local switches [#4433 @rjbou] +* Add package variable `opamfile-loc`, containing the location of installed package opam file [#4402 @rjbou] * Fix `arch` detection when using 32bit mode on ARM64 [#4462 @kit-ty-kate] * Fix `arch` detection of i486 [#4462 @kit-ty-kate] -* The stdout of pre- and post-session hooks is now propagated to the user - [#4382 @AltGr - fix #4359] -* Run switch pre/post sessions hooks [#4476 @rjbou - fix #4472] - +* Skip loading the switch state for variable lookup when possible [#4428 @rjbou] +* Fix package variables display when no config file is found [#4428 @rjbou] +* Fix `opam option depext-bypass-=["XXX"]` [#4428 @rjbou] +* Lint: add a check that strings in filtered package formula are booleans or variables [#443 @rjbou - fix #4439] +* Fix handling of filename-encoded pkgname in opam files [#4401 @AltGr - fix ocaml-opam/opam-publish#107] +* Don't recompile when modifying the package flags [#4477 @AltGr] +* Add depext support for NetBSD and DragonFlyBSD [#4396 @kit-ty-kate] +* Fix depexts on OpenBSD, FreeBSD and Gentoo: Allow short names and full name paths for ports-based systems [#4396 @kit-ty-kate] +* Handle the case where `os-family=ubuntu` as `os-family=debian` [#4441 @alan-j-hu] +* Update opam's opam files to 2.0 [#4371 @AltGr] +* Makefile: Add rule `custom-libinstall` for `opam-custom-install` use [#4401 @AltGr] +* Use the archive caches when running `opam admin cache` [#4384 @AltGr - fix #4352] +* Fix explosion of `opam admin check --cycles` on repositories with huge cliques [#4392 @AltGr] +* Much improved format-preserving printer [#4298 #4302 @rjbou - fix #3993] +* Fix missing conflict message when trying to remove required packages [#4362 @AltGr] +* Fix the Z3 backend for upgrades [#4393 @AltGr] +* Fix cases where opam would wrongly complain about action cycles [#4358 @AltGr - fix #4357] +* Fix permission denied fallback for openssl [#4449 @Blaisorblade - fix #4448] +* Add debug & verbose log for patch & subst applications [#4464 @rjbou - fix #4453] +* Be more robust w.r.t. new caches updates when `--read-only` is not used [#4467 @AltGr - fix #4354] +* Improved and extended tests [#4375 #4395 #4428 #4385 #4467 #4475 #4483 @emillon @rjbou @AltGr @freevoid @dra27] +* Switched to Github actions [#4463 @rjbou] + +2.1.0~beta2: +* Reduced startup times, in particular for `opam exec` [#4341 @altgr] +* Fixed the sandboxing check on fresh inits [#4342 @altgr] +* Fixed cases where `--with-version` was not respected by `opam pin` [#4346 @altgr] +* Upgraded the bootstrap OCaml compiler from 4.09.1 to 4.11.1 [#4242 @avsm @dra27 @MisterDA @rjbou] + +2.1.0~beta: +* (*) `--cli` / `OPAMCLI` option added [#4316 @dra27] +* `--help/--version` documented in wrong section for aliases [#4317 @dra27] +* `opam lock --help` missing common information {#4317 @dra27] +* (+) `--yes` passed to all commands, and plugins [#4316 @dra27] +* On init, check availability of sandbox and propose to disable + [#4284 @rjbou - fix #4089] +* config upgrade: on the fly upgrade if no write lock required [#4313 @rjbou] +* (*) Add `pin scan` subcommand to list available pins [#4285 @rjbou] +* (*) Add `--normalise` option to print a normalised list when scanning, that can + be taken by `opam pin add` [#4285 @rjbou] +* (*) Add `with-version` option to set the pinned package version [#4301 @rjbou] +* Add error message in case git repo is empty [#4303 @rjbou - fix #3905] +* Lock: Support -d as alias of --direct-only (to match plugin) [#4319 @dra27] +* Switch: Support -n as an alias of --no-action (to match opam-pin) + [#4324 @dra27] +* List: form no longer advertised as valid for --columns [#4322 @dra27] +* Admin: form no longer advertised as valid for --columns in list + [#4322 @dra27] +* Solver: Don't penalise packages with more recent 'hidden-versions' + [#4312 @AltGr] +* `OpamCommand.pin` refactor, including adding `OpamClient.PIN.url_pins` to pin + a list of package with url [#4285 #4301 @rjbou] +* `OpamPinCommand.source_pin', for new package confirmation, don't check that + no opam file is given as argument [#4301 @rjbou] +* CLI: Provide all functions in the client library [#4329 @AltGr] +* Process: don't display status line if not verbose, and status line disabled + [#4285 @rjbou] +* Optimise package name comparison [#4328 @AltGr - fix #4245] + +2.1.0~alpha3: +* Confirmation on non-compiler switch invariant: not on dryrun, Y by default + [#4289 @AltGr] +* (*) Fix pin kind automatic detection consistency [#4300 @rjbou]: With `opam + pin target', when opam file is not versioned and at root, vcs-pin the package + instead of path-pin, and with `opam pin add nv target', take opam file even + if not versioned. +* External dependencies: Fix non-interactive mode on OpenSuse [#4293 + @kit-ty-kate] +* src-ext: bump topkg to 1.0.2 and dune to 2.6.2, with a second compiler + built in case main one is < 4.07.0 (dune restriction) [#4294 @dra27] +* Allow Z3 backend to return sub-optimal solutions on timeout, add + `OPAMSOLVERALLOWSUBOPTIMAL` environment variable [#4289 @AltGr] +* Add an optional solver relying on opam-0install-cudf [#4240 @kit-ty-kate] + +2.1.0~alpha2: +* Remove m4 from the list of recommended tools [#4184 @kit-ty-kate] +* Fix config solver field ignored at init [#4243 @rjbou - fix #4241] +* Fix atoms formula restriction with `--all` at upgrade [#4221 @rjbou - fix + #4218] +* Copy instead of calling rsync when archives are in a local cache [#4270 + @kit-ty-kate] +* Opam file build using dune, removal of opam-%.install makefile target + [#4178 @rjbou #4229 @kit-ty-kate - fix #4173] +* Use version var in opam file instead of equal current version number in + opamlib dependencies [#4178 @rjbou] +* src ext: fix extlib url [#4248 @rjbou] +* Add `_build` to rsync exclusion list [#4230 @rjbou - fix #4195] +* Recursive opam file lookup: ignore `_build` [#4230 @rjbou] +* Assume-built fix & rewriting [#4211 @rjbou] +* Fix autoremove env var handling [#4219 @rjbou - fix #4217] +* Fix Not_found with `opam switch create . --deps` [#4151 @AltGr] +* Package Var: resolve self `name` variable for orphan packages [#4228 @rjbou + - fix #4224] +* (*) Reject (shell) character on switch names [#4237 @rjbou - fix #4231] +* Fix `OPAMSWITCH` empty string setting, consider as unset [#4237 @rjbou] +* opam-installer: For paths, remove use of empty switch in favor of a + context-less module [#4237 @rjbou] +* Add missing depext to unavailable reasons [#4194 @rjbou #4279 @rjbou - fix + #4176] +* (*) Bump config file version to 2.1 (new depext fields) [#4280 @rjbou - fix + #4266] +* Add depext handling on new pinned packages [#4194 @rjbou - fix #4189] +* Don't keep unpinned package version if it exists in repo [#4073 @rjbou - + fix #3630] +* Fix path resolving when pinning with `file://` [#4209 @rjbou - fix #4208] +* (*) Disable recursive & subpath pinning (only present experimentally in opam + 2.1.0~alpha) [#4252 @rjbou] +* Add switch depext-bypass as modifiable field [#4194 @rjbou - fix #4177] +* Add `--no-depexts` option to disable depexts packages unavailability [#4194 + @rjbou - fix #4205] +* Warn if packages are not listed because of depexts unavailability [#4194 + @rjbou - fix #4205] +* (*) Display error message for all not found packages [#4179 @rjbou - fix + #4164] +* (*) Keep package order given via cli [#4179 @rjbou - fix #4163] +* `--sort`` apply to with all options, not only `--just-file` [#4179 @rjbou] +* Add scope display to Not found message [#4192 @rjbou] +* No scope needed for variable display [#4192 @rjbou - fix #4183] +* Fix package variable resolution [#4192 @rjbou - fix #4182] +* opam option: Fix messages advertising a command in an obsolete format + [#4194 @rjbou] +* E65: check that url local paths are absolute [#4209 @rjbou] +* Fix arch query depext [#4200 @rjbou] +* Add message when adding a package to `depext-bypass` [#4194 @rjbou] +* Fix performance issue of depext under Docker/debian [#4165 @AltGr] +* Handle debian virtual packages [#4269 @AltGr @rjbou - fix #4251] +* Refactor `OpamSysInteract` package status [#4152 #4200 @rjbou] +* Add environment variables handling on depext query [#4200 @rjbou] +* Add depext Macport support [#4152 @rjbou] +* Homebrew/depext: add no auto update env var for install, accept `pkgname` + and `pkgname@version` on query [#4200 @rjbou] +* Tag packages with missing depexts in Cudf [#4235 @AltGr] +* Force LC_ALL=C for depext query commands [#4200 @rjbou] +* Put back opam-depext-2.0's behaviour with regards to asking users' consent + before installing system packages [#4168 @kit-ty-kate @rjbou] +* Add OPAMDEPEXTYES env variable to pass --yes options to system package + manager [#4168 @kit-ty-kate @rjbou] +* Fix system install command dryrun [#4200 @rjbou] +* (+) Add --depext-only to install only external dependencies, regardless of + config depext status [#4238 @rjbou] +* Move system install confirmation message after opam packages install [#4238 + @rjbou] +* Error if '--depext-only' is given with '--assume-depexts' or '--no-depexts' + [#4238 @rjbou] +* Sanddbox: no error when linked directory doesn't exist (e.g. XDG defined) + [#4278 @kit-ty-kate] +* Sandbox: add quotes to avoid space unwanted behaviors [#4278 @kit-ty-kate] +* Fix temp files repository cleaning [#4197 @rjbou] +* Fix admin cache synchronisation message [#4193 @rjbou - fix #4167] +* Fix mismatching extra files detection [#4198 @rjbou] +* Fix Cudf generation for compat with external solvers [#4261 @AltGr] +* Check for a solution before calling the solver [#4263 @AltGr] +* Add the package flag 'hidden-version' to discourage selection by the solver + [#4281 @AltGr] +* Tweak the default criteria to handle 'missing-depexts' and 'hidden-version' + flags [#4281 @AltGr] +* Disable chrono when timestamps are disables [#4206 @rjbou] +* Expose some functionality in the `OpamAction`, `OpamPath` and + `OpamSwitchState` modules for use without a `switch` value (introduce a + functor to permit replicating switch layout in different contexts) [#4147 + @timberston] +* Std: Add map_reduce to Set and Map [#4263 @AltGr] +* Fix regression in command resolution from #4072 (ocaml code for looking up + commands in PATH) [#4265 @dra27] +* Use OCaml 4.09.1 for the make cold target [#4257 @dra27] +* Add show cram test [#4206 @rjbou] +* Add envrionnement variable handling on cram test [#4206 @rjbou] + + +2.1.0~alpha: +* Recursive & subpath based pin [#3499 @rjbou @hngrgr - fix #3174 #3477] +* Define switch invariants rather than "base packages" [#3894 @AltGr] +* Don't warn on switch creation with 'ocaml' as invariant [#4108 @AltGr] +* Better error handling on switch creation [#4121 @AltGr] +* Integrate lock plugin [#3746 @rjbou - fix #3734 #3769 #3694] +* Add configuration modifications as opam config subcommands [#3992 @rjbou] +* opam var and opam option outside of opam config [#4116 @rjbou - fix #4119] +* Enable option var optimisation switch load [#4138 @rjbou] +* Integrate depext plugin [#3975 @rjbou @AltGr - fix #3790 #1519 #2426 #3692] +* Enable command/output display only from verbose level 3 [#4141 @rjbou] +* Add `opam install --check ` checks that `` dependencies are already + installed in the switch. It reports missing ones and exits with 1, 0 otherwise. + It is used on a check only purpose, additionally to `--deps-only`. + [#3854 @rjbou - fix #3823] +* Add `opam install --ignore-conflicts` to use with `--deps-only` in case + it is needed to install dependencies without taking conflicts into account. + [#3853 @rjbou - fix #3846] +* Add `opam show --just-file ` shows information of a given file, + without loading the switch state. It can be combined with other options, as + field filter `--field`. It deprecates `--file` option. [#3729 @rjbou - fix + #3721] +* Add `opam show --all-versions ` displays information of all versions of + the given package. It can be used in combination of field filter. [#3867 @rjbou + - fix #2980] +* Add `opam show --sort ` display on stdout a sorted opam file: all fields + are alphabetically sorted. [#3866 @rjbou - fix ocaml/opam2web#173] +* opam show better error handling. [#4118 @rjbou - fix #3875] +* `opam show --field` are no longer required to end with a colon. + [#3931 @rjbou] +* (*) `opam show --normalise` disable terminal width wrapping. [#3868 @rjbou - + fix #3751] +* `opam env --check` permit to indicates if an opam environment is + synchronized: returns 0 if up-to-date, 1 otherwise. [#4074 @rjbou - fix #3725] +* Add `opam switch export --freeze` to record VCS commit hash when a VCS url is + specified. [#4055 @hannesm] +* Add `opam switch export --full` option, include extra-files in switch export, + on import create an overlay directory with the file contents. [#4040 @hannesm] +* Optimize repository loading: we store the repository contents as .tar.gz + files in ~/.opam/repo instead. [#3752 @AltGr - fix #3721] +* Handle failure or interruption of tar during `opam update`. [#3861 @AltGr] +* Fallback in case repository archive doesn't exist. [#4008 @rjbou] +* In case repository archive is corrupted, delete it and ask to launch an + update. [#4075 @rjbou - fix #4053] +* When adding a repository, an error is displayed in case of mismatching urls, + now both urls are displayed. [#4086 @rjbou - fix #4085] +* Handle url backend change to VCS of a package from repository. [#4007 @rjbou + - fix #3991] +* Allow local compiler switch creation. [#3720 @rjbou - fix #3713] +* Switch creation, fix multiple compiler candidate. [#3884 @rjbou - fix #3874] +* Make reinstall handling stricter. [#3907 @AltGr] +* (*) `opam list --resolve`: restrain test dependencies to direct one instead of + listing all test dependencies of queried package(s) [#3923 @rjbou - fix + ocaml/opam-depext#121] +* Update pin-depends confirmation message to add a skip option. [#3852 @rjbou - + fix #3840] +* Add OPAMDROPWORKINGDIR environment variable for C. [#3792 @rjbou - fix #3727] +* Don't restrain copy to versioned file. [#3759 @rjbou] +* Don't fetch sources when working-dir is set. [#4046 @rjbou] +* Update in place source copy: [#3948 @rjbou] + - review `sync_dirty` on VCS: + - use VCS to synchronize, then rsync & remove others files + - exclude `_build`, `_opam` & VCS directories + - when `--inplace-build` is given, it does a dirty synchronization of the + sources, in order to keep tracking package stats (clean, local or dirty). +* Fix `working-dir` messages on update command. [#3824 @rjbou] +* Working-dir fixes. [#3982 @rjbou] +* Update download errors handling during actions processing. [#3811 @AltGr] +* Update `ftp` command, to pass url last. [#3910 @hannesm] +* Terminate (with double dashes) list of command-line download option. + [#3913 @cfcs] +* Repository: remove 'file://' prefix for darcs. [#3761 @rjbou] +* Opam{Git,Hg}: Fix diffs in presence of binary file. [#3879 @kit-ty-kate] +* Set core.autocrlf and core.eol for Git remotes. [#3882 @dra27] +* Add a git clean on `reset_tree` to keep source dir clean. [#3948 @rjbou] +* Lint W62: Add a lint check for SPDX license. [#3976 @AltGr] +* Lint: add result in json output. [#3848 @rjbou - fix #3046] +* Add lint codes in manpage. [#3903 @rjbou] +* Default configuration file: add `getconf` to required tools. [#3813 @rjbou] +* Clarify message in `opam init --yes`. [#3892 @dra27] +* Shell setup: don't advice an infinite sourcing loop. [#3832 @rjbou] +* Default configuration file: Add compilation target globals, `sys-ocaml-arch, + `sys-ocaml-cc`, and `sys-ocaml-libc`. [#3900 @dra27] +* Ensure that environment is initialized lazily, not before init functions are + called [#4111 @gasche] +* Fix OPAMLOGS handling, and logdir `opam_init` argument [#4117 @rjbou - fix + #4076] +* Include base packages configuration variables in opam config report. + [#3798 @dra27] +* Determine jobs number at launch (`OpamStateConfig`) [#4004 @rjbou - fix + #3986] +* Fully test native Windows in the testsuite. [#3260 @dra27] +* Allow native Windows to use Cygwin tool. [#3348 @dra27] +* Deal with Windows path conventions (backslashes, .exe, etc.) [#3350 @dra27] +* Correct display of dir separator on Windows. [#3893 @dra27] +* Tested wrong variable in OPAMW_HasGlyp. [#3898 @dra27] +* Default use `fetch` on FreeBSD, `ftp` on OpenBSD. [#3904 @hannesm] +* Don't overwrite user's sandbow script modification. [#4020 #4092 @rjbou] +* Handle `CCACHE_DIR` environment variable in sandbox script. [#4087 @rjbou - + fix #4079] +* Follow links of `~/.cache` & `~/.cache/dune` for bwrap call. [#4087 @rjbou - + fix #4086] +* On MacOS sandbox script, always read write mount `/tmp` [#3742 @rjbou - fix + ocaml/opam-repository#13339] +* Environment file right handling for empty switch. [#3899 @dra27] +* Add colon for fish MANPATH fix. [#4084 @rjbou - fix #4078] +* Update zsh check interactive terminal [#4095 @OCamlPro-mattiasdrp #4128 + @AltGr] +* Add package selection to `opam admin add-hashes` [#3787 @rjbou - fix #3767] +* Download files (patches, etc.) using a safe filename. [#3900 @dra27] +* `opam admin --add-constraints`, add constraint on depopts. [#4002 @rjbou - + fix #3994] +* Add `format-version` field to all opam files. [#3478 @AltGr] [#3906 @AltGr] +* Clarify pin depend parse error. [#3762 @rjbou] +* Opam file extensions (`x-` fields) enhancement. [#4049 @hannesm] +* Add support for Z3 as a solver backend. [#3845 @AltGr] +* Interleave download actions with build/install actions. [#3777 @Armael] + [#4083 @rjbou - fix #4080] +* Generalization of the job scheduler: provide separate job pools for different + subsets of the tasks. [#3778 @AltGr] +* Refactor the return types of `OpamSolution.{apply,resolve_and_apply}` + [#3781 @Armael] +* Use the scheduler pools to respect the download-jobs parameter. + [#3791 @AltGr] +* Set the right opam file `format-version` field on upgrade. [#4014 @rjbou] +* Streamline the output from download action. [#3794 @AltGr] +* Use a character that displays better on terminals for download action. [ + #3802 @AltGr] +* Change symbol for download action. [#3862 @AltGr] +* Include the version number in "compilation failed" message. [#4052 @Armael] +* Propagate `--force` remove option to directory tracking revert function. + [#4094 @rjbou - fix #4091] +* Add `OpamDirTack.string_of_changes` [#4107 @rjbou @hannesm] +* Introduce state `drop` function to replace `ignore (unlock ..)` for more + lock-type-safety. [#3783 @gasche - #3812 @rjbou] +* Change `OpamStateTypes.switch_state.conf_files `from package_map` to + `name_map` [#3799 @dra27] +* Fix handling of availability outside of switches. [#3864 @AltGr] +* Sorting formulas function. [#3945 @rjbou] +* Sort formula: fix `compare_formula` & add `compare` [#3960 @rjbou] +* Patch rewrite test. [#3456 @dra27] +* Command errors display: differentiate command not found & permission denied. + [#3865 @rjbou] +* Factorize option functions in `OpamProcess` [#4016 @nobrakal] +* Use ocaml code for looking up commands in `PATH` [#4072 @Armael] +* Copy files using OCaml code instead of calling to cp or install + [#4064 @Armael] +* Sort & clean pkg:depend. [#4060 @rjbou - fix #4057] +* Add `of_json` functions & crowbar. [#3776 @gasche] +* JSON (de)serialization for OpamParallel graph. [#3786 @gasche] +* Url: catch failure & specific exception. [#3946 @rjbou] +* Update: don't update installed dev package that is not pinned. [#3947 @rjbou] +* Use `OpamArg` helpers for option. [#4059 @rjbou] +* Steps towards sudo-enabled make install. [#3522 @dra27] +* Port build system to Dune (1.2) [#3618 @dra27] +* Update shell/msvs-detect to 0.4.0. [#3869 @dra27] +* Sort out repository script mode. [#3963 @dra27] +* Preliminary support for Dune 2.0. [#3965 @dra27] +* Update mccs.1.1+11 [#4109 #4146 @MisterDA] +* Fix developer mode option. [#3646 @rjbou] +* Ensure configure generates consistently. [#3935 @dra27] +* Documentation [#3542 @0xflotus] [#3571 @hannesm] [#3780 @gasche] + [#3944 @tchajed] [#3955 @nbraud] [#4106 @vp2177] [#3863 @dra27] [#3554 @rjbou - fix + #3540 #2255c #3612 #3606c] [#4058 @rjbou] [#4114 @rjbou @AltGr] 2.0.7: -* Properly escape Windows paths on manpages [#4129 @AltGr @rjbou] -* Fix opam installer opam file [#4058 @rjbou] -* Fix various warnings [#4132 @rjbou @AltGr - fix 4100] -* Fix dune 2.5.0 promote-install-files duplication [#4132 @rjbou ] -* opam exec: display command not found message [#4133 @rjbou - fix #4131] +* opam exec: display command not found message. [#4133 @rjbou - fix #4131] +* Escape Windows paths on manpages. [#4129 @AltGr @rjbou - fix #4100] +* Fix opam installer opam file. [#4058 @rjbou] +* Fix various warnings. [#4132 @rjbou @AltGr - fix #4100] 2.0.6: -* Don't remove git cache objects that may be used [#3831 @AltGr] -* Don't include .gitattributes in index.tar.gz [#3873 @dra27] -* Update FAQ uri [#3941 @dra27] -* Lock: add warning in case of missing locked file [#3939 @rjbou] -* Directory tracking: fix cached entries retrieving with precise - tracking [#4038 @hannesm] -* Build: - * Add sanity checks [#3934 @dra27] - * Build man pages using dune [#3902] #dra27] - * Add patch and bunzip check for make cold [#4006 @rjbou - fix #3842] -* Shell: - * fish: add colon for fish manpath [#3886 @rjbou - fix #3878] -* Sandbox: - * Add dune cache as rw [#4019 @rjbou - fix #4012] - * Do not fail if $HOME/.ccache is missing [#3957 @mseri] -* opam-devel file: avoid copying extraneous files in opam-devel example [#3999 @maroneze] +* Do not fail if `$HOME/.ccache` is missing. [#3957 @mseri - fix + https://discuss.ocaml.org/t/dune-1-11-1-compilation-failed/4248] +* Add dune cache as rw. [#4019 @rjbou - fix #4012] +* Check both size and mtime for dirtrack cached entries. [#4038 @hannesm] +* Build man pages with dune. [#3937 @AltGr @dra27] +* make cold: fail if patch or bunzip2 missing. [#4006 @rjbou - fix #3842] +* Documentation [#3999 @maroneze] 2.0.5: -* Bump src_ext Dune to 1.6.3, allows compilation with OCaml 4.08.0. [#3887 @dra27] -* Support Dune 1.7.0 and later [#3888 @dra27 - fix #3870] -* Bump the ocaml_mccs lib-ext, to include latest changes [#3896 @AltGr] -* Fix cppo detection in configure [#3917 @dra27] -* Read jobs variable from OpamStateConfig [#3916 @dra27] -* Linting: - * add check upstream option [#3758 @rjbou] - * add warning for with-test in run-test field [#3765, #3860 @rjbou] - * fix misleading `doc` filter warning [#3871 @rjbou] -* Fix typos [#3891 @dra27, @mehdid] +* `opam lint --check-upstream` enables lint checks on archive urls. This + option lead to download archives to check their checksum. [#3758 @rjbou] +* Lint W59: No url checksum given (if `check-upstream` enabled). + [#3758 @rjbou] +* Lint E60: Unavailable archive or checksum mismatching (if `check-upstream` + enabled). [#3758 @rjbou] +* Lint E61: Out-of-scope `with-test` variable in `run-test:` field. + [#3763 @rjbou - fix AltGr/Camelus/issues/27] +* Lint W58: Restrain warning to filters. [#3871 @rjbou - fix + ocaml/opam-repository#14280 (comment)] +* Lint E61: Restrain to `run-test:`. [#3860 @rjbou] +* Read jobs variable from `OpamStateConfig` [#3881 @dra27] +* Fix cppo detection. [#3915 @rjbou] +* Documentation [#3809#3891 @dra27] 2.0.4: -* Add (rudimentary) error handling to `make man` -* Sandboxing on MacOS: considering the possibility that TMPDIR in unset -* display: Fix opam var display -* pin: - * update source of (version) pinned directory - * fix `--ignore-pin-depends` with autopin - * fix pinnings not installing/upgrading already pinned packages (introduced in 2.0.2) -* opam clean: Ignore errors trying to remove directories -* remove wrong mismatching extra-files warning -* urls: fix hg opam 1.2 url parsing -* lint: warning error 47 on descr first line -* dirtrack: fix precise tracking mode -* system: - * lock & signals: don't interrupt at non terminal signals - * shell: fix fish manpath setting - * git: use git -c diff.noprefix=false diff in case it is defined in user config +* Remove mismatching extra-files: sort list before comparing them. + [#3744 @rjbou] +* Update source of (version) pinned directory. [#3726 @rjbou - fix #3651] +* Fix `--ignore-pin-depends` with autopin. [#3736 @AltGr] +* Fix pin not installing/upgrading already pinned package. [#3800 @AltGr] +* Fix hg opam1.2 url parsing. [#3754 @rjbou] +* Use `git -c diff.noprefix=false diff`. [#3788/#3628 @Blaisorblade - fix + #3627] +* Lint W47: Update warning message. [#3753 @rjbou - fix #3738] +* Harmonization of `opam config list` and `opam config var `: resolve + variable first with switch state (loading it only for package defined + variables), otherwise, global state. [#3723 @rjbou - fix #3717] +* Considering the possibility that TMPDIR is unset. [#3597 @herbelin - fix + #3576] +* Unconditionally display MANPATH when fish version is 2.7 or late. + [#3728 @gregory-nisbet] +* Fix precise tracking mode: missing `to_hex` conversion. [#3796 @rjbou] +* Catch signal to select ones that are really cancelling a blocking state (e.g. + waiting for a lock to be released). [#3541 @rjbou] +* `opam clean`: ignore errors trying to remove directories. + [#3732 @kit-ty-kate] +* Documentation [#3731 @MisterDA] 2.0.3: -* Fix manpage remaining $ (OPAMBESTEFFORT) -* Fix OPAMROOTISOK handling -* Regenerate missing environment file +* On init, retrieve `root is ok` from global options instead of initialization. + [#3704 @rjbou - fix #3695] +* Regenerate missing environment file. [#3691 @rjbou - fix #3690 #3594] +* Documentation [#3703 @rjbou - fix #3700] 2.0.2: -* Update build from jbuilder to dune -* Doc: - * update man page - * add message for deprecated options - * reinsert removed ones - * deprecate `no-aspcud` -* Pin: - * upgrade pin depends on pinning - * include descr & url files on pinning 1.2 opam files -* Sandbox: - * handle symlinks in bwrap - * allow use of internal sockets on - * change one-line conditional to if statement which was incompatible with set -e - * make /var readonly instead of empty and rw -* Path: resolve default opam root path -* System: suffix .out for read_command_output stdout files (#3644) -* Locked: check consistency with opam file when reading lock file to suggest regeneration message -* Show: remove pin depends messages -* Cudf: Fix closure computation in the presence of cycles -* List: Fix some cases of listing coinstallable packages -* Format upgrade: extract archived source files of version-pinned packages -* Core: add is_archive in OpamSystem and OpamFilename -* Init: don't fail if empty compiler given -* Lint: fix light_uninstall flag for error 52 -* Update cold compiler to 4.07.1 +* Check consistency with opam file when reading lock file to suggest + regeneration message [#3680 @rjbou - fix #3654] +* Remove pin depends messages. [#3679 @rjbou] +* Upgrade pin depends on pinning. [#3684 @rjbou - fix #3508] +* To avoid lint warning 57 (description error) on 1.2 opam file pinning, add + auxiliary files (descr, url) before linting. [#3687 @rjbou] +* Don't check hash with --no-checksum on pull_upstream. [#3658 @rjbou - fix + #3652] +* Lint E52: Fix `light_uninstall` flag. [#3631 @rjbou - fix #3625] +* On init, don't fail if empty compiler given. [#3633 @rjbou - fix #3614] +* Sandbox: make `/var` read-only instead of empty and rw. [#3605 @bobot - fix + #3604] +* Handle symlinks in bwrap sandbox. [#3661 @mroch - fix #3660] +* Sandbox: Change one-line conditional to `if` statement which was incompatible + with `set -e`. [#3645 @rjbou - fix #3607] +* Release use of unix sockets on MacOS. [#3663 @silene - fix #3659] +* Fix closure computation in the presence of cycle. [#3670 @AltGr - fix #3662 + #3666] +* Fix some cases of listing coinstallable package. [#3689 @AltGr] +* Extract archived source files of version-pinned packages. [#3610 @rjbou - fix + #3600] +* Add function to upgrade opam file, including its auxiliary files: descr, url, + files/. [#3624 @rjbou] +* Set `.out` suffix for `read_command_output` stdout file. [#3644 @rjbou] + {2.0.2} +* Default opam root is resolved at creation, in order to have the correct + linked path. [#3681 @rjbou - fix #3622] +* Reinsert and deprecate `alias-of` & `no-autoinstall` option. [#3685 @rjbou - + fix #3390] +* Updates for OCaml 4.07. [#3474 @dra27] +* Documentation [#3656 @rjbou - fix #3634 #3653 #3639] [#3685 @rjbou - fix + #3390] 2.0.1: -* Cold boot for MacOS/CentOS/Alpine -* Install checksum validation on MacOS -* Archive extraction for OpenBSD now defaults to using gtar -* Fix compilation of mccs on MacOS and Nix platforms -* Do not use GNU-sed specific features in the release Makefile, to fix build on OpenBSD/FreeBSD -* Cleaning to enable reproducible builds -* Update configure scripts -* git: fix git fetch by sha1 for git < 2.14 -* linting: add test variable warning and empty description error -* upgrade: convert pinned but not installed opam files -* error reporting: more comprehensible error message for tar extraction, and upgrade of git-url compilers -* opam show: upgrade given local files -* list: as opam 2.0.0 list doesn't return non-zero code if list is empty, add --silent option for a silent output and returns 1 if list is empty +* Add `opam list --silent` to not write in the output, exit with return + code 0 if the list is not empty, 1 otherwise. [#3533 @rjbou - fix #3525] +* Fix `opam list --external` [#3558 @rjbou - fix #3557 + ocaml/opam-repository#12677] +* Show command with local opam file returns local file information. Fixes also + the non 1.2 conversion to 2.0 format of local files with `opam show --raw`. + [#3536 @rjbou - fix #3423] +* Show command display string fields printed with quotes, as lists. + [#3368 @rjbou - fix #3365] +* Pin edit: fix editing an opam file without a name field. [#3535 @rjbou] +* Don't execute validation hook if update is empty. [#3490 @hannesm] +* Git: fallback, fetch all repository remotes to get SHA1 with git < 2.1. + [#3561 @rjbou - fix #3523 #3548] +* Lint E57: A description or a synopsis must be present and non empty. + [#3581 @rjbou - fix ocaml/opam-repository#12729] +* Lint W58: Advise to use `with-test` and `with-doc` variables if `test` + and `doc` are present. [#3591 @rjbou - fix #3580 ocaml/opam-repository#12729] +* Add `gtar` as OpenBSD required tool, as tar does not support the `J` flag. + [#3538 @adamsteen] +* Hash: fallback to internal library in case of openssl error. [#3543 @rjbou - + fix ocaml/opam-repository#12613] +* Respect user's TMPDIR when invoking bwrap sandbox. [#3487 @3noch] +* Add a way to mount unusual path in bwrap sandbox: introduction of + `OPAM_USER_PATH_RO` environment variable. [#3540 @ErwanGa] +* opam admin: handle non http backend on repository upgrade: compute hash only + for http or distant rsync backend [#3596 @rjbou - fix #3590] +* Upgrade to opam 2.0 format overlay opam files of pinned package. + [#3528 @rjbou - fix #3513] +* Add compiler file translation to opam 2.0 format function. [#3530 @rjbou - + fix ocaml/opam-repository#12523] +* Tar extract fail error message: if a tar extract fails, it checks the + presence of underlying commands (bzip2, xz, lzma, gzip) to display the error + message in verbose mode. [#3502 @rjbou - fix #3497] +* Remove link files only if it exists. [#3519 @rjbou - fix + ocaml/opam-depext#104] +* Remove GNUism from bootstrap-ocaml.sh [#3481 @dra27 - fix #3480] +* Avoid sed -i, a GNU sed extension, use mv instead [#3603 @hannesm] +* Add patch & bunzip2 check in configure. [#3531 @rjbou - fix #3520] +* Not having wget or curl is now only a hard-error if src_ext/archives doesn't + contain the archives (i.e. if make -C src_ext cache-archives has not been run) + which means that this should no longer be a requirement for building with the + "full" tarball. [#3572 @dra27 - fix #3551] +* C++ test for MCCS is now moved to the correct place and the message as to + whether the solver will be build should work correctly with --disable-checks. + Note that not having a C++ compiler is not considered an error if + --disable-checks is specified because configure is permitted to believe that + the MCCS library exists even if it couldn't detect it. [#3572 @dra27] 2.0.0 * Fixes and documentation diff -Nru opam-2.0.10/configure opam-2.1.2/configure --- opam-2.0.10/configure 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/configure 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.69 for opam 2.0.10. +# Generated by GNU Autoconf 2.69 for opam 2.1.2. # # # Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. @@ -9,7 +9,7 @@ # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. # -# Copyright 2012-2017 OcamlPro SAS +# Copyright 2012-2019 OcamlPro SAS ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## @@ -197,7 +197,8 @@ as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && - test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else @@ -578,18 +579,55 @@ # Identity of this package. PACKAGE_NAME='opam' PACKAGE_TARNAME='opam' -PACKAGE_VERSION='2.0.10' -PACKAGE_STRING='opam 2.0.10' +PACKAGE_VERSION='2.1.2' +PACKAGE_STRING='opam 2.1.2' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_default_prefix=`echo "os_type: ${OCAML_OS_TYPE}" | sed -e "s;^os_type: Win32;C:/OPAM;" -e "s;^os_type:.*;/usr/local;"` +# Factoring default headers for most tests. +ac_includes_default="\ +#include +#ifdef HAVE_SYS_TYPES_H +# include +#endif +#ifdef HAVE_SYS_STAT_H +# include +#endif +#ifdef STDC_HEADERS +# include +# include +#else +# ifdef HAVE_STDLIB_H +# include +# endif +#endif +#ifdef HAVE_STRING_H +# if !defined STDC_HEADERS && defined HAVE_MEMORY_H +# include +# endif +# include +#endif +#ifdef HAVE_STRINGS_H +# include +#endif +#ifdef HAVE_INTTYPES_H +# include +#endif +#ifdef HAVE_STDINT_H +# include +#endif +#ifdef HAVE_UNISTD_H +# include +#endif" + ac_subst_vars='LTLIBOBJS LIBOBJS hasalldeps ac_ct_CXX CXXFLAGS CXX +OCAML_PKG_opam_0install_cudf OCAML_PKG_mccs OCAML_PKG_opam_file_format OCAML_PKG_dose3_algo @@ -605,9 +643,9 @@ CONF_MANIFEST_O RUNTIME_GCC_S MANIFEST_ARCH -TOOL_ARCH fetch LN_S +DUNE_SECONDARY BUNZIP2 PATCH CPPO @@ -615,13 +653,17 @@ FETCH OCAMLFIND OCAMLOBJINFO -CC64_JBUILD INC_PREPEND LIB_PREPEND PATH_PREPEND CC64 CONF_CFLAGS CONF_OCAMLFLAGS +CONF_LIBACL_LINK +EGREP +GREP +CPP +OPAM_0INSTALL_SOLVER_ENABLED MCCS_ENABLED DEVELOPER OBJEXT @@ -700,6 +742,8 @@ enable_checks enable_developer_mode with_mccs +with_0install_solver +with_libacl with_private_runtime enable_cold_check enable_certificate_check @@ -712,6 +756,7 @@ LDFLAGS LIBS CPPFLAGS +CPP CXX CXXFLAGS CCC' @@ -1265,7 +1310,7 @@ # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures opam 2.0.10 to adapt to many kinds of systems. +\`configure' configures opam 2.1.2 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1327,7 +1372,7 @@ if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of opam 2.0.10:";; + short | recursive ) echo "Configuration of opam 2.1.2:";; esac cat <<\_ACEOF @@ -1351,6 +1396,11 @@ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --without-mccs Compile without a built-in Cudf solver (only works if 'mccs' is not otherwise installed) + --with-0install-solver Compile with the built-in 0install solver + (--without-0install-solver only works if + 'opam-0install-cudf' is not otherwise installed) + + --with-libacl Compile opam with libacl support --with-private-runtime For a mingw-w64 build, manifest the runtime DLLs locally in Opam.Runtime.arch @@ -1362,6 +1412,7 @@ LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory + CPP C preprocessor CXX C++ compiler command CXXFLAGS C++ compiler flags @@ -1431,14 +1482,14 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF -opam configure 2.0.10 +opam configure 2.1.2 generated by GNU Autoconf 2.69 Copyright (C) 2012 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. -Copyright 2012-2017 OcamlPro SAS +Copyright 2012-2019 OcamlPro SAS _ACEOF exit fi @@ -1485,6 +1536,249 @@ } # ac_fn_c_try_compile +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp + +# ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists, giving a warning if it cannot be compiled using +# the include files in INCLUDES and setting the cache variable VAR +# accordingly. +ac_fn_c_check_header_mongrel () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if eval \${$3+:} false; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +else + # Is the header compilable? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 +$as_echo_n "checking $2 usability... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_header_compiler=yes +else + ac_header_compiler=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 +$as_echo "$ac_header_compiler" >&6; } + +# Is the header present? +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 +$as_echo_n "checking $2 presence... " >&6; } +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <$2> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + ac_header_preproc=yes +else + ac_header_preproc=no +fi +rm -f conftest.err conftest.i conftest.$ac_ext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 +$as_echo "$ac_header_preproc" >&6; } + +# So? What about this header? +case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( + yes:no: ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 +$as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; + no:yes:* ) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 +$as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 +$as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 +$as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 +$as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 +$as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} + ;; +esac + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + eval "$3=\$ac_header_compiler" +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_mongrel + +# ac_fn_c_try_run LINENO +# ---------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. Assumes +# that executables *can* be run. +ac_fn_c_try_run () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then : + ac_retval=0 +else + $as_echo "$as_me: program exited with status $ac_status" >&5 + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=$ac_status +fi + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_run + +# ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES +# ------------------------------------------------------- +# Tests whether HEADER exists and can be compiled using the include files in +# INCLUDES, setting the cache variable VAR accordingly. +ac_fn_c_check_header_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +$as_echo_n "checking for $2... " >&6; } +if eval \${$3+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$4 +#include <$2> +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + eval "$3=yes" +else + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +eval ac_res=\$$3 + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +$as_echo "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_header_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + # ac_fn_cxx_try_compile LINENO # ---------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. @@ -1526,7 +1820,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by opam $as_me 2.0.10, which was +It was created by opam $as_me 2.1.2, which was generated by GNU Autoconf 2.69. Invocation command line was $ $0 $@ @@ -1890,6 +2184,9 @@ fi +# XXX This isn't strictly correct for Windows +MIN_OCAML_VERSION=4.02.3 + # 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. @@ -3099,7 +3396,7 @@ if test "x$OCAMLC" = "xno"; then - as_fn_error $? "You must install the OCaml compiler" "$LINENO" 5 + as_fn_error $? "You must install the OCaml compiler, at least version $MIN_OCAML_VERSION (or run: make compiler)" "$LINENO" 5 fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking OCaml Sys.os_type" >&5 $as_echo_n "checking OCaml Sys.os_type... " >&6; } @@ -3161,6 +3458,22 @@ fi +# Check whether --with-0install-solver was given. +if test "${with_0install_solver+set}" = set; then : + withval=$with_0install_solver; +fi + + + + +# Check whether --with-libacl was given. +if test "${with_libacl+set}" = set; then : + withval=$with_libacl; +else + with_libacl=auto +fi + + # Check whether --with-private_runtime was given. if test "${with_private_runtime+set}" = set; then : @@ -3239,8 +3552,6 @@ if test ${WIN32} -eq 1 ; then : MIN_OCAML_VERSION=4.06.0 -else - MIN_OCAML_VERSION=4.02.3 fi @@ -3328,7 +3639,7 @@ 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=$?` + eval `PATH="$PRE_BOOTSTRAP_PATH" bash ./shell/msvs-detect --arch=$SDK_ARCH; echo RESULT=$?` if test "x$MSVS_NAME" = "x" ; then : if test ${RESULT} -eq 0 ; then : @@ -3958,11 +4269,18 @@ if test "x${enable_developer_mode}" = "xyes"; then : + DEVELOPER=true + echo "-D" > src/core/developer + echo "DEVELOPER" >> src/core/developer + else + DEVELOPER=false + rm -f src/core/developer + fi if test "x${with_mccs}" = "xno"; then : @@ -3972,87 +4290,590 @@ MCCS_ENABLED=true fi +if test "x${with_0install_solver}" = "xyes"; then : + OPAM_0INSTALL_SOLVER_ENABLED=true -CONF_OCAMLFLAGS="-w -67" - -if test "x${CI}" != "x"; then : - - CONF_OCAMLFLAGS="${CONF_OCAMLFLAGS} -w -67" +else + OPAM_0INSTALL_SOLVER_ENABLED=false fi -if test "x${CI}" != "x" -o "x${enable_developer_mode}" = "xyes"; then : - - if test "x${CCOMP_TYPE}" = "xmsvc"; then : - CONF_CFLAGS="\"/WX\"" +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +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 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : - CONF_CFLAGS="-Werror" - +else + # Broken: fails on valid input. +continue fi +rm -f conftest.err conftest.i conftest.$ac_ext + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext - CONF_CFLAGS= - +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break fi + done + ac_cv_prog_CPP=$CPP +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer to if __STDC__ is defined, since + # exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include +#else +# include +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : -if test ${WIN32} -eq 1 -a "$GCC" = "yes"; then : +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext - # Pre-Visual Studio 2013 Microsoft C compiler adhere to the C89 standard, well - # at least the bit of it which requires variable declarations to appear before - # statements. Adding this warning to GCC prevents accidentally using C99 and - # then getting unexpected C2143 errors from older Microsoft C compilers. I'm - # not aware of an equivalent option for the Microsoft C compiler. - CC="$CC -Wdeclaration-after-statement" + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } fi -if test ${WIN32} -eq 1 ; then : - - if test "$GCC" = "yes"; then : +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +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 "x${CC64}" = "x" ; then : - if test "$ARCH" = "i386" ; then : - T_CC64=x86_64-w64-mingw32-gcc -else - T_CC64=i686-w64-mingw32-gcc -fi -fi - if test -n "$ac_tool_prefix"; then - # Extract the first word of "${ac_tool_prefix}${T_CC64}", so it can be a program name with args. -set dummy ${ac_tool_prefix}${T_CC64}; ac_word=$2 -{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 -$as_echo_n "checking for $ac_word... " >&6; } -if ${ac_cv_prog_CC64+:} false; then : +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else - if test -n "$CC64"; then - ac_cv_prog_CC64="$CC64" # Let the user override the test. -else -as_save_IFS=$IFS; IFS=$PATH_SEPARATOR -for as_dir in $PATH + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do - if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then - ac_cv_prog_CC64="${ac_tool_prefix}${T_CC64}" - $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 - break 2 - fi -done + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break done -IFS=$as_save_IFS + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac -fi -fi -CC64=$ac_cv_prog_CC64 + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 +$as_echo_n "checking for ANSI C header files... " >&6; } +if ${ac_cv_header_stdc+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#include +#include + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_header_stdc=yes +else + ac_cv_header_stdc=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + +if test $ac_cv_header_stdc = yes; then + # SunOS 4.x string.h does not declare mem*, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "memchr" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "free" >/dev/null 2>&1; then : + +else + ac_cv_header_stdc=no +fi +rm -f conftest* + +fi + +if test $ac_cv_header_stdc = yes; then + # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. + if test "$cross_compiling" = yes; then : + : +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +#include +#if ((' ' & 0x0FF) == 0x020) +# define ISLOWER(c) ('a' <= (c) && (c) <= 'z') +# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) +#else +# define ISLOWER(c) \ + (('a' <= (c) && (c) <= 'i') \ + || ('j' <= (c) && (c) <= 'r') \ + || ('s' <= (c) && (c) <= 'z')) +# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) +#endif + +#define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) +int +main () +{ + int i; + for (i = 0; i < 256; i++) + if (XOR (islower (i), ISLOWER (i)) + || toupper (i) != TOUPPER (i)) + return 2; + return 0; +} +_ACEOF +if ac_fn_c_try_run "$LINENO"; then : + +else + ac_cv_header_stdc=no +fi +rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ + conftest.$ac_objext conftest.beam conftest.$ac_ext +fi + +fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 +$as_echo "$ac_cv_header_stdc" >&6; } +if test $ac_cv_header_stdc = yes; then + +$as_echo "#define STDC_HEADERS 1" >>confdefs.h + +fi + +# On IRIX 5.3, sys/types and inttypes.h are conflicting. +for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ + inttypes.h stdint.h unistd.h +do : + as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` +ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default +" +if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : + cat >>confdefs.h <<_ACEOF +#define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 +_ACEOF + +fi + +done + + +if test "x${with_libacl}" != "xno"; then : + + have_libacl=yes + ac_fn_c_check_header_mongrel "$LINENO" "acl/libacl.h" "ac_cv_header_acl_libacl_h" "$ac_includes_default" +if test "x$ac_cv_header_acl_libacl_h" = xyes; then : + +else + have_libacl=no +fi + + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for library containing acl_get_perm" >&5 +$as_echo_n "checking for library containing acl_get_perm... " >&6; } +if ${ac_cv_search_acl_get_perm+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_func_search_save_LIBS=$LIBS +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char acl_get_perm (); +int +main () +{ +return acl_get_perm (); + ; + return 0; +} +_ACEOF +for ac_lib in '' acl; do + if test -z "$ac_lib"; then + ac_res="none required" + else + ac_res=-l$ac_lib + LIBS="-l$ac_lib $ac_func_search_save_LIBS" + fi + if ac_fn_c_try_link "$LINENO"; then : + ac_cv_search_acl_get_perm=$ac_res +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext + if ${ac_cv_search_acl_get_perm+:} false; then : + break +fi +done +if ${ac_cv_search_acl_get_perm+:} false; then : + +else + ac_cv_search_acl_get_perm=no +fi +rm conftest.$ac_ext +LIBS=$ac_func_search_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_search_acl_get_perm" >&5 +$as_echo "$ac_cv_search_acl_get_perm" >&6; } +ac_res=$ac_cv_search_acl_get_perm +if test "$ac_res" != no; then : + test "$ac_res" = "none required" || LIBS="$ac_res $LIBS" + +else + have_libacl=no +fi + + if test "x${SYSTEM}" = "xcygwin"; then : + + if test "x${have_libacl}" = "xno"; then : + as_fn_error $? "opam on Cygwin requires libacl" "$LINENO" 5 +else + with_libacl=yes +fi + +fi + if test "x${with_libacl}${have_libacl}" = "xyesno"; then : + as_fn_error $? "libacl not found" "$LINENO" 5 +fi + +fi +if test "x${with_libacl}" = "xyes"; then : + + if test "x${ac_cv_search_acl_get_perm}" = "xnone required"; then : + + + +else + + CONF_LIBACL_LINK=${ac_cv_search_acl_get_perm} + + +fi + +fi + +CONF_OCAMLFLAGS="-w -67" + +if test "x${CI}" != "x"; then : + + CONF_OCAMLFLAGS="${CONF_OCAMLFLAGS} -w -67" + +fi +if test "x${CI}" != "x" -o "x${enable_developer_mode}" = "xyes"; then : + + if test "x${CCOMP_TYPE}" = "xmsvc"; then : + + CONF_CFLAGS="\"/WX\"" + +else + + CONF_CFLAGS="-Werror" + +fi + +else + + CONF_CFLAGS= + +fi + + + +if test ${WIN32} -eq 1 -a "$GCC" = "yes"; then : + + # Pre-Visual Studio 2013 Microsoft C compiler adhere to the C89 standard, well + # at least the bit of it which requires variable declarations to appear before + # statements. Adding this warning to GCC prevents accidentally using C99 and + # then getting unexpected C2143 errors from older Microsoft C compilers. I'm + # not aware of an equivalent option for the Microsoft C compiler. + CC="$CC -Wdeclaration-after-statement" + + +fi + +if test ${WIN32} -eq 1 ; then : + + if test "$GCC" = "yes"; then : + + if test "x${CC64}" = "x" ; then : + + if test "$ARCH" = "i386" ; then : + T_CC64=x86_64-w64-mingw32-gcc +else + T_CC64=i686-w64-mingw32-gcc +fi +fi + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}${T_CC64}", so it can be a program name with args. +set dummy ${ac_tool_prefix}${T_CC64}; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC64+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC64"; then + ac_cv_prog_CC64="$CC64" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC64="${ac_tool_prefix}${T_CC64}" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC64=$ac_cv_prog_CC64 if test -n "$CC64"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC64" >&5 $as_echo "$CC64" >&6; } @@ -4118,7 +4939,8 @@ if test "x${CC64}" != "xno" ; then : - CC64_JBUILD="(run ${CC64} -o \"%{targets}\" -I ../core -Wdeclaration-after-statement %{source})" + echo "${CC64} -o " > src/stubs/win32/cc64 + echo " -Wdeclaration-after-statement " >> src/stubs/win32/cc64 fi @@ -4148,7 +4970,7 @@ fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a way to invoke an $COMP_ARCH C compiler" >&5 $as_echo_n "checking for a way to invoke an $COMP_ARCH C compiler... " >&6; } - eval `PATH="$PRE_BOOTSTRAP_PATH" ./shell/msvs-detect --arch=$COMP_ARCH` + eval `PATH="$PRE_BOOTSTRAP_PATH" bash ./shell/msvs-detect --arch=$COMP_ARCH` if test "x$MSVS_NAME" = "x" ; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 @@ -4161,8 +4983,14 @@ $as_echo "from $MSVS_NAME" >&6; } CL_FULL="`PATH="${MSVS_PATH}:${PATH}" which cl | cygpath -f - -w`" MSVS_PATH="`echo "${MSVS_PATH}" | cygpath -f - -wp`" - CC64_JBUILD="(setenv PATH \"${MSVS_PATH}\" (setenv LIB \"${MSVS_LIB};${LIB}\" (setenv INCLUDE \"${MSVS_INC};${INCLUDE}\" (run \"${CL_FULL}\" /nologo \"/Fe%{targets}\" /I../core %{source}))))" - CC64_JBUILD="$(echo $CC64_JBUILD|sed -e 's/\\/\\\\/g')" + echo "cl /nologo /Fe" > src/stubs/win32/cc64 + echo " " >> src/stubs/win32/cc64 + echo "PATH" >> src/stubs/win32/cc64 + echo "${MSVS_PATH}" >> src/stubs/win32/cc64 + echo "LIB" >> src/stubs/win32/cc64 + echo "${MSVS_LIB}" >> src/stubs/win32/cc64 + echo "INCLUDE" >> src/stubs/win32/cc64 + echo "${MSVS_INC}" >> src/stubs/win32/cc64 fi @@ -4171,23 +4999,11 @@ { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: Complementary C compiler not found - opam-putenv will not be built" >&5 $as_echo "$as_me: WARNING: Complementary C compiler not found - opam-putenv will not be built" >&2;} - BUILD_PUTENV=0 - CC64_JBUILD= - -else - - BUILD_PUTENV=1 fi -else - - BUILD_PUTENV=0 - CC64_JBUILD= - fi - if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ocamlobjinfo", so it can be a program name with args. set dummy ${ac_tool_prefix}ocamlobjinfo; ac_word=$2 @@ -4823,6 +5639,44 @@ fi + + + + # Used to indicate true or false condition + ax_compare_version=false + + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + + ax_compare_version_A=`echo "$OCAMLVERSION" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ + -e 's/Z\([0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/[^0-9]//g'` + + + ax_compare_version_B=`echo "4.08.0" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ + -e 's/Z\([0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/[^0-9]//g'` + + + ax_compare_version=`echo "x$ax_compare_version_A +x$ax_compare_version_B" | sed 's/^ *//' | sort -r | sed "s/x${ax_compare_version_A}/false/;s/x${ax_compare_version_B}/true/;1q"` + + + + if test "$ax_compare_version" = "true" ; then + DUNE_SECONDARY=src_ext/secondary/ocaml/bin/ocaml + else DUNE_SECONDARY= + fi + + + if test "x${COLD_CHECK}" = "xyes" ; then if test "x$PATCH" = "x" ; then as_fn_error $? "You must have patch installed." "$LINENO" 5 @@ -4868,26 +5722,18 @@ if test ${WIN32} -eq 1 -a "x${CCOMP_TYPE}" = "xcc"; then : - CONF_MANIFEST_O="\"opam-manifest.o\"" + CONF_MANIFEST_O=opam-manifest.o if test "$ARCH" = "i386" ; then : - TOOL_ARCH=i686 MANIFEST_ARCH=x86 RUNTIME_GCC_S=libgcc_s_sjlj-1 else - TOOL_ARCH=x86_64 MANIFEST_ARCH=amd64 RUNTIME_GCC_S=libgcc_s_seh-1 fi - RUNTIME=`${TOOL_ARCH}-w64-mingw32-gcc -print-sysroot`/mingw/bin - echo Linking ${SYSTEM} runtime DLLs - ${LN_S} -fv ${RUNTIME}/${RUNTIME_GCC_S}.dll src/client/${RUNTIME_GCC_S}.dll - ${LN_S} -fv ${RUNTIME}/libstdc++-6.dll src/client/libstdc++-6.dll - ${LN_S} -fv ${RUNTIME}/libwinpthread-1.dll src/client/libwinpthread-1.dll - @@ -5195,9 +6041,41 @@ + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OCaml findlib package opam-0install-cudf" >&5 +$as_echo_n "checking for OCaml findlib package opam-0install-cudf... " >&6; } + + unset found + unset pkg + found=no + for pkg in opam-0install-cudf ; do + if $OCAMLFIND query $pkg >/dev/null 2>/dev/null; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: found" >&5 +$as_echo "found" >&6; } + OCAML_PKG_opam_0install_cudf=$pkg + found=yes + break + fi + done + if test "$found" = "no" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: not found" >&5 +$as_echo "not found" >&6; } + OCAML_PKG_opam_0install_cudf=no + fi + + + + if test "x${with_mccs}" = "xno" && test "x$OCAML_PKG_mccs" != "xno"; then : as_fn_error $? "Option --without-mccs is not available without uninstalling the 'mccs' package" "$LINENO" 5 fi +if test "x${with_0install_solver}" = "xno" && test "x$OCAML_PKG_opam_0install_cudf" != "xno"; then : + as_fn_error $? "Option --without-0install-solver is not available without uninstalling the 'opam-0install-cudf' package" "$LINENO" 5 +fi + +if test "x$OCAML_PKG_opam_0install_cudf" != "xno"; then : + OPAM_0INSTALL_SOLVER_ENABLED=true + +fi if test "x$MCCS_ENABLED" = "xtrue"; then : @@ -5486,6 +6364,48 @@ fi +if test "x$OPAM_0INSTALL_SOLVER_ENABLED" = "xtrue" -a "x$OCAML_PKG_opam_0install_cudf" = "xno"; then : + + + + + # Used to indicate true or false condition + ax_compare_version=false + + # Convert the two version strings to be compared into a format that + # allows a simple string comparison. The end result is that a version + # string of the form 1.12.5-r617 will be converted to the form + # 0001001200050617. In other words, each number is zero padded to four + # digits, and non digits are removed. + + ax_compare_version_A=`echo "$OCAMLVERSION" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ + -e 's/Z\([0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/[^0-9]//g'` + + + ax_compare_version_B=`echo "4.08" | sed -e 's/\([0-9]*\)/Z\1Z/g' \ + -e 's/Z\([0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/Z\([0-9][0-9][0-9]\)Z/Z0\1Z/g' \ + -e 's/[^0-9]//g'` + + + ax_compare_version=`echo "x$ax_compare_version_A +x$ax_compare_version_B" | sed 's/^ *//' | sort -r | sed "s/x${ax_compare_version_A}/false/;s/x${ax_compare_version_B}/true/;1q"` + + + + if test "$ax_compare_version" = "true" ; then + + as_fn_error $? "Your version of OCaml: $OCAMLVERSION does not support the requested 0install-solver. \ + You can either re-run the configure script with --without-0install-solver or use make cold" "$LINENO" 5 + + fi + + +fi echo @@ -5498,7 +6418,8 @@ test "x$OCAML_PKG_dose3_common" = "xno" || test "x$OCAML_PKG_opam_file_format" = "xno" || test "x$CPPO" = "x" || - test "x$OCAML_PKG_mccs$MCCS_ENABLED" = "xnotrue";}; then : + test "x$OCAML_PKG_mccs$MCCS_ENABLED" = "xnotrue" || + test "x$OCAML_PKG_opam_0install_cudf$OPAM_0INSTALL_SOLVER_ENABLED" = "xnotrue";}; then : echo "============================================================================" echo "Some dependencies are missing. If you are just interested in the stand-alone" @@ -5519,27 +6440,7 @@ prefix=$ac_default_prefix fi -if test ${BUILD_PUTENV} -eq 1; then : - - ac_config_files="$ac_config_files src/tools/opam-putenv.inc" - - -else - - > src/tools/opam-putenv.inc - -fi -if test -n "${CONF_MANIFEST_O}" ; then : - - ac_config_files="$ac_config_files src/client/manifest.inc" - - -else - - > src/client/manifest.inc - -fi -ac_config_files="$ac_config_files Makefile.config src/client/opamManifest.inc" +ac_config_files="$ac_config_files Makefile.config src/ocaml-flags-configure.sexp src/stubs/c-flags.sexp src/stubs/libacl/c-libraries.sexp" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -6083,7 +6984,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" -This file was extended by opam $as_me 2.0.10, which was +This file was extended by opam $as_me 2.1.2, which was generated by GNU Autoconf 2.69. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -6136,7 +7037,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ -opam config.status 2.0.10 +opam config.status 2.1.2 configured by $0, generated by GNU Autoconf 2.69, with options \\"\$ac_cs_config\\" @@ -6247,10 +7148,10 @@ for ac_config_target in $ac_config_targets do case $ac_config_target in - "src/tools/opam-putenv.inc") CONFIG_FILES="$CONFIG_FILES src/tools/opam-putenv.inc" ;; - "src/client/manifest.inc") CONFIG_FILES="$CONFIG_FILES src/client/manifest.inc" ;; "Makefile.config") CONFIG_FILES="$CONFIG_FILES Makefile.config" ;; - "src/client/opamManifest.inc") CONFIG_FILES="$CONFIG_FILES src/client/opamManifest.inc" ;; + "src/ocaml-flags-configure.sexp") CONFIG_FILES="$CONFIG_FILES src/ocaml-flags-configure.sexp" ;; + "src/stubs/c-flags.sexp") CONFIG_FILES="$CONFIG_FILES src/stubs/c-flags.sexp" ;; + "src/stubs/libacl/c-libraries.sexp") CONFIG_FILES="$CONFIG_FILES src/stubs/libacl/c-libraries.sexp" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac @@ -6701,13 +7602,39 @@ fi +rm -f src/manifest/dune src/manifest/install.inc +if test "x${with_private_runtime}" != "xno"; then : + + if test ${WIN32} -eq 1 -a "x${CCOMP_TYPE}" = "xcc"; then : + + cd src/manifest + ${LN_S} -f dune-manifest dune + ${LN_S} -f install.${ARCH} install.inc + cd ../.. -rm -f src/stubs/dune +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: --with-private-runtime ignored (not building on mingw)" >&5 +$as_echo "$as_me: WARNING: --with-private-runtime ignored (not building on mingw)" >&2;} + +fi + +fi + +rm -f src/stubs/win32/dune if test ${WIN32} -eq 1; then : - cd src/stubs + cd src/stubs/win32 ${LN_S} -f dune-win32 dune - cd ../.. + cd ../../.. + +fi +rm -f src/stubs/libacl/dune +if test "x${with_libacl}" = "xyes" ; then : + + cd src/stubs/libacl + ${LN_S} -f dune-libacl dune + cd ../../.. fi @@ -6718,7 +7645,7 @@ mandir="`eval echo ${mandir}`" mandir="`eval echo ${mandir}`" -if test "x$MCCS_ENABLED" = "xfalse"; then : +if test "x$MCCS_ENABLED" = "xfalse" -a "x$OPAM_0INSTALL_SOLVER_ENABLED" = "xfalse"; then : echo "Opam will be built WITHOUT a built-in solver" diff -Nru opam-2.0.10/configure.ac opam-2.1.2/configure.ac --- opam-2.0.10/configure.ac 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/configure.ac 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ dnl The line below must be formatted AC_INIT(opam,VERSION) with no extra spaces -AC_INIT(opam,2.0.10) -AC_COPYRIGHT(Copyright 2012-2017 OcamlPro SAS) +AC_INIT(opam,2.1.2) +AC_COPYRIGHT(Copyright 2012-2019 OcamlPro SAS) AC_CONFIG_MACRO_DIR([m4]) @@ -14,9 +14,12 @@ export PATH="$PATH_PREPEND$PATH" ]) +# XXX This isn't strictly correct for Windows +MIN_OCAML_VERSION=4.02.3 + AC_PROG_OCAML if test "x$OCAMLC" = "xno"; then - AC_MSG_ERROR([You must install the OCaml compiler]) + AC_MSG_ERROR([You must install the OCaml compiler, at least version $MIN_OCAML_VERSION (or run: make compiler)]) fi AC_CHECK_OCAML_OS_TYPE AS_IF([ test "${OCAML_OS_TYPE}" = "Win32"],[ @@ -54,6 +57,15 @@ AC_HELP_STRING([--without-mccs], [Compile without a built-in Cudf solver (only works if 'mccs' is not otherwise installed)]),[],[MCCS_DEFAULT=yes] ) +AC_ARG_WITH([0install-solver], + AC_HELP_STRING([--with-0install-solver], + [Compile with the built-in 0install solver (--without-0install-solver only works if 'opam-0install-cudf' is not otherwise installed)]) +) + + +AC_ARG_WITH([libacl], + AC_HELP_STRING([--with-libacl], + [Compile opam with libacl support]),,[with_libacl=auto]) AC_ARG_WITH([private_runtime], AC_HELP_STRING([--with-private-runtime], @@ -77,7 +89,7 @@ # Check that OCaml version is greater or equal to 4.02.3 # Native Windows builds require at least 4.06.0 for the Unicode runtime. AS_IF([test "x${enable_version_check}" != "xno"], [ - AS_IF([ test ${WIN32} -eq 1 ],[MIN_OCAML_VERSION=4.06.0],[MIN_OCAML_VERSION=4.02.3]) + AS_IF([ test ${WIN32} -eq 1 ],[MIN_OCAML_VERSION=4.06.0]) AX_COMPARE_VERSION( [$OCAMLVERSION], [lt], [$MIN_OCAML_VERSION], AC_MSG_ERROR([Your version of OCaml: $OCAMLVERSION is not supported])) @@ -106,7 +118,7 @@ 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=$?` + eval `PATH="$PRE_BOOTSTRAP_PATH" bash ./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]) @@ -127,9 +139,34 @@ ]) 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${enable_developer_mode}" = "xyes"],[ + AC_SUBST(DEVELOPER,true) + echo "-D" > src/core/developer + echo "DEVELOPER" >> src/core/developer +],[ + AC_SUBST(DEVELOPER,false) + rm -f src/core/developer +]) AS_IF([test "x${with_mccs}" = "xno"], [AC_SUBST(MCCS_ENABLED,false)], [AC_SUBST(MCCS_ENABLED,true)]) +AS_IF([test "x${with_0install_solver}" = "xyes"], [AC_SUBST(OPAM_0INSTALL_SOLVER_ENABLED,true)], [AC_SUBST(OPAM_0INSTALL_SOLVER_ENABLED,false)]) + +AS_IF([test "x${with_libacl}" != "xno"],[ + have_libacl=yes + AC_CHECK_HEADER([acl/libacl.h],,[have_libacl=no]) + AC_SEARCH_LIBS([acl_get_perm],[acl],,[have_libacl=no]) + AS_IF([test "x${SYSTEM}" = "xcygwin"],[ + AS_IF([test "x${have_libacl}" = "xno"],[AC_MSG_ERROR([opam on Cygwin requires libacl])],[with_libacl=yes]) + ]) + AS_IF([test "x${with_libacl}${have_libacl}" = "xyesno"],[AC_MSG_ERROR([libacl not found])]) +]) +AS_IF([test "x${with_libacl}" = "xyes"],[ + AS_IF([test "x${ac_cv_search_acl_get_perm}" = "xnone required"],[ + AC_SUBST(CONF_LIBACL_LINK,[]) + ],[ + AC_SUBST(CONF_LIBACL_LINK,[${ac_cv_search_acl_get_perm}]) + ]) +]) CONF_OCAMLFLAGS="-w -67" @@ -164,7 +201,8 @@ AS_IF([ test "$ARCH" = "i386" ],[T_CC64=x86_64-w64-mingw32-gcc],[T_CC64=i686-w64-mingw32-gcc])]) AC_CHECK_TOOL(CC64,[${T_CC64}],[no]) AS_IF([ test "x${CC64}" != "xno" ],[ - CC64_JBUILD="(run ${CC64} -o \"%{targets}\" -I ../core -Wdeclaration-after-statement %{source})" + echo "${CC64} -o " > src/stubs/win32/cc64 + echo " -Wdeclaration-after-statement " >> src/stubs/win32/cc64 ]) ],[ AC_MSG_CHECKING([whether Microsoft Linker needs a PATH shim]) @@ -180,7 +218,7 @@ AC_MSG_RESULT([$PATH_PREPEND_RESULT]) AS_IF([ test "$ARCH" = "i386" ],[COMP_ARCH=x64],[COMP_ARCH=x86]) AC_MSG_CHECKING([for a way to invoke an $COMP_ARCH C compiler]) - eval `PATH="$PRE_BOOTSTRAP_PATH" ./shell/msvs-detect --arch=$COMP_ARCH` + eval `PATH="$PRE_BOOTSTRAP_PATH" bash ./shell/msvs-detect --arch=$COMP_ARCH` AS_IF([ test "x$MSVS_NAME" = "x" ], [ AC_MSG_RESULT([no]) CC64=no @@ -188,22 +226,20 @@ AC_MSG_RESULT([from $MSVS_NAME]) CL_FULL="`PATH="${MSVS_PATH}:${PATH}" which cl | cygpath -f - -w`" MSVS_PATH="`echo "${MSVS_PATH}" | cygpath -f - -wp`" - CC64_JBUILD="(setenv PATH \"${MSVS_PATH}\" (setenv LIB \"${MSVS_LIB};${LIB}\" (setenv INCLUDE \"${MSVS_INC};${INCLUDE}\" (run \"${CL_FULL}\" /nologo \"/Fe%{targets}\" /I../core %{source}))))" - CC64_JBUILD="$(echo $CC64_JBUILD|sed -e 's/\\/\\\\/g')" + echo "cl /nologo /Fe" > src/stubs/win32/cc64 + echo " " >> src/stubs/win32/cc64 + echo "PATH" >> src/stubs/win32/cc64 + echo "${MSVS_PATH}" >> src/stubs/win32/cc64 + echo "LIB" >> src/stubs/win32/cc64 + echo "${MSVS_LIB}" >> src/stubs/win32/cc64 + echo "INCLUDE" >> src/stubs/win32/cc64 + echo "${MSVS_INC}" >> src/stubs/win32/cc64 ]) ]) AS_IF([ test "x${CC64}" = "xno" ],[ AC_MSG_WARN([Complementary C compiler not found - opam-putenv will not be built]) - BUILD_PUTENV=0 - CC64_JBUILD= - ],[ - BUILD_PUTENV=1 ]) -],[ - BUILD_PUTENV=0 - CC64_JBUILD= ]) -AC_SUBST(CC64_JBUILD) AC_PROG_FINDLIB @@ -224,6 +260,11 @@ AC_CHECK_TOOL(PATCH,patch) AC_CHECK_TOOL(BUNZIP2,bunzip2) +AX_COMPARE_VERSION([$OCAMLVERSION], [lt], [4.08.0], + [DUNE_SECONDARY=src_ext/secondary/ocaml/bin/ocaml], + [DUNE_SECONDARY=]) +AC_SUBST([DUNE_SECONDARY]) + if test "x${COLD_CHECK}" = "xyes" ; then if test "x$PATCH" = "x" ; then AC_MSG_ERROR([You must have patch installed.]) @@ -255,22 +296,14 @@ AS_IF([test "x${with_private_runtime}" != "xno"],[ AS_IF([test ${WIN32} -eq 1 -a "x${CCOMP_TYPE}" = "xcc"],[ - CONF_MANIFEST_O="\"opam-manifest.o\"" + CONF_MANIFEST_O=opam-manifest.o AS_IF([ test "$ARCH" = "i386" ],[ - TOOL_ARCH=i686 MANIFEST_ARCH=x86 RUNTIME_GCC_S=libgcc_s_sjlj-1 ],[ - TOOL_ARCH=x86_64 MANIFEST_ARCH=amd64 RUNTIME_GCC_S=libgcc_s_seh-1 ]) - RUNTIME=`${TOOL_ARCH}-w64-mingw32-gcc -print-sysroot`/mingw/bin - echo Linking ${SYSTEM} runtime DLLs - ${LN_S} -fv ${RUNTIME}/${RUNTIME_GCC_S}.dll src/client/${RUNTIME_GCC_S}.dll - ${LN_S} -fv ${RUNTIME}/libstdc++-6.dll src/client/libstdc++-6.dll - ${LN_S} -fv ${RUNTIME}/libwinpthread-1.dll src/client/libwinpthread-1.dll - AC_SUBST(TOOL_ARCH) AC_SUBST(MANIFEST_ARCH) AC_SUBST(RUNTIME_GCC_S) ],[ @@ -294,9 +327,16 @@ AC_CHECK_OCAML_PKG(dose3.algo,dose.algo) AC_CHECK_OCAML_PKG([opam-file-format]) AC_CHECK_OCAML_PKG([mccs]) +AC_CHECK_OCAML_PKG([opam-0install-cudf]) AS_IF([test "x${with_mccs}" = "xno" && test "x$OCAML_PKG_mccs" != "xno"], [AC_MSG_ERROR([Option --without-mccs is not available without uninstalling the 'mccs' package])]) +AS_IF([test "x${with_0install_solver}" = "xno" && test "x$OCAML_PKG_opam_0install_cudf" != "xno"], + [AC_MSG_ERROR([Option --without-0install-solver is not available without uninstalling the 'opam-0install-cudf' package])]) + +dnl -- Enable 0install-solver if the package is available +AS_IF([test "x$OCAML_PKG_opam_0install_cudf" != "xno"], + [AC_SUBST(OPAM_0INSTALL_SOLVER_ENABLED,true)]) dnl -- that's what we would like to do, but no way to disable mccs in jbuilder dnl -- if it's installed, at the moment @@ -322,6 +362,12 @@ ]) ]) +AS_IF([test "x$OPAM_0INSTALL_SOLVER_ENABLED" = "xtrue" -a "x$OCAML_PKG_opam_0install_cudf" = "xno"],[ + AX_COMPARE_VERSION([$OCAMLVERSION], [lt], [4.08],[ + AC_MSG_ERROR([Your version of OCaml: $OCAMLVERSION does not support the requested 0install-solver. \ + You can either re-run the configure script with --without-0install-solver or use make cold]) + ]) +]) dnl echo dnl echo "extlib........................ ${OCAML_PKG_extlib}" @@ -341,7 +387,8 @@ test "x$OCAML_PKG_dose3_common" = "xno" || test "x$OCAML_PKG_opam_file_format" = "xno" || test "x$CPPO" = "x" || - test "x$OCAML_PKG_mccs$MCCS_ENABLED" = "xnotrue";}],[ + test "x$OCAML_PKG_mccs$MCCS_ENABLED" = "xnotrue" || + test "x$OCAML_PKG_opam_0install_cudf$OPAM_0INSTALL_SOLVER_ENABLED" = "xnotrue";}],[ echo "============================================================================" echo "Some dependencies are missing. If you are just interested in the stand-alone" echo "'opam' binary, run 'make lib-ext' to download and include them." @@ -356,28 +403,37 @@ prefix=$ac_default_prefix fi -AS_IF([ test ${BUILD_PUTENV} -eq 1],[ - AC_CONFIG_FILES(src/tools/opam-putenv.inc) -],[ - > src/tools/opam-putenv.inc -]) -AS_IF([ test -n "${CONF_MANIFEST_O}" ],[ - AC_CONFIG_FILES(src/client/manifest.inc) -],[ - > src/client/manifest.inc -]) AC_CONFIG_FILES( Makefile.config - src/client/opamManifest.inc + src/ocaml-flags-configure.sexp + src/stubs/c-flags.sexp + src/stubs/libacl/c-libraries.sexp ) AC_OUTPUT +rm -f src/manifest/dune src/manifest/install.inc +AS_IF([test "x${with_private_runtime}" != "xno"],[ + AS_IF([test ${WIN32} -eq 1 -a "x${CCOMP_TYPE}" = "xcc"],[ + cd src/manifest + ${LN_S} -f dune-manifest dune + ${LN_S} -f install.${ARCH} install.inc + cd ../.. + ],[ + AC_MSG_WARN([--with-private-runtime ignored (not building on mingw)]) + ]) +]) -rm -f src/stubs/dune +rm -f src/stubs/win32/dune AS_IF([ test ${WIN32} -eq 1],[ - cd src/stubs + cd src/stubs/win32 ${LN_S} -f dune-win32 dune - cd ../.. + cd ../../.. +]) +rm -f src/stubs/libacl/dune +AS_IF([ test "x${with_libacl}" = "xyes" ],[ + cd src/stubs/libacl + ${LN_S} -f dune-libacl dune + cd ../../.. ]) echo @@ -387,7 +443,7 @@ mandir="`eval echo ${mandir}`" mandir="`eval echo ${mandir}`" -AS_IF([test "x$MCCS_ENABLED" = "xfalse"],[ +AS_IF([test "x$MCCS_ENABLED" = "xfalse" -a "x$OPAM_0INSTALL_SOLVER_ENABLED" = "xfalse"],[ echo "Opam will be built WITHOUT a built-in solver" ],[ echo "Opam will be built WITH a built-in solver" diff -Nru opam-2.0.10/debian/changelog opam-2.1.2/debian/changelog --- opam-2.0.10/debian/changelog 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/changelog 2022-02-14 14:58:35.000000000 +0000 @@ -1,3 +1,10 @@ +opam (2.1.2-1) unstable; urgency=medium + + * Team upload + * New upstream release + + -- Stéphane Glondu Mon, 14 Feb 2022 15:58:35 +0100 + opam (2.0.10-1) unstable; urgency=medium [ Stéphane Glondu ] diff -Nru opam-2.0.10/debian/clean opam-2.1.2/debian/clean --- opam-2.0.10/debian/clean 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/clean 2022-02-14 10:30:42.000000000 +0000 @@ -3,7 +3,6 @@ config.log config.status src/core/opamTypes.cmti -src/core/opamVersion.ml tests/fulltest.log tests/packages/*.tar.gz tests/test.log diff -Nru opam-2.0.10/debian/control opam-2.1.2/debian/control --- opam-2.0.10/debian/control 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/control 2022-02-14 10:30:55.000000000 +0000 @@ -7,14 +7,14 @@ Nicolas Braud-Santoni Build-Depends: debhelper-compat (= 13), - ocaml-nox (>= 4.3.0~), + ocaml, ocamlbuild, cppo (>= 1.6.0), ocaml-findlib (>= 1.2.4), dh-ocaml (>= 0.9), ocaml-dune, libcudf-ocaml-dev, - libdose3-ocaml-dev (>= 6.0.1~), + libdose3-ocaml-dev (>= 7), libre-ocaml-dev (>= 1.7.2~), libcmdliner-ocaml-dev (>= 1.0.2~), libextlib-ocaml-dev, @@ -28,7 +28,7 @@ texlive-latex-extra , texlive-latex-recommended , texlive-fonts-recommended , - pandoc , + man2html-base , latex-make Standards-Version: 4.6.0 Rules-Requires-Root: no diff -Nru opam-2.0.10/debian/patches/0001-Add-a-test-target.patch opam-2.1.2/debian/patches/0001-Add-a-test-target.patch --- opam-2.0.10/debian/patches/0001-Add-a-test-target.patch 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/patches/0001-Add-a-test-target.patch 2022-02-14 14:58:35.000000000 +0000 @@ -10,14 +10,14 @@ 1 file changed, 1 insertion(+) diff --git a/Makefile b/Makefile -index 77ce9f7..c73931a 100644 +index f24bf26..6b9619d 100644 --- a/Makefile +++ b/Makefile -@@ -153,6 +153,7 @@ checker: - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) src/tools/opam_check.exe +@@ -177,6 +177,7 @@ uninstall: opam.install + $(OPAMINSTALLER) -u $(OPAMINSTALLER_FLAGS) opam-installer.install - .PHONY: tests tests-local tests-git + .PHONY: tests +test: tests - tests: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) opam.install src/tools/opam_check.exe - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) doc/man/opam-topics.inc doc/man/opam-admin-topics.inc + tests: $(DUNE_DEP) src/client/no-git-version + @$(DUNE) runtest $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) src/ tests/ --no-buffer; \ + ret=$$?; \ diff -Nru opam-2.0.10/debian/patches/0002-opamFileTools.lint-Fix-typo-grammar-in-error-message.patch opam-2.1.2/debian/patches/0002-opamFileTools.lint-Fix-typo-grammar-in-error-message.patch --- opam-2.0.10/debian/patches/0002-opamFileTools.lint-Fix-typo-grammar-in-error-message.patch 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/patches/0002-opamFileTools.lint-Fix-typo-grammar-in-error-message.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From: Nicolas Braud-Santoni -Date: Sat, 16 Jan 2021 17:32:04 +0100 -Subject: opamFileTools.lint: Fix typo & grammar in error message - -Forwarded: https://github.com/ocaml/opam/pull/3955 ---- - src/state/opamFileTools.ml | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/state/opamFileTools.ml b/src/state/opamFileTools.ml -index ec677a6..f76d931 100644 ---- a/src/state/opamFileTools.ml -+++ b/src/state/opamFileTools.ml -@@ -671,8 +671,9 @@ let lint ?check_extra_files ?(check_upstream=false) t = - if not_corresponding = [] then None - else - let msg = -- Printf.sprintf "Cheksum%s %s don't verify archive" -- (if List.length chks = 1 then "" else "s") -+ let is_singular = function [_] -> true | _ -> false in -+ Printf.sprintf "Checksum%s match the archive: %s" -+ (if is_singular not_corresponding then " doesn't" else "s don't") - (OpamStd.List.to_string OpamHash.to_string not_corresponding) - in - Some msg) diff -Nru opam-2.0.10/debian/patches/0003-Fix-compilation-with-Dose3-7.0.0.patch opam-2.1.2/debian/patches/0003-Fix-compilation-with-Dose3-7.0.0.patch --- opam-2.0.10/debian/patches/0003-Fix-compilation-with-Dose3-7.0.0.patch 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/debian/patches/0003-Fix-compilation-with-Dose3-7.0.0.patch 2022-02-14 14:58:35.000000000 +0000 @@ -0,0 +1,160 @@ +From: Mehdi Dogguy +Date: Sun, 28 Feb 2021 19:27:24 +0100 +Subject: Fix compilation with Dose3 7.0.0 + +--- + src/client/opamAdminCheck.ml | 2 ++ + src/client/opamAdminRepoUpgrade.ml | 2 ++ + src/solver/opamBuiltinMccs.ml.real | 2 +- + src/solver/opamCudf.ml | 9 ++++++--- + src/solver/opamCudf.mli | 4 ++-- + src/solver/opamCudfSolver.ml | 4 ++-- + src/solver/opamSolver.ml | 4 +++- + 7 files changed, 18 insertions(+), 9 deletions(-) + +diff --git a/src/client/opamAdminCheck.ml b/src/client/opamAdminCheck.ml +index fa89224..a38f9d5 100644 +--- a/src/client/opamAdminCheck.ml ++++ b/src/client/opamAdminCheck.ml +@@ -8,6 +8,8 @@ + (* *) + (**************************************************************************) + ++module Algo = Dose_algo ++ + open OpamTypes + open OpamPackage.Set.Op + +diff --git a/src/client/opamAdminRepoUpgrade.ml b/src/client/opamAdminRepoUpgrade.ml +index 93c0ce6..5e97c01 100644 +--- a/src/client/opamAdminRepoUpgrade.ml ++++ b/src/client/opamAdminRepoUpgrade.ml +@@ -8,6 +8,8 @@ + (* *) + (**************************************************************************) + ++module Algo = Dose_algo ++ + open OpamTypes + open OpamProcess.Job.Op + open OpamStd.Option.Op +diff --git a/src/solver/opamBuiltinMccs.ml.real b/src/solver/opamBuiltinMccs.ml.real +index 5ae58a3..b61cb17 100644 +--- a/src/solver/opamBuiltinMccs.ml.real ++++ b/src/solver/opamBuiltinMccs.ml.real +@@ -42,7 +42,7 @@ let call solver_backend ext ~criteria ?timeout cudf = + ~verbose:OpamCoreConfig.(abs !r.debug_level >= 2) + ?timeout criteria cudf + with +- | None -> raise Common.CudfSolver.Unsat ++ | None -> raise Dose_common.CudfSolver.Unsat + | Some (preamble, univ) -> Some preamble, univ + | exception Mccs.Timeout -> raise (Timeout None) + +diff --git a/src/solver/opamCudf.ml b/src/solver/opamCudf.ml +index f282653..c3aa41b 100644 +--- a/src/solver/opamCudf.ml ++++ b/src/solver/opamCudf.ml +@@ -9,6 +9,9 @@ + (* *) + (**************************************************************************) + ++module Common = Dose_common ++module Algo = Dose_algo ++ + open OpamTypes + open OpamTypesBase + +@@ -1315,7 +1318,7 @@ let call_external_solver ~version_map univ req = + ignore (dump_cudf_request ~version_map cudf_request + criteria OpamSolverConfig.(!r.cudf_file)); + (* Wrap a return of exn Timeout through Depsolver *) +- let check_request_using ~call_solver ~criteria ~explain req = ++ let check_request_using ~call_solver ~explain req = + let timed_out = ref false in + let call_solver args = + try call_solver args with +@@ -1323,7 +1326,7 @@ let call_external_solver ~version_map univ req = + | OpamCudfSolver.Timeout None -> raise (Timeout None) + in + let r = +- Algo.Depsolver.check_request_using ~call_solver ~criteria ~explain req ++ Algo.Depsolver.check_request_using ~call_solver ~explain req + in + if !timed_out then raise (Timeout (Some r)) else r + in +@@ -1335,7 +1338,7 @@ let call_external_solver ~version_map univ req = + let r = + check_request_using + ~call_solver:(OpamSolverConfig.call_solver ~criteria) +- ~criteria ~explain:true cudf_request ++ ~explain:true cudf_request + in + log "Solver call done in %.3fs" (chrono ()); + r +diff --git a/src/solver/opamCudf.mli b/src/solver/opamCudf.mli +index fc15444..73d57ed 100644 +--- a/src/solver/opamCudf.mli ++++ b/src/solver/opamCudf.mli +@@ -32,7 +32,7 @@ module Map: OpamStd.MAP with type key = Package.t + module Graph: sig + (** Graph of cudf packages *) + +- include module type of Algo.Defaultgraphs.PackageGraph.G ++ include module type of Dose_algo.Defaultgraphs.PackageGraph.G + + (** Build a graph from a CUDF universe. Warning: dependency edges are towards + the dependency, which is the reverse of what happens in the action +@@ -198,7 +198,7 @@ val string_of_vpkgs: Cudf_types.vpkg list -> string + + val make_conflicts: + version_map:int package_map -> Cudf.universe -> +- Algo.Diagnostic.diagnosis -> ('a, conflict) result ++ Dose_algo.Diagnostic.diagnosis -> ('a, conflict) result + val cycle_conflict: + version_map:int package_map -> Cudf.universe -> + string list list -> ('a, conflict) result +diff --git a/src/solver/opamCudfSolver.ml b/src/solver/opamCudfSolver.ml +index 6dda089..2ceebdf 100644 +--- a/src/solver/opamCudfSolver.ml ++++ b/src/solver/opamCudfSolver.ml +@@ -57,7 +57,7 @@ let call_external_solver command ~criteria ?timeout (_, universe,_ as cudf) = + in + OpamFilename.remove solver_in; + if not (OpamFilename.exists solver_out) then +- raise (Common.CudfSolver.Error "no output") ++ raise (Dose_common.CudfSolver.Error "no output") + else if + (let ic = OpamFilename.open_in solver_out in + try +@@ -65,7 +65,7 @@ let call_external_solver command ~criteria ?timeout (_, universe,_ as cudf) = + i = "FAIL" + with End_of_file -> close_in ic; false) + then +- raise Common.CudfSolver.Unsat ++ raise Dose_common.CudfSolver.Unsat + else + let r = + Cudf_parser.load_solution_from_file +diff --git a/src/solver/opamSolver.ml b/src/solver/opamSolver.ml +index 2fd53ea..e7f1137 100644 +--- a/src/solver/opamSolver.ml ++++ b/src/solver/opamSolver.ml +@@ -9,6 +9,8 @@ + (* *) + (**************************************************************************) + ++module Algo = Dose_algo ++ + open OpamTypes + open OpamTypesBase + open OpamPackage.Set.Op +@@ -90,7 +92,7 @@ let cudf_versions_map universe packages = + pmap OpamPackage.Map.empty + + let name_to_cudf name = +- Common.CudfAdd.encode (OpamPackage.Name.to_string name) ++ Dose_common.CudfAdd.encode (OpamPackage.Name.to_string name) + + let constraint_to_cudf version_map name (op,v) = + let nv = OpamPackage.create name v in diff -Nru opam-2.0.10/debian/patches/0003-opamCommands-Fix-typo-grammar-in-check.patch opam-2.1.2/debian/patches/0003-opamCommands-Fix-typo-grammar-in-check.patch --- opam-2.0.10/debian/patches/0003-opamCommands-Fix-typo-grammar-in-check.patch 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/patches/0003-opamCommands-Fix-typo-grammar-in-check.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -From: Nicolas Braud-Santoni -Date: Sat, 16 Jan 2021 17:32:04 +0100 -Subject: opamCommands: Fix typo & grammar in check - -Forwarded: https://github.com/ocaml/opam/pull/3955 ---- - src/client/opamCommands.ml | 5 +++-- - 1 file changed, 3 insertions(+), 2 deletions(-) - -diff --git a/src/client/opamCommands.ml b/src/client/opamCommands.ml -index 682cbc5..246d1a0 100644 ---- a/src/client/opamCommands.ml -+++ b/src/client/opamCommands.ml -@@ -1462,8 +1462,9 @@ let update = - "Do the update, then return with code 0 if there were any upstream \ - changes, 1 if there were none. Repositories or development packages \ - that failed to update are considered without changes. With \ -- $(b,--upgrade), behaves like $(b,opam upgrade --check), that is, \ -- returns 0 only if there are currently availbale updates." in -+ $(b,--upgrade), applies to the upgrade step: that is $(b,opam update \ -+ --upgrade --check) behaves like $(b,opam update && opam upgrade --check), \ -+ returning 0 if there are available upgrades, rather than upstream updates." in - let update global_options jobs names repos_only dev_only all check upgrade = - apply_global_options global_options; - OpamStateConfig.update diff -Nru opam-2.0.10/debian/patches/0004-Fix-compilation-with-Base64-3.5.0.patch opam-2.1.2/debian/patches/0004-Fix-compilation-with-Base64-3.5.0.patch --- opam-2.0.10/debian/patches/0004-Fix-compilation-with-Base64-3.5.0.patch 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/debian/patches/0004-Fix-compilation-with-Base64-3.5.0.patch 2022-02-14 14:58:35.000000000 +0000 @@ -0,0 +1,47 @@ +From: Stephane Glondu +Date: Mon, 14 Feb 2022 11:28:19 +0100 +Subject: Fix compilation with Base64 3.5.0 + +--- + src/client/dune | 2 +- + src/client/opamSwitchCommand.ml | 7 ++++++- + 2 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/src/client/dune b/src/client/dune +index 09151a8..7de0490 100644 +--- a/src/client/dune ++++ b/src/client/dune +@@ -3,7 +3,7 @@ + (public_name opam-client) + (synopsis "OCaml Package Manager client and CLI library") + (modules (:standard \ opamMain get_git_version)) +- (libraries opam-state opam-solver opam-repository re extlib cmdliner) ++ (libraries opam-state opam-solver opam-repository re extlib cmdliner base64) + (flags (:standard + (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) +diff --git a/src/client/opamSwitchCommand.ml b/src/client/opamSwitchCommand.ml +index 5e28167..90a3358 100644 +--- a/src/client/opamSwitchCommand.ml ++++ b/src/client/opamSwitchCommand.ml +@@ -14,6 +14,11 @@ open OpamStateTypes + open OpamPackage.Set.Op + open OpamStd.Op + ++let base64_decode_string ?pad ?alphabet input = ++ match Base64.decode ?pad ?alphabet input with ++ | Ok res -> res ++ | Error _ -> assert false ++ + module S = OpamFile.SwitchSelections + + let log fmt = OpamConsole.log "SWITCH" fmt +@@ -400,7 +405,7 @@ let import_t ?ask importfile t = + OpamPath.Switch.extra_files_dir t.switch_global.root t.switch + in + OpamHash.Map.iter (fun hash content -> +- let value = Base64.decode_string content in ++ let value = base64_decode_string content in + let my = OpamHash.compute_from_string ~kind:(OpamHash.kind hash) value in + if OpamHash.contents my = OpamHash.contents hash then + let dst = diff -Nru opam-2.0.10/debian/patches/0004-Use-HOME-env-variable-instead-of.patch opam-2.1.2/debian/patches/0004-Use-HOME-env-variable-instead-of.patch --- opam-2.0.10/debian/patches/0004-Use-HOME-env-variable-instead-of.patch 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/patches/0004-Use-HOME-env-variable-instead-of.patch 2022-02-14 14:58:35.000000000 +0000 @@ -8,21 +8,21 @@ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/client/opamAdminCommand.ml b/src/client/opamAdminCommand.ml -index 216143d..a2302d6 100644 +index 65a1afb..db797f9 100644 --- a/src/client/opamAdminCommand.ml +++ b/src/client/opamAdminCommand.ml -@@ -261,7 +261,8 @@ let add_hashes_command_doc = - let add_hashes_command = +@@ -281,7 +281,8 @@ let add_hashes_command_doc = + let add_hashes_command cli = let command = "add-hashes" in let doc = add_hashes_command_doc in - let cache_dir = OpamFilename.Dir.of_string "~/.cache/opam-hash-cache" in + let home = try Sys.getenv "HOME" with Not_found -> Sys.getcwd () in + let cache_dir = OpamFilename.Dir.of_string (Filename.concat home "/.cache/opam-hash-cache") in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P (Printf.sprintf diff --git a/src/client/opamAdminRepoUpgrade.ml b/src/client/opamAdminRepoUpgrade.ml -index 704a009..0be53b2 100644 +index b3af2e5..93c0ce6 100644 --- a/src/client/opamAdminRepoUpgrade.ml +++ b/src/client/opamAdminRepoUpgrade.ml @@ -145,8 +145,9 @@ let all_base_packages = @@ -35,4 +35,4 @@ + OpamFilename.of_string (Filename.concat home "/.cache/opam-compilers-to-packages/url-hashes") let do_upgrade repo_root = - let repo = OpamRepositoryBackend.local repo_root in + let write_opam ?(add_files=[]) opam = diff -Nru opam-2.0.10/debian/patches/0005-Port-to-Dose3-6.0.1.patch opam-2.1.2/debian/patches/0005-Port-to-Dose3-6.0.1.patch --- opam-2.0.10/debian/patches/0005-Port-to-Dose3-6.0.1.patch 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/patches/0005-Port-to-Dose3-6.0.1.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,142 +0,0 @@ -From: Mehdi Dogguy -Date: Sun, 28 Feb 2021 19:27:24 +0100 -Subject: Port to Dose3 6.0.1 - ---- - src/client/opamAdminCheck.ml | 2 ++ - src/client/opamAdminRepoUpgrade.ml | 2 ++ - src/solver/opamBuiltinMccs.ml.real | 2 +- - src/solver/opamCudf.ml | 5 ++++- - src/solver/opamCudf.mli | 4 ++-- - src/solver/opamCudfSolver.ml | 4 ++-- - src/solver/opamSolver.ml | 4 +++- - 7 files changed, 16 insertions(+), 7 deletions(-) - -diff --git a/src/client/opamAdminCheck.ml b/src/client/opamAdminCheck.ml -index 05543a2..f23bfc4 100644 ---- a/src/client/opamAdminCheck.ml -+++ b/src/client/opamAdminCheck.ml -@@ -8,6 +8,8 @@ - (* *) - (**************************************************************************) - -+module Algo = Dose_algo -+ - open OpamTypes - open OpamPackage.Set.Op - -diff --git a/src/client/opamAdminRepoUpgrade.ml b/src/client/opamAdminRepoUpgrade.ml -index 0be53b2..635e792 100644 ---- a/src/client/opamAdminRepoUpgrade.ml -+++ b/src/client/opamAdminRepoUpgrade.ml -@@ -8,6 +8,8 @@ - (* *) - (**************************************************************************) - -+module Algo = Dose_algo -+ - open OpamTypes - open OpamProcess.Job.Op - open OpamStd.Option.Op -diff --git a/src/solver/opamBuiltinMccs.ml.real b/src/solver/opamBuiltinMccs.ml.real -index ab39ab3..2b41982 100644 ---- a/src/solver/opamBuiltinMccs.ml.real -+++ b/src/solver/opamBuiltinMccs.ml.real -@@ -35,7 +35,7 @@ let call solver_backend ext ~criteria ?timeout cudf = - ~verbose:OpamCoreConfig.(!r.debug_level >= 2) - ?timeout criteria cudf - with -- | None -> raise Common.CudfSolver.Unsat -+ | None -> raise Dose_common.CudfSolver.Unsat - | Some (preamble, univ) -> Some preamble, univ - | exception Mccs.Timeout -> raise Timeout - -diff --git a/src/solver/opamCudf.ml b/src/solver/opamCudf.ml -index e07ff54..ad7293c 100644 ---- a/src/solver/opamCudf.ml -+++ b/src/solver/opamCudf.ml -@@ -9,6 +9,9 @@ - (* *) - (**************************************************************************) - -+module Common = Dose_common -+module Algo = Dose_algo -+ - open OpamTypes - open OpamTypesBase - -@@ -634,7 +637,7 @@ let call_external_solver ~version_map univ req = - let r = - Algo.Depsolver.check_request_using - ~call_solver:(OpamSolverConfig.call_solver ~criteria) -- ~criteria ~explain:true cudf_request -+ ~explain:true cudf_request - in - log "Solver call done in %.3f" (chrono ()); - r -diff --git a/src/solver/opamCudf.mli b/src/solver/opamCudf.mli -index cb8158d..c928ccf 100644 ---- a/src/solver/opamCudf.mli -+++ b/src/solver/opamCudf.mli -@@ -24,7 +24,7 @@ module Map: OpamStd.MAP with type key = Cudf.package - module Graph: sig - (** Graph of cudf packages *) - -- include module type of Algo.Defaultgraphs.PackageGraph.G -+ include module type of Dose_algo.Defaultgraphs.PackageGraph.G - - (** Build a graph from a CUDF universe. Warning: dependency edges are towards - the dependency, which is the reverse of what happens in the action -@@ -180,7 +180,7 @@ val string_of_vpkgs: Cudf_types.vpkg list -> string - - val make_conflicts: - version_map:int package_map -> Cudf.universe -> -- Algo.Diagnostic.diagnosis -> ('a, conflict) result -+ Dose_algo.Diagnostic.diagnosis -> ('a, conflict) result - val cycle_conflict: - version_map:int package_map -> Cudf.universe -> - string list list -> ('a, conflict) result -diff --git a/src/solver/opamCudfSolver.ml b/src/solver/opamCudfSolver.ml -index 03486f1..2651373 100644 ---- a/src/solver/opamCudfSolver.ml -+++ b/src/solver/opamCudfSolver.ml -@@ -57,7 +57,7 @@ let call_external_solver command ~criteria ?timeout (_, universe,_ as cudf) = - in - OpamFilename.remove solver_in; - if not (OpamFilename.exists solver_out) then -- raise (Common.CudfSolver.Error "no output") -+ raise (Dose_common.CudfSolver.Error "no output") - else if - (let ic = OpamFilename.open_in solver_out in - try -@@ -65,7 +65,7 @@ let call_external_solver command ~criteria ?timeout (_, universe,_ as cudf) = - i = "FAIL" - with End_of_file -> close_in ic; false) - then -- raise Common.CudfSolver.Unsat -+ raise Dose_common.CudfSolver.Unsat - else - let r = - Cudf_parser.load_solution_from_file -diff --git a/src/solver/opamSolver.ml b/src/solver/opamSolver.ml -index 3748e75..bbf995f 100644 ---- a/src/solver/opamSolver.ml -+++ b/src/solver/opamSolver.ml -@@ -9,6 +9,8 @@ - (* *) - (**************************************************************************) - -+module Algo = Dose_algo -+ - open OpamTypes - open OpamTypesBase - open OpamPackage.Set.Op -@@ -84,7 +86,7 @@ let cudf_versions_map universe packages = - pmap OpamPackage.Map.empty - - let name_to_cudf name = -- Common.CudfAdd.encode (OpamPackage.Name.to_string name) -+ Dose_common.CudfAdd.encode (OpamPackage.Name.to_string name) - - let constraint_to_cudf version_map name (op,v) = - let nv = OpamPackage.create name v in diff -Nru opam-2.0.10/debian/patches/series opam-2.1.2/debian/patches/series --- opam-2.0.10/debian/patches/series 2021-12-01 15:59:38.000000000 +0000 +++ opam-2.1.2/debian/patches/series 2022-02-14 14:58:35.000000000 +0000 @@ -1,5 +1,4 @@ 0001-Add-a-test-target.patch -0002-opamFileTools.lint-Fix-typo-grammar-in-error-message.patch -0003-opamCommands-Fix-typo-grammar-in-check.patch 0004-Use-HOME-env-variable-instead-of.patch -0005-Port-to-Dose3-6.0.1.patch +0003-Fix-compilation-with-Dose3-7.0.0.patch +0004-Fix-compilation-with-Base64-3.5.0.patch diff -Nru opam-2.0.10/doc/index.html opam-2.1.2/doc/index.html --- opam-2.0.10/doc/index.html 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/index.html 2021-12-07 16:09:27.000000000 +0000 @@ -58,8 +58,10 @@ Generic stdlib functions (String, List, Option, Sys submodules...) opamConsole.ml Console output, ANSI color, logging and user querying -opamCompat.ml.4.01/4.02 +opamCompat.ml Compatibility layer (Bytes, etc.) for different OCaml versions +opamSHA.ml + Pure OCaml implementation of SHA256/512 hashing functions System handling @@ -80,10 +82,24 @@ URL parsing and printing, with support for our different backends Windows support -opamStubsTypes.ml - Types in the stubs definitions (shared between both implementations) opamStubs.ml C stubs for Windows. A “dummy” alternate is provided for Unix, which doesn’t require any C code +opamStubsTypes.ml + Types in the stubs definitions (shared between both implementations) + + + src/stubs + opam-stubs library + + + + C stubs. This library is built on Windows-only and automatically pulled into opam-core if needed + + +opamInject.c + Code for process injection shared between opamWindows.c and opam-putenv.c +opamWindows.c + C stubs themselves src/format @@ -103,7 +119,7 @@ opamPath.ml Defines the file hierarchy in ~/.opam -basic types, used as keys +Basic types, used as keys opamPackage.ml The package type, and package name type (name+version, values often called "nv" in the code) @@ -112,9 +128,11 @@ opamSwitch.ml The switch type opamVariable.ml - opam variables with scope (global or module) + Opam variables with scope (global or module) +opamSysPkg.ml + The system package type -more advanced types +More advanced types opamFilter.ml Formulas on variables, as used in opam files build scripts @@ -122,10 +140,12 @@ Formulas on packages, opt. with sub-formulas on versions, and conversion functions -file format +File format opamLineLexer.mll A simple lexer to list of lines, which are lists of words +opamInterpLexer.mll + Opam format variable interpolation processor opamPp.ml Bidirectional transformations on top of the parser and printer opamFormat.ml @@ -185,6 +205,12 @@ opamSolver.ml Entry point, conversion of universe to cudf, dependencies computation +Built-in solver backends +opamBuiltinMccs.ml.real + Direct interface to the mccs solver, if present +opamBuiltinZ3.ml.real + Direct interface to the Z3 solver, if present + src/state opam-state library @@ -222,22 +248,10 @@ Specific query and handling of pinned packages opamUpdate.ml Synchronisation and downloading of repositories and package sources - - - src/stubs - opam-stubs library - - - - C stubs. This library is built on Windows-only and automatically pulled into opam-core if needed - - -opamInject.c - Code for process injection shared between opamWindows.c and opam-putenv.c -opamWindows.c - C stubs themselves -opamWin32Stubs.ml - OCaml external declarations for the stubs +opamSysInteract.ml + Interaction with system package manager, for external dependencies management +opamSpdxList.ml + (generated) SPDX short IDs licenses list, used for linting packages src/client @@ -266,6 +280,8 @@ Functions for the "opam switch" subcommand opamListCommand.ml Functions for the "opam list" subcommand +opamLockCommand.ml + Functions for the "opam lock" subcommand opamInitDefaults.ml Defines the built-in "opamrc" to use by default on "opam init" opamClient.ml @@ -282,17 +298,39 @@ (generated) Current git version of opam opamArg.ml Command-line argument parsers and helpers +opamArgTools.ml + Command-line argument cli versioning helpers opamAdminCommand.ml All sub-commands of the "opam admin" command opamCommands.ml Opam CLI commands and their handlers as Cmdliner terms +opamCLIVersion.ml + Functions for the CLI versioning +opamCliMain.ml + Front-end wrappers and callers for the main opam entry point Main opam CLI opamMain.ml Main opam entry point - src/tools + src/crowbar + crowbar tests + + + + Crowbar generators and tests for internal data structures + + +test.ml + Main crowbar tests entry point +opamCrowbar.ml + Definition of utils structures and functions +opamXxx_crowbar.ml + Definition of tests for module Xxx + + + src/tools Extra tools and utilities @@ -302,8 +340,6 @@ Tiny library for admin-scripts, included in opam-admin.top opam-putenv.c Tiny C tool used on Windows for cross-architecture process injection -opam_check.ml - Tiny tool used in internal checks ("make tests") opam_installer.ml Handles opam's ".install" files diff -Nru opam-2.0.10/doc/Makefile opam-2.1.2/doc/Makefile --- opam-2.0.10/doc/Makefile 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/Makefile 2021-12-07 16:09:27.000000000 +0000 @@ -1,7 +1,7 @@ -include ../Makefile.config ifeq ($(DUNE),) - DUNE_EXE = ../src_ext/dune-local/_build_bootstrap/install/default/bin/dune$(EXE) + DUNE_EXE = ../src_ext/dune-local/_boot/install/default/bin/dune$(EXE) ifeq ($(shell command -v cygpath 2>/dev/null),) DUNE := $(DUNE_EXE) else @@ -14,17 +14,11 @@ DUNE_PROFILE ?= release DUNE_ARGS ?= -ifndef OPAM - OPAM = $(DUNE) exec --profile=$(DUNE_PROFILE) -- opam -endif - -TOPICS = $(shell $(OPAM) help topics) -TOPICS_ADMIN = cache filter index lint list upgrade - -HELPFMT = --help=groff - -ifndef OPAM_INSTALLER - OPAM_INSTALLER = $(DUNE) exec --profile=$(DUNE_PROFILE) -- opam-installer +ifeq ($(DUNE_PROFILE_ARG),release) + # TODO Replace with --release when we require dune >= 2.5 + DUNE_PROFILE_ARG = --profile=release +else + DUNE_PROFILE_ARG = --profile=$(DUNE_PROFILE) endif .PHONY: man html pages @@ -51,7 +45,7 @@ html: rm -rf html - cd .. && $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) @doc + cd .. && $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) @doc cp -r ../_build/default/_doc/_html html sed 's/%{OPAMVERSION}%/'$(version)'/g' index.html > html/index.html # Not to break older links, add manpages to the `ocamldoc` dir diff -Nru opam-2.0.10/doc/man/dune opam-2.1.2/doc/man/dune --- opam-2.0.10/doc/man/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/man/dune 2021-12-07 16:09:27.000000000 +0000 @@ -1,3 +1,7 @@ +; opam must always be invoked as %{bin:opam} to ensure that the manifested runtime on mingw is +; assembled, if it was selected at configure-time (%{exe:opamMain.exe} is not executable in this +; case. + (rule (targets opam.1) (deps opam-topics.inc opam-admin-topics.inc) @@ -22,14 +26,29 @@ (rule (targets opam-topics.inc) + (deps %{bin:opam} using-built-opam) (mode promote) - (action (with-stdout-to %{targets} (run %{exe:dune_man.exe} %{bin:opam} opam)))) + (action (with-stdout-to %{targets} (run %{exe:dune_man.exe} opam)))) (rule (targets opam-admin-topics.inc) + (deps %{bin:opam} using-built-opam) (mode promote) - (action (with-stdout-to %{targets} (run %{exe:dune_man.exe} %{bin:opam} opam admin)))) + (action (with-stdout-to %{targets} (run %{exe:dune_man.exe} opam admin)))) (include opam-topics.inc) (include opam-admin-topics.inc) + +; This ensures that no opam command run will block asking for input +(env + (_ (env-vars ("OPAMYES" "no")))) + +; This ensure that %{bin:opam} really refers to the opam built in the tree +(rule + (with-stdout-to check_local_build.ml + (echo "let s = Sys.argv.(1) in exit (if not (Filename.is_implicit s) && Filename.is_relative s then 0 else 1)"))) + +(rule + (with-stdout-to using-built-opam (run ocaml %{dep:check_local_build.ml} %{bin:opam}))) + diff -Nru opam-2.0.10/doc/man/dune_man.ml opam-2.1.2/doc/man/dune_man.ml --- opam-2.0.10/doc/man/dune_man.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/man/dune_man.ml 2021-12-07 16:09:27.000000000 +0000 @@ -4,6 +4,7 @@ \ (with-stdout-to %s-%s.0 (echo \"\")))\n\ (rule\n\ \ (targets %s-%s.1 %s-%s.err)\n\ + \ (deps using-built-opam)\n\ \ (action (progn (with-stderr-to %s-%s.err\n\ \ (with-stdout-to %s-%s.1 (run %s %s --help=groff)))\n\ \ (diff %s-%s.err %%{dep:%s-%s.0}))))\n\ @@ -12,8 +13,8 @@ let () = let cmd,args = match Array.to_list Sys.argv with - | _::_::cmd::args -> cmd, args - | _ -> invalid_arg "Missing command argument" + | _::cmd::args -> cmd, args + | [] | [_] -> invalid_arg "Missing command argument" in let cline = String.concat " " (cmd :: args) ^ " help topics" in let topics = diff -Nru opam-2.0.10/doc/man/opam-admin-topics.inc opam-2.1.2/doc/man/opam-admin-topics.inc --- opam-2.0.10/doc/man/opam-admin-topics.inc 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/man/opam-admin-topics.inc 2021-12-07 16:09:27.000000000 +0000 @@ -4,6 +4,7 @@ (with-stdout-to opam-admin-help.0 (echo ""))) (rule (targets opam-admin-help.1 opam-admin-help.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-help.err (with-stdout-to opam-admin-help.1 (run %{bin:opam} admin help --help=groff))) (diff opam-admin-help.err %{dep:opam-admin-help.0})))) @@ -12,6 +13,7 @@ (with-stdout-to opam-admin-add-hashes.0 (echo ""))) (rule (targets opam-admin-add-hashes.1 opam-admin-add-hashes.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-add-hashes.err (with-stdout-to opam-admin-add-hashes.1 (run %{bin:opam} admin add-hashes --help=groff))) (diff opam-admin-add-hashes.err %{dep:opam-admin-add-hashes.0})))) @@ -20,6 +22,7 @@ (with-stdout-to opam-admin-add-constraint.0 (echo ""))) (rule (targets opam-admin-add-constraint.1 opam-admin-add-constraint.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-add-constraint.err (with-stdout-to opam-admin-add-constraint.1 (run %{bin:opam} admin add-constraint --help=groff))) (diff opam-admin-add-constraint.err %{dep:opam-admin-add-constraint.0})))) @@ -28,6 +31,7 @@ (with-stdout-to opam-admin-filter.0 (echo ""))) (rule (targets opam-admin-filter.1 opam-admin-filter.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-filter.err (with-stdout-to opam-admin-filter.1 (run %{bin:opam} admin filter --help=groff))) (diff opam-admin-filter.err %{dep:opam-admin-filter.0})))) @@ -36,6 +40,7 @@ (with-stdout-to opam-admin-list.0 (echo ""))) (rule (targets opam-admin-list.1 opam-admin-list.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-list.err (with-stdout-to opam-admin-list.1 (run %{bin:opam} admin list --help=groff))) (diff opam-admin-list.err %{dep:opam-admin-list.0})))) @@ -44,6 +49,7 @@ (with-stdout-to opam-admin-check.0 (echo ""))) (rule (targets opam-admin-check.1 opam-admin-check.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-check.err (with-stdout-to opam-admin-check.1 (run %{bin:opam} admin check --help=groff))) (diff opam-admin-check.err %{dep:opam-admin-check.0})))) @@ -52,6 +58,7 @@ (with-stdout-to opam-admin-lint.0 (echo ""))) (rule (targets opam-admin-lint.1 opam-admin-lint.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-lint.err (with-stdout-to opam-admin-lint.1 (run %{bin:opam} admin lint --help=groff))) (diff opam-admin-lint.err %{dep:opam-admin-lint.0})))) @@ -60,6 +67,7 @@ (with-stdout-to opam-admin-upgrade.0 (echo ""))) (rule (targets opam-admin-upgrade.1 opam-admin-upgrade.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-upgrade.err (with-stdout-to opam-admin-upgrade.1 (run %{bin:opam} admin upgrade --help=groff))) (diff opam-admin-upgrade.err %{dep:opam-admin-upgrade.0})))) @@ -68,6 +76,7 @@ (with-stdout-to opam-admin-cache.0 (echo ""))) (rule (targets opam-admin-cache.1 opam-admin-cache.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-cache.err (with-stdout-to opam-admin-cache.1 (run %{bin:opam} admin cache --help=groff))) (diff opam-admin-cache.err %{dep:opam-admin-cache.0})))) @@ -76,6 +85,7 @@ (with-stdout-to opam-admin-make.0 (echo ""))) (rule (targets opam-admin-make.1 opam-admin-make.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-make.err (with-stdout-to opam-admin-make.1 (run %{bin:opam} admin make --help=groff))) (diff opam-admin-make.err %{dep:opam-admin-make.0})))) @@ -84,6 +94,7 @@ (with-stdout-to opam-admin-index.0 (echo ""))) (rule (targets opam-admin-index.1 opam-admin-index.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin-index.err (with-stdout-to opam-admin-index.1 (run %{bin:opam} admin index --help=groff))) (diff opam-admin-index.err %{dep:opam-admin-index.0})))) diff -Nru opam-2.0.10/doc/man/opam-topics.inc opam-2.1.2/doc/man/opam-topics.inc --- opam-2.0.10/doc/man/opam-topics.inc 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/man/opam-topics.inc 2021-12-07 16:09:27.000000000 +0000 @@ -4,6 +4,7 @@ (with-stdout-to opam-help.0 (echo ""))) (rule (targets opam-help.1 opam-help.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-help.err (with-stdout-to opam-help.1 (run %{bin:opam} help --help=groff))) (diff opam-help.err %{dep:opam-help.0})))) @@ -12,14 +13,25 @@ (with-stdout-to opam-admin.0 (echo ""))) (rule (targets opam-admin.1 opam-admin.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-admin.err (with-stdout-to opam-admin.1 (run %{bin:opam} admin --help=groff))) (diff opam-admin.err %{dep:opam-admin.0})))) (rule + (with-stdout-to opam-lock.0 (echo ""))) +(rule + (targets opam-lock.1 opam-lock.err) + (deps using-built-opam) + (action (progn (with-stderr-to opam-lock.err + (with-stdout-to opam-lock.1 (run %{bin:opam} lock --help=groff))) + (diff opam-lock.err %{dep:opam-lock.0})))) + +(rule (with-stdout-to opam-clean.0 (echo ""))) (rule (targets opam-clean.1 opam-clean.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-clean.err (with-stdout-to opam-clean.1 (run %{bin:opam} clean --help=groff))) (diff opam-clean.err %{dep:opam-clean.0})))) @@ -28,6 +40,7 @@ (with-stdout-to opam-lint.0 (echo ""))) (rule (targets opam-lint.1 opam-lint.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-lint.err (with-stdout-to opam-lint.1 (run %{bin:opam} lint --help=groff))) (diff opam-lint.err %{dep:opam-lint.0})))) @@ -36,6 +49,7 @@ (with-stdout-to opam-source.0 (echo ""))) (rule (targets opam-source.1 opam-source.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-source.err (with-stdout-to opam-source.1 (run %{bin:opam} source --help=groff))) (diff opam-source.err %{dep:opam-source.0})))) @@ -44,6 +58,7 @@ (with-stdout-to opam-unpin.0 (echo ""))) (rule (targets opam-unpin.1 opam-unpin.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-unpin.err (with-stdout-to opam-unpin.1 (run %{bin:opam} unpin --help=groff))) (diff opam-unpin.err %{dep:opam-unpin.0})))) @@ -52,6 +67,7 @@ (with-stdout-to opam-pin.0 (echo ""))) (rule (targets opam-pin.1 opam-pin.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-pin.err (with-stdout-to opam-pin.1 (run %{bin:opam} pin --help=groff))) (diff opam-pin.err %{dep:opam-pin.0})))) @@ -60,6 +76,7 @@ (with-stdout-to opam-switch.0 (echo ""))) (rule (targets opam-switch.1 opam-switch.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-switch.err (with-stdout-to opam-switch.1 (run %{bin:opam} switch --help=groff))) (diff opam-switch.err %{dep:opam-switch.0})))) @@ -68,6 +85,7 @@ (with-stdout-to opam-remote.0 (echo ""))) (rule (targets opam-remote.1 opam-remote.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-remote.err (with-stdout-to opam-remote.1 (run %{bin:opam} remote --help=groff))) (diff opam-remote.err %{dep:opam-remote.0})))) @@ -76,6 +94,7 @@ (with-stdout-to opam-repository.0 (echo ""))) (rule (targets opam-repository.1 opam-repository.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-repository.err (with-stdout-to opam-repository.1 (run %{bin:opam} repository --help=groff))) (diff opam-repository.err %{dep:opam-repository.0})))) @@ -84,6 +103,7 @@ (with-stdout-to opam-env.0 (echo ""))) (rule (targets opam-env.1 opam-env.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-env.err (with-stdout-to opam-env.1 (run %{bin:opam} env --help=groff))) (diff opam-env.err %{dep:opam-env.0})))) @@ -92,30 +112,43 @@ (with-stdout-to opam-exec.0 (echo ""))) (rule (targets opam-exec.1 opam-exec.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-exec.err (with-stdout-to opam-exec.1 (run %{bin:opam} exec --help=groff))) (diff opam-exec.err %{dep:opam-exec.0})))) (rule - (with-stdout-to opam-var.0 (echo ""))) -(rule - (targets opam-var.1 opam-var.err) - (action (progn (with-stderr-to opam-var.err - (with-stdout-to opam-var.1 (run %{bin:opam} var --help=groff))) - (diff opam-var.err %{dep:opam-var.0})))) - -(rule (with-stdout-to opam-config.0 (echo ""))) (rule (targets opam-config.1 opam-config.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-config.err (with-stdout-to opam-config.1 (run %{bin:opam} config --help=groff))) (diff opam-config.err %{dep:opam-config.0})))) (rule + (with-stdout-to opam-option.0 (echo ""))) +(rule + (targets opam-option.1 opam-option.err) + (deps using-built-opam) + (action (progn (with-stderr-to opam-option.err + (with-stdout-to opam-option.1 (run %{bin:opam} option --help=groff))) + (diff opam-option.err %{dep:opam-option.0})))) + +(rule + (with-stdout-to opam-var.0 (echo ""))) +(rule + (targets opam-var.1 opam-var.err) + (deps using-built-opam) + (action (progn (with-stderr-to opam-var.err + (with-stdout-to opam-var.1 (run %{bin:opam} var --help=groff))) + (diff opam-var.err %{dep:opam-var.0})))) + +(rule (with-stdout-to opam-upgrade.0 (echo ""))) (rule (targets opam-upgrade.1 opam-upgrade.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-upgrade.err (with-stdout-to opam-upgrade.1 (run %{bin:opam} upgrade --help=groff))) (diff opam-upgrade.err %{dep:opam-upgrade.0})))) @@ -124,6 +157,7 @@ (with-stdout-to opam-update.0 (echo ""))) (rule (targets opam-update.1 opam-update.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-update.err (with-stdout-to opam-update.1 (run %{bin:opam} update --help=groff))) (diff opam-update.err %{dep:opam-update.0})))) @@ -132,6 +166,7 @@ (with-stdout-to opam-reinstall.0 (echo ""))) (rule (targets opam-reinstall.1 opam-reinstall.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-reinstall.err (with-stdout-to opam-reinstall.1 (run %{bin:opam} reinstall --help=groff))) (diff opam-reinstall.err %{dep:opam-reinstall.0})))) @@ -140,6 +175,7 @@ (with-stdout-to opam-uninstall.0 (echo ""))) (rule (targets opam-uninstall.1 opam-uninstall.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-uninstall.err (with-stdout-to opam-uninstall.1 (run %{bin:opam} uninstall --help=groff))) (diff opam-uninstall.err %{dep:opam-uninstall.0})))) @@ -148,6 +184,7 @@ (with-stdout-to opam-remove.0 (echo ""))) (rule (targets opam-remove.1 opam-remove.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-remove.err (with-stdout-to opam-remove.1 (run %{bin:opam} remove --help=groff))) (diff opam-remove.err %{dep:opam-remove.0})))) @@ -156,6 +193,7 @@ (with-stdout-to opam-install.0 (echo ""))) (rule (targets opam-install.1 opam-install.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-install.err (with-stdout-to opam-install.1 (run %{bin:opam} install --help=groff))) (diff opam-install.err %{dep:opam-install.0})))) @@ -164,6 +202,7 @@ (with-stdout-to opam-info.0 (echo ""))) (rule (targets opam-info.1 opam-info.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-info.err (with-stdout-to opam-info.1 (run %{bin:opam} info --help=groff))) (diff opam-info.err %{dep:opam-info.0})))) @@ -172,6 +211,7 @@ (with-stdout-to opam-show.0 (echo ""))) (rule (targets opam-show.1 opam-show.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-show.err (with-stdout-to opam-show.1 (run %{bin:opam} show --help=groff))) (diff opam-show.err %{dep:opam-show.0})))) @@ -180,6 +220,7 @@ (with-stdout-to opam-search.0 (echo ""))) (rule (targets opam-search.1 opam-search.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-search.err (with-stdout-to opam-search.1 (run %{bin:opam} search --help=groff))) (diff opam-search.err %{dep:opam-search.0})))) @@ -188,6 +229,7 @@ (with-stdout-to opam-list.0 (echo ""))) (rule (targets opam-list.1 opam-list.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-list.err (with-stdout-to opam-list.1 (run %{bin:opam} list --help=groff))) (diff opam-list.err %{dep:opam-list.0})))) @@ -196,6 +238,7 @@ (with-stdout-to opam-init.0 (echo ""))) (rule (targets opam-init.1 opam-init.err) + (deps using-built-opam) (action (progn (with-stderr-to opam-init.err (with-stdout-to opam-init.1 (run %{bin:opam} init --help=groff))) (diff opam-init.err %{dep:opam-init.0})))) @@ -206,6 +249,7 @@ (files opam-help.1 opam-admin.1 + opam-lock.1 opam-clean.1 opam-lint.1 opam-source.1 @@ -216,8 +260,9 @@ opam-repository.1 opam-env.1 opam-exec.1 - opam-var.1 opam-config.1 + opam-option.1 + opam-var.1 opam-upgrade.1 opam-update.1 opam-reinstall.1 diff -Nru opam-2.0.10/doc/pages/Distribution.md opam-2.1.2/doc/pages/Distribution.md --- opam-2.0.10/doc/pages/Distribution.md 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/doc/pages/Distribution.md 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,126 @@ +# opam and other package managers: distributions list + +This page tracks the state of binary packaging of opam on upstream +distributions. If you do package up opam for your various OS, please feel free +to add it below, update [this file](https://github.com/ocaml/opam/tree/master/doc/pages/Distribution.md) +and open a [pull request](https://github.com/ocaml/opam/compare). + +Opam is in [![Packaging status](https://repology.org/badge/tiny-repos/opam.svg?header=)](https://repology.org/project/opam/versions) repository families. + +The pages/files linked are the ones that give the best overview of the available +versions. + +Those [_pkgs_](http://pkgs.org/search/opam) and +[_repology_]() pages may be used to +get an up-to-date overview of official packages on most Linux distributions. + +## _Official_ packages + +OS/distrubtion, their latest opam version and their maintainers: + +* Arch Linux + [![Arch package](https://repology.org/badge/version-for-repo/arch/opam.svg?header=)](https://www.archlinux.org/packages/?q=opam) + * [Package search](https://www.archlinux.org/packages/community/x86_64/opam/) + * Alexander F. Rødseth [@xyproto](https://github.com/xyproto) + +* Debian Linux (9, oldstable, Stretch) + [![Debian Oldstable package](https://repology.org/badge/version-for-repo/debian_oldstable/opam.svg?header=)](https://packages.debian.org/oldstable/source/opam) + * [Package search](https://packages.debian.org/search?keywords=opam&searchon=names&suite=all§ion=all) + * Mehdi Dogguy [@mehdid](https://github.com/mehdid), nicoo [@nbraud](https://www.github.com/nbraud) + +* Debian Linux (10, stable, Buster) + [![Debian Stable package](https://repology.org/badge/version-for-repo/debian_stable/opam.svg?header=)](https://packages.debian.org/stable/source/opam) + * [Package search](https://packages.debian.org/search?keywords=opam&searchon=names&suite=all§ion=all) + * Mehdi Dogguy [@mehdid](https://github.com/mehdid), nicoo [@nbraud](https://www.github.com/nbraud) + +* Debian Linux (11, testing, Bullseye) + [![Debian Testing package](https://repology.org/badge/version-for-repo/debian_testing/opam.svg?header=)](https://packages.debian.org/testing/source/opam) + * [Package search](https://packages.debian.org/search?keywords=opam&searchon=names&suite=all§ion=all) + * Mehdi Dogguy [@mehdid](https://github.com/mehdid), nicoo [@nbraud](https://www.github.com/nbraud) + +* Debian Linux (unstable, sid) + [![Debian Unstable package](https://repology.org/badge/version-for-repo/debian_unstable/opam.svg?header=)](https://packages.debian.org/unstable/source/opam) + * [Package search](https://packages.debian.org/search?keywords=opam&searchon=names&suite=all§ion=all) + * Mehdi Dogguy [@mehdid](https://github.com/mehdid), nicoo [@nbraud](https://www.github.com/nbraud) + +* Fedora 32 + [![Fedora 32 package](https://repology.org/badge/version-for-repo/fedora_32/opam.svg?header=)](https://src.fedoraproject.org/rpms/opam) + * [Package page](https://apps.fedoraproject.org/packages/opam) + * Ben Rosser [@TC01](https://www.github.com/TC01) + +* FreeBSD + [![FreeBSD port](https://repology.org/badge/version-for-repo/freebsd/opam.svg?header=)](https://www.freshports.org/devel/ocaml-opam) + * [Package search](http://www.freebsd.org/cgi/ports.cgi?query=opam&stype=all) + * Hannes Mehnert [@hannesm](https://www.github.com/hannesm) + +* Gnu Guix + [![GNU Guix package](https://repology.org/badge/version-for-repo/gnuguix/opam.svg?header=)](https://guix.gnu.org/packages/opam-2.0.6/) + * [Package definition](https://git.savannah.gnu.org/cgit/guix.git/tree/gnu/packages/ocaml.scm#n428) + * Julien Lepiller [@roptat](https://github.com/roptat) + +* Homebrew (MacOS X) + [![Homebrew package](https://repology.org/badge/version-for-repo/homebrew/opam.svg?header=)](https://formulae.brew.sh/formula/opam) + +* Macports (MacOS X) + [![MacPorts package](https://repology.org/badge/version-for-repo/macports/opam.svg?header=)](https://ports.macports.org/port/opam/summary) + * [Package definition](https://github.com/macports/macports-ports/blob/master/sysutils/opam/Portfile) + * Perry E. Metzger [@pmetzger](https://www.github.com/pmetzger) + +* Mageia Linux (Cauldron) + [![Mageia Cauldron package](https://repology.org/badge/version-for-repo/mageia_cauldron/opam.svg?header=)](https://madb.mageia.org/package/show/source/1/application/0/release/cauldron/name/opam) + * [Package definition](http://svnweb.mageia.org/packages/cauldron/opam/current/SPECS/opam.spec?view=markup) + * David Geiger [@david-geiger](https://www.github.com/david-geiger) + +* NixOS + [![nixpkgs stable package](https://repology.org/badge/version-for-repo/nix_stable/opam.svg?header=)](https://github.com/NixOS/nixpkgs/blob/release-20.03/pkgs/development/tools/ocaml/opam/default.nix#L114) + * [Package definitions](https://github.com/NixOS/nixpkgs/tree/master/pkgs/development/tools/ocaml/opam) + * Henry Till + +* OpenBSD + [![OpenBSD port](https://repology.org/badge/version-for-repo/openbsd/opam.svg?header=)](http://openports.se/sysutils/opam) + * [Package page](http://ports.su/sysutils/opam,-main) + * Christopher Zimmerman [@madroach](https://github.com/madroach) + +* OpenSuse (Tumbleweed) + [![openSUSE Tumbleweed package](https://repology.org/badge/version-for-repo/opensuse_tumbleweed/opam.svg?header=)](https://build.opensuse.org/package/show/openSUSE:Factory/opam) + * Anil Madhavapeddy [@avsm](https://www.github.com/avsm) + +* Ubuntu Linux (16.04, LTS, xenial) + [![Ubuntu 16.04 package](https://repology.org/badge/version-for-repo/ubuntu_16_04/opam.svg?header=)](https://packages.ubuntu.com/source/xenial/opam) + * [Package search](http://packages.ubuntu.com/search?keywords=opam&searchon=names&suite=all§ion=all) - bwrap unavailable + +* Ubuntu Linux (18.04, LTS, bionic) + [![Ubuntu 18.04 package](https://repology.org/badge/version-for-repo/ubuntu_18_04/opam.svg?header=)](https://packages.ubuntu.com/source/bionic/opam) + * [Package search](http://packages.ubuntu.com/search?keywords=opam&searchon=names&suite=all§ion=all) + +* Ubuntu Linux (19.10, LTS, eoan) + [![Ubuntu 20.10 package](https://repology.org/badge/version-for-repo/ubuntu_20_10/opam.svg?header=)](https://packages.ubuntu.com/source/eoan/opam) + * [Package search](http://packages.ubuntu.com/search?keywords=opam&searchon=names&suite=all§ion=all) + +* Ubuntu Linux (20.10, groovy) + [![Ubuntu 20.10 package](https://repology.org/badge/version-for-repo/ubuntu_20_10/opam.svg?header=)](https://packages.ubuntu.com/source/groovy/opam) + * [Package search](http://packages.ubuntu.com/search?keywords=opam&searchon=names&suite=all§ion=all) + +## Third party packages + + +* CentOS (6,7) + * Anil Madhavapeddy [@avsm](https://www.github.com/avsm) + +* Exherbo Linux + * 1.1.1 [Package page](http://git.exherbo.org/summer/packages/dev-ocaml/opam/index.html) (_ocaml-unofficial_) + * nicoo [@nbraud](https://www.github.com/nbraud) + +* Ubuntu Linux PPA + * 2.0.4 [Anil's official opam PPA](https://launchpad.net/~avsm) and [ppa post](https://discuss.ocaml.org/t/opam-2-0-experimental-ppas/2446) + * Anil Madhavapeddy [@avsm](https://www.github.com/avsm) + +* Windows + * [MinGW repo](https://github.com/fdopen/opam-repository-mingw) - Andreas Hauptmann [@fdopen](https://www.github.com/fdopen) + * David Allsopp [@dra27](https://www.github.com/dra27) + +If you can't find latest version packages for your distribution here, see [the +opam installation page](Install.html) for binaries or building from source. + +[Docker containers](http://hub.docker.com/r/ocaml/opam) for severals +distributions and OCaml compiler versions are also available. diff -Nru opam-2.0.10/doc/pages/FAQ.md opam-2.1.2/doc/pages/FAQ.md --- opam-2.0.10/doc/pages/FAQ.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/FAQ.md 2021-12-07 16:09:27.000000000 +0000 @@ -45,9 +45,9 @@ #### 🐫 What changes does opam do to my filesystem? -opam is designed to be run strictly as user (non-root), and apart for the -explicit options provided during `opam init`, only writes within `~/.opam` (and -`/tmp`). This directory — the default "opam root" — contains configuration, +opam is designed to be run strictly as user (non-root), and except for the +explicit options provided during `opam init`, opam only writes within `~/.opam` +(and `/tmp`). This directory — the default "opam root" — contains configuration, various internal data, a cache of downloaded archives, and your OCaml installations. @@ -89,11 +89,18 @@ - Only the _package_ build/install/remove commands are protected: if you install a program using opam and execute it, it will run with your standard user rights. +- If your installation uses unusual paths (opam root outside `HOME`, system + folder, etc.), since `2.0.1` you can use the environment variable + `OPAM_USER_PATH_RO` to have them handled by then sandbox script, e.g. This + variable format is the same as `PATH`, you can add it in your shell + configuration file, e.g `export OPAM_USER_PATH_RO=/rw/usrlocal:/media`. + Contained paths are added as read-only. - If needed, for special cases like unprivileged containers, sandboxing can be - disabled on `opam init` with the `--disable-sandboxing` flag. Or by using a - [custom `opamrc`](Manual.html#configfield-wrap-build-commands). Use wisely, - broken Makefiles that run `rm -rf /` - [__do__ happen](https://github.com/ocaml/opam/issues/3231). + disabled on `opam init` with the `--disable-sandboxing` flag (only for + non-initialised opam). Or by using a [custom + `opamrc`](Manual.html#configfield-wrap-build-commands). Use wisely, broken + Makefiles that run `rm -rf /` [__do__ + happen](https://github.com/ocaml/opam/issues/3231). --- @@ -165,9 +172,20 @@ The file format is human-readable, so you are free to edit the file before doing the `import` if you need to customise the installation. -You may also want to have a look at the `opam lock` plugin, that can memorise -the precise set of installed dependencies for a local package, and the -associated `opam install DIR --locked` command that can restore them. +### 🐫 How to share my working switch setup for a specific package ? + +When working on a project, it is sometimes needed to share a set of +dependencies that you know (locally) the project is working with. You can share +this set by generating a _locked_ opam file. Ths is easily done using the [`lock` +command](man/opam-lock.html): it creates an opam file with a `depends:` field +populated with all dependencies, at their exact version in the current +(working) switch. You can then share this `opam.locked` file, or check it +in your version-control system. + +```shell +$ opam lock # generate a .opam.lock file +$ opam install --locked # use locked file, if present +``` --- @@ -201,7 +219,10 @@ --- -#### 🐫 Some package fail during compilation, complaining about missing dependencies ("m4", "libgtk", etc.) +#### 🐫 Some packages fail during compilation, complaining about missing dependencies ("m4", "libgtk", etc.) + +> NOTE: since opam 2.1.0, the following is directly handled by opam, without +> relying on a plugin. They probably depend on system, non-OCaml libraries: they need to be installed using your system package manager (apt-get, yum, pacman, homebrew, etc.) since @@ -384,9 +405,33 @@ #### 🐫 opam is slow on top of NFS. How can I make it faster? opam root is usually located in the `home` directory, which, on top of NFS, -slow down opam operations. Locating opam root in `/tmp` is neither a solution, -you could loose your opam configuration at each reboot. +slows down opam operations. Locating opam root in `/tmp` is not a solution: +you could lose your opam configuration at each reboot. You can use the [`nfsopam`](https://github.com/UnixJunkie/nfsopam) script to -have the best of both world: persistence of NFS directory and fast operation of -local directory. +have the best of both worlds: persistence of NFS directories and fast operations +of local directories. + +--- + +#### 🐫 What does the `--cli` option do? Should I be using it everywhere? + +`--cli` was introduced in opam 2.1 to deal with changes in the command line +interface between releases. It tells opam to interpret the command line as a +specific version, in particular it means that new options or options which +have had their meaning altered will not be available, or will be behave as they +did in that version. It only affects the command-line - it does not, for +example, stop a root from being upgraded from an older version to the current +version. + +We recommend using it in scripts (and programs which call opam) since they can +then expect to work seamlessly with future versions of the opam client. It's +also a good idea to use it in blog posts, or other documentation you may share, +since it allows copy-and-paste to work reliably (a user with a newer version of +opam should have no issues and a user with an older opam gets a clearer error +message). + +We don't recommend using it in day-to-day use of opam in the shell, because +you'll be typing more and you won't get to notice exciting new features! If the +behaviour of a command or option is altered, and you write something which in no +longer valid, opam will try to tell you what the new command should look like. diff -Nru opam-2.0.10/doc/pages/index.menu opam-2.1.2/doc/pages/index.menu --- opam-2.0.10/doc/pages/index.menu 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/index.menu 2021-12-07 16:09:27.000000000 +0000 @@ -12,6 +12,7 @@ FAQ.md Tricks.md Packaging.md +Distribution.md External_solvers.md Manual.md diff -Nru opam-2.0.10/doc/pages/Install.md opam-2.1.2/doc/pages/Install.md --- opam-2.0.10/doc/pages/Install.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/Install.md 2021-12-07 16:09:27.000000000 +0000 @@ -13,6 +13,9 @@ `~/.opam` on first run if needed (if using our installer script, a backup can be made automatically). +To upgrade shell scripts, and enable sandboxing, don't forget to run `opam init +--reinit -ni`. + Then see the [Upgrade guide](Upgrade_guide.html) to check the changes. @@ -28,13 +31,14 @@ pre-compiled binary, backup your opam data if from an older version, and run `opam init`. -(If you have troule with `curl`, just +(If you have trouble with `curl`, just [download the script](https://raw.githubusercontent.com/ocaml/opam/master/shell/install.sh) and run `sh install.sh`) We provide pre-compiled binaries for: - Linux i686, amd64, arm7, arm64 -- OSX (intel 64 bits) +- OSX (intel 64 bits, arm64) +- We do not at present provide an official Windows distribution of opam, but please see [this separately maintained distribution](https://fdopen.github.io/opam-repository-mingw/) (other platforms are available using the other methods below) If you don't like scripts, you can just pick your download @@ -42,17 +46,22 @@ `opam`, and set it as executable, e.g. ``` -sudo cp /usr/local/bin/opam -sudo chmod a+x /usr/local/bin/opam +sudo install /usr/local/bin/opam ``` +> Note that this script is intended for end-users, not CI. For that purpose, +> you can use pre-built [Docker images for various +> configurations](https://hub.docker.com/r/ocaml/opam2/). ## Using your distribution's package system -This is generally the recommended way, when available and up-to-date. Here is a -list of supported distributions: +This is generally the recommended way, **when available and up-to-date** (you +can check [here](Distribution.html) the latest +available release per distribution). Here is a list of supported distributions: + +#### Arch Linux -#### Archlinux +[![badge](https://repology.org/badge/version-for-repo/arch/opam.svg)](https://repology.org/project/opam/versions) The [opam](https://www.archlinux.org/packages/community/x86_64/opam/) package is available in the official distribution. To install it simply run: @@ -62,15 +71,17 @@ ``` If you'd like to use the development version there is an [opam-git](https://aur.archlinux.org/packages/opam-git/) -package available in the [AUR](https://wiki.archlinux.org/index.php/AUR). -Assuming you have [yaourt](https://aur.archlinux.org/packages/yaourt) installed just run the following command: +package available in the [AUR](https://wiki.archlinux.org/index.php/Arch_User_Repository). +Assuming you have [yay](https://github.com/Jguer/yay) installed just run the following command: ``` -yaourt -S opam-git +yay -S opam-git ``` #### Debian +[![badge](https://repology.org/badge/version-for-repo/debian_stable/opam.svg)](https://repology.org/project/opam/versions) [![badge](https://repology.org/badge/version-for-repo/debian_testing/opam.svg)](https://repology.org/project/opam/versions) [![badge](https://repology.org/badge/version-for-repo/debian_unstable/opam.svg)](https://repology.org/project/opam/versions) + Binary packages of opam are available for the [stable](http://packages.debian.org/jessie/opam), [testing](http://packages.debian.org/stretch/opam) and @@ -99,11 +110,21 @@ #### [Fedora](http://fedoraproject.org), [CentOS](http://centos.org) and RHEL -No native packages at the moment, you will need to use our pre-built binaries, -or build from sources. +[![Fedora 32](https://repology.org/badge/version-for-repo/fedora_32/opam.svg)](https://repology.org/project/opam/versions) + +The opam package for Fedora can be installed with the command: + +``` +dnf install opam +``` + +There is not currently a package for CentOS/RHEL. You will need to use our +pre-built binaries, or build from sources. #### Mageia +[![badge](https://repology.org/badge/version-for-repo/mageia_cauldron/opam.svg)](https://repology.org/project/opam/versions) + The opam package for Mageia can be installed with the command: ``` @@ -112,16 +133,18 @@ #### OpenBSD -Opam builds via sources fine on OpenBSD 5.6 or earlier, and is available in the -ports and packages tree on OpenBSD 5.7 or higher. +[![badge](https://repology.org/badge/version-for-repo/openbsd/opam.svg)](https://repology.org/project/opam/versions) + +The opam package for OpenBSD can be installed with the command (since OpenBSD 5.7): ``` -cd /usr/ports/sysutils/opam -make install +pkg_add opam ``` #### FreeBSD +[![badge](https://repology.org/badge/version-for-repo/freebsd/opam.svg)](https://repology.org/project/opam/versions) + Opam is available in the ports and packages tree on FreeBSD 11 or higher. ``` @@ -131,12 +154,18 @@ #### OSX -Opam packages for [homebrew](http://mxcl.github.com/homebrew/) and -[MacPorts](http://www.macports.org/) are available: +[![badge](https://repology.org/badge/version-for-repo/homebrew/opam.svg)](https://repology.org/project/opam/versions) [![badge](https://repology.org/badge/version-for-repo/macports/opam.svg)](https://repology.org/project/opam/versions) + +Opam packages for [homebrew](http://mxcl.github.com/homebrew/) and [MacPorts](http://www.macports.org/) are available. +homebrew need a prior installation of `gpatch`, as opam uses gnu-specific options. ``` -brew install opam # Homebrew -port install opam # MacPort +# Homebrew +brew install gpatch +brew install opam + +# MacPort +port install opam ``` See also @@ -145,12 +174,33 @@ #### Ubuntu -Ubuntu has native packages for opam: +[![badge](https://repology.org/badge/version-for-repo/ubuntu_20_04/opam.svg)](https://repology.org/project/opam/versions) +##### Versions 18.04 and newer +There is a [ppa](https://launchpad.net/~avsm/+archive/ubuntu/ppa) available that contains the current stable version of `opam`. ``` +add-apt-repository ppa:avsm/ppa +apt update apt install opam ``` +##### Versions older than 18.04 +Use the binary distribution. Instructions provided at https://opam.ocaml.org/doc/Install.html#Binary-distribution + +#### Windows + +Full support for Windows is planned for opam 2.2, and we expect to provide an opam package in [Chocolatey](https://chocolatey.org/) and [winget](https://docs.microsoft.com/en-us/windows/package-manager/). If you'd like to help out, please get in touch! +#### Guix & Guix System + +[![badge](https://repology.org/badge/version-for-repo/gnuguix/opam.svg)](https://repology.org/project/opam/versions) + +The opam package for [guix](https://www.gnu.org/software/guix/) can be installed with the command: + +``` +# Guix +guix install opam +``` + ## From Sources #### Getting the Sources @@ -163,12 +213,9 @@ don't require any extra downloads, just the OCaml compiler -- 4.02.3 or later for the latest version): -* [2.0.0~rc](https://github.com/ocaml/opam/releases/download/2.0.0-rc/opam-full-2.0.0-rc.tar.gz) - MD5: 6e89905dbe9203dee3e883b70e210285 - SHA384: 8a9ee03cdcd78a7d44e92c9b1c6e841605a49ecff4ebd977a632708ef6250f9f3ec488ecd1852f76d1b6cfc2d8ad9117 -* [1.2.2](https://github.com/ocaml/opam/releases/download/1.2.2/opam-full-1.2.2.tar.gz) - MD5: 7d348c2898795e9f325fb80eaaf5eae8 - SHA384: 3a0a7868b5f510c1248959ed350eecacfe1abd886e373fd31066ce10871354010ef057934df026e5fad389ead6c2857d +* [2.0.8](https://github.com/ocaml/opam/releases/download/2.0.8/opam-full-2.0.8.tar.gz) + - MD5: 69e95d318fec8027b9eb6af6075a2a13 + - SHA384: f534860f511768f78f646be4248df58ecaf699dc55eea90e21f0d8d6e2bd23235a9ca132fcf17bf854cf3c25adfab4c8 Follow the instructions in the included [`README.md`](https://github.com/ocaml/opam#readme) to get opam built and @@ -178,16 +225,3 @@ > ``` > OCAMLPARAM="safe-string=0,_" make lib-ext > ``` - - -#### Using ocamlbrew - -[ocamlbrew](https://github.com/hcarty/ocamlbrew) is a script that can bootstrap -an OCaml environment including opam, from source. This option does not require -an existing OCaml installation, or a pre-compiled opam binary for your platform. -To bootstrap a new OCaml environment including opam, make sure that you have the -necessary pre-requisites installed to run ocamlbrew, and then run: - -``` -curl -kL https://raw.github.com/hcarty/ocamlbrew/master/ocamlbrew-install | env OCAMLBREW_FLAGS="-r" bash -``` diff -Nru opam-2.0.10/doc/pages/Manual.md opam-2.1.2/doc/pages/Manual.md --- opam-2.0.10/doc/pages/Manual.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/Manual.md 2021-12-07 16:09:27.000000000 +0000 @@ -2,49 +2,49 @@ .opam {font-family: Tahoma,Verdana,sans-serif; font-size: 110%; font-weight: lighter; line-height: 90.9%} --> -# The opam manual +# The opam manual -This manual gathers reference information on opam and its file formats. It is +This manual gathers reference information on opam and its file formats. It is primarily of use for packagers, package maintainers and repository maintainers. -* For simple usage of opam, see the [Usage](Usage.html) page, and the +* For simple usage of opam, see the [Usage](Usage.html) page, and the comprehensive built-in documentation [`opam [command] --help`](man/index.html). * For a gentler introduction to packaging, see the [Packaging guide](Packaging.html) -* If you want to hack on opam or build related tools, the API documentation can +* If you want to hack on opam or build related tools, the API documentation can be browsed [here](api/index.html) ## File hierarchies ### opam root -opam holds its configuration, metadata, logs, temporary directories and caches +opam holds its configuration, metadata, logs, temporary directories and caches within a directory that we will call _opam root_. By default, this is `~/.opam`, and we may refer to it by this name in this manual for the sake of simplicity, but this can be changed using the `OPAMROOT` environment variable or the `--root` command-line argument. -An existing opam root is required for opam to operate normally, and one is +An existing opam root is required for opam to operate normally, and one is created upon running `opam init`. The initial configuration can be defined through a configuration file at `~/.opamrc`, `/etc/opamrc` or at a location -specified through the `--config` command-line option. If none is present, opam +specified through the `--config` command-line option. If none is present, opam defaults to its built-in configuration that binds to the OCaml repository at `https://opam.ocaml.org`. -Except in explicit cases, opam only alters files within its opam root. It is +Except in explicit cases, opam only alters files within its opam root. It is organised as follows: -- [`~/.opam/config`](#config): the global opam configuration file +- [`~/.opam/config`](#config): the global opam configuration file - `~/.opam/repo/`: contains the mirrors of the configured package repositories - [`~/.opam/repo/repos-config`](#repos-config): lists the configured package repositories and their URLs - `~/.opam/repo/`: mirror of the given repository -- `~/.opam/opam-init/`: contains opam configuration scripts for the outside world, e.g. shell environment initialisation +- `~/.opam/opam-init/`: contains opam configuration scripts for the outside world, e.g. shell environment initialisation - `~/.opam/download-cache/`: caches of downloaded files - `~/.opam/plugins/`: reserved for plugins - `~/.opam/`: prefixes of named [switches](#Switches) ### Repositories -Repositories are collection of opam package definitions. They respect the +Repositories are collection of opam package definitions. They respect the following hierarchy: - [`/repo`](#repo): repository configuration file - [`/packages//./opam`](#opam): holds the metadata @@ -64,16 +64,27 @@ cache), needed when serving over HTTP. It can be generated using `opam admin index`. -opam repositories can be accessed using local or +opam repositories can be accessed using local or remote (ssh) paths, HTTP URLs, or one of the supported version control systems (git, Mercurial, Darcs). A repository is set up using ``` -opam repository add +opam repository add [--this-switch|--all-switches|--set-default] ``` -and can subsequently be then selected for use in specific switches using `opam -repository select `. Use `opam repository list --all` for an overview of +The last flag sets what switches are affected by the new repository: +- `--this-switch` (**default**) selects only the current switch +- `--all-switches` affects all the currently existing switches +- `--set-default` affects all switches created in the future + +Creating a new switch using e.g. a custom repository overlay on the default +repository can be done in a single call using: +``` +opam switch create --repos==,default +``` +which will define the new repository `` at `` if needed. + +Use `opam repository list --all` for an overview of configured repositories. Repository selection is always ordered, with the definition of a given version of a package being taken from the repository with the lowest index where it is found. @@ -84,14 +95,14 @@ ### Switches -opam is designed to hold any number of concurrent installation prefixes, called +opam is designed to hold any number of concurrent installation prefixes, called _switches_. Switches are isolated from each other and have their own set of installed packages, selection of repositories, and configuration options. All package-related commands operate on a single switch, and require one to be selected. The current switch can be selected in the following ways: -- globally, using `opam switch `. opam will use that switch for all +- globally, using `opam switch `. opam will use that switch for all further commands, except when overridden in one of the following ways. - for local switches, which are external to the opam root, when in the directory where the switch resides or a descendant. @@ -101,13 +112,13 @@ - through the `--switch ` command-line flag, for a single command. Switches have their own prefix, normally `~/.opam/`, where packages get -intalled ; to use what is installed in a switch, some environment variables need +installed ; to use what is installed in a switch, some environment variables need to be set, _e.g._ to make executables installed into `~/.opam//bin` visible, that directory needs to be added to `PATH`, but individual packages can define their own settings as well. Command `opam env` returns the environment updates corresponding to the -current switch, in a format readable by your shell, and when needed opam will +current switch, in a format readable by your shell, and when needed opam will prompt you to run: ``` @@ -120,7 +131,7 @@ being held at `~/.opam/`, it will be created in the given directory, as a `_opam` subdirectory. Local switches are automatically selected depending on the current directory, see above. -- If a `` is selected, opam will install the corresponding packages +- If a `` is selected, opam will install the corresponding packages and their dependencies in the new switch. These packages will be marked as _base_, protected against removal and unaffected by upgrade commands. `` can be selected among packages which have the `compiler` flag @@ -136,7 +147,7 @@ - `/`: prefix of the switch, holding the installation hierarchy in the UNIX `/usr` standard (with subdirectories `bin`, `lib`, `share`, `man`, `doc`, `etc`...) -- `/.opam-switch/`: holds all opam data regarding this switch +- `/.opam-switch/`: holds all opam data regarding this switch - [`/.opam-switch/switch-config`: switch-specific configuration](#switch-config) - [`/.opam-switch/switch-state`: stores the sets @@ -194,7 +205,7 @@ version. Whenever an install, reinstall or upgrade command-line refers to a pinned -package, opam first fetches its latest source. `opam +package, opam first fetches its latest source. `opam update [--development]` is otherwise the standard way to update the sources of all the packages pinned in the current switch. @@ -210,7 +221,7 @@ Syntax is given in a BNF-like notation. Non-terminals are written ``, terminals are either plain text or written in double-quotes (`"terminal"`), curly brackets denote zero or more repetitions when suffixed with `*`, or one or -more when suffixed with `+`, and square brackets denote zero or one occurence. +more when suffixed with `+`, and square brackets denote zero or one occurrence. Parentheses are for grouping. `(")` and `(""")` respectively mean one and three quotation mark characters. @@ -251,10 +262,10 @@ ::= ( "(*" { }* "*)" ) | ( "#" { }* ) ``` -The opam file formats share a common base syntax. The +The opam file formats share a common base syntax. The files are UTF-8 encoded and define a list of _fields_ and _sections_. -opam uses a range of different files, each allowing their own set of fields and +opam uses a range of different files, each allowing their own set of fields and sections, in a specific format. Base values can be literal booleans, integers or strings, identifiers, and @@ -267,7 +278,7 @@ sub-expressions. Comments may be either enclosed in `(*` and `*)`, or `#` and newline. They are -ignored by opam. +ignored by opam. ### Package Formulas @@ -351,7 +362,7 @@ #### Usage -Variables may appear at a few different places in opam files and configuration. +Variables may appear at a few different places in opam files and configuration. They can be used in two forms: - raw idents: `foo < bar` @@ -381,7 +392,7 @@ three scopes: 1. Global variables correspond to the general current configuration, or to the - current switch settings (system setup, opam configuration, current switch + current switch settings (system setup, opam configuration, current switch name, etc.). For example `opam-version`, `arch`, or `make`. 2. Package variables have the form `package-name:var-name` and contain values specific to a given package, for example `foo:installed` is a boolean @@ -414,7 +425,8 @@ variables by running: ``` -opam config list +opam config list # opam 2.0 +opam var # opam 2.1.0 ``` #### Global variables @@ -495,13 +507,16 @@ - `build-id`: a hash identifying the precise package version and metadata, and that of all its dependencies +- `opamfile`: + if the package is installed, path of its opam file, from opam internals, + otherwise not defined Extra variables can be defined by any package at installation time, using a [`.config`](#lt-pkgname-gt-config) file with a [`variables {}`](#dotconfigsection-variables) field. Additionally, the following are limited to some package fields (`depends:`, -`depopts:`, `build:`, `install:`, `remove:`, `run-test:`): +`depopts:`, `build:`, `install:`, `remove:`): - `with-test`: only true if tests have been enabled for this specific package @@ -575,7 +590,7 @@ package formula. The definition is similar to that of ``, except that two cases -`` and ` ` ar added to `` +`` and ` ` are added to `` ```BNF ::= @@ -639,7 +654,7 @@ `darcs://`. This assumes http transport for `hg` and `darcs`, _i.e._ `hg://` is short for `hg+http://` - Version control bound to a specific URL: `+://`, e.g. `git://`, - `hg+https://`, `git+file://`, etc. (**NOTE:** this has been added in opam 1.2.1) + `hg+https://`, `git+file://`, etc. (**NOTE:** this has been added in opam 1.2.1) In addition, version control URLs may be suffixed with the `#` character and a reference name (branch, commit, HEAD...): `git://foo.com/git/bar#master`, @@ -669,7 +684,7 @@ ## Specific file formats This section describes the precise file formats of the different kinds of files -used by opam. +used by opam. ### Public configuration files @@ -692,7 +707,7 @@ * `redirect: [ { } ... ]`: List of URLs to (permanently) redirect to if their filters evaluate to `true`. Can be used to serve different repositories for different OSes or different - versions of opam. Relative URLs are supported from + versions of opam. Relative URLs are supported from opam 2.0, but discouraged for compatibility reasons. * `archive-mirrors: [ ... ]`: Archive proxy URLs specific to this repository, with the same semantics as the @@ -706,12 +721,12 @@ #### opamrc This file has a format close to that of [config](#config), and can be used to -define an initial setup for opam. When running `opam init`, if `~/.opamrc` or +define an initial setup for opam. When running `opam init`, if `~/.opamrc` or `/etc/opamrc` is present, or if `--config` was specified, the configuration options from that file will be used, overriding the defaults. -The default, built-in initial config of opam can be seen with `opam init ---help`. +The default, built-in initial config of opam can be +seen with `opam init --show-default-opamrc`. - `opam-version: `: the file format version. @@ -742,7 +757,8 @@ [`best-effort-prefix-criteria:`](#configfield-best-effort-prefix-criteria), [`solver:`](#configfield-solver), [`global-variables:`](#configfield-global-variables), - [`default-compiler:`](#configfield-default-compiler): + [`default-compiler:`](#configfield-default-compiler), + [`default-invariant:`](#configfield-default-invariant): these have the same format as the same-named fields in the [config](#config) file, and will be imported to that file on `opam init`. [`default-compiler:`](#configfield-default-compiler) is additionally used to @@ -752,7 +768,7 @@ Package definitions can be a single [`opam`](#opam) file. A [`files/`](#files) subdirectory can also be used to add files over the package source. Older -versions of opam used [`descr`](#descr) and +versions of opam used [`descr`](#descr) and [`url`](#url) files besides the `opam` file, and this is still supported, but the preferred way is now to include their information into the `opam` file instead. @@ -787,8 +803,8 @@ a list of strings listing the original authors of the software. - `license: [ ... ]`: - the abbreviated name(s) of the license(s) under which the source software is - available. + The SPDX ID of the license(s) under which the source software is available + (see http://spdx.org/licenses/). - `homepage: [ ... ]`, `doc: [ ... ]`, @@ -817,7 +833,7 @@ - `substs: [ ... ]`: a list of files relative to the project source root. These files will be - generated from their `.in` counterparts, with variable interpolations + generated from their `.in` counterparts, with [variable interpolations](#Interpolation) expanded. - @@ -884,17 +900,18 @@ - `run-test: [ [ { } ... ] { } ... ]`: specific instructions for running the package tests, in a format similar to - the [`build:`](#opamfield-build) field. + the [`build:`](#opamfield-build) field. Run only when the package is + explicitly installed with `--with-test`. - `remove: [ [ { } ... ] { } ... ]`: commands to run before removing the package, in the same format as `build:` and `install:`. - As of `2.0`, opam tracks the files added to the prefix during package + As of `2.0`, opam tracks the files added to the prefix during package installation, and automatically removes them on package removal, so this should not be needed anymore in most cases (and may even be harmful if files from different packages overlap, which remove scripts generally don't handle). Use it for special actions, like reverting updates to files, or stopping - daemons: removing what was just added is alredy taken care of. + daemons: removing what was just added is already taken care of. The commands are run from the root of a fresh copy of the package source, unless the [`light-uninstall`](#opamflag-light-uninstall) package flag is @@ -919,8 +936,8 @@ Note that, in case of failed or interrupted builds, opam can not guarantee the invariant that `!build` dependencies are always installed. * `with-test` dependencies are only needed when building tests (when the - package is explicitely installed with `--with-test`) - * likewise, `with-doc` dependecies are only required when building the + package is explicitly installed with `--with-test`) + * likewise, `with-doc` dependencies are only required when building the package documentation - @@ -956,14 +973,18 @@ considered incompatible. This is useful to define sets of mutually conflicting packages. -- - `depexts: [ [ ... ] { } ... ]`: - the package external dependencies. This field may be used to describe the - dependencies of the package toward software or packages external to the opam ecosystem, for various systems. Each - `[ ... ] { }` element declares the strings to the left as - identifiers to required system-managed packages, while the filter to the right - allows one to select the systems they will be active on. +- `depexts: [ [ ... ] { } ... ]`: + the package external dependencies. This field is used to describe the + dependencies of the package toward packages external to the opam ecosystem; opam will then + use its knowledge of the system package manager to determine the availability + of the package, and install these external dependencies on the system as + prerequisites of the package, asking the user for administrator rights if + required. + + Each `[ ... ] { }` element declares the strings to the + left as identifiers to required system-managed packages, while the filter to + the right allows one to select the systems they will be active on. The filters typically use variables [`arch`](#opamvar-arch), [`os`](#opamvar-os), [`os-distribution`](#opamvar-os-distribution), @@ -994,7 +1015,7 @@ - `available: [ ]`: can be used to add constraints on the OS and other global variables. - In case the filter doesn't evaluete to `true`, the package is disabled. + In case the filter doesn't evaluate to `true`, the package is disabled. This field is evaluated before request solving or any actions take place ; it can only refer to global variables, since it shouldn't depend on the @@ -1020,7 +1041,7 @@ when creating a fresh prefix through the `opam switch` command. - `conf`: this is a "`conf`" package, that is intended to document capabilities of the system, or the presence of - software installed outside of opam. As such, the package may not + software installed outside of opam. As such, the package may not include a source URL or install anything, but just do some checks, and fail to install if they don't pass. `conf` packages should have a name starting with `conf-`, and include the appropriate @@ -1062,6 +1083,15 @@ defines environment updates that will be applied when running the package's build, install and remove scripts. + The following environment variables are set by opam (but can be overridden by `build-env`): + - `CDPATH=` + - `MAKEFLAGS=` + - `MAKELEVEL=` + - `OPAM_PACKAGE_NAME=` (`` is the name of the package being built/installed/removed) + - `OPAM_PACKAGE_VERSION=` (`` is the version of the package being built/installed/removed) + - `OPAMCLI=2.0` (since opam 2.1) + - `TMP` and `TMPDIR` are set by the sandbox script (bubblewrap), but should not be relied on since the sandbox is not used on all platforms and can be disabled by the user. + - `extra-source "{" "}"`: allows the definition of extra files that need downloading into the source tree before the package can be patched (if necessary) and built. The format is @@ -1077,19 +1107,21 @@ - `pin-depends: [ [ ] ... ]`: this field has no effect on the package repository, but is useful for in-source specification of development packages. When source-pinning the - package, either through `opam pin` or `opam install `, opam will prompt to pin every specified `` to the - associated ``. There are two important limitations: - - 1. `pin-depends:` are NOT transitive, that is, `pin-depends:` of packages + package, either through `opam pin` or `opam install `, opam + will prompt to pin every specified `` to the associated ``. + There are two important limitations: + + 1. If you want the pinned package be a dependency you need to add its + `` to `depends:` field. + 2. `pin-depends:` are NOT transitive, that is, `pin-depends:` of packages getting pinned through `pin-depends:` are ignored - 2. they won't get updated on `opam update`, the users will need to use `opam + 3. They won't get updated on `opam update`, the users will need to use `opam pin` or `opam install|upgrade DIR` again to get the new pins if the field has changed. Even then, this won't unpin any packages that would have been removed from `pin-depends:`. - `x-*: `: - extra fields prefixed with `x-` can be defined for use by external tools. opam + extra fields prefixed with `x-` can be defined for use by external tools. opam will ignore them except for some search operations. #### descr @@ -1100,7 +1132,7 @@ This information can be embedded in `opam` package definition files using the [`synopsis:`](#opamfield-synopsis) and [`description:`](#opamfield-description) -fields since opam version 2.0. However, if a `descr` file is present alongside +fields since opam version 2.0. However, if a `descr` file is present alongside the `opam` file, it takes precedence. #### url @@ -1141,7 +1173,7 @@ will be copied over the root of the package source. If already present, files are overwritten, and directories are recursively merged. [`opam`](#opam) file fields like [`patches:`](#opamfield-patches) refer to files at that same root, -so patches specific to opam are typically included in +so patches specific to opam are typically included in this subdirectory. Also see the [`extra-sources:`](#opamsection-extra-sources) opam section, which has @@ -1151,16 +1183,16 @@ This file format describes the installation from a source directory to an -installation prefix. It will be used by opam if +installation prefix. It will be used by opam if present at the root of the package's source directory after the `build:` instructions have been run: it can thus be generated by the build system, be -static in the package source, or be added by opam +static in the package source, or be added by opam through the [`files/`](#files) mechanism. To avoid duplicating efforts for managing installations, a stand-alone -`opam-installer` tool is provided with opam that can perform installations and +`opam-installer` tool is provided with opam that can perform installations and uninstallations from these files, or even generate corresponding shell scripts, -without requiring opam. +without requiring opam. All the fields have the form @@ -1171,7 +1203,7 @@ The following take a list of filenames (relative to the root of the package source) to be installed to the field's respective directory. An optional relative path and destination filename can be given using the postfix braces -syntax. A leading `?` in the origin filename is stripped and informs opam to +syntax. A leading `?` in the origin filename is stripped and informs opam to continue silently when the file is not found. Absolute paths, or paths referencing the parent directory (`..`), are not @@ -1180,10 +1212,10 @@ - `lib:` installs to `/lib//` - `lib_root:` - installs to `/lib/` (since opam 2.0.0) + installs to `/lib/` (since opam 2.0.0) - `libexec:` installs to `/lib//`, but the `exec` bit is set (since - opam 1.2.1) + opam 1.2.1) - `libexec_root:` installs to `/lib/`, with the `exec` bit set (since opam 2.0.0) @@ -1196,7 +1228,7 @@ - `share:` installs to `/share//` - `share_root:` - installs relative to `/share/` (since opam 1.2.0) + installs relative to `/share/` (since opam 1.2.0) - `etc:` installs to `/etc//` - `doc:` @@ -1217,8 +1249,8 @@ #### .config -This file is used by packages to give opam specific options upon -installation. A file with this name will be installed by opam into +This file is used by packages to give opam specific options upon +installation. A file with this name will be installed by opam into `/.opam-switch/config/` if found at the root of the package source tree after its installation instructions have been run. @@ -1226,29 +1258,30 @@ the file format version. - `file-depends: [ "[" "]" ... ]`: when a package defines `absolute-filename` - `hash` bindings using this field, - on state-changing operations, opam will check that the file at the given path + on state-changing operations, opam will check that the file at the given path still exists and has the given hash. This can be used to guarantee the consistency of packages that rely on system-wide files or system packages when those are changed, _e.g._ by `apt-get upgrade`. The user will be warned if the file was removed, and the package marked for reinstallation if it was changed. If the checksum is zero, then the file is assumed not to exist and opam will detect its appearance as requiring the package to be marked for reinstallation. -- `variables: "{" { : ( | [ ... ] | ) ... } +- `variables "{" { : ( | [ ... ] | ) ... } "}"`: allows the definition of package variables, that will be available as `:` to dependent packages. ### Local configuration files -These files are local to the opam root, and managed by opam. [`config`](#config) +These files are local to the opam root, and managed by opam. [`config`](#config) and [`switch-config`](#switch-config) can be manually edited to set configuration -options when opam isn't running. [`switch-state`](#switch-state) and +options when opam isn't running. [`switch-state`](#switch-state) and [`repos-config`](#repos-config) store internal state and are documented here, but -shouldn't be edited except by opam. +shouldn't be edited except by opam. #### config This file is stored as `~/.opam/config` and defines global configuration options -for opam. +for opam. Field values can be displayed and some of +them modified with [`opam option --global`](man/opam-option.html). - `opam-version: `: the version of the format of this opam root, used in particular to trigger @@ -1259,15 +1292,15 @@ [repos-config](#reposconfigfield-repositories) file. - `installed-switches: [ ... ]`: lists the switches configured in this opam root, either internal or local. - Deleted local switches are collected by opam automatically, and it is possible + Deleted local switches are collected by opam automatically, and it is possible to use local switches that are not recorded in this field. It remains useful - for cross-switch listings, repository configuration updates, or opam format + for cross-switch listings, repository configuration updates, or opam format migrations. - `switch: `: the currently globally selected switch. - `jobs: `: - the number of concurrent jobs to run for build processes. The default value is - calculated from the number of cores. + the number of concurrent jobs to run for build processes. If not defined, the + value is calculated from the number of cores. - `download-jobs: `: the maximum number of concurrent downloads. The default value is 3. - `download-command: [ ( | ) { } ... ]`: @@ -1363,7 +1396,8 @@ modified during the installation of the package. Note that this hook is run after the scan for installed files is done, so any additional installed files won't be recorded and must be taken - care of by a `pre-remove-commands` hook. + care of by a `pre-remove-commands` hook. However, modified or deleted installed + files during the `post-install-commands` will be handled correctly by `opam`. - `pre-session-commands: [ [ { } ... ] { } ... ]`, `post-session-commands: [ [ { } ... ] { } ... ]`: These commands will be run once respectively before and after the sequence of @@ -1373,7 +1407,7 @@ expected final state for `pre-session`, and to the actually reached state for `post-session`. - `installed`: all installed packages with versions. - - `new`: all packages or versions that are geting installed but wheren't + - `new`: all packages or versions that are getting installed but weren't present before the session. - `removed`: all packages or versions that were installed before, but no longer after the session. Note that an upgrade of `foo.0.1` to `foo.0.2` is considered @@ -1414,7 +1448,37 @@ - `default-compiler: [ ... ]`: a list of compiler package choices. On `opam init`, the first available compiler in the list will be chosen for creating the initial switch if - `--bare` wasn't specified. + `--bare` wasn't specified. Note that `default-invariant:` will still be used, + so the alternatives listed here should be compatible with it. + +- `default-invariant: [ ... ]`: + the default switch invariant that will be set on newly created switches, in + cases where nothing else was specified. + +- `depext: `: + enable or disable system dependency handling. When packages declare + dependencies on system packages using the [depexts](#opamfield-depexts) field + (typically, bindings to C libraries like SDL, require the library to be + installed, which is outside the scope of opam), if this is set to `true` (the + default), opam will check the availability of such dependencies using the host + system package manager, and prompt the user to install them when needed. + +- `depext-run-installs: `: + if `true` (the default), opam is allowed to run installations through the host + system package manager (_e.g._ `apt`, `yum` or `brew`) when required for the + installation of opam packages through their [depexts](#opamfield-depexts). + This is generally done through `sudo`, and always after prompting the user + (unless `--yes` was specified). if disabled, the installation command is + printed to stdout, and opam pauses to let the user proceed. + +- `depext-cannot-install: `: + instructs opam that no system package can be installed on the system. Any opam + package declaring system dependencies towards a system package that is not yet + installed will be marked as unavailable. + +- `depext-bypass: [ ... ] `: + assume the listed system packages to be already installed, bypassing the + checks normally done when `depext` is enabled. #### switch-config @@ -1453,6 +1517,8 @@ [`post-remove-commands:`](#configfield-post-remove-commands), [`post-session-commands:`](#configfield-post-session-commands): as the corresponding [global config](#config) fields. +- [`depext-bypass:`](#configfield-depext-bypass): + as the corresponding [global config](#config) field. - `paths "{" { : ... } "}"`: defines the standard paths within the switch: recognised fields include `prefix:`, `bin:`, `sbin:`, `lib:`, `share:`, `etc:`, `doc:`, `man:`, @@ -1460,10 +1526,13 @@ - `variables "{" { : ( | [ ... ] | ) ... } "}"`: allows the definition of variables local to the switch. +As [config](#config), field values can be displayed and some of them modified +with [`opam option`](man/opam-option.html). + #### switch-state This file, located at `/.opam-switch/switch-state`, is used by -opam to store the current state of a switch. All +opam to store the current state of a switch. All fields are lists of `` (_i.e._ `[ "." ... ]`). - `opam-version: `: diff -Nru opam-2.0.10/doc/pages/Packaging.md opam-2.1.2/doc/pages/Packaging.md --- opam-2.0.10/doc/pages/Packaging.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/Packaging.md 2021-12-07 16:09:27.000000000 +0000 @@ -7,7 +7,7 @@ ## Creating a package definition file -For complete documentation of the format, see [the manual](Manual.html#Packagedefinitions). +For complete documentation of the format, see [the manual](Manual.html#Package-definitions). If your project does not yet have a package definition, get to the root of its source, and then either @@ -44,10 +44,12 @@ use: ``` -build: ["dune" "build" "-p" name] +build: ["dune" "build" "-p" name "-j" jobs] ``` See [below](#The-file-format-in-more-detail) for more on the format. +It is recommended to use [`opam lint`](man/opam-lint.html) to check the +validity of your opam file. ## Testing @@ -75,7 +77,7 @@ First tag the project. Assuming this is version 0.1: ``` git tag -a 0.1 -git push 0.1 +git push origin 0.1 ``` Alternatively, you can create a release using the web UI (https://github.com/USER/REPO/releases/new). @@ -97,6 +99,10 @@ > `opam publish` can be re-run any number of times to update an existing > submission, or propose changes to an already released package. +> To fill the pull request, `opam publish` clones _opam repository_ over ssh, +> you need to have your *ssh keys* registered in your [Github +> account](https://help.github.com/en/articles/connecting-to-github-with-ssh). + ### Without opam-publish First, you will need to add a section in the following format to the package @@ -113,7 +119,7 @@ push back, as such: ``` -git clone git@github.com:USER/opam-repository --branch 2.0.0 +git clone git@github.com:USER/opam-repository cd opam-repository cp OPAM_FILE packages/NAME/NAME.VERSION/opam git add packages @@ -123,9 +129,6 @@ Then, back to the web UI, Github should propose to file a pull-request for your newly pushed branch. If not, select the `new pull request` button on the left. -Make sure to file your pull-request against the `2.0.0` base branch, since -package definitions in 1.2 format are not yet accepted on `master`. - ## The file format in more detail @@ -136,7 +139,8 @@ * `synopsis` should be a one-line description of what your package does, used in listings. It is recommended to also add a `description` field for a longer explanation (hint: you may delimit long strings with triple-quotation mark - delimiters `"""` to avoid escaping issues). + delimiters `"""` to avoid escaping issues). Since opam 2.0.1, linting requires + to have at least synopsis or description filled. * You'll probably be the `maintainer` for now, so give a way to contact you in case your package needs maintenance. * Most interesting is the `build` field, that tells opam how to compile the @@ -144,9 +148,9 @@ containing arguments either as a string (`"./configure"`) or a variable name (`make`, defined by default to point at the chosen "make" command -- think `$(MAKE)` in Makefiles). `%{prefix}%` is another syntax to replace variables - within strings. `opam config list` will give you a list of available - variables. `build` instructions shouldn't need to write outside of the - package's source directory. + within strings. `opam config list` (or `opam var` with opam 2.1.0) will give + you a list of available variables. `build` instructions shouldn't need to + write outside of the package's source directory. * `install` is similar to `build`, but tells opam how to install. The example above should indeed be `install: [ [make "install"] ]`, but the extra square brackets are optional when there is a single element. This field can be @@ -176,15 +180,15 @@ This is just a very short introduction, don't be afraid to consult [the reference](Manual.html#opam) for details and more: -* [**Version constraints**](Manual.html#PackageFormulas): an optional version +* [**Version constraints**](Manual.html#Package-Formulas): an optional version constraint can be added after any package name in `depends`: simply write `"package" {>= "3.2"}`. Warning, versions are strings too, don't forget the quotes. -* [**Formulas**](Manual.html#PackageFormulas): depends are by default a +* [**Formulas**](Manual.html#Package-Formulas): depends are by default a conjunction (all of them are required), but you can use the logical "and" `&` and "or" `|` operators, and even group with parentheses. The same is true for version constraints: `("pkg1" & "pkg2") | "pkg3" {>= "3.2" & != "3.7"}`. -* [**Build depends**](Manual.html#Filteredpackageformulas): you may add, among +* [**Build depends**](Manual.html#Filtered-package-formulas): you may add, among others, the key `build` in the version constraints, _e.g._ `"package" {build & >= "3.2"}`, to indicate that there is no run-time dependency to this package: it is required but won't trigger rebuilds of your @@ -206,7 +210,8 @@ package formula like `depends`. simple list of package names. If you require specific versions, add a `conflicts` field with the ones that won't work. * [**Variables**](Manual.html#Variables): you can get a list of predefined - variables that you can use in your opam rules with `opam config list`. + variables that you can use in your opam rules with `opam config list` (or + `opam var` with opam 2.1.0). * [**Filters**](Manual.html#Filters): dependencies, commands and single command arguments may need to be omitted depending on the environment. This uses the same optional argument syntax as above, postfix curly braces, with boolean @@ -214,6 +219,6 @@ ``` ["./configure" "--with-foo" {ocaml-version > "3.12"} "--prefix=%{prefix}%"] - [make "byte"] { !ocaml-native } - [make "native"] { ocaml-native } + [make "byte"] { !ocaml:native } + [make "native"] { ocaml:native } ``` diff -Nru opam-2.0.10/doc/pages/Specifying_Solver_Preferences.md opam-2.1.2/doc/pages/Specifying_Solver_Preferences.md --- opam-2.0.10/doc/pages/Specifying_Solver_Preferences.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/Specifying_Solver_Preferences.md 2021-12-07 16:09:27.000000000 +0000 @@ -67,8 +67,6 @@ to instruct a solver to minimise downgrades, and mininise the installed size, among other criteria. -The `aspcud` solver supports this extended language starting from its version 1.8.0, which unfortunately is not the version shipped by default with Ubuntu precise or Debian Wheezy. - ### News in aspcud 1.9.x Starting from version 1.9.0, `aspcud` adds support for three extra selectors, that are particularly useful to perform local upgrades. Here they are: @@ -80,12 +78,3 @@ Using this extended set of package selector, it is now finally possible to specify user preferences that describe optimisations to be applied only to the packages explicitly mentioned in the request. For example, `-notuptodate(request),-count(changed)` would find a solution that tries to bring all packages mentioned in the request to their latest version, while leaving all the rest as untouched as possible. And if we have added to each package a `priority` value, we could also play with preferences like `+sum(upgraderequest,priority),-count(changed)` to get the packages mentioned in the upgrade request to the version with the highest possible priority, while leaving all the rest as untouched as possible. - -## Preferences only work with the external solvers - -For portability reasons, `opam` also embarks an ad-hoc solver module that is built by wrapping a set of heuristics around the code of the SAT-solver which is used in the [Dose Library](http://dose.gforge.inria.fr/public_html/) for detecting broken packages. This solver module has no support for user preferences, and is not able to manage correctly large package repositories: it is highly recommended that you install an external CUDF solver (`aspcud` is the one best supported today). - -## Using external solvers in the Cloud - -Thanks to support from [Irill](http://www.irill.org/), it is now possible to use an external solver for `opam` on any platform, over the network. See the [CUDF solver farm](http://cudf-solvers.irill.org/) for instructions. -The latest version of the solver is on the farm, so you can use the full preferences language with it. diff -Nru opam-2.0.10/doc/pages/Tricks.md opam-2.1.2/doc/pages/Tricks.md --- opam-2.0.10/doc/pages/Tricks.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/Tricks.md 2021-12-07 16:09:27.000000000 +0000 @@ -139,3 +139,17 @@ For `bash`, just include `$(COMMAND)` in your definition of `PS1` (making sure the `$` is escaped). For `zsh`, use `setopt prompt_subst`, and include `$(COMMAND)` in the definition of `prompt`. + +--- + +#### How to install a maximum number of packages ? + +The following sequence of commands tries to install as much packages as possible in a local switch with OCaml `$(VERSION)`. + +```shell +opam update +opam switch create . ocaml-base-compiler.$(VERSION) +export OPAMSOLVERTIMEOUT=3600 +opam list --available -s | xargs opam install --best-effort --yes +# Be patient +``` diff -Nru opam-2.0.10/doc/pages/Upgrade_guide.md opam-2.1.2/doc/pages/Upgrade_guide.md --- opam-2.0.10/doc/pages/Upgrade_guide.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/Upgrade_guide.md 2021-12-07 16:09:27.000000000 +0000 @@ -42,7 +42,8 @@ option [`--working-dir`](man/opam-install.html#lbAH) can be used to temporarily make opam fetch uncommitted changes (see also -[`--inplace-build`](man/opam-install.html#lbAG)). +[`--inplace-build`](man/opam-install.html#lbAG)), and [`--assume-built`](man/opam-install.html#lbAG) +to run only installation instructions, assuming that build has been done locally. Upon pinning, opam 2.0 will also select the current branch by default if unspecified, so that later running `git checkout BRANCH` in the source tree doesn't affect the pinned package. @@ -87,9 +88,10 @@ - [`opam install --destdir`](man/opam-install.html#lbAF) can be used to copy build artefacts of given packages to an external prefix -- __sandboxing__: on Linux, all package commands will now be sandboxed by +- __sandboxing__: on Linux and MacOS, all package commands will now be sandboxed by default. The [`bubblewrap`](https://github.com/projectatomic/bubblewrap) tool - is now required to this end. + is now required to this end on Linux, and the `sandbox_exec` command is used + on MacOS. See [`faq entry`](FAQ.html#Why-does-opam-require-bwrap) for more details. ## File formats @@ -101,25 +103,38 @@ > should be transparent unless you publish packages: > > - [**The main repository**](https://github.com/ocaml/opam-repository/tree/master) -> remains in format **1.2** for now. This means the the `master` branch, and -> the contents of `https://opam.ocaml.org/packages`. -> - [**There is a 2.0.0 branch**](https://github.com/ocaml/opam-repository/tree/master) -> that is served at `opam.ocaml.org/2.0`. -> -> Everything from 1.2 is converted to 2.0 when needed (on the fly by opam, -> [automatically](https://github.com/AltGr/camelus/tree/2.0) on the git -> repository, or manually using -> [`opam admin upgrade`](man/opam-admin-upgrade.html)). -> +> is now in format **2.0**. This means the `master` branch, and the +> contents of `https://opam.ocaml.org/packages`. +> - [**There is a 1.2 branch**](https://github.com/ocaml/opam-repository/tree/1.2) +> that is served at `opam.ocaml.org/1.2.2`. > > When publishing packages, remember that: > -> - packages in **1.2 format** must be published to `master`, and they will be -> available to **everyone** > - packages in **2.0 format** must be published to the `2.0.0` branch — e.g. > using the new > [opam-publish.2.0](https://github.com/ocaml/opam-publish/tree/2.0). They > will **only** be available to opam 2.0 users. +> - packages in **1.2 format** are no more accepted, expect for relevant fixes. +> In that case they must be published to `1.2` branch. +> +> - [`opam-publish.2.0.0`](https://github.com/ocaml/opam-publish/tree/2.0) has +> a fully revamped interface, and many new features, like filing a single PR +> for multiple packages. It files pull-request in 2.0 format only to master +> branch of the repository. The new version of +> [`dune-release.1.0.1`](https://github.com/samoht/dune-release/tree/master) +> handles the new format. +> +> - It is also advised to keep in-source opam files in 1.2 format until that +> date, so as not to break uses of `opam pin add --dev-repo` by opam 1.2 users. +> +> - The small `opam-package-upgrade` plugin can be used to upgrade single 1.2 +> opam files to 2.0 format. You can also use API to upgrade you opam files, +> using +> [`OpamFormatUpgrade.opam_file`](https://opam.ocaml.org/doc/api/opam-state/OpamFormatUpgrade/#val-opam_file), +> available in package `opam-state`. +> +> - More advice for package maintainers and custom repositories in this [blog +> post](https://opam.ocaml.org/blog/opam-2-0-0-repo-upgrade-roadmap/#Advice-for-package-maintainers). - compiler definition files: these no longer exist, as compilers have been replaced by normal package definitions (that should have @@ -164,8 +179,7 @@ - __the conversion from the 1.2 format is done internally and automatic, both for repositories and when pinning.__ Be careful, however, not to submit 2.0 - format files if they are to be used by opam 1.2, or published to the main - repository before it makes the transition. + format files if they are to be used by opam 1.2. ### New features diff -Nru opam-2.0.10/doc/pages/Usage.md opam-2.1.2/doc/pages/Usage.md --- opam-2.0.10/doc/pages/Usage.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/pages/Usage.md 2021-12-07 16:09:27.000000000 +0000 @@ -45,6 +45,14 @@ added and you should simply run `opam update`, or it's not available on your system or OCaml version — `opam install PACKAGE` will give you the reason. +## `--cli` option + +Since opam 2.1, opam is able to be invoked using a previous version of its +command line. It's recommended that all opam commands in scripts use it, and +also blog posts and other sources of information, so you may come across +examples which include it. We don't recommend using it in the shell because it's +more to type! + ## Details on commands ### opam init @@ -120,11 +128,12 @@ opam pin add ``` -Where `` may be a version, but also a local path, the URL of an archive, -or even a git, mercurial or darcs URL. The package will be kept up-to-date with -its origin on `opam update` and when explicitly mentioned in a command, so that -you can simply run `opam upgrade ` to re-compile it from its -upstream. If the upstream includes opam metadata, that will be used as well. +Where `` may be a version, but also an [URL](Manual.html#URLs) of a +local path, an archive, or even a git, mercurial or darcs repository. The +package will be kept up-to-date with its origin on `opam update` and when +explicitly mentioned in a command, so that you can simply run `opam upgrade +` to re-compile it from its upstream. If the upstream includes +opam metadata, that will be used as well. ``` opam pin add camlpdf 1.7 # version pin @@ -133,7 +142,7 @@ opam pin add opam-lib --dev-repo # upstream repository ``` -This actually a powerful mechanism to divert any package definition, and can +This is actually a powerful mechanism to divert any package definition, and can even be used to locally create packages that don't have entries in the repositories. diff -Nru opam-2.0.10/doc/release/readme.md opam-2.1.2/doc/release/readme.md --- opam-2.0.10/doc/release/readme.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/doc/release/readme.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -## Steps to follow for each release - -* Update version (and copyright year) in `configure.ac`, `shell/opam_installer.sh` -* Run `make configure` to regenerate `./configure` -* Run `make tests`, `opam-rt` (with and without aspcud) -- now checked by travis -* Run `make doc` to re-generate the API documetation - --- - -* update the CHANGELOG -* tag the release (git tag -a 1.2.1; git push origin 1.2.1) -* create a release on github based on your tag (https://github.com/ocaml/opam/releases/new) - --- - -* Generate an inclusive source tarball (and the binary for your current arch while you're at it): -``` -./shell/release.sh full-archive binary publish -n git-name:git-token -``` -* Check that it's been properly uploaded on https://github.com/ocaml/opam/releases -* Ask people on other archs (and with write access to opam) to run -``` -wget https://raw.github.com/ocaml/opam/master/shell/release.sh && \ -bash -ue ./release.sh -t $VERSION -``` - --- - -* Add some news about the release on the platform blog -* Update the installation instructions in doc/pages -* Update the opam-lib, opamfu, opam2web opam packages -* Announce ! (platform-list, caml-list) diff -Nru opam-2.0.10/dune opam-2.1.2/dune --- opam-2.0.10/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/dune 2021-12-07 16:09:27.000000000 +0000 @@ -1 +1 @@ -(ignored_subdirs (bootstrap)) +(dirs :standard \ bootstrap) diff -Nru opam-2.0.10/dune-project opam-2.1.2/dune-project --- opam-2.0.10/dune-project 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/dune-project 2021-12-07 16:09:27.000000000 +0000 @@ -1,2 +1,2 @@ -(lang dune 1.2) +(lang dune 1.11) (name opam) diff -Nru opam-2.0.10/.gitattributes opam-2.1.2/.gitattributes --- opam-2.0.10/.gitattributes 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/.gitattributes 2021-12-07 16:09:27.000000000 +0000 @@ -3,15 +3,22 @@ # Shell scripts, autoconf, etc. must have LF endings, even on Windows *.sh text eol=lf -*.zsh text eol=lf -configure text eol=lf -diff +configure text eol=lf -diff linguist-generated configure.ac text eol=lf msvs-detect text eol=lf check_linker text eol=lf *.m4 text eol=lf +changelog_checker text eol=lf +*.cmd text eol=crlf -# Treat patches as binary for safety -*.patch binary +# For diffing simplicity, the patch re-write test uses LF endings on Windows +tests/patcher-test.reference text eol=lf + +# Don't normalise patch files +*.patch -text # Actual binary files *.pdf binary + +# Avoid conflicts in master_changes.md +master_changes.md merge=union diff -Nru opam-2.0.10/.github/issue_template.md opam-2.1.2/.github/issue_template.md --- opam-2.0.10/.github/issue_template.md 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/.github/issue_template.md 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,7 @@ +> If your issue concerns a package not building, please report to +> https://github.com/ocaml/opam-repository/issues or to the package maintainer +> unless you are confident it is an issue in the opam tool itself. + +``` + +``` diff -Nru opam-2.0.10/.github/pull_request_template.md opam-2.1.2/.github/pull_request_template.md --- opam-2.0.10/.github/pull_request_template.md 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/.github/pull_request_template.md 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +Please update `master_changes.md` file with your changes. diff -Nru opam-2.0.10/.github/scripts/changelog_checker opam-2.1.2/.github/scripts/changelog_checker --- opam-2.0.10/.github/scripts/changelog_checker 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/.github/scripts/changelog_checker 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,60 @@ +# This script check that the current master changelog has been updated by PR, +# ignoring some internal files. +# It is used byt the changelog_check github action. + +# (c) Copyright Raja Boujbel OCamlPro 2020 + +set -ue + +IGNORE=" +.gitattributes +.github +.gitignore +.ocamlinit +.ocp-indent +.ocplint +.travis-ci.sh +.travis.yml +AUTHORS +CONTRIBUTING.md +README.md +appveyor.patch +appveyor.yml +appveyor_build.cmd +appveyor_test.sh +master_changes.md +shell/install.sh +" +changelog=master_changes.md +diffile=/tmp/diff + +git fetch origin $GITHUB_BASE_REF --depth=1 --quiet +echo "> base commit" +git show origin/$GITHUB_BASE_REF --format=oneline -s + +git diff origin/$GITHUB_BASE_REF --name-only --diff-filter=AMRCX > $diffile +updated=0 +grep -sq $changelog $diffile || updated=1 + +echo "> all changes" +cat $diffile + +for ign in $IGNORE ; do + sed -i "/^${ign//\//\\\/}/d" $diffile +done + +echo "> kept changes" +cat $diffile + +num_changes=`wc -l $diffile | cut -f 1 -d ' '` + +if [ $num_changes -ne 0 ] ; then + if [ $updated -eq 0 ] ; then + echo -e "\033[32mChangelog updated\033[m" + else + echo -e "\033[31mPlease update changelog in master_changes.md\033[m" + exit 1 + fi +else + echo -e "\033[33mCommitted files not concerned by changelog\033[m" +fi diff -Nru opam-2.0.10/.github/scripts/hygiene.sh opam-2.1.2/.github/scripts/hygiene.sh --- opam-2.0.10/.github/scripts/hygiene.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/.github/scripts/hygiene.sh 2021-12-07 16:09:27.000000000 +0000 @@ -79,6 +79,51 @@ esac (set +x ; echo -en "::endgroup::check configure\r") 2>/dev/null + +### +# Check install.sh +### + +if [ "$GITHUB_EVENT_NAME" = "pull_request" ] ; then + (set +x ; echo -en "::group::check install.sh\r") 2>/dev/null + if ! git diff "$BASE_REF_SHA..$PR_REF_SHA" --name-only --exit-code -- shell/install.sh > /dev/null ; then + echo "shell/install.sh updated - checking it" + eval $(grep '^\(OPAM_BIN_URL_BASE\|DEV_VERSION\|VERSION\)=' shell/install.sh) + echo "OPAM_BIN_URL_BASE=$OPAM_BIN_URL_BASE" + echo "VERSION = $VERSION" + echo "DEV_VERSION = $DEV_VERSION" + for VERSION in $DEV_VERSION $VERSION; do + eval $(grep '^TAG=' shell/install.sh) + echo "TAG = $TAG" + ARCHES=0 + + while read -r key sha + do + ARCHES=1 + URL="$OPAM_BIN_URL_BASE$TAG/opam-$TAG-$key" + echo "Checking $URL" + check=$(curl -Ls "$URL" | sha512sum | cut -d' ' -f1) + if [ "$check" = "$sha" ] ; then + echo "Checksum as expected ($sha)" + else + echo -e "[\e[31mERROR\e[0m] Checksum downloaded: $check" + echo -e "[\e[31mERROR\e[0m] Checksum install.sh: $sha" + ERROR=1 + fi + done < <(sed -ne "s/.*opam-$TAG-\([^)]*\).*\"\([^\"]*\)\".*/\1 \2/p" shell/install.sh) + done + if [ $ARCHES -eq 0 ] ; then + echo "[\e[31mERROR\e[0m] No sha512 checksums were detected in shell/install.sh" + echo "That can't be right..." + ERROR=1 + fi + else + echo "No changes in install.sh" + fi + (set +x ; echo -en "::endgroup::check install.sh\r") 2>/dev/null +fi + + ### # Check src_ext patches ### @@ -98,4 +143,21 @@ cd .. (set +x ; echo -en "::endgroup::check src_ext patches\r") 2>/dev/null +### +# Default cli version check +### + +if [ "$GITHUB_EVENT_NAME" = "push" ] && [ "$BRANCH" = "master" ]; then + (set +x ; echo -en "::group::check default cli\r") 2>/dev/null + CURRENT_MAJOR="`sed -n "s/^AC_INIT(opam,\([0-9]\+\)[^0-9]*.*)$/\1/p" configure.ac`" + DEFAULT_CLI_MAJOR="`sed -n "/let *default *=/s/.*(\([0-9]*\)[^0-9]*.*/\1/p" src/client/opamCLIVersion.ml`" + if [ $CURRENT_MAJOR -eq $DEFAULT_CLI_MAJOR ]; then + echo "Major viersion is default cli one: $CURRENT_MAJOR" + else + echo -e "[\e[31mERROR\e[0m] Major version $CURRENT_MAJOR and default cli version $DEFAULT_CLI_MAJOR mismatches" + (set +x ; echo -en "::endgroup::check default cli\r") 2>/dev/null + ERROR=1 + fi +fi + exit $ERROR diff -Nru opam-2.0.10/.github/scripts/main.sh opam-2.1.2/.github/scripts/main.sh --- opam-2.0.10/.github/scripts/main.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/.github/scripts/main.sh 2021-12-07 16:09:27.000000000 +0000 @@ -14,10 +14,15 @@ (set +x ; echo -en "::group::build opam\r") 2>/dev/null if [[ $OPAM_TEST -eq 1 ]] ; then export OPAMROOT=$OPAMBSROOT + # If the cached root is newer, regenerate a binary compatible root + opam env || { rm -rf $OPAMBSROOT; init-bootstrap; } eval $(opam env) fi ./configure --prefix ~/local --with-mccs + if [ "$OPAM_TEST" != "1" ]; then + echo 'DUNE_PROFILE=dev' >> Makefile.config + fi if [[ $OPAM_TEST$OPAM_COLD -eq 0 ]] ; then make lib-ext @@ -25,7 +30,7 @@ if [ $OPAM_UPGRADE -eq 1 ]; then unset-dev-version fi - make all + make all admin rm -f ~/local/bin/opam make install @@ -35,6 +40,27 @@ opam config report if [ "$OPAM_TEST" = "1" ]; then + # test if an upgrade is needed + set +e + opam list 2> /dev/null + rcode=$? + if [ $rcode -eq 10 ]; then + echo "Recompiling for an opam root upgrade" + (set +x ; echo -en "::group::rebuild opam\r") 2>/dev/null + unset-dev-version + make all admin + rm -f ~/local/bin/opam + make install + opam list 2> /dev/null + rcode=$? + set -e + if [ $rcode -ne 10 ]; then + echo -e "\e[31mBad return code $rcode, should be 10\e[0m"; + exit $rcode + fi + (set +x ; echo -en "::endgroup::rebuild opam\r") 2>/dev/null + fi + set -e # Note: these tests require a "system" compiler and will use the one in $OPAMBSROOT make tests @@ -57,17 +83,17 @@ git checkout -b $BRANCH origin/$BRANCH fi else - git checkout opam2.0 - git reset --hard origin/opam2.0 + git checkout master + git reset --hard origin/master fi - test -d _opam || opam switch create . --empty - opam install ocaml-system + test -d _opam || opam switch create . --no-install --formula '"ocaml-system"' eval $(opam env) - opam pin --kind=path $GITHUB_WORKSPACE --yes --no-action - opam pin . -yn - OPAMSOLVERTIMEOUT=3600 opam install opam-rt --deps-only - make + opam pin $GITHUB_WORKSPACE -yn --with-version to-test + # opam lib pins defined in opam-rt are ignored as there is a local pin + opam pin . -yn --ignore-pin-depends + opam install opam-rt --deps-only opam-devel.to-test + make || { opam reinstall opam-client -y; make; } (set +x ; echo -en "::endgroup::opam-rt\r") 2>/dev/null fi ) @@ -75,7 +101,6 @@ export PATH=~/local/bin:$PATH if [ $OPAM_UPGRADE -eq 1 ]; then - OPAM12CACHE=`eval echo $OPAM12CACHE` OPAM12=$OPAM12CACHE/bin/opam if [[ ! -f $OPAM12 ]]; then mkdir -p $OPAM12CACHE/bin @@ -128,7 +153,10 @@ # The SHA is fixed so that upstream changes shouldn't affect CI. The SHA needs # to be moved forwards when a new version of OCaml is added to ensure that the # ocaml-system package is available at the correct version. - opam init --bare default "git+https://github.com/ocaml/opam-repository#$OPAM_TEST_REPO_SHA" + opam init --bare default git+https://github.com/ocaml/opam-repository#$OPAM_TEST_REPO_SHA + cat >> $(opam var root --global 2>/dev/null)/config <~/.opamrc -required-tools: [ - ["curl" "wget"] - {"A download tool is required, check env variables OPAMCURL or OPAMFETCH"} - "diff" - "patch" - "tar" - "unzip" -] -wrap-build-commands: [] -wrap-install-commands: [] -wrap-remove-commands: [] -EOF - # used only for TEST jobs init-bootstrap () { - if [ "$OPAM_TEST" = "1" ]; then + if [ "$OPAM_TEST" = "1" ] || [ -n "$SOLVER" ]; then set -e export OPAMROOT=$OPAMBSROOT # The system compiler will be picked up opam init --yes --no-setup git+https://github.com/ocaml/opam-repository#$OPAM_REPO_SHA + + cat >> $OPAMROOT/config <.cached +export OPAMROOT=$OPAMBSROOT +echo $OPAMROOT + +case "$SOLVER" in + z3) + PKGS=$SOLVER + ;; + 0install) + PKGS="$SOLVER opam-0install-cudf" + ;; + *) + echo -e "\e[31mSolver $SOLVER not handled\e[0m"; + exit 3 + ;; +esac + +opam switch create $SOLVER ocaml-system || true +opam install $PKGS +opam install . --deps +opam clean --logs --switch-cleanup +eval $(opam env) +./configure +make diff -Nru opam-2.0.10/.github/workflows/changelog_check.yml opam-2.1.2/.github/workflows/changelog_check.yml --- opam-2.0.10/.github/workflows/changelog_check.yml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/.github/workflows/changelog_check.yml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,12 @@ +name: Changelog check + +on: + pull_request: + branches: master + +jobs: + diff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: bash .github/scripts/changelog_checker diff -Nru opam-2.0.10/.github/workflows/ci.yml opam-2.1.2/.github/workflows/ci.yml --- opam-2.0.10/.github/workflows/ci.yml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/.github/workflows/ci.yml 2021-12-07 16:09:27.000000000 +0000 @@ -3,15 +3,17 @@ on: [ push, pull_request ] env: - OPAMBSVERSION: 2.0.7 + OPAMBSVERSION: 2.1.0-beta4 OPAMBSROOT: ~/.cache/.opam.cached OPAM12CACHE: ~/.cache/opam1.2/cache - OPAM_TEST_REPO_SHA: b02110b549a0b5275806ce27444fabac71093a0e - OPAM_REPO_SHA: 754c005bee4b198787ffc01f82e6c1f9c6356c2c + # This should be identical to the value in appveyor.yml + OPAM_TEST_REPO_SHA: 38e8f54c584fa3cfe779890f7a56fe88ee38be78 + OPAM_REPO_SHA: 38e8f54c584fa3cfe779890f7a56fe88ee38be78 # Default ocaml version for some jobs OCAML_VERSION: 4.12.0 ## variables for cache paths GH_OCAML_CACHE: ~/.cache/ocaml-local/** + SOLVER: defaults: run: @@ -22,6 +24,30 @@ #### # Caches #### +# ocaml-cache: +# runs-on: ${{ matrix.os }} +# strategy: +# matrix: +# os: [ ubuntu-latest ] +# ocamlv: [ 4.02.3, 4.03.0, 4.04.2, 4.05.0, 4.06.1, 4.07.1, 4.08.1, 4.09.1, 4.10.2, 4.11.2 ] +# include: +# - os: macos-latest +# ocamlv: 4.11.2 +# fail-fast: false +# steps: +# - uses: actions/checkout@v2 +# - name: ocaml ${{ matrix.ocamlv }} cache +# id: ocaml-cache +# uses: actions/cache@v2 +# with: +# path: ${{ env.GH_OCAML_CACHE }} +# key: ${{ runner.os }}-ocaml-${{ matrix.ocamlv }}-${{ hashFiles ('.github/scripts/ocaml-cache.sh', '.github/scripts/preamble.sh') }} +# - name: Building ocaml ${{ matrix.ocamlv }} cache +# if: steps.ocaml-cache.outputs.cache-hit != 'true' +# env: +# OCAML_VERSION: ${{ matrix.ocamlv }} +# run: bash -exu .github/scripts/ocaml-cache.sh ocaml + archives-cache: runs-on: ubuntu-latest steps: @@ -31,7 +57,7 @@ uses: actions/cache@v2 with: path: src_ext/archives - key: archives-2.0-${{ hashFiles('src_ext/Makefile.sources', 'src_ext/Makefile') }} + key: archives-${{ hashFiles('src_ext/Makefile.sources', 'src_ext/Makefile') }} - name: Retrieve archives if: steps.archives.outputs.cache-hit != 'true' run: |#bash -exu .github/scripts/caches.sh archives @@ -82,7 +108,7 @@ # Opam tests #### test: - needs: [ build ] + needs: [ build, archives-cache ] runs-on: ${{ matrix.os }} strategy: matrix: @@ -114,7 +140,7 @@ path: | ${{ env.OPAMBSROOT }}/** ~/.cache/opam-local/bin/** - key: opam-2.0-${{ runner.os }}-${{ env.OPAMBSVERSION }}-${{ matrix.ocamlv }}-${{ env.OPAM_REPO_SHA }}-${{ hashFiles ('.github/scripts/opam-bs-cache.sh', '.github/scripts/preamble.sh') }} + key: opam-${{ runner.os }}-${{ env.OPAMBSVERSION }}-${{ matrix.ocamlv }}-${{ env.OPAM_REPO_SHA }}-${{ hashFiles ('.github/scripts/opam-bs-cache.sh', '.github/scripts/preamble.sh') }} - name: opam root cache if: steps.opam-bootstrap.outputs.cache-hit != 'true' run: bash -exu .github/scripts/opam-bs-cache.sh @@ -123,12 +149,15 @@ uses: actions/cache@v2 with: path: ~/.cache/opam-rt/** - key: ${{ runner.os }}-opam-rt-2.0-${{ matrix.ocamlv }} + key: ${{ runner.os }}-opam-rt-${{ matrix.ocamlv }} - name: Test env: OCAML_VERSION: ${{ matrix.ocamlv }} run: bash -exu .github/scripts/main.sh +#### +# Opam cold +#### cold: needs: [ build, archives-cache ] runs-on: ${{ matrix.os }} @@ -147,7 +176,7 @@ uses: actions/cache@v2 with: path: src_ext/archives - key: archives-2.0-${{ hashFiles('src_ext/Makefile.sources', 'src_ext/Makefile') }} + key: archives-${{ hashFiles('src_ext/Makefile.sources', 'src_ext/Makefile') }} - name: Cold env: OCAML_VERSION: ${{ matrix.ocamlv }} @@ -157,6 +186,59 @@ make lib-pkg bash -exu .github/scripts/main.sh +#### +# Compile solver backends +#### + solvers: + needs: [ build ] + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: [ ubuntu-latest, macos-latest ] + ocamlv: [ 4.12.0 ] + solver: [ z3, 0install ] + fail-fast: false + env: + SOLVER: ${{ matrix.solver }} + OPAM_REPO_SHA: 9355b17f9c22a2b44740e3fc5f25f24771929f26 + OPAMBSROOT: ~/.cache/opam.${{ matrix.solver }}.cached + steps: + - uses: actions/checkout@v2 + - name: install deps + if: ${{ matrix.os == 'ubuntu-latest' }} + run: sudo apt install bubblewrap + - name: ocaml ${{ matrix.ocamlv }} cache - test + id: ocaml-cache-test + uses: actions/cache@v2 + with: + path: ${{ env.GH_OCAML_CACHE }} + key: ${{ runner.os }}-ocaml-${{ matrix.ocamlv }}-${{ matrix.solver }}-${{ hashFiles ('.github/scripts/ocaml-cache.sh', '.github/scripts/preamble.sh') }}-test + - name: Building ocaml ${{ env.OCAML_VERSION }} cache - test + if: steps.ocaml-cache-test.outputs.cache-hit != 'true' + env: + OCAML_VERSION: ${{ matrix.ocamlv }} + run: bash -exu .github/scripts/ocaml-cache.sh ocaml + - name: opam bootstrap cache + id: opam-bootstrap + uses: actions/cache@v2 + with: + path: | + ~/.cache/opam.${{ matrix.solver }}.cached + ~/.cache/opam-local/bin/** + key: opam-${{ matrix.solver }}-${{ runner.os }}-${{ env.OPAMBSVERSION }}-${{ matrix.ocamlv }}-${{ hashFiles ('.github/scripts/opam-bs-cache.sh', '.github/scripts/preamble.sh') }} + - name: opam root cache + env: + OPAMBSROOT: ~/.cache/opam.${{ matrix.solver }}.cached + if: steps.opam-bootstrap.outputs.cache-hit != 'true' + run: bash -exu .github/scripts/opam-bs-cache.sh + - name: Compile + env: + OCAML_VERSION: ${{ matrix.ocamlv }} + run: bash -exu .github/scripts/solvers.sh + +#### +# Upgrade from 1.2 to current +#### upgrade: needs: [ build ] runs-on: ${{ matrix.os }} @@ -204,7 +286,7 @@ uses: actions/cache@v2 with: path: src_ext/archives - key: archives-2.0-${{ hashFiles('src_ext/Makefile.sources', 'src_ext/Makefile') }} + key: archives-${{ hashFiles('src_ext/Makefile.sources', 'src_ext/Makefile') }} - name: Hygiene env: # Defined only on pull request jobs diff -Nru opam-2.0.10/.gitignore opam-2.1.2/.gitignore --- opam-2.0.10/.gitignore 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/.gitignore 2021-12-07 16:09:27.000000000 +0000 @@ -4,6 +4,8 @@ _olint/ bootstrap/ tests/tmp/ +tests/.merlin +tests/reftests/.merlin .*.swp src_ext/lib src_ext/cudf/ @@ -16,12 +18,14 @@ src_ext/ocamlgraph/ src_ext/dune-local/ src_ext/result/ +src_ext/stdlib-shims/ src_ext/opam-file-format/ src_ext/camlp4 src_ext/findlib src_ext/ocamlbuild src_ext/topkg src_ext/seq +src_ext/secondary/ src_ext/*.*stamp src_ext/*.tbz src_ext/*.tar.gz @@ -66,12 +70,18 @@ config.status aclocal.m4 autom4te.cache -src/*/.merlin -src/client/manifest.inc -src/client/opamManifest.inc -src/client/*.dll -src/stubs/dune -src/tools/opam-putenv.inc +src/ocaml-flags-configure.sexp +src/**/.merlin +src/client/linking.sexp +src/client/no-git-version +src/core/developer +src/manifest/dune +src/manifest/install.inc +src/stubs/libacl/dune +src/stubs/libacl/c-libraries.sexp +src/stubs/win32/cc64 +src/stubs/win32/dune +src/stubs/c-flags.sexp # doc doc/dev-manual/*aux doc/dev-manual/*.html @@ -82,4 +92,5 @@ doc/tutorials/opam.wiki doc/dev-manual/*.out doc/pages/*.html +doc/*.build src/x_build_libs.ocp diff -Nru opam-2.0.10/ISSUE_TEMPLATE.md opam-2.1.2/ISSUE_TEMPLATE.md --- opam-2.0.10/ISSUE_TEMPLATE.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/ISSUE_TEMPLATE.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -> If your issue concerns a package not building, please report to -> https://github.com/ocaml/opam-repository/issues or to the package maintainer -> unless you are confident it is an issue in the opam tool itself. - -``` - -``` diff -Nru opam-2.0.10/Makefile opam-2.1.2/Makefile --- opam-2.0.10/Makefile 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/Makefile 2021-12-07 16:09:27.000000000 +0000 @@ -5,8 +5,14 @@ all: opam opam-installer @ +admin: + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) opam-admin.install + +DUNE_PROMOTE_ARG = +DUNE_PROMOTE_ARG += --promote-install-files + ifeq ($(DUNE),) - DUNE_EXE = src_ext/dune-local/_build_bootstrap/install/default/bin/dune$(EXE) + DUNE_EXE = src_ext/dune-local/dune.exe ifeq ($(shell command -v cygpath 2>/dev/null),) DUNE := $(DUNE_EXE) else @@ -14,6 +20,11 @@ endif else DUNE_EXE= + # NB make does not export the PATH update in Makefile.config to $(shell ...) + ifeq ($(shell PATH='$(PATH)' $(DUNE) build --root . --help=plain 2>/dev/null \ + | grep -F -- '$(DUNE_PROMOTE_ARG) '),) + DUNE_PROMOTE_ARG = + endif endif OPAMINSTALLER = ./opam-installer$(EXE) @@ -21,37 +32,47 @@ ALWAYS: @ -DUNE_PROMOTE_ARG := $(shell dune build --help=plain 2>/dev/null | sed -ne 's/^[[:space:]]*\(--promote-install-files\)[[:space:]]*$$/ \1/p;s/^[[:space:]]*\(--promote-install-files\)\[.*$$/ \1/p') DUNE_DEP = $(DUNE_EXE) -JBUILDER_ARGS ?= +JBUILDER_ARGS ?= DUNE_ARGS ?= $(JBUILDER_ARGS) DUNE_PROFILE ?= release -src_ext/dune-local/_build_bootstrap/install/default/bin/dune$(EXE): src_ext/dune-local.stamp - cd src_ext/dune-local && ocaml bootstrap.ml && ./boot.exe --release +ifeq ($(DUNE_PROFILE_ARG),release) + # TODO Replace with --release when we require dune >= 2.5 + DUNE_PROFILE_ARG = --profile=release +else + DUNE_PROFILE_ARG = --profile=$(DUNE_PROFILE) +endif + +src_ext/dune-local/dune.exe: src_ext/dune-local.stamp $(DUNE_SECONDARY) +ifeq ($(DUNE_SECONDARY),) + cd src_ext/dune-local && ocaml bootstrap.ml +else + cd src_ext/dune-local && ( unset OCAMLLIB ; unset CAML_LD_LIBRARY_PATH ; PATH="$(dir $(realpath $(DUNE_SECONDARY))):$$PATH" ../../$(DUNE_SECONDARY) bootstrap.ml ) +endif src_ext/dune-local.stamp: $(MAKE) -C src_ext dune-local.stamp dune: $(DUNE_DEP) - @$(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) @install + @$(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) @install opam: $(DUNE_DEP) build-opam processed-opam.install - $(LN_S) -f _build/default/src/client/opamMain.exe $@$(EXE) + @$(LN_S) -f _build/default/src/client/opamMain.exe $@$(EXE) ifneq ($(MANIFEST_ARCH),) @mkdir -p Opam.Runtime.$(MANIFEST_ARCH) - @cp -f src/client/Opam.Runtime.$(MANIFEST_ARCH).manifest Opam.Runtime.$(MANIFEST_ARCH)/ - @cd Opam.Runtime.$(MANIFEST_ARCH) && $(LN_S) -f ../src/client/libstdc++-6.dll . - @cd Opam.Runtime.$(MANIFEST_ARCH) && $(LN_S) -f ../src/client/libwinpthread-1.dll . - @cd Opam.Runtime.$(MANIFEST_ARCH) && $(LN_S) -f ../src/client/$(RUNTIME_GCC_S).dll . + @cp -f src/manifest/Opam.Runtime.$(MANIFEST_ARCH).manifest Opam.Runtime.$(MANIFEST_ARCH)/ + @cd Opam.Runtime.$(MANIFEST_ARCH) && $(LN_S) -f ../_build/install/default/bin/Opam.Runtime.$(MANIFEST_ARCH)/libstdc++-6.dll . + @cd Opam.Runtime.$(MANIFEST_ARCH) && $(LN_S) -f ../_build/install/default/bin/Opam.Runtime.$(MANIFEST_ARCH)/libwinpthread-1.dll . + @cd Opam.Runtime.$(MANIFEST_ARCH) && $(LN_S) -f ../_build/install/default/bin/Opam.Runtime.$(MANIFEST_ARCH)/$(RUNTIME_GCC_S).dll . endif opam-installer: $(DUNE_DEP) build-opam-installer processed-opam-installer.install - $(LN_S) -f _build/default/src/tools/opam_installer.exe $@$(EXE) + @$(LN_S) -f _build/default/src/tools/opam_installer.exe $@$(EXE) -opam-admin.top: ALWAYS $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) src/tools/opam_admin_top.bc - $(LN_S) -f _build/default/src/tools/opam_admin_top.bc $@$(EXE) +opam-admin.top: $(DUNE_DEP) + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) src/tools/opam_admin_topstart.bc + $(LN_S) -f _build/default/src/tools/opam_admin_topstart.bc $@$(EXE) lib-ext: $(MAKE) -j -C src_ext lib-ext @@ -76,8 +97,8 @@ distclean: clean clean-ext rm -rf autom4te.cache bootstrap rm -f Makefile.config config.log config.status aclocal.m4 - rm -f src/*.META src/*/.merlin src/stubs/dune src/client/*.dll - rm -f src/tools/opam-putenv.inc src/client/manifest.inc src/client/opamManifest.inc + rm -f src/*.META src/*/.merlin src/manifest/dune src/manifest/install.inc src/stubs/libacl/dune src/stubs/win32/dune src/stubs/win32/cc64 src/ocaml-flags-configure.sexp src/stubs/libacl/c-libraries.sexp + rm -f src/client/linking.sexp src/stubs/c-flags.sexp src/core/developer src/core/version OPAMINSTALLER_FLAGS = --prefix "$(DESTDIR)$(prefix)" OPAMINSTALLER_FLAGS += --mandir "$(DESTDIR)$(mandir)" @@ -93,35 +114,35 @@ endif ifneq ($(LIBINSTALL_DIR),) - OPAMINSTALLER_FLAGS += --libdir "$(LIBINSTALL_DIR)" + OPAMINSTALLER_FLAGS += --libdir "$(LIBINSTALL_DIR)" --docdir "$(LIBINSTALL_DIR)/../doc" endif opam-devel.install: $(DUNE_DEP) $(DUNE) build $(DUNE_ARGS) -p opam opam.install - sed -e "s/bin:/libexec:/" opam.install > $@ + sed -e "/lib\/opam\/opam/d" -e "s/bin:/libexec:/" opam.install > $@ opam-%.install: $(DUNE_DEP) $(DUNE) build $(DUNE_ARGS) -p opam-$* $@ .PHONY: build-opam-installer build-opam-installer: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) opam-installer.install$(DUNE_PROMOTE_ARG) + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) $(DUNE_PROMOTE_ARG) -- opam-installer.install opam-installer.install: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) opam-installer.install$(DUNE_PROMOTE_ARG) + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) $(DUNE_PROMOTE_ARG) -- opam-installer.install .PHONY: build-opam build-opam: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) opam-installer.install opam.install$(DUNE_PROMOTE_ARG) + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) $(DUNE_PROMOTE_ARG) -- opam-installer.install opam.install opam.install: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) opam-installer.install opam.install$(DUNE_PROMOTE_ARG) + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) $(DUNE_PROMOTE_ARG) -- opam-installer.install opam.install OPAMLIBS = core format solver repository state client opam-%: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) opam-$*.install$(DUNE_PROMOTE_ARG) + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) $(DUNE_PROMOTE_ARG) -- opam-$*.install opam-lib: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) $(patsubst %,opam-%.install,$(OPAMLIBS))$(DUNE_PROMOTE_ARG) + $(DUNE) build $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) $(DUNE_PROMOTE_ARG) -- $(patsubst %,opam-%.install,$(OPAMLIBS)) installlib-%: opam-installer opam-%.install $(if $(wildcard src_ext/lib/*),\ @@ -135,6 +156,12 @@ libinstall: $(DUNE_DEP) opam-admin.top $(OPAMLIBS:%=installlib-%) @ +custom-libinstall: $(DUNE_DEP) opam-lib opam + for p in $(OPAMLIBS); do \ + ./opam$(EXE) custom-install --no-recompilations opam-$$p.$(version) -- \ + $(DUNE) install --root . opam-$$p; \ + done + processed-%.install: %.install sed -f process.sed $^ > $@ @@ -149,18 +176,56 @@ $(OPAMINSTALLER) -u $(OPAMINSTALLER_FLAGS) $< $(OPAMINSTALLER) -u $(OPAMINSTALLER_FLAGS) opam-installer.install -checker: - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) src/tools/opam_check.exe - -.PHONY: tests tests-local tests-git -tests: $(DUNE_DEP) - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) opam.install src/tools/opam_check.exe - $(DUNE) build --profile=$(DUNE_PROFILE) $(DUNE_ARGS) doc/man/opam-topics.inc doc/man/opam-admin-topics.inc - $(DUNE) runtest --force --no-buffer --profile=$(DUNE_PROFILE) $(DUNE_ARGS) src/ tests/ +.PHONY: tests +tests: $(DUNE_DEP) src/client/no-git-version + @$(DUNE) runtest $(DUNE_PROFILE_ARG) --root . $(DUNE_ARGS) src/ tests/ --no-buffer; \ + ret=$$?; \ + echo "### TESTS RESULT SUMMARY ###"; \ + for t in _build/default/tests/reftests/*.test; do \ + printf "%-30s" $$(basename $$t .test); \ + if diff -q --strip-trailing-cr $$t $${t%.test}.out >/dev/null; \ + then printf '\033[32m[ OK ]\033[m\n'; \ + else printf '\033[31m[FAIL]\033[m\n'; \ + fi; \ + done; \ + test $$ret -eq 0 + +.PHONY: crowbar +# only run the quickcheck-style tests, not very covering +crowbar: $(DUNE_DEP) + $(DUNE) exec --root . -- src/crowbar/test.exe + +.PHONY: crowbar-afl +# runs the real AFL deal, but needs to be done in a +afl switch +crowbar-afl: $(DUNE_DEP) + $(DUNE) build --root . -- src/crowbar/test.exe + mkdir -p /tmp/opam-crowbar-input -p /tmp/opam-crowbar-output + echo foo > /tmp/opam-crowbar-input/foo + afl-fuzz -i /tmp/opam-crowbar-input -o /tmp/opam-crowbar-output dune exec src/crowbar/test.exe @@ + +INTERMEDIATE: src/client/no-git-version +src/client/no-git-version: + touch src/client/no-git-version # tests-local, tests-git -tests-%: - $(MAKE) -C tests $* +tests-%: $(DUNE_DEP) src/client/no-git-version + $(DUNE) build $(DUNE_ARGS) $(DUNE_PROFILE_ARG) --root . @reftest-legacy-$* --force + +reftest-gen: src/client/no-git-version + echo >tests/reftests/dune.inc + $(DUNE) build $(DUNE_ARGS) $(DUNE_PROFILE_ARG) --root . @reftest-gen --auto-promote --force + +reftest-runner: $(DUNE_DEP) src/client/no-git-version + $(DUNE) build $(DUNE_ARGS) $(DUNE_PROFILE_ARG) --root . tests/reftests/run.exe + +reftests: $(DUNE_DEP) src/client/no-git-version + $(DUNE) build $(DUNE_ARGS) $(DUNE_PROFILE_ARG) --root . @reftest + +reftest-%: $(DUNE_DEP) src/client/no-git-version + $(DUNE) build $(DUNE_ARGS) $(DUNE_PROFILE_ARG) --root . @reftest-$* --force + +reftests-meld: + meld `for t in tests/reftests/*.test; do echo --diff $$t _build/default/$${t%.test}.out; done` .PHONY: doc doc: all @@ -187,15 +252,18 @@ .PHONY: compiler cold compiler: - env MAKE=$(MAKE) ./shell/bootstrap-ocaml.sh $(OCAML_PORT) + env MAKE=$(MAKE) BOOTSTRAP_EXTRA_OPTS= BOOTSTRAP_OPT_TARGET=opt.opt BOOTSTRAP_ROOT=.. BOOTSTRAP_DIR=bootstrap ./shell/bootstrap-ocaml.sh $(OCAML_PORT) + +src_ext/secondary/ocaml/bin/ocaml: + env MAKE=$(MAKE) BOOTSTRAP_EXTRA_OPTS="--disable-ocamldoc --disable-debug-runtime --disable-debugger" BOOTSTRAP_OPT_TARGET=opt BOOTSTRAP_ROOT=../.. BOOTSTRAP_DIR=src_ext/secondary ./shell/bootstrap-ocaml.sh $(OCAML_PORT) cold: compiler - env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" ./configure --enable-cold-check $(CONFIGURE_ARGS) - env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" $(MAKE) lib-ext - env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" $(MAKE) + env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" CAML_LD_LIBRARY_PATH= ./configure --enable-cold-check $(CONFIGURE_ARGS) + env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" CAML_LD_LIBRARY_PATH= $(MAKE) lib-ext + env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" CAML_LD_LIBRARY_PATH= $(MAKE) cold-%: - env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" $(MAKE) $* + env PATH="`pwd`/bootstrap/ocaml/bin:$$PATH" CAML_LD_LIBRARY_PATH= $(MAKE) $* .PHONY: run-appveyor-test run-appveyor-test: diff -Nru opam-2.0.10/Makefile.config.in opam-2.1.2/Makefile.config.in --- opam-2.0.10/Makefile.config.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/Makefile.config.in 2021-12-07 16:09:27.000000000 +0000 @@ -9,6 +9,7 @@ CONF_OCAMLFLAGS = @CONF_OCAMLFLAGS@ MCCS_ENABLED = @MCCS_ENABLED@ +OPAM_0INSTALL_SOLVER_ENABLED = @OPAM_0INSTALL_SOLVER_ENABLED@ OCAMLLIB = @OCAMLLIB@ @@ -17,6 +18,7 @@ OCAMLC = @OCAMLC@ OCAMLOPT = @OCAMLOPT@ DUNE = @DUNE@ +DUNE_SECONDARY = @DUNE_SECONDARY@ LN_S = @LN_S@ EXE = @EXE@ @@ -33,4 +35,6 @@ MANIFEST_ARCH = @MANIFEST_ARCH@ RUNTIME_GCC_S = @RUNTIME_GCC_S@ +PATCH = @PATCH@ + export OCAMLVERSION OCAMLFIND OCAML OCAMLC OCAMLOPT EXE PATH INCLUDE LIB CPATH LIBRARY_PATH OCAMLLIB diff -Nru opam-2.0.10/master_changes.md opam-2.1.2/master_changes.md --- opam-2.0.10/master_changes.md 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/master_changes.md 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,121 @@ +Working version changelog, used as a base for the changelog and the release +note. +Possibly scripts breaking changes are prefixed with ✘. +New option/command/subcommand are prefixed with ◈. + +## Version + * + +## Global CLI + * + * Fix typo in error message for opam var [#4786 @kit-ty-kate - fix #4785] + * Add cli 2.2 handling [#4853 @rjbou] + * --no-depexts is the default in CLI 2.0 mode [#4908 @dra27] + +## Plugins + * + +## Init + * + +## Config report + * + +## Install + * + +## Remove + * + +## Switch + * + +## Pin + * + +## List + * + +## Show + * + +## Var + * + +## Option + * + +## Lint + * + +## Lock + * + +## Opamfile + * + +## External dependencies + * + +## Sandbox + * + +## Repository management + * + +## VCS + * + +## Build + * + +## Infrastructure + * + +## Admin + * + +## Opam installer + * + +## State + * + +# Opam file format + * + +## Solver + * + +## Client + * + +## Internal + * Generalise `mk_tristate_opt` to `mk_state_opt` [#4575 @rjbou] + * Fix `mk_state_opt` and rename to `mk_enum_opt` [#4626 @rjbou] + * Add `mk_enum_opt_all` for state flags that appears more than once [#4582 @rjbou] + * Fix `opam exec` on native Windows when calling cygwin executables [#4588 @AltGr] + * Fix temporary file with a too long name causing errors on Windows [#4590 @AltGr] + * CLI: Add flag deprecation and replacement helper [#4595 @rjbou] + * Win32 Console: fix VT100 support [#3897 #4710 @dra27] + * Tidied the opam files [#4620 @dra27] + * Externalise cli versioning tools from `OpamArg` into `OpamArgTools` [#4606 @rjbou] + * Each library defines its own environment variables, that fills the config record [#4606 @rjbou] + * Harden cygpath wrapper [#4625 @dra27] + * Reset the plugin symlinks when the root is upgraded [#4641 @dra27 - partial fix for #4619] + * Formalise opam dev version detection with `OpamVersion.is_dev_version` [#4665 @dra27] + * Add `OpamStd.String.is_prefix_of` [#4694 @rjbou @dra27] + * Fix `OpamStd.Format.pretty_list`: `last` argument dropped if list contains more than 2 elements [#4694 @rjbou] + * + +## Test + * + +## Shell + * + +## Doc + * + +## Security fixes + * diff -Nru opam-2.0.10/.ocamlinit opam-2.1.2/.ocamlinit --- opam-2.0.10/.ocamlinit 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/.ocamlinit 2021-12-07 16:09:27.000000000 +0000 @@ -1,4 +1,5 @@ #use "topfind";; +#use "down.top";; #require "opam-client";; OpamClientConfig.opam_init ();; diff -Nru opam-2.0.10/opam-admin.opam opam-2.1.2/opam-admin.opam --- opam-2.0.10/opam-admin.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-admin.opam 2021-12-07 16:09:27.000000000 +0000 @@ -17,7 +17,7 @@ bug-reports: "https://github.com/ocaml/opam/issues" license: "LGPL-2.1-only WITH OCaml-LGPL-linking-exception" depends: [ - "dune" {>= "1.2.1"} - "re" {>= "1.7.2"} - "opam-file-format" {>= "2.0.0~rc2"} + "dune" {>= "1.11.0"} + "re" {>= "1.9.0"} + "opam-file-format" {>= "2.1.3"} ] diff -Nru opam-2.0.10/opam-client.opam opam-2.1.2/opam-client.opam --- opam-2.0.10/opam-client.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-client.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ opam-version: "2.0" -version: "2.0.10" -synopsis: "Client library for opam 2.0" +version: "2.1.2" +synopsis: "Client library for opam 2.1" description: """ Actions on the opam root, switches, installations, and front-end. """ @@ -31,7 +31,7 @@ "opam-solver" {= version} "extlib" {>= "1.7.3" & < "1.7.8"} "opam-repository" {= version} - "re" {>= "1.7.2"} + "re" {>= "1.9.0"} "cmdliner" {>= "1.0.0"} - "dune" {>= "1.2.1"} + "dune" {>= "1.11.0"} ] diff -Nru opam-2.0.10/opam-core.opam opam-2.1.2/opam-core.opam --- opam-2.0.10/opam-core.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-core.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ opam-version: "2.0" -version: "2.0.10" -synopsis: "Core library for opam 2.0" +version: "2.1.2" +synopsis: "Core library for opam 2.1" description: """ Small standard library extensions, and generic system interaction modules used by opam. """ @@ -30,8 +30,8 @@ "base-unix" "base-bigarray" "ocamlgraph" - "re" {>= "1.5.0"} - "dune" {>= "1.2.1"} + "re" {>= "1.9.0"} + "dune" {>= "1.11.0"} "cppo" {build & >= "1.1.0"} ] conflicts: "extlib-compat" diff -Nru opam-2.0.10/opam-devel.opam opam-2.1.2/opam-devel.opam --- opam-2.0.10/opam-devel.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-devel.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ opam-version: "2.0" -version: "2.0.10" -synopsis: "Bootstrapped development binary for opam 2.0" +version: "2.1.2" +synopsis: "Bootstrapped development binary for opam 2.1" description: """ This package compiles (bootstraps) opam. For consistency and safety of the installation, the binaries are not installed into the PATH, but into lib/opam-devel, from where the user can manually install them system-wide. """ @@ -24,13 +24,12 @@ build: [ ["./configure" "--disable-checks" "--prefix" prefix] [make "%{name}%.install"] - [make "tests"] {with-test} ] depends: [ "ocaml" {>= "4.02.3"} "opam-client" {= version} - "cmdliner" {>= "1.0.0"} - "dune" {>= "1.2.1"} + "cmdliner" {>= "0.9.8"} + "dune" {>= "1.11.0"} "conf-openssl" {with-test} "conf-diffutils" {with-test} ] diff -Nru opam-2.0.10/opam-format.opam opam-2.1.2/opam-format.opam --- opam-2.0.10/opam-format.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-format.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ opam-version: "2.0" -version: "2.0.10" -synopsis: "Format library for opam 2.0" +version: "2.1.2" +synopsis: "Format library for opam 2.1" description: """ Definition of opam datastructures and its file interface. """ @@ -28,7 +28,7 @@ depends: [ "ocaml" {>= "4.02.3"} "opam-core" {= version} - "opam-file-format" {>= "2.0.0~rc2"} - "re" {>= "1.5.0"} - "dune" {>= "1.2.1"} + "opam-file-format" {>= "2.1.3"} + "re" {>= "1.9.0"} + "dune" {>= "1.11.0"} ] diff -Nru opam-2.0.10/opam-installer.opam opam-2.1.2/opam-installer.opam --- opam-2.0.10/opam-installer.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-installer.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,5 +1,5 @@ opam-version: "2.0" -version: "2.0.10" +version: "2.1.2" synopsis: "Installation of files to a prefix, following opam conventions" description: """ opam-installer is a small tool that can read *.install files, as defined by opam [1], and execute them to install or remove package files without going through opam. @@ -30,6 +30,6 @@ depends: [ "ocaml" {>= "4.02.3"} "opam-format" {= version} - "cmdliner" {>= "1.0.0"} - "dune" {>= "1.2.1"} + "cmdliner" {>= "0.9.8"} + "dune" {>= "1.11.0"} ] diff -Nru opam-2.0.10/opam.opam opam-2.1.2/opam.opam --- opam-2.0.10/opam.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,5 +1,5 @@ opam-version: "2.0" -version: "2.0.10" +version: "2.1.2" synopsis: "Meta-package for Dune" maintainer: "opam-devel@lists.ocaml.org" authors: [ @@ -18,6 +18,6 @@ bug-reports: "https://github.com/ocaml/opam/issues" license: "LGPL-2.1-only WITH OCaml-LGPL-linking-exception" depends: [ - "dune" {>= "1.2.1"} + "dune" {>= "1.11.0"} "opam-client" {= version} ] diff -Nru opam-2.0.10/opam-repository.opam opam-2.1.2/opam-repository.opam --- opam-2.0.10/opam-repository.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-repository.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ opam-version: "2.0" -version: "2.0.10" -synopsis: "Repository library for opam 2.0" +version: "2.1.2" +synopsis: "Repository library for opam 2.1" description: """ This library includes repository and remote sources handling, including curl/wget, rsync, git, mercurial, darcs backends. """ @@ -28,5 +28,5 @@ depends: [ "ocaml" {>= "4.02.3"} "opam-format" {= version} - "dune" {>= "1.2.1"} + "dune" {>= "1.11.0"} ] diff -Nru opam-2.0.10/opam-solver.opam opam-2.1.2/opam-solver.opam --- opam-2.0.10/opam-solver.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-solver.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ opam-version: "2.0" -version: "2.0.10" -synopsis: "Solver library for opam 2.0" +version: "2.1.2" +synopsis: "Solver library for opam 2.1" description: """ Solver and Cudf interaction. This library is based on the Cudf and Dose libraries, and handles calls to the external solver from opam. """ @@ -31,6 +31,13 @@ "mccs" {>= "1.1+9"} "dose3" {>= "5" & < "6.0"} "cudf" {>= "0.7"} - "re" {>= "1.5.0"} - "dune" {>= "1.2.1"} + "dune" {>= "1.11.0"} +] +depopts: [ + "z3" + "opam-0install-cudf" +] +conflicts: [ + "z3" {< "4.8.4"} + "opam-0install-cudf" {< "0.4"} ] diff -Nru opam-2.0.10/opam-state.opam opam-2.1.2/opam-state.opam --- opam-2.0.10/opam-state.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/opam-state.opam 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ opam-version: "2.0" -version: "2.0.10" -synopsis: "State library for opam 2.0" +version: "2.1.2" +synopsis: "State library for opam 2.1" description: """ Handling of the ~/.opam hierarchy, repository and switch states. """ @@ -28,5 +28,5 @@ depends: [ "ocaml" {>= "4.02.3"} "opam-repository" {= version} - "dune" {>= "1.2.1"} + "dune" {>= "1.11.0"} ] diff -Nru opam-2.0.10/README.md opam-2.1.2/README.md --- opam-2.0.10/README.md 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/README.md 2021-12-07 16:09:27.000000000 +0000 @@ -1,8 +1,8 @@ # opam - A package manager for OCaml -|2.0| -|--| -| [![GH actions](https://github.com/ocaml/opam/workflows/Builds,%20tests%20&%20co/badge.svg?branch=2.0)](https://github.com/ocaml/opam/actions?query=workflow%3A%22Builds%2C+tests+%26+co%22+branch%3A2.0) [![TravisCI 2.0 Build Status](https://travis-ci.org/ocaml/opam.svg?branch=2.0)](https://travis-ci.org/ocaml/opam) [![AppVeyor 2.0 Build Status](https://ci.appveyor.com/api/projects/status/github/ocaml/opam?branch=2.0&svg=true)](https://ci.appveyor.com/project/AltGr/opam) | +|master|2.0| +|--|--| +|[![GH actions](https://github.com/ocaml/opam/workflows/Builds,%20tests%20&%20co/badge.svg)](https://github.com/ocaml/opam/actions?query=workflow%3A%22Builds%2C+tests+%26+co%22+branch%3Amaster) [![AppVeyor Build Status](https://ci.appveyor.com/api/projects/status/github/ocaml/opam?branch=master&svg=true)](https://ci.appveyor.com/project/AltGr/opam) | [![2.0 GH actions](https://github.com/ocaml/opam/workflows/Builds,%20tests%20&%20co/badge.svg?branch=2.0)](https://github.com/ocaml/opam/actions?query=workflow%3A%22Builds%2C+tests+%26+co%22+branch%3A2.0) [![AppVeyor 2.0 Build Status](https://ci.appveyor.com/api/projects/status/github/ocaml/opam?branch=2.0&svg=true)](https://ci.appveyor.com/project/AltGr/opam) | Opam is a source-based package manager for OCaml. It supports multiple simultaneous compiler installations, flexible package constraints, and a Git-friendly development @@ -42,7 +42,7 @@ `./configure` once done * Run `make libinstall` at the end -_Note_: If you install on your system (without changeing the prefix), you will +_Note_: If you install on your system (without changing the prefix), you will need to install as root (`sudo`). As sudo do not propagate environment variables, there wil be some errors. You can use `sudo -E "PATH=$PATH" in order to be sure to have the good environment for install. @@ -217,7 +217,7 @@ All other code is: -Copyright 2012-2016 OCamlPro +Copyright 2012-2020 OCamlPro Copyright 2012 INRIA All rights reserved. Opam is distributed under the terms of the GNU Lesser diff -Nru opam-2.0.10/release/Dockerfile.in opam-2.1.2/release/Dockerfile.in --- opam-2.0.10/release/Dockerfile.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/release/Dockerfile.in 2021-12-07 16:09:27.000000000 +0000 @@ -6,15 +6,15 @@ RUN apt-get update && apt-get install bzip2 g++ make patch wget libltdl-dev --yes && apt-get clean --yes RUN useradd -U --create-home opam -ADD https://caml.inria.fr/pub/distrib/ocaml-4.04/ocaml-4.04.2.tar.gz /root/ +ADD https://caml.inria.fr/pub/distrib/ocaml-4.07/ocaml-4.07.1.tar.gz /root/ WORKDIR /root -RUN tar xzf ocaml-4.04.2.tar.gz -WORKDIR ocaml-4.04.2 +RUN tar xzf ocaml-4.07.1.tar.gz +WORKDIR ocaml-4.07.1 RUN ./configure %CONF% -prefix /usr/local RUN make world opt.opt RUN make install -RUN rm -rf /root/ocaml-4.04.2 /root/ocaml-4.04.2.tar.gz +RUN rm -rf /root/ocaml-4.07.1 /root/ocaml-4.07.1.tar.gz ENV PATH /usr/local/bin:/usr/bin:/bin USER opam diff -Nru opam-2.0.10/release/Makefile opam-2.1.2/release/Makefile --- opam-2.0.10/release/Makefile 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/release/Makefile 2021-12-07 16:09:27.000000000 +0000 @@ -12,7 +12,7 @@ # currently hardcoded in Dockerfile.in OCAML_URL = https://caml.inria.fr/pub/distrib/ocaml-$(basename $(OCAMLV))/ocaml-$(OCAMLV).tar.gz -HOST_OS = $(shell uname -s | tr A-Z a-z) +HOST_OS = $(shell uname -s | tr A-Z a-z | sed 's/darwin/macos/') HOST = $(shell uname -m | sed 's/amd64/x86_64/')-$(HOST_OS) all: $(patsubst %,out/opam-$(VERSION)-%,$(TARGETS)) @@ -21,7 +21,7 @@ mkdir -p out cd out && curl -OfL $(FULL_ARCHIVE_URL) || { \ git clone $(GIT_URL) -b $(TAG) --depth 1 opam-full-$(VERSION); \ - sed 's/^AC_INIT(opam,.*)/AC_INIT(opam,$(OPAM_VERSION))/' > \ + sed 's/^AC_INIT(opam,.*)/AC_INIT(opam,$(OPAM_VERSION))/' opam-full-$(VERSION)/configure.ac > \ opam-full-$(VERSION)/configure.ac.tmp; \ mv opam-full-$(VERSION)/configure.ac.tmp \ opam-full-$(VERSION)/configure.ac; \ @@ -37,7 +37,7 @@ build/Dockerfile.armhf-linux: Dockerfile.in mkdir -p build && sed 's/%TARGET_TAG%/armhf-jessie/g' $^ | sed 's/%CONF%//g' >$@ build/Dockerfile.arm64-linux: Dockerfile.in - mkdir -p build && sed 's/%TARGET_TAG%/arm64-jessie/g' $^ | sed 's/%CONF%//g' >$@ + mkdir -p build && sed 's/%TARGET_TAG%/arm64-stretch/g' $^ | sed 's/%CONF%//g' >$@ build/%.image: build/Dockerfile.% @@ -52,11 +52,11 @@ -Wl,-Bdynamic \ -static-libgcc -CLINKING_darwin = \ +CLINKING_macos = \ -lunix -lmccs_stubs -lmccs_glpk_stubs \ -lstdc++ -CLINKING_openbsd = $(CLINKING_darwin) +CLINKING_openbsd = $(CLINKING_macos) LINKING = (-noautolink $(patsubst %,-cclib %,$(CLINKING_$(1)))) diff -Nru opam-2.0.10/release/readme.md opam-2.1.2/release/readme.md --- opam-2.0.10/release/readme.md 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/release/readme.md 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,29 @@ +# Steps to follow for each release + +## Finalise opam code for release +* update version in opam files, configure.ac +* run `make configure` to regenerate `./configure` [checked by github actions] +* update copyright headers +* run `make tests`, `opam-rt` [checked by github actions & appveyor] +* update the CHANGE file: take `master_changes.md` content to fil it + +## Github release + +[ once bump version & changes PRs merged ] +* tag the release (git tag -a 2.2.0; git push origin 2.2.0) +* /!\ Once the tag pushed, it can be updated only in case of severe issue +* create a release (or prerelease if intermediate release) draft on github based on your tag (https://github.com/ocaml/opam/releases/new) +* generate opam artifacts, using `shell/release.sh`, it requires to have Docker install with several remotes, the different arches +* add releases notes (content of `master_changes.md`) in the release +* upload signature of artefacts +* finalise the release (publish) + +## Publish the release + +* add hashes in `install.sh` (and check signatures) +* publish opam packages in opam-repository + +## Announce! + +* a blog entry in opam.ocaml.org +* a announcement in discuss.ocaml.org diff -Nru opam-2.0.10/shell/bootstrap-ocaml.sh opam-2.1.2/shell/bootstrap-ocaml.sh --- opam-2.0.10/shell/bootstrap-ocaml.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/shell/bootstrap-ocaml.sh 2021-12-07 16:09:27.000000000 +0000 @@ -10,23 +10,31 @@ 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'` -MD5=`sed -ne 's/MD5_ocaml *= *//p' ../src_ext/Makefile | tr -d '\r'` +BOOTSTRAP_DIR=${BOOTSTRAP_DIR:-bootstrap} +BOOTSTRAP_ROOT=${BOOTSTRAP_ROOT:-..} +BOOTSTRAP_OPT_TARGET=${BOOTSTRAP_OPT_TARGET:-opt.opt} +mkdir -p "$BOOTSTRAP_DIR" +cd "$BOOTSTRAP_DIR" +URL=`sed -ne 's/URL_ocaml *= *//p' $BOOTSTRAP_ROOT/src_ext/Makefile | tr -d '\r'` +MD5=`sed -ne 's/MD5_ocaml *= *//p' $BOOTSTRAP_ROOT/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'` +FV_URL=`sed -ne 's/URL_flexdll *= *//p' $BOOTSTRAP_ROOT/src_ext/Makefile | tr -d '\r'` FLEXDLL=`echo ${FV_URL}| sed -e 's|.*/\([^/]*\)|\1|'` if [ ! -e ${V}.tar.gz ]; then - cp ../src_ext/archives/${V}.tar.gz . 2>/dev/null || ${CURL} ${URL} + cp $BOOTSTRAP_ROOT/src_ext/archives/${V}.tar.gz . 2>/dev/null || ${CURL} ${URL} fi ACTUALMD5=`openssl md5 ${V}.tar.gz 2> /dev/null | cut -f 2 -d ' '` -if [ "$ACTUALMD5" != "$MD5" ]; then - echo "Bad checksum for ${V}.tar.gz:" - echo "- expected: $MD5" - echo "- actual: $ACTUALMD5" +if [ -z "$ACTUALMD5" ]; then + echo "Blank checksum returned; is `openssl` installed?" exit 2 +else + if [ "$ACTUALMD5" != "$MD5" ]; then + echo "Bad checksum for ${V}.tar.gz:" + echo "- expected: $MD5" + echo "- actual: $ACTUALMD5" + exit 2 + fi fi if [ ${GEN_CONFIG_ONLY} -eq 0 ] ; then @@ -57,7 +65,7 @@ "msvc") HOST=i686-pc-windows if ! command -v ml > /dev/null ; then - eval `../../shell/msvs-detect --arch=x86` + eval `$BOOTSTRAP_ROOT/../shell/msvs-detect --arch=x86` if [ -n "${MSVS_NAME}" ] ; then PATH_PREPEND="${MSVS_PATH}" LIB_PREPEND="${MSVS_LIB};" @@ -68,7 +76,7 @@ "msvc64") HOST=x86_64-pc-windows if ! command -v ml64 > /dev/null ; then - eval `../../shell/msvs-detect --arch=x64` + eval `$BOOTSTRAP_ROOT/../shell/msvs-detect --arch=x64` if [ -n "${MSVS_NAME}" ] ; then PATH_PREPEND="${MSVS_PATH}" LIB_PREPEND="${MSVS_LIB};" @@ -92,10 +100,10 @@ HOST=i686-w64-mingw32 elif [ ${TRY64} -eq 1 ] && command -v ml64 > /dev/null ; then HOST=x86_64-pc-windows - PATH_PREPEND=`bash ../../shell/check_linker` + PATH_PREPEND=`bash $BOOTSTRAP_ROOT/../shell/check_linker` elif command -v ml > /dev/null ; then HOST=i686-pc-windows - PATH_PREPEND=`bash ../../shell/check_linker` + PATH_PREPEND=`bash $BOOTSTRAP_ROOT/../shell/check_linker` else if [ ${TRY64} -eq 1 ] ; then HOST=x86_64-pc-windows @@ -104,7 +112,7 @@ HOST=i686-pc-windows HOST_ARCH=x86 fi - eval `../../shell/msvs-detect --arch=${HOST_ARCH}` + eval `$BOOTSTRAP_ROOT/../shell/msvs-detect --arch=${HOST_ARCH}` if [ -z "${MSVS_NAME}" ] ; then echo "No appropriate C compiler was found -- unable to build OCaml" exit 1 @@ -122,46 +130,55 @@ PREFIX=`cd .. ; pwd`/ocaml WINPREFIX=`echo ${PREFIX} | cygpath -f - -m` if [ ${GEN_CONFIG_ONLY} -eq 0 ] ; then - # --disable-ocamldoc can change to --disable-stdlib-manpages when bumped to 4.11 - PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" Lib="${LIB_PREPEND}${Lib}" Include="${INC_PREPEND}${Include}" ./configure --prefix "$WINPREFIX" --build=$BUILD --host=$HOST --disable-ocamldoc + PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" \ + Lib="${LIB_PREPEND}${Lib}" \ + Include="${INC_PREPEND}${Include}" \ + ./configure --prefix "$WINPREFIX" \ + --build=$BUILD --host=$HOST \ + --disable-stdlib-manpages \ + $BOOTSTRAP_EXTRA_OPTS fi cd .. if [ ! -e ${FLEXDLL} ]; then - cp ../src_ext/archives/${FLEXDLL} . 2>/dev/null || ${CURL} ${FV_URL} + cp $BOOTSTRAP_ROOT/src_ext/archives/${FLEXDLL} . 2>/dev/null || ${CURL} ${FV_URL} fi cd ${V} if [ ${GEN_CONFIG_ONLY} -eq 0 ] ; then - tar -xzf ../${FLEXDLL} + tar -xzf $BOOTSTRAP_ROOT/${FLEXDLL} rm -rf flexdll mv flexdll-* flexdll PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" Lib="${LIB_PREPEND}${Lib}" Include="${INC_PREPEND}${Include}" make -j flexdll - PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" Lib="${LIB_PREPEND}${Lib}" Include="${INC_PREPEND}${Include}" make -j world.opt + PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" Lib="${LIB_PREPEND}${Lib}" Include="${INC_PREPEND}${Include}" make -j world + PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" Lib="${LIB_PREPEND}${Lib}" Include="${INC_PREPEND}${Include}" make -j $BOOTSTRAP_OPT_TARGET PATH="${PATH_PREPEND}${PREFIX}/bin:${PATH}" Lib="${LIB_PREPEND}${Lib}" Include="${INC_PREPEND}${Include}" make install fi OCAMLLIB=${WINPREFIX}/lib/ocaml else PREFIX=`cd .. ; pwd`/ocaml if [ ${GEN_CONFIG_ONLY} -eq 0 ] ; then - ./configure -prefix "${PREFIX}" - ${MAKE:-make} world opt.opt + ./configure --prefix "${PREFIX}" $BOOTSTRAP_EXTRA_OPTS --disable-stdlib-manpages + ${MAKE:-make} world + ${MAKE:-make} $BOOTSTRAP_OPT_TARGET ${MAKE:-make} install fi OCAMLLIB=${PREFIX}/lib/ocaml fi if [ ${GEN_CONFIG_ONLY} -eq 0 ] ; then - echo "${URL} ${FV_URL}" > ../installed-tarball + echo "${URL} ${FV_URL}" > $BOOTSTRAP_ROOT/installed-tarball fi # Generate src_ext/Makefile.config PATH_PREPEND=`echo "${PATH_PREPEND}" | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` -echo "export PATH:=${PATH_PREPEND}${PREFIX}/bin:\$(PATH)" > ../../src_ext/Makefile.config +echo "export PATH:=${PATH_PREPEND}${PREFIX}/bin:\$(PATH)" > $BOOTSTRAP_ROOT/../src_ext/Makefile.config if [ -n "${LIB_PREPEND}" ] ; then LIB_PREPEND=`echo ${LIB_PREPEND} | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` - echo "export Lib:=${LIB_PREPEND}\$(Lib)" >> ../../src_ext/Makefile.config + echo "export Lib:=${LIB_PREPEND}\$(Lib)" >> $BOOTSTRAP_ROOT/../src_ext/Makefile.config fi if [ -n "${INC_PREPEND}" ] ; then INC_PREPEND=`echo ${INC_PREPEND} | sed -e 's/#/\\\\#/g' -e 's/\\$/$$/g'` - echo "export Include:=${INC_PREPEND}\$(Include)" >> ../../src_ext/Makefile.config + echo "export Include:=${INC_PREPEND}\$(Include)" >> $BOOTSTRAP_ROOT/../src_ext/Makefile.config fi -echo "export OCAMLLIB=${OCAMLLIB}" >> ../../src_ext/Makefile.config +echo "export OCAMLLIB=${OCAMLLIB}" >> $BOOTSTRAP_ROOT/../src_ext/Makefile.config +echo 'unexport CAML_LD_LIBRARY_PATH' >> $BOOTSTRAP_ROOT/../src_ext/Makefile.config +echo 'unexport OPAM_SWITCH_PREFIX' >> $BOOTSTRAP_ROOT/../src_ext/Makefile.config diff -Nru opam-2.0.10/shell/bundle.sh opam-2.1.2/shell/bundle.sh --- opam-2.0.10/shell/bundle.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/shell/bundle.sh 2021-12-07 16:09:27.000000000 +0000 @@ -2,7 +2,7 @@ set -ue OCAMLV=4.04.1 -OPAMV=2.0.10 +OPAMV=2.0.0 OPAM_REPO=https://opam.ocaml.org/2.0 DEBUG= MAKESELF= diff -Nru opam-2.0.10/shell/context_flags.ml opam-2.1.2/shell/context_flags.ml --- opam-2.0.10/shell/context_flags.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/shell/context_flags.ml 2021-12-07 16:09:27.000000000 +0000 @@ -7,12 +7,17 @@ let _ocaml_minor = String.sub Sys.ocaml_version p (String.index_from Sys.ocaml_version p '.' - p) |> int_of_string in match Sys.argv.(1) with | "flags" -> - Printf.printf "()" + print_string "()" +| "mingw-arch" -> + if Config.system = "mingw64" then + print_string "x86_64" + else + print_string "i686" | "clibs" -> if Sys.win32 then - Printf.printf "(-ladvapi32 -lgdi32 -luser32 -lshell32)" + print_string "(-ladvapi32 -lgdi32 -luser32 -lshell32)" else - Printf.printf "()" + print_string "()" | _ -> Printf.eprintf "Unrecognised context instruction: %s\n" Sys.argv.(1); exit 1 diff -Nru opam-2.0.10/shell/get_version.ml opam-2.1.2/shell/get_version.ml --- opam-2.0.10/shell/get_version.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/shell/get_version.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,12 @@ +let c = open_in Sys.argv.(1) in +try while true do + let line = input_line c in + if String.length line > 8 && String.sub line 0 8 = "AC_INIT(" then + let idx1 = String.index line ',' in + let idx2 = String.index line ')' in + print_string @@ String.sub line (idx1 + 1) (idx2 - idx1 - 1); + raise Exit +done with Exit -> close_in c; exit 0 + | End_of_file + | Sys_error _ -> + close_in c; exit 1 diff -Nru opam-2.0.10/shell/link_runtime.ml opam-2.1.2/shell/link_runtime.ml --- opam-2.0.10/shell/link_runtime.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/shell/link_runtime.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,39 @@ +#directory "+compiler-libs";; +#load "ocamlcommon.cma";; +#load "unix.cma";; + +let tool_arch = + if Config.system = "mingw64" then "x86_64" else "i686" + +let runtime = + let c = Unix.open_process_in @@ Printf.sprintf "%s-w64-mingw32-gcc -print-sysroot | cygpath -f - -w" tool_arch in + let sysroot = input_line c in + if Unix.close_process_in c <> Unix.WEXITED 0 then + exit 1 + else + Filename.concat sysroot @@ Filename.concat "mingw" "bin" + +let copy_file source dest = + let {Unix.st_size} = Unix.stat source in + let buffer = Bytes.create st_size in + let c = open_in_bin source in + really_input c buffer 0 st_size; + close_in c; + let c = open_out_bin dest in + output c buffer 0 st_size; + close_out c + +let link_item item = + let runtime = Filename.concat runtime item in + if not (Sys.file_exists runtime) then begin + Printf.eprintf "%s not found!\n" runtime; + exit 1 + end; + if Sys.file_exists item then + Unix.unlink item; + try + Unix.symlink runtime item + with Unix.Unix_error _ -> + copy_file runtime item + +let () = Array.iteri (fun idx item -> if idx > 0 then link_item item) Sys.argv diff -Nru opam-2.0.10/shell/md5check.ml opam-2.1.2/shell/md5check.ml --- opam-2.0.10/shell/md5check.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/shell/md5check.ml 2021-12-07 16:09:27.000000000 +0000 @@ -11,7 +11,7 @@ let () = if md5 <> md5_of_file then ( Printf.eprintf - "MD5 for %s differ:\n\ + "MD5 for %s differs:\n\ \ expected: %s\n\ \ actual: %s\n" file md5 md5_of_file; diff -Nru opam-2.0.10/shell/pkg-stats.sh opam-2.1.2/shell/pkg-stats.sh --- opam-2.0.10/shell/pkg-stats.sh 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/shell/pkg-stats.sh 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,36 @@ +#!/bin/sh -ue + +if [ ! -d "packages" ] || [ ! -d ".git" ] ; then + echo "Please run from a git clone of an opam repository" + exit 2 +fi + +OUT="pkg-stats.svg" + +DATA=$(mktemp pkg-data.XXXX) +trap "rm -f \"$DATA\"" EXIT + +echo "Gathering git data..." +git log --date-order --reverse --format=%ct --name-status --diff-filter=ACD 2>&1 |\ +awk -n '/^[0-9]/ { printf "%s\t%s\n",$1,count } + /^[AC].*\/opam$/ { count++ } + /^D.*\/opam$/ { count-- }' \ +>"$DATA" + +echo "Generating plot..." +gnuplot < "" then - let c = open_in file in - let magic_l = String.length magic in - (* End_of_file is permitted to leak as the failure of this build step *) - let rec process () = - let line = input_line c in - let line_l = String.length line in - if line_l > magic_l then - if String.sub line 0 magic_l = magic then begin - close_in c; - Scanf.unescaped @@ String.sub line magic_l (line_l - magic_l - 1) - end else - process () - else - process () - in - process () - else - Sys.argv.(2) -in -let cin = open_in Sys.argv.(3) in -let name_l = String.length name in -let rec process () = - match input_line cin with - | exception End_of_file -> - close_in cin - | line -> - begin - try - let idx = String.index line '@' in - let line_l = String.length line in - if line_l > idx + name_l - 1 && String.sub line idx name_l = name then begin - if idx > 0 then - print_string (String.sub line 0 idx); - print_string value; - print_endline (String.sub line (idx + name_l) (line_l - idx - name_l)); - end else - print_endline line - with Not_found -> - print_endline line - end; - process () -in -process () diff -Nru opam-2.0.10/src/client/default-manifest.xmlf opam-2.1.2/src/client/default-manifest.xmlf --- opam-2.0.10/src/client/default-manifest.xmlf 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/default-manifest.xmlf 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - diff -Nru opam-2.0.10/src/client/dune opam-2.1.2/src/client/dune --- opam-2.0.10/src/client/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/dune 2021-12-07 16:09:27.000000000 +0000 @@ -2,33 +2,39 @@ (name opam_client) (public_name opam-client) (synopsis "OCaml Package Manager client and CLI library") - (modules (:standard \ opamMain opamManifest get_git_version)) - (libraries opam-state opam-solver re cmdliner) + (modules (:standard \ opamMain get_git_version)) + (libraries opam-state opam-solver opam-repository re extlib cmdliner) (flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp))) (wrapped false)) (executable (name opamMain) + ; This name needs to be updated in doc/man/dune if changed (public_name opam) (package opam) - (modules opamMain opamManifest) + (modules opamMain) (flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp) (:include linking.sexp))) - (link_flags (:include manifest.sexp)) - (libraries opam-client)) + (libraries opam-client + (select link-opam-manifest from + (opam-client.manifest -> pull-manifest) + ( -> dummy) + ))) (rule - (targets manifest.sexp) - (deps (:script ../../shell/subst_var.ml) ../../config.status (:input manifest.sexp.in)) - (action (with-stdout-to %{targets} (run ocaml %{script} CONF_MANIFEST_O "" %{input})))) + (with-stdout-to dummy (echo ""))) -(include opamManifest.inc) - -(include manifest.inc) +(rule + (targets pull-manifest) + (deps (:obj ../manifest/opam-manifest.o)) + (action (progn (system "cp %{obj} opam-manifest.o 2> %{null} || copy %{obj} opam-manifest.o") + (with-stdout-to %{targets} (echo ""))))) (rule (targets git-sha) @@ -59,4 +65,6 @@ (with-stdout-to opamGitVersion.ml (run ocaml %{dep:get_git_version.ml}))) (rule - (with-stdout-to linking.sexp (run echo "()"))) + (targets linking.sexp) + (mode fallback) + (action (with-stdout-to %{targets} (echo "()")))) diff -Nru opam-2.0.10/src/client/manifest.inc.in opam-2.1.2/src/client/manifest.inc.in --- opam-2.0.10/src/client/manifest.inc.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/manifest.inc.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -(rule - (targets opam-manifest.o) - (deps (:rc opam.rc)) - (action (run @TOOL_ARCH@-w64-mingw32-windres %{rc} %{targets}))) - -(rule - (with-stdout-to opam.exe.manifest - (progn (echo "\n") - (echo "\n") - (cat opam-@SYSTEM@.xmlf) - (cat default-manifest.xmlf) - (echo "")))) - -(rule - (targets opam.rc) - (deps (:manifest opam.exe.manifest)) - (action (with-stdout-to %{targets} (echo "#include \nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST %{manifest}")))) - -(install - (section bin) - (files (Opam.Runtime.@MANIFEST_ARCH@.manifest as Opam.Runtime.@MANIFEST_ARCH@\Opam.Runtime.@MANIFEST_ARCH@.manifest) - (libstdc++-6.dll as Opam.Runtime.@MANIFEST_ARCH@\libstdc++-6.dll) - (libwinpthread-1.dll as Opam.Runtime.@MANIFEST_ARCH@\libwinpthread-1.dll) - (@RUNTIME_GCC_S@.dll as Opam.Runtime.@MANIFEST_ARCH@\@RUNTIME_GCC_S@.dll)) - (package opam)) diff -Nru opam-2.0.10/src/client/manifest.sexp.in opam-2.1.2/src/client/manifest.sexp.in --- opam-2.0.10/src/client/manifest.sexp.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/manifest.sexp.in 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -(@CONF_MANIFEST_O@) diff -Nru opam-2.0.10/src/client/opamAction.ml opam-2.1.2/src/client/opamAction.ml --- opam-2.0.10/src/client/opamAction.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAction.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -19,129 +19,223 @@ module PackageActionGraph = OpamSolver.ActionGraph -(* Install the package files *) -let process_dot_install st nv build_dir = +(* Preprocess install: returns a list of files to install, and their respective + install functions *) +let preprocess_dot_install_t st nv build_dir = + if not (OpamFilename.exists_dir build_dir) then [], None else let root = st.switch_global.root in - if OpamStateConfig.(!r.dryrun) then begin - OpamConsole.msg "Installing %s.\n" (OpamPackage.to_string nv); None - end else - if not (OpamFilename.exists_dir build_dir) then None else - OpamFilename.in_dir build_dir (fun () -> - log "Installing %s.\n" (OpamPackage.to_string nv); - let name = nv.name in - let config_f = OpamPath.Builddir.config build_dir nv in - let config = OpamFile.Dot_config.read_opt config_f in - let install_f = OpamPath.Builddir.install build_dir nv in - let install = OpamFile.Dot_install.safe_read install_f in - - (* .install *) - let install_f = OpamPath.Switch.install root st.switch name in - if install <> OpamFile.Dot_install.empty then - OpamFile.Dot_install.write install_f install; - - (* .config *) - (match config with - | Some config -> + let switch_prefix = OpamPath.Switch.root root st.switch in + let file_wo_prefix f = OpamFilename.remove_prefix switch_prefix f in + + let name = nv.name in + let install_f = OpamPath.Builddir.install build_dir nv in + let install = OpamFile.Dot_install.safe_read install_f in + + (* .install *) + let install_loc = OpamPath.Switch.install root st.switch name in + if install <> OpamFile.Dot_install.empty then + OpamFile.Dot_install.write install_loc install; + + (* .config *) + let (files_and_installs, config) = + let config_f = OpamPath.Builddir.config build_dir nv in + let config = OpamFile.Dot_config.read_opt config_f in + (match config with + | Some config -> + let file = + OpamPath.Switch.config root st.switch name + |> OpamFile.filename + |> file_wo_prefix + in + let inst _ = let dot_config = OpamPath.Switch.config root st.switch name in - OpamFile.Dot_config.write dot_config config - | None -> ()); + OpamFile.Dot_config.write dot_config config; None + in + ([(file, inst)], Some config) + | None -> ([], None)) + in + + let check ~src ~dst base = + let src_file = OpamFilename.create src base.c in + if base.optional && not (OpamFilename.exists src_file) then + log "Not installing %a is not present and optional." + (slog OpamFilename.to_string) src_file; + let exists = OpamFilename.exists src_file in + let warn = + if not base.optional && not exists then + Some (dst, base.c) else None + in + exists, warn + in + + (* Install a list of files *) + let install_files (exec, dst_fn, files_fn) = + let dst_dir = dst_fn root st.switch name in + let files = files_fn install in + let dir_and_install = + if OpamFilename.exists_dir dst_dir || files = [] then [] else + let dir = OpamFilename.remove_prefix_dir switch_prefix dst_dir in + let inst _ = + log "creating %a" (slog OpamFilename.Dir.to_string) dst_dir; + OpamFilename.mkdir dst_dir; + None + in + [dir, inst] + in + dir_and_install @ List.rev @@ + List.rev_map (fun (base, dst) -> + let (base, append) = + if exec && + not (OpamFilename.exists (OpamFilename.create build_dir base.c)) + then + let base' = + {base with c = OpamFilename.Base.add_extension base.c "exe"} in + if OpamFilename.exists (OpamFilename.create build_dir base'.c) then + (OpamConsole.warning + ".install file is missing .exe extension for %s" + (OpamFilename.Base.to_string base.c); + (base', true)) + else + (base, false) + else + (base, false) in + let src_file = OpamFilename.create build_dir base.c in + let dst_file = match dst with + | None -> OpamFilename.create dst_dir (OpamFilename.basename src_file) + | Some d -> + if append && not (OpamFilename.Base.check_suffix d ".exe") then + OpamFilename.create dst_dir + (OpamFilename.Base.add_extension d "exe") + else + OpamFilename.create dst_dir d in + let file = file_wo_prefix dst_file in + let inst warning = + if append then warning (OpamFilename.to_string src_file) `Add_exe; + let check, warn = check ~src:build_dir ~dst:dst_dir base in + if check then + OpamFilename.install ~warning ~exec ~src:src_file ~dst:dst_file (); + warn + in + file, inst) + files + in + + let module P = OpamPath.Switch in + let module I = OpamFile.Dot_install in + let instdir_gen fpath r s _ = fpath r s st.switch_config in + let instdir_pkg fpath r s n = fpath r s st.switch_config n in + + let to_install = [ + (* bin *) + true, (instdir_gen P.bin), I.bin; + + (* sbin *) + true, (instdir_gen P.sbin), I.sbin; + + (* lib *) + false, (instdir_pkg P.lib), I.lib; + true, (instdir_pkg P.lib), I.libexec; + false, (instdir_gen P.lib_dir), I.lib_root; + true, (instdir_gen P.lib_dir), I.libexec_root; + + (* toplevel *) + false, (instdir_gen P.toplevel), I.toplevel; + + true, (instdir_gen P.stublibs), I.stublibs; + + (* Man pages *) + false, (instdir_gen P.man_dir), I.man; + + (* Shared files *) + false, (instdir_pkg P.share), I.share; + false, (instdir_gen P.share_dir), I.share_root; - let warnings = ref [] in - let check ~src ~dst base = - let src_file = OpamFilename.create src base.c in - if base.optional && not (OpamFilename.exists src_file) then - log "Not installing %a is not present and optional." - (slog OpamFilename.to_string) src_file; - if not base.optional && not (OpamFilename.exists src_file) then ( - warnings := (dst, base.c) :: !warnings - ); - OpamFilename.exists src_file in - - (* Install a list of files *) - let install_files exec dst_fn files_fn = - let dst_dir = dst_fn root st.switch name in - let files = files_fn install in - if not (OpamFilename.exists_dir dst_dir) && files <> [] then ( - log "creating %a" (slog OpamFilename.Dir.to_string) dst_dir; - OpamFilename.mkdir dst_dir; - ); - List.iter (fun (base, dst) -> - let src_file = OpamFilename.create build_dir base.c in - let dst_file = match dst with - | None -> OpamFilename.create dst_dir (OpamFilename.basename src_file) - | Some d -> OpamFilename.create dst_dir d in - if check ~src:build_dir ~dst:dst_dir base then - OpamFilename.install ~exec ~src:src_file ~dst:dst_file (); - ) files in - - let module P = OpamPath.Switch in - let module I = OpamFile.Dot_install in - let instdir_gen fpath r s _ = fpath r s st.switch_config in - let instdir_pkg fpath r s n = fpath r s st.switch_config n in - - (* bin *) - install_files true (instdir_gen P.bin) I.bin; - - (* sbin *) - install_files true (instdir_gen P.sbin) I.sbin; - - (* lib *) - install_files false (instdir_pkg P.lib) I.lib; - install_files true (instdir_pkg P.lib) I.libexec; - install_files false (instdir_gen P.lib_dir) I.lib_root; - install_files true (instdir_gen P.lib_dir) I.libexec_root; - - (* toplevel *) - install_files false (instdir_gen P.toplevel) I.toplevel; - - install_files true (instdir_gen P.stublibs) I.stublibs; - - (* Man pages *) - install_files false (instdir_gen P.man_dir) I.man; - - (* Shared files *) - install_files false (instdir_pkg P.share) I.share; - install_files false (instdir_gen P.share_dir) I.share_root; - - (* Etc files *) - install_files false (instdir_pkg P.etc) I.etc; - - (* Documentation files *) - install_files false (instdir_pkg P.doc) I.doc; - - (* misc *) - List.iter - (fun (src, dst) -> + (* Etc files *) + false, (instdir_pkg P.etc), I.etc; + + (* Documentation files *) + false, (instdir_pkg P.doc), I.doc; + ] + in + + let files_and_installs = + List.fold_left (fun acc toi -> List.rev_append (install_files toi) acc) + files_and_installs to_install + in + + (* misc *) + let misc_files = + List.map (fun (src, dst) -> + let file = file_wo_prefix dst in + let inst warning = let src_file = OpamFilename.create (OpamFilename.cwd ()) src.c in if OpamFilename.exists dst && OpamConsole.confirm "Overwriting %s?" (OpamFilename.to_string dst) then - OpamFilename.install ~src:src_file ~dst () + OpamFilename.install ~warning ~src:src_file ~dst () else begin OpamConsole.msg "Installing %s to %s.\n" (OpamFilename.Base.to_string src.c) (OpamFilename.to_string dst); if OpamConsole.confirm "Continue?" then - OpamFilename.install ~src:src_file ~dst () - end - ) (I.misc install); - - if !warnings <> [] then ( - let print (dir, base) = - Printf.sprintf " - %s to %s\n" - (OpamFilename.to_string (OpamFilename.create build_dir base)) - (OpamFilename.Dir.to_string dir) in - OpamConsole.error "Installation of %s failed" - (OpamPackage.to_string nv); - let msg = - Printf.sprintf - "Some files in %s couldn't be installed:\n%s" - (OpamFile.to_string install_f) - (String.concat "" (List.map print !warnings)) + OpamFilename.install ~warning ~src:src_file ~dst () + end; + None in - failwith msg - ); + (file, inst)) (I.misc install) + in - config - ) + List.rev_append files_and_installs misc_files, config + +(* Returns function to install package files from [.install] *) +let preprocess_dot_install st nv build_dir = + let files_and_installs, config = preprocess_dot_install_t st nv build_dir in + let root = st.switch_global.root in + let files, installs = List.split files_and_installs in + let really_process_dot_install () = + if OpamStateConfig.(!r.dryrun) then + OpamConsole.msg "Installing %s.\n" (OpamPackage.to_string nv) + else if OpamFilename.exists_dir build_dir then + let (warning, had_windows_warnings) = + if OpamFormatConfig.(!r.strict) then + let had_warnings = ref false in + let install_warning dst warning = + let () = + match warning with + | `Add_exe | `Install_script | `Cygwin | `Cygwin_libraries -> + had_warnings := true + | _ -> + () + in + OpamSystem.default_install_warning dst warning + in + (install_warning, (fun () -> !had_warnings)) + else + (OpamSystem.default_install_warning, (fun () -> false)) + in + OpamFilename.in_dir build_dir @@ fun () -> + log "Installing %s.\n" (OpamPackage.to_string nv); + let warnings = + OpamStd.List.filter_map (fun install -> install warning) installs + in + if warnings <> [] then + (let install_f = OpamPath.Switch.install root st.switch nv.name in + let print (dir, base) = + Printf.sprintf " - %s to %s\n" + (OpamFilename.to_string (OpamFilename.create build_dir base)) + (OpamFilename.Dir.to_string dir) + in + OpamConsole.error "Installation of %s failed" + (OpamPackage.to_string nv); + let msg = + Printf.sprintf + "Some files in %s couldn't be installed:\n%s" + (OpamFile.to_string install_f) + (String.concat "" (List.map print warnings)) + in + failwith msg); + if had_windows_warnings () then + failwith "Strict mode is enabled - previous warnings considered fatal" + in + files, really_process_dot_install, config let download_package st nv = log "download_package: %a" (slog OpamPackage.to_string) nv; @@ -156,44 +250,59 @@ OpamFile.OPAM.safe_read >>= OpamFile.OPAM.version_opt) = Some nv.version - then - Done None + then Done None else - (OpamUpdate.cleanup_source st - (OpamPackage.Map.find_opt nv st.installed_opams) - (OpamSwitchState.opam st nv); - OpamProcess.Job.catch (fun e -> - let na = - match e with - | OpamDownload.Download_fail (s,l) -> (s,l) - | e -> (None, Printexc.to_string e) - in - Done (Some na)) - @@ fun () -> - OpamUpdate.download_package_source st nv dir @@| function - | Some (Not_available (s,l)) -> Some (s,l) - | None | Some (Up_to_date () | Result ()) -> None) + let print_action msg = + OpamConsole.msg "%s retrieved %s.%s (%s)\n" + (if not (OpamConsole.utf8 ()) then "->" + else OpamActionGraph. + (action_color (`Fetch ()) (action_strings (`Fetch ())))) + (OpamConsole.colorise `bold (OpamPackage.name_to_string nv)) + (OpamPackage.version_to_string nv) + msg; + in + OpamUpdate.cleanup_source st + (OpamPackage.Map.find_opt nv st.installed_opams) + (OpamSwitchState.opam st nv); + OpamProcess.Job.catch (fun e -> + let na = + match e with + | OpamDownload.Download_fail (s,l) -> (s,l) + | e -> (None, Printexc.to_string e) + in + Done (Some na)) + @@ fun () -> + OpamUpdate.download_package_source st nv dir @@| function + | Some (Not_available (s, l)), _ -> + let msg = match s with None -> l | Some s -> s in + OpamConsole.error "Failed to get sources of %s: %s" + (OpamPackage.to_string nv) msg; + Some (s, l) + | _, ((name, Not_available (s, l)) :: _) -> + let msg = match s with None -> l | Some s -> s in + OpamConsole.error "Failed to get extra source \"%s\" of %s: %s" + name (OpamPackage.to_string nv) msg; + Some (s, l) + | Some (Result msg), _ -> + print_action msg; None + | Some (Up_to_date msg), _ -> + print_action msg; None + | None, [] -> None + | None, (e :: es as extras) -> + if List.for_all (function _, Up_to_date _ -> true | _ -> false) extras then + print_action "cached" + else begin match e, es with + | (_, Result msg), [] -> print_action msg + | _, _ -> print_action (Printf.sprintf "%d extra sources" (List.length extras)) + end; + None (* Prepare the package build: * apply the patches * substitute the files *) -let prepare_package_build st nv dir = - let opam = OpamSwitchState.opam st nv in - +let prepare_package_build env opam nv dir = let patches = OpamFile.OPAM.patches opam in - let rec iter_patches f = function - | [] -> Done [] - | (patchname,filter)::rest -> - if OpamFilter.opt_eval_to_bool (OpamPackageVar.resolve ~opam st) filter - then - OpamFilename.patch (dir // OpamFilename.Base.to_string patchname) dir - @@+ function - | None -> iter_patches f rest - | Some err -> - iter_patches f rest @@| fun e -> (patchname, err) :: e - else iter_patches f rest - in let print_apply basename = log "%s: applying %s.\n" (OpamPackage.name_to_string nv) (OpamFilename.Base.to_string basename); @@ -202,21 +311,51 @@ (OpamConsole.colorise `green (OpamPackage.name_to_string nv)) (OpamFilename.Base.to_string basename) in + let print_subst basename = + let file = OpamFilename.Base.to_string basename in + let file_in = file ^ ".in" in + log "%s: expanding opam variables in %s, generating %s.\n" + (OpamPackage.name_to_string nv) + file_in file; + if OpamConsole.verbose () then + OpamConsole.msg + "[%s: subst] expanding opam variables in %s, generating %s\n" + (OpamConsole.colorise `green (OpamPackage.name_to_string nv)) + file_in file + in - if OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.fake) then - iter_patches print_apply patches @@| fun _ -> None - else - + let apply_patches ?(dryrun=false) () = + let patch base = + if dryrun then Done None else + OpamFilename.patch + (dir // OpamFilename.Base.to_string base) dir + in + let rec aux = function + | [] -> Done [] + | (patchname,filter)::rest -> + if OpamFilter.opt_eval_to_bool env filter then + (print_apply patchname; + patch patchname @@+ function + | None -> aux rest + | Some err -> aux rest @@| fun e -> (patchname, err) :: e) + else aux rest + in + aux patches + in let subst_patches, subst_others = List.partition (fun f -> List.mem_assoc f patches) (OpamFile.OPAM.substs opam) in + if OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.fake) then + (List.iter print_subst (OpamFile.OPAM.substs opam); + apply_patches ~dryrun:true ()) @@| fun _ -> None + else let subst_errs = OpamFilename.in_dir dir @@ fun () -> List.fold_left (fun errs f -> try - OpamFilter.expand_interpolations_in_file - (OpamPackageVar.resolve ~opam st) f; + print_subst f; + OpamFilter.expand_interpolations_in_file env f; errs with e -> (f, e)::errs) [] subst_patches @@ -226,25 +365,19 @@ let text = OpamProcess.make_command_text (OpamPackage.Name.to_string nv.name) "patch" in - - OpamProcess.Job.with_text text @@ - iter_patches (fun base -> - let patch = dir // OpamFilename.Base.to_string base in - print_apply base; - OpamFilename.patch patch dir) - patches + OpamProcess.Job.with_text text (apply_patches ()) @@+ fun patching_errors -> (* Substitute the configuration files. We should be in the right directory to get the correct absolute path for the - substitution files (see [substitute_file] and + substitution files (see [OpamFilter.expand_interpolations_in_file] and [OpamFilename.of_basename]. *) let subst_errs = OpamFilename.in_dir dir @@ fun () -> List.fold_left (fun errs f -> try - OpamFilter.expand_interpolations_in_file - (OpamPackageVar.resolve ~opam st) f; + print_subst f; + OpamFilter.expand_interpolations_in_file env f; errs with e -> (f, e)::errs) subst_errs subst_others @@ -302,24 +435,53 @@ (Done None) (OpamFile.OPAM.extra_sources opam) in let check_extra_files = - try - List.iter (fun (src,base,hash) -> - if not (OpamHash.check_file (OpamFilename.to_string src) hash) then - failwith - (Printf.sprintf "Bad hash for %s" (OpamFilename.to_string src)) + let extra_files = + let extra_files = + OpamFile.OPAM.get_extra_files + ~repos_roots:(OpamRepositoryState.get_root st.switch_repos) + opam + in + if extra_files <> [] then extra_files else + match OpamFile.OPAM.extra_files opam with + | None -> [] + | Some xs -> + (* lookup in switch-local hashmap overlay *) + let extra_files_dir = + OpamPath.Switch.extra_files_dir st.switch_global.root st.switch + in + OpamStd.List.filter_map (fun (base, hash) -> + let src = + OpamFilename.create extra_files_dir + (OpamFilename.Base.of_string (OpamHash.contents hash)) + in + if OpamFilename.exists src then + Some (src, base, hash) + else None) + xs + in + let bad_hash = + OpamStd.List.filter_map (fun (src, base, hash) -> + if OpamHash.check_file (OpamFilename.to_string src) hash then + (OpamFilename.copy ~src ~dst:(OpamFilename.create dir base); None) else - OpamFilename.copy ~src ~dst:(OpamFilename.create dir base)) - (OpamFile.OPAM.get_extra_files opam); - None - with e -> Some e + Some src) extra_files + in + if bad_hash = [] then None else + Some (Failure + (Printf.sprintf "Bad hash for %s" + (OpamStd.Format.itemize OpamFilename.to_string bad_hash))); in OpamFilename.mkdir dir; get_extra_sources_job @@+ function Some _ as err -> Done err | None -> check_extra_files |> function Some _ as err -> Done err | None -> - prepare_package_build st nv dir + let opam = OpamSwitchState.opam st nv in + prepare_package_build (OpamPackageVar.resolve ~opam st) opam nv dir let compilation_env t opam = - OpamEnv.get_full ~set_opamroot:true ~set_opamswitch:true ~force_path:true t ~updates:([ + let open OpamParserTypes in + let scrub = OpamClientConfig.(!r.scrubbed_environment_variables) in + OpamEnv.get_full ~scrub ~set_opamroot:true ~set_opamswitch:true + ~force_path:true t ~updates:([ "CDPATH", Eq, "", Some "shell env sanitization"; "MAKEFLAGS", Eq, "", Some "make env sanitization"; "MAKELEVEL", Eq, "", Some "make env sanitization"; @@ -329,6 +491,7 @@ "OPAM_PACKAGE_VERSION", Eq, OpamPackage.Version.to_string (OpamFile.OPAM.version opam), Some "build environment definition"; + "OPAMCLI", Eq, "2.0", Some "opam CLI version"; ] @ OpamFile.OPAM.build_env opam) @@ -361,12 +524,11 @@ ~inner:(OpamFile.Switch_config.wrappers t.switch_config) let get_wrapper t opam wrappers ?local getter = - let root = t.switch_global.root in - let hook_vnam = OpamVariable.of_string "hooks" in - let hook_vval = Some (OpamVariable.dirname (OpamPath.hooks_dir root)) in - let local_env = match local with - | Some e -> OpamVariable.Map.add hook_vnam hook_vval e - | None ->OpamVariable.Map.singleton hook_vnam hook_vval + let local_env = + let hook_env = OpamEnv.hook_env t.switch_global.root in + match local with + | Some e -> OpamVariable.Map.union (fun _ v -> v) e hook_env + | None -> hook_env in OpamFilter.commands (OpamPackageVar.resolve ~local:local_env ~opam t) (getter wrappers) |> @@ -404,16 +566,19 @@ OpamPackage.Set.(elements @@ inter st.compiler_packages st.installed_roots)); if OpamPackage.Set.mem nv st.pinned then - match OpamFile.OPAM.get_url opam with + match OpamFile.OPAM.url opam with | None -> "pinned" - | Some u -> + | Some url -> + let u = OpamFile.URL.url url in let src = OpamPath.Switch.pinned_package st.switch_global.root st.switch nv.name in let rev = OpamProcess.Job.run (OpamRepository.revision src u) in - Printf.sprintf "pinned(%s%s)" + Printf.sprintf "pinned(%s%s%s)" (OpamUrl.to_string u) + (OpamStd.Option.to_string (fun s -> "("^s^")") + (OpamFile.URL.subpath url)) (OpamStd.Option.to_string (fun r -> "#"^OpamPackage.Version.to_string r) rev) else @@ -692,26 +857,36 @@ let name = OpamPackage.name_to_string nv in let wrappers = get_wrappers t in let mk_cmd = make_command t opam ~dir:build_dir in - OpamProcess.Job.of_fun_list - (List.map (fun cmd () -> mk_cmd cmd) - (get_wrapper t opam wrappers OpamFile.Wrappers.pre_build) @ - List.map (fun ((cmd,args) as ca) () -> - mk_cmd ~text_command:ca @@ - cmd_wrapper t opam wrappers OpamFile.Wrappers.wrap_build cmd args) - commands) - @@+ fun result -> - let local = - opam_local_env_of_status OpamStd.Option.Op.(result >>| snd) - in + let jobs = + let check_result cmd r = + if OpamProcess.is_success r then Done None else Done (Some (cmd, r)) + in + List.map (fun cmd -> function + | None -> let cmd = mk_cmd cmd in cmd @@> check_result cmd + | some -> Done some) + (get_wrapper t opam wrappers OpamFile.Wrappers.pre_build) + @ + List.map (fun ((cmd,args) as cmd_args) -> function + | None -> + let base_cmd = OpamProcess.command cmd args in + (mk_cmd ~text_command:cmd_args @@ + cmd_wrapper t opam wrappers OpamFile.Wrappers.wrap_build cmd args) + @@> check_result base_cmd + | some -> Done some) + commands + in + OpamProcess.Job.seq jobs None @@+ + fun result -> + let local = opam_local_env_of_status OpamStd.Option.Op.(result >>| snd) in OpamProcess.Job.of_fun_list ~keep_going:true (List.map (fun cmd () -> mk_cmd cmd) (get_wrapper t opam wrappers ~local OpamFile.Wrappers.post_build)) @@+ fun post_result -> match result, post_result with - | Some (cmd, result), _ | None, Some (cmd, result) -> + | Some (cmd, result), _ | _, Some (cmd, result) -> OpamConsole.error "The compilation of %s failed at %S." - name (OpamProcess.string_of_command cmd); + (OpamPackage.to_string nv) (OpamProcess.string_of_command cmd); Done (Some (OpamSystem.Process_error result)) | None, None -> if commands <> [] && OpamConsole.verbose () then @@ -758,19 +933,44 @@ ) | [] -> Done None in - let install_job () = + let root = t.switch_global.root in + let switch_prefix = OpamPath.Switch.root root t.switch in + let pre_install_wrappers = + get_wrapper t opam wrappers OpamFile.Wrappers.pre_install + in + let pre_install () = (* let text = OpamProcess.make_command_text name "install" in - * OpamProcess.Job.with_text text *) + * OpamProcess.Job.with_text text *) OpamProcess.Job.of_fun_list - (List.map (fun cmd () -> mk_cmd cmd) - (get_wrapper t opam wrappers OpamFile.Wrappers.pre_install)) - @@+ fun error -> - (match error with - | None -> run_commands commands - | Some (_, result) -> Done (Some (OpamSystem.Process_error result))) - @@| function - | Some e -> Right e - | None -> try Left (process_dot_install t nv dir) with e -> Right e + (List.map (fun cmd () -> mk_cmd cmd) pre_install_wrappers) + in + let install_job () = + pre_install () + @@+ function + | Some (_, result) -> Done (Right (OpamSystem.Process_error result)) + | None -> + run_commands commands @@| function + | Some e -> Right e + | None -> + try + let _, process_dot_install, config = preprocess_dot_install t nv dir in + process_dot_install (); + Left config + with e -> Right e + in + let install_and_track_job () = + pre_install () + @@+ function + | Some (_, result) -> + Done (Right (OpamSystem.Process_error result), OpamStd.String.Map.empty) + | None -> + let installed_files, process_dot_install, config = + preprocess_dot_install t nv dir + in + OpamDirTrack.track_files ~prefix:switch_prefix installed_files + (fun () -> process_dot_install () ; Done None) + @@+ function + | _, changes -> Done (Left config, changes) in let post_install status changes = let local = @@ -788,31 +988,42 @@ (OpamVariable.of_string "installed-files") (Some (L added)) in + let hooks = + get_wrapper t opam wrappers ~local OpamFile.Wrappers.post_install + in + let has_hooks = match hooks with [] -> false | _ -> true in OpamProcess.Job.of_fun_list ~keep_going:true - (List.map (fun cmd () -> mk_cmd cmd) - (get_wrapper t opam wrappers ~local OpamFile.Wrappers.post_install)) + (List.map (fun cmd () -> mk_cmd cmd) hooks) @@+ fun error_post -> match status, error_post with | Right err, _ -> Done (Right err, changes) | _, Some (_cmd, r) -> Done (Right (OpamSystem.Process_error r), changes) - | Left config, None -> Done (Left config, changes) + | Left config, None -> + let changes = + if has_hooks then + OpamDirTrack.update switch_prefix changes + else + changes + in + Done (Left config, changes) in - let root = t.switch_global.root in - let switch_prefix = OpamPath.Switch.root root t.switch in let rel_meta_dir = OpamFilename.(Base.of_string (remove_prefix_dir switch_prefix (OpamPath.Switch.meta root t.switch))) in - OpamDirTrack.track switch_prefix - ~except:(OpamFilename.Base.Set.singleton rel_meta_dir) - install_job + (if commands = [] && pre_install_wrappers = [] then + install_and_track_job () + else + OpamDirTrack.track switch_prefix + ~except:(OpamFilename.Base.Set.singleton rel_meta_dir) + install_job) @@+ fun (status, changes) -> post_install status changes @@+ function | Right e, changes -> remove_package t ~silent:true ~changes ~build_dir:dir nv @@+ fun () -> OpamStd.Exn.fatal e; Done (Right e) - | config, changes -> + | Left config, changes -> let changes_f = OpamPath.Switch.changes root t.switch nv.name in OpamFile.Changes.write changes_f changes; OpamConsole.msg "%s installed %s.%s\n" @@ -836,4 +1047,4 @@ OpamConsole.warning "%s claims to be a plugin but no %s file was found" name (OpamFilename.to_string target) ); - Done config + Done (Left config) diff -Nru opam-2.0.10/src/client/opamAction.mli opam-2.1.2/src/client/opamAction.mli --- opam-2.0.10/src/client/opamAction.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAction.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -29,6 +29,12 @@ val prepare_package_source: rw switch_state -> package -> dirname -> exn option OpamProcess.job +(** [prepare_package_build env opam pkg dir] is a lower level version + of `prepare_package_source`, without requiring a switch and + without handling extra downloads. *) +val prepare_package_build: + OpamFilter.env -> OpamFile.OPAM.t -> package -> dirname -> exn option OpamProcess.job + (** [build_package t build_dir pkg] builds the package [pkg] within [build_dir]. Returns [None] on success, [Some exn] on error. See {!download_package} and {!prepare_package_source} for the previous diff -Nru opam-2.0.10/src/client/opamAdminCheck.ml opam-2.1.2/src/client/opamAdminCheck.ml --- opam-2.0.10/src/client/opamAdminCheck.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAdminCheck.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017-2018 OCamlPro *) +(* Copyright 2017-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -58,6 +58,7 @@ u_installed_roots = OpamPackage.Set.empty; u_pinned = OpamPackage.Set.empty; u_base = OpamPackage.Set.empty; + u_invariant = OpamFormula.Empty; u_attrs = []; u_reinstall = OpamPackage.Set.empty; } @@ -170,7 +171,7 @@ * "Number of vertices: before merge %d, after merge %d\n" * count (OpamCudf.Graph.nb_vertex g); *) let it = ref 0 in - let rec extract_cycles acc rpath v g = + let rec extract_cycles acc seen rpath v g = incr it; let rec find_pref acc v = function | [] -> None @@ -180,17 +181,19 @@ else find_pref (v1::acc) v r in match find_pref [] v rpath with - | Some cy -> cy :: acc + | Some cy -> cy :: acc, seen | None -> + if OpamCudf.Set.mem v seen then acc, seen else + let seen = OpamCudf.Set.add v seen in let rpath = v::rpath in (* split into sub-graphs for each successor *) List.fold_left - (fun acc s -> extract_cycles acc rpath s g) - acc (OpamCudf.Graph.succ g v) + (fun (acc, seen) s -> extract_cycles acc seen rpath s g) + (acc, seen) (OpamCudf.Graph.succ g v) in let p0 = List.find (OpamCudf.Graph.mem_vertex g) pkgs in - let r = extract_cycles acc [] p0 g in (* OpamConsole.msg "Iterations: %d\n" !it; *) + let r, _seen = extract_cycles acc OpamCudf.Set.empty [] p0 g in node_map, r ) (OpamCudf.Map.empty, []) scc @@ -301,7 +304,7 @@ can be installed with a.va1 is vb1). An aggregate should not contain more than one version per package name. *) let aggregate packages deps revdeps = - if OpamStd.Config.env_bool "NOAGGREGATE" = Some true then + if OpamClientConfig.E.noaggregate () = Some true then PkgSet.fold (fun nv -> PkgSetSet.add (PkgSet.singleton nv)) packages PkgSetSet.empty else @@ -402,8 +405,7 @@ aggregates PkgSet.empty let check ~quiet ~installability ~cycles ~obsolete ~ignore_test repo_root = - let repo = OpamRepositoryBackend.local repo_root in - let pkg_prefixes = OpamRepository.packages_with_prefixes repo in + let pkg_prefixes = OpamRepository.packages_with_prefixes repo_root in let opams = OpamPackage.Map.fold (fun nv prefix acc -> let opam_file = OpamRepositoryPath.opam repo_root prefix nv in diff -Nru opam-2.0.10/src/client/opamAdminCommand.ml opam-2.1.2/src/client/opamAdminCommand.ml --- opam-2.0.10/src/client/opamAdminCommand.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAdminCommand.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2017 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -14,11 +14,26 @@ open OpamStateTypes open Cmdliner +type command = unit Cmdliner.Term.t * Cmdliner.Term.info + +let checked_repo_root () = + let repo_root = OpamFilename.cwd () in + if not (OpamFilename.exists_dir (OpamRepositoryPath.packages_dir repo_root)) + then + OpamConsole.error_and_exit `Bad_arguments + "No repository found in current directory.\n\ + Please make sure there is a \"packages%s\" directory" OpamArg.dir_sep; + repo_root + +let global_options cli = + let apply_cli options = { options with OpamArg.cli = options.OpamArg.cli} in + Term.(const apply_cli $ OpamArg.global_options cli) + let admin_command_doc = "Tools for repository administrators" let admin_command_man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P (Printf.sprintf "This command can perform various actions on repositories in the opam \ format. It is expected to be run from the root of a repository, i.e. a \ @@ -31,11 +46,11 @@ let index_command_doc = "Generate an inclusive index file for serving over HTTP." -let index_command = +let index_command cli = let command = "index" in let doc = index_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "An opam repository can be served over HTTP or HTTPS using any web \ server. To that purpose, an inclusive index needs to be generated \ first: this command generates the files the opam client will expect \ @@ -43,36 +58,31 @@ are done to the contents of the repository." ] in - let urls_txt_arg = - Arg.(value & vflag `minimal_urls_txt [ - `no_urls_txt, info ["no-urls-txt"] ~doc: - "Don't generate a 'urls.txt' file. That index file is no longer \ - needed from opam 2.0 on, but is still used by older versions."; - `full_urls_txt, info ["full-urls-txt"] ~doc: - "Generate an inclusive 'urls.txt', for a repository that will be \ - used by opam versions earlier than 2.0."; - `minimal_urls_txt, info ["minimal-urls-txt"] ~doc: - "Generate a minimal 'urls.txt' file, that only includes the 'repo' \ - file. This allows opam versions earlier than 2.0 to read that file, \ - and be properly redirected to a repository dedicated to their \ - version, assuming a suitable 'redirect:' field is defined, instead \ - of failing. This is the default."; - ]) - in - let cmd global_options urls_txt = - OpamArg.apply_global_options global_options; - let repo_root = OpamFilename.cwd () in - if not (OpamFilename.exists_dir OpamFilename.Op.(repo_root / "packages")) - then - OpamConsole.error_and_exit `Bad_arguments - "No repository found in current directory.\n\ - Please make sure there is a \"packages/\" directory"; + let urls_txt_arg cli = + OpamArg.mk_vflag ~cli `minimal_urls_txt [ + OpamArg.cli_original, `no_urls_txt, ["no-urls-txt"], + "Don't generate a 'urls.txt' file. That index file is no longer \ + needed from opam 2.0 on, but is still used by older versions."; + OpamArg.cli_original, `full_urls_txt, ["full-urls-txt"], + "Generate an inclusive 'urls.txt', for a repository that will be \ + used by opam versions earlier than 2.0."; + OpamArg.cli_original, `minimal_urls_txt, ["minimal-urls-txt"], + "Generate a minimal 'urls.txt' file, that only includes the 'repo' \ + file. This allows opam versions earlier than 2.0 to read that file, \ + and be properly redirected to a repository dedicated to their \ + version, assuming a suitable 'redirect:' field is defined, instead \ + of failing. This is the default."; + ] + in + let cmd global_options urls_txt () = + OpamArg.apply_global_options cli global_options; + let repo_root = checked_repo_root () in let repo_file = OpamRepositoryPath.repo repo_root in let repo_def = match OpamFile.Repo.read_opt repo_file with | None -> OpamConsole.warning "No \"repo\" file found. Creating a minimal one."; - OpamFile.Repo.create ~opam_version:OpamVersion.current_nopatch () + OpamFile.Repo.create () | Some r -> r in let repo_stamp = @@ -111,12 +121,27 @@ OpamHTTP.make_index_tar_gz repo_root; OpamConsole.msg "Done.\n"; in - Term.(const cmd $ OpamArg.global_options $ urls_txt_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ urls_txt_arg cli) +let cache_urls repo_root repo_def = + let global_dl_cache = + OpamStd.Option.Op.(OpamStateConfig.(load ~lock_kind:`Lock_read !r.root_dir) +! + OpamFile.Config.empty) + |> OpamFile.Config.dl_cache + in + let repo_dl_cache = + OpamStd.List.filter_map (fun rel -> + if OpamStd.String.contains ~sub:"://" rel + then OpamUrl.parse_opt ~handle_suffix:false rel + else Some OpamUrl.Op.(OpamUrl.of_string + (OpamFilename.Dir.to_string repo_root) / rel)) + (OpamFile.Repo.dl_cache repo_def) + in + repo_dl_cache @ global_dl_cache (* Downloads all urls of the given package to the given cache_dir *) -let package_files_to_cache repo_root cache_dir ?link (nv, prefix) = +let package_files_to_cache repo_root cache_dir cache_urls ?link (nv, prefix) = match OpamFileTools.read_opam (OpamRepositoryPath.packages repo_root prefix nv) @@ -135,10 +160,10 @@ Done errors | (first_checksum :: _) as checksums -> OpamRepository.pull_file_to_cache label - ~cache_dir + ~cache_urls ~cache_dir checksums (OpamFile.URL.url urlf :: OpamFile.URL.mirrors urlf) - @@| function + @@| fun r -> match OpamRepository.report_fetch_result nv r with | Not_available (_,m) -> OpamPackage.Map.update nv (fun l -> m::l) [] errors | Up_to_date () | Result () -> @@ -169,11 +194,11 @@ OpamProcess.Job.seq urls OpamPackage.Map.empty let cache_command_doc = "Fills a local cache of package archives" -let cache_command = +let cache_command cli = let command = "cache" in let doc = cache_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Downloads the archives for all packages to fill a local cache, that \ can be used when serving the repository." ] @@ -184,42 +209,37 @@ "Name of the cache directory to use.") in let no_repo_update_arg = - Arg.(value & flag & info ["no-repo-update";"n"] ~doc: - "Don't check, create or update the 'repo' file to point to the \ - generated cache ('archive-mirrors:' field).") + OpamArg.mk_flag ~cli OpamArg.cli_original ["no-repo-update";"n"] + "Don't check, create or update the 'repo' file to point to the \ + generated cache ('archive-mirrors:' field)." in let link_arg = - Arg.(value & opt (some OpamArg.dirname) None & - info ["link"] ~docv:"DIR" ~doc: - (Printf.sprintf - "Create reverse symbolic links to the archives within $(i,DIR), in \ - the form $(b,DIR%sPKG.VERSION%sFILENAME)." - OpamArg.dir_sep OpamArg.dir_sep)) + OpamArg.mk_opt ~cli OpamArg.cli_original ["link"] "DIR" + (Printf.sprintf + "Create reverse symbolic links to the archives within $(i,DIR), in \ + the form $(b,DIR%sPKG.VERSION%sFILENAME)." + OpamArg.dir_sep OpamArg.dir_sep) + Arg.(some OpamArg.dirname) None in let jobs_arg = - Arg.(value & opt OpamArg.positive_integer 8 & - info ["jobs"; "j"] ~docv:"JOBS" ~doc: - "Number of parallel downloads") - in - let cmd global_options cache_dir no_repo_update link jobs = - OpamArg.apply_global_options global_options; - let repo_root = OpamFilename.cwd () in - if not (OpamFilename.exists_dir OpamFilename.Op.(repo_root / "packages")) - then - OpamConsole.error_and_exit `Bad_arguments - "No repository found in current directory.\n\ - Please make sure there is a \"packages\" directory"; + OpamArg.mk_opt ~cli OpamArg.cli_original ["jobs"; "j"] + "JOBS" "Number of parallel downloads" + OpamArg.positive_integer 8 + in + let cmd global_options cache_dir no_repo_update link jobs () = + OpamArg.apply_global_options cli global_options; + let repo_root = checked_repo_root () in let repo_file = OpamRepositoryPath.repo repo_root in let repo_def = OpamFile.Repo.safe_read repo_file in - let repo = OpamRepositoryBackend.local repo_root in - let pkg_prefixes = OpamRepository.packages_with_prefixes repo in + let pkg_prefixes = OpamRepository.packages_with_prefixes repo_root in + let cache_urls = cache_urls repo_root repo_def in let errors = OpamParallel.reduce ~jobs ~nil:OpamPackage.Map.empty ~merge:(OpamPackage.Map.union (fun a _ -> a)) - ~command:(package_files_to_cache repo_root cache_dir ?link) + ~command:(package_files_to_cache repo_root cache_dir cache_urls ?link) (List.sort (fun (nv1,_) (nv2,_) -> (* Some pseudo-randomisation to avoid downloading all files from the same host simultaneously *) @@ -229,8 +249,8 @@ (OpamPackage.Map.bindings pkg_prefixes)) in + let cache_dir_url = OpamFilename.remove_prefix_dir repo_root cache_dir in if not no_repo_update then - let cache_dir_url = OpamFilename.remove_prefix_dir repo_root cache_dir in if not (List.mem cache_dir_url (OpamFile.Repo.dl_cache repo_def)) then (OpamConsole.msg "Adding %s to %s...\n" cache_dir_url (OpamFile.to_string repo_file); @@ -239,31 +259,31 @@ (cache_dir_url :: OpamFile.Repo.dl_cache repo_def) repo_def)); - if not (OpamPackage.Map.is_empty errors) then ( - OpamConsole.error "Got some errors while processing: %s" - (OpamStd.List.concat_map ", " OpamPackage.to_string - (OpamPackage.Map.keys errors)); - OpamConsole.errmsg "%s" - (OpamStd.Format.itemize (fun (nv,el) -> - Printf.sprintf "[%s] %s" (OpamPackage.to_string nv) - (String.concat "\n" el)) - (OpamPackage.Map.bindings errors)) - ); - - OpamConsole.msg "Done.\n"; - in - Term.(const cmd $ OpamArg.global_options $ - cache_dir_arg $ no_repo_update_arg $ link_arg $ jobs_arg), - OpamArg.term_info command ~doc ~man + if not (OpamPackage.Map.is_empty errors) then ( + OpamConsole.error "Got some errors while processing: %s" + (OpamStd.List.concat_map ", " OpamPackage.to_string + (OpamPackage.Map.keys errors)); + OpamConsole.errmsg "%s" + (OpamStd.Format.itemize (fun (nv,el) -> + Printf.sprintf "[%s] %s" (OpamPackage.to_string nv) + (String.concat "\n" el)) + (OpamPackage.Map.bindings errors)) + ); + + OpamConsole.msg "Done.\n"; + in + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ + cache_dir_arg $ no_repo_update_arg $ link_arg $ jobs_arg) let add_hashes_command_doc = "Add archive hashes to an opam repository." -let add_hashes_command = +let add_hashes_command cli = let command = "add-hashes" in let doc = add_hashes_command_doc in let cache_dir = OpamFilename.Dir.of_string "~/.cache/opam-hash-cache" in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P (Printf.sprintf "This command scans through package definitions, and add hashes as \ requested (fetching the archives if required). A cache is generated \ @@ -273,17 +293,19 @@ in let hash_kinds = [`MD5; `SHA256; `SHA512] in let hash_types_arg = - let hash_kind_conv = - Arg.enum - (List.map (fun k -> OpamHash.string_of_kind k, k) - hash_kinds) - in - Arg.(non_empty & pos_all hash_kind_conv [] & info [] ~docv:"HASH_ALGO" ~doc: - "The hash, or hashes to be added") + OpamArg.nonempty_arg_list "HASH_ALGO" "The hash, or hashes to be added" + (Arg.enum + (List.map (fun k -> OpamHash.string_of_kind k, k) + hash_kinds)) + in + let packages = + OpamArg.mk_opt ~cli OpamArg.(cli_from cli2_1) ["p";"packages"] + "PACKAGES" "Only add hashes for the given packages" + Arg.(list OpamArg.package) [] in let replace_arg = - Arg.(value & flag & info ["replace"] ~doc: - "Replace the existing hashes rather than adding to them") + OpamArg.mk_flag ~cli OpamArg.cli_original ["replace"] + "Replace the existing hashes rather than adding to them" in let hash_tables = let t = Hashtbl.create (List.length hash_kinds) in @@ -367,25 +389,45 @@ | None -> ()); h in - let cmd global_options hash_types replace = - OpamArg.apply_global_options global_options; - let repo_root = OpamFilename.cwd () in - if not (OpamFilename.exists_dir OpamFilename.Op.(repo_root / "packages")) - then - OpamConsole.error_and_exit `Bad_arguments - "No repository found in current directory.\n\ - Please make sure there is a \"packages\" directory"; - let repo = OpamRepositoryBackend.local repo_root in + let cmd global_options hash_types replace packages () = + OpamArg.apply_global_options cli global_options; + let repo_root = checked_repo_root () in let cache_urls = - let repo_file = OpamRepositoryPath.repo repo_root in - List.map (fun rel -> - if OpamStd.String.contains ~sub:"://" rel - then OpamUrl.of_string rel - else OpamUrl.Op.(OpamUrl.of_string - (OpamFilename.Dir.to_string repo_root) / rel)) - (OpamFile.Repo.dl_cache (OpamFile.Repo.safe_read repo_file)) + cache_urls repo_root + (OpamFile.Repo.safe_read (OpamRepositoryPath.repo repo_root)) + in + let pkg_prefixes = + let pkgs_map = OpamRepository.packages_with_prefixes repo_root in + if packages = [] then pkgs_map + else + (let pkgs_map, missing_pkgs = + List.fold_left (fun ((map: string option OpamPackage.Map.t),error) (n,vo)-> + match vo with + | Some v -> + let nv = OpamPackage.create n v in + (match OpamPackage.Map.find_opt nv pkgs_map with + | Some pre ->( OpamPackage.Map.add nv pre map), error + | None -> map, (n,vo)::error) + | None -> + let n_map = OpamPackage.packages_of_name_map pkgs_map n in + if OpamPackage.Map.is_empty n_map then + map, (n,vo)::error + else + (OpamPackage.Map.union (fun _nv _nv' -> assert false) n_map map), + error + ) (OpamPackage.Map.empty, []) packages + in + if missing_pkgs <> [] then + OpamConsole.warning "Not found package%s %s. Ignoring them." + (if List.length missing_pkgs = 1 then "" else "s") + (OpamStd.List.concat_map ~left:"" ~right:"" ~last_sep:" and " ", " + (fun (n,vo) -> + OpamConsole.colorise `underline + (match vo with + | Some v -> OpamPackage.to_string (OpamPackage.create n v) + | None -> OpamPackage.Name.to_string n)) missing_pkgs); + pkgs_map) in - let pkg_prefixes = OpamRepository.packages_with_prefixes repo in let has_error = OpamPackage.Map.fold (fun nv prefix has_error -> let opam_file = OpamRepositoryPath.opam repo_root prefix nv in @@ -449,47 +491,43 @@ if has_error then OpamStd.Sys.exit_because `Sync_error else OpamStd.Sys.exit_because `Success in - Term.(const cmd $ OpamArg.global_options $ - hash_types_arg $ replace_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ + hash_types_arg $ replace_arg $ packages) let upgrade_command_doc = "Upgrades repository from earlier opam versions." -let upgrade_command = +let upgrade_command cli = let command = "upgrade" in let doc = upgrade_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P (Printf.sprintf - "This command reads repositories from earlier opam versions, and \ - converts them to repositories suitable for the current opam version. \ - Packages might be created or renamed, and any compilers defined in the \ - old format ('compilers%s' directory) will be turned into packages, \ - using a pre-defined hierarchy that assumes OCaml compilers." - OpamArg.dir_sep) + "This command reads repositories from earlier opam versions, and \ + converts them to repositories suitable for the current opam version. \ + Packages might be created or renamed, and any compilers defined in the \ + old format ('compilers%s' directory) will be turned into packages, \ + using a pre-defined hierarchy that assumes OCaml compilers." + OpamArg.dir_sep) ] in let clear_cache_arg = - let doc = - Printf.sprintf - "Instead of running the upgrade, clear the cache of archive hashes (held \ - in ~%s.cache), that is used to avoid re-downloading files to obtain \ - their hashes at every run." OpamArg.dir_sep - in - Arg.(value & flag & info ["clear-cache"] ~doc) + OpamArg.mk_flag ~cli OpamArg.cli_original ["clear-cache"] + (Printf.sprintf + "Instead of running the upgrade, clear the cache of archive hashes (held \ + in ~%s.cache), that is used to avoid re-downloading files to obtain \ + their hashes at every run." OpamArg.dir_sep) in let create_mirror_arg = - let doc = + OpamArg.mk_opt ~cli OpamArg.cli_original ["m"; "mirror"] "URL" "Don't overwrite the current repository, but put an upgraded mirror in \ place in a subdirectory, with proper redirections. Needs the URL the \ repository will be served from to put in the redirects (older versions \ of opam don't understand relative redirects)." - in - Arg.(value & opt (some OpamArg.url) None & - info ~docv:"URL" ["m"; "mirror"] ~doc) + Arg.(some OpamArg.url) None in - let cmd global_options clear_cache create_mirror = - OpamArg.apply_global_options global_options; + let cmd global_options clear_cache create_mirror () = + OpamArg.apply_global_options cli global_options; if clear_cache then OpamAdminRepoUpgrade.clear_cache () else match create_mirror with | None -> @@ -503,27 +541,27 @@ \ opam admin index" | Some m -> OpamAdminRepoUpgrade.do_upgrade_mirror (OpamFilename.cwd ()) m in - Term.(const cmd $ OpamArg.global_options $ - clear_cache_arg $ create_mirror_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ + clear_cache_arg $ create_mirror_arg) let lint_command_doc = "Runs 'opam lint' and reports on a whole repository" -let lint_command = +let lint_command cli = let command = "lint" in let doc = lint_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command gathers linting results on all files in a repository. The \ warnings and errors to show or hide can be selected" ] in let short_arg = - OpamArg.mk_flag ["s";"short"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["s";"short"] "Print only packages and warning/error numbers, without explanations" in let list_arg = - OpamArg.mk_flag ["list";"l"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["list";"l"] "Only list package names, without warning details" in let include_arg = @@ -531,33 +569,32 @@ OpamArg.positive_integer in let exclude_arg = - OpamArg.mk_opt_all ["exclude";"x"] "INT" + OpamArg.mk_opt_all ~cli OpamArg.cli_original ["exclude";"x"] "INT" "Exclude the given warnings or errors" OpamArg.positive_integer in let ignore_arg = - OpamArg.mk_opt_all ["ignore-packages";"i"] "INT" + OpamArg.mk_opt_all ~cli OpamArg.cli_original ["ignore-packages";"i"] "INT" "Ignore any packages having one of these warnings or errors" OpamArg.positive_integer in let warn_error_arg = - OpamArg.mk_flag ["warn-error";"W"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["warn-error";"W"] "Return failure on any warnings, not only on errors" in - let cmd global_options short list incl excl ign warn_error = - OpamArg.apply_global_options global_options; + let cmd global_options short list incl excl ign warn_error () = + OpamArg.apply_global_options cli global_options; let repo_root = OpamFilename.cwd () in if not (OpamFilename.exists_dir OpamFilename.Op.(repo_root / "packages")) then OpamConsole.error_and_exit `Bad_arguments "No repository found in current directory.\n\ Please make sure there is a \"packages\" directory"; - let repo = OpamRepositoryBackend.local repo_root in - let pkg_prefixes = OpamRepository.packages_with_prefixes repo in + let pkg_prefixes = OpamRepository.packages_with_prefixes repo_root in let ret = OpamPackage.Map.fold (fun nv prefix ret -> let opam_file = OpamRepositoryPath.opam repo_root prefix nv in - let w, _ = OpamFileTools.lint_file opam_file in + let w, _ = OpamFileTools.lint_file ~handle_dirname:true opam_file in if List.exists (fun (n,_,_) -> List.mem n ign) w then ret else let w = List.filter (fun (n,_,_) -> @@ -587,18 +624,18 @@ in OpamStd.Sys.exit_because (if ret then `Success else `False) in - Term.(const cmd $ OpamArg.global_options $ - short_arg $ list_arg $ include_arg $ exclude_arg $ ignore_arg $ - warn_error_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ + short_arg $ list_arg $ include_arg $ exclude_arg $ ignore_arg $ + warn_error_arg) let check_command_doc = "Runs some consistency checks on a repository" -let check_command = +let check_command cli = let command = "check" in let doc = check_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command runs consistency checks on a repository, and prints a \ report to stdout. Checks include packages that are not installable \ (due e.g. to a missing dependency) and dependency cycles. The \ @@ -607,40 +644,35 @@ ] in let ignore_test_arg = - OpamArg.mk_flag ["ignore-test-doc";"i"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["ignore-test-doc";"i"] "By default, $(b,{with-test}) and $(b,{with-doc}) dependencies are \ included. This ignores them, and makes the test more tolerant." in let print_short_arg = - OpamArg.mk_flag ["s";"short"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["s";"short"] "Only output a list of uninstallable packages" in let installability_arg = - OpamArg.mk_flag ["installability"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["installability"] "Do the installability check (and disable the others by default)" in let cycles_arg = - OpamArg.mk_flag ["cycles"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["cycles"] "Do the cycles check (and disable the others by default)" in let obsolete_arg = - OpamArg.mk_flag ["obsolete"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["obsolete"] "Analyse for obsolete packages" in let cmd global_options ignore_test print_short - installability cycles obsolete = - OpamArg.apply_global_options global_options; - let repo_root = OpamFilename.cwd () in + installability cycles obsolete () = + OpamArg.apply_global_options cli global_options; + let repo_root = checked_repo_root () in let installability, cycles, obsolete = if installability || cycles || obsolete then installability, cycles, obsolete else true, true, false in - if not (OpamFilename.exists_dir OpamFilename.Op.(repo_root / "packages")) - then - OpamConsole.error_and_exit `Bad_arguments - "No repository found in current directory.\n\ - Please make sure there is a \"packages\" directory"; let pkgs, unav_roots, uninstallable, cycle_packages, obsolete = OpamAdminCheck.check ~quiet:print_short ~installability ~cycles ~obsolete ~ignore_test @@ -676,9 +708,9 @@ (pr obsolete "obsolete packages")); OpamStd.Sys.exit_because (if all_ok then `Success else `False) in - Term.(const cmd $ OpamArg.global_options $ ignore_test_arg $ print_short_arg - $ installability_arg $ cycles_arg $ obsolete_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ ignore_test_arg $ print_short_arg + $ installability_arg $ cycles_arg $ obsolete_arg) let pattern_list_arg = OpamArg.arg_list "PATTERNS" @@ -686,34 +718,37 @@ $(b,NAME.VERSION)" Arg.string -let env_arg = - Arg.(value & opt (list string) [] & info ["environment"] ~doc:( - Printf.sprintf - "Use the given opam environment, in the form of a list of \ - comma-separated 'var=value' bindings, when resolving variables. This \ - is used e.g. when computing available packages: if undefined, \ - availability of packages will be assumed as soon as it can not be \ - resolved purely from globally defined variables. Note that, unless \ - overridden, variables like 'root' or 'opam-version' may be taken \ - from the current opam installation. What is defined in \ - $(i,~%s.opam%sconfig) is always ignored." - OpamArg.dir_sep OpamArg.dir_sep)) - -let state_selection_arg = - let docs = OpamArg.package_selection_section in - Arg.(value & vflag OpamListCommand.Available [ - OpamListCommand.Any, info ~docs ["A";"all"] - ~doc:"Include all, even uninstalled or unavailable packages"; - OpamListCommand.Available, info ~docs ["a";"available"] - ~doc:"List only packages that are available according to the defined \ - $(b,environment). Without $(b,--environment), equivalent to \ - $(b,--all)."; - OpamListCommand.Installable, info ~docs ["installable"] - ~doc:"List only packages that are installable according to the \ - defined $(b,environment) (this calls the solver and may be \ - more costly; a package depending on an unavailable may be \ - available, but is never installable)"; - ]) +let env_arg cli = + OpamArg.mk_opt ~cli OpamArg.cli_original ["environment"] + "VAR=VALUE[;VAR=VALUE]" + (Printf.sprintf + "Use the given opam environment, in the form of a list of \ + comma-separated 'var=value' bindings, when resolving variables. This \ + is used e.g. when computing available packages: if undefined, \ + availability of packages will be assumed as soon as it can not be \ + resolved purely from globally defined variables. Note that, unless \ + overridden, variables like 'root' or 'opam-version' may be taken \ + from the current opam installation. What is defined in \ + $(i,~%s.opam%sconfig) is always ignored." + OpamArg.dir_sep OpamArg.dir_sep) + Arg.(list string) [] + +let state_selection_arg cli = + OpamArg.mk_vflag ~cli ~section:OpamArg.package_selection_section + OpamListCommand.Available [ + OpamArg.cli_original, OpamListCommand.Any, ["A";"all"], + "Include all, even uninstalled or unavailable packages"; + OpamArg.cli_original, OpamListCommand.Available, ["a";"available"], + "List only packages that are available according to the defined \ + $(b,environment). Without $(b,--environment), this will include \ + any packages for which availability is not resolvable at this \ + point."; + OpamArg.cli_original, OpamListCommand.Installable, ["installable"], + "List only packages that are installable according to the defined \ + $(b,environment) (this calls the solver and may be more costly; \ + a package depending on an unavailable one may be available, but \ + is never installable)"; + ] let get_virtual_switch_state repo_root env = let env = @@ -723,10 +758,16 @@ | None -> OpamVariable.of_string s, B true) env in - let repo = OpamRepositoryBackend.local repo_root in + let repo = { + repo_name = OpamRepositoryName.of_string "local"; + repo_url = OpamUrl.empty; + repo_trust = None; + } in let repo_file = OpamRepositoryPath.repo repo_root in let repo_def = OpamFile.Repo.safe_read repo_file in - let opams = OpamRepositoryState.load_repo_opams repo in + let opams = + OpamRepositoryState.load_opams_from_dir repo.repo_name repo_root + in let gt = { global_lock = OpamSystem.lock_none; root = OpamStateConfig.(!r.root_dir); @@ -736,15 +777,18 @@ global_variables = OpamVariable.Map.empty; } in let singl x = OpamRepositoryName.Map.singleton repo.repo_name x in + let repos_tmp = + let t = Hashtbl.create 1 in + Hashtbl.add t repo.repo_name (lazy repo_root); t + in let rt = { repos_global = gt; repos_lock = OpamSystem.lock_none; repositories = singl repo; repos_definitions = singl repo_def; repo_opams = singl opams; + repos_tmp; } in - let st = OpamSwitchState.load_virtual ~repos_list:[repo.repo_name] gt rt in - if env = [] then st else let gt = {gt with global_variables = OpamVariable.Map.of_list @@ @@ -752,42 +796,35 @@ var, (lazy (Some value), "Manually defined")) env } in - {st with - switch_global = gt; - available_packages = lazy ( - OpamPackage.keys @@ - OpamPackage.Map.filter (fun package opam -> - OpamFilter.eval_to_bool ~default:false - (OpamPackageVar.resolve_switch_raw ~package gt - OpamSwitch.unset OpamFile.Switch_config.empty) - (OpamFile.OPAM.available opam)) - st.opams - )} - -let or_arg = - Arg.(value & flag & info ~docs:OpamArg.package_selection_section ["or"] - ~doc:"Instead of selecting packages that match $(i,all) the \ - criteria, select packages that match $(i,any) of them") + OpamSwitchState.load_virtual + ~repos_list:[repo.repo_name] + ~avail_default:(env = []) + gt rt + +let or_arg cli = + OpamArg.mk_flag ~cli OpamArg.cli_original ~section:OpamArg.package_selection_section ["or"] + "Instead of selecting packages that match $(i,all) the \ + criteria, select packages that match $(i,any) of them" let list_command_doc = "Lists packages from a repository" -let list_command = +let list_command cli = let command = "list" in let doc = list_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command is similar to 'opam list', but allows listing packages \ directly from a repository instead of what is available in a given \ opam installation."; - `S "ARGUMENTS"; - `S "OPTIONS"; + `S Manpage.s_arguments; + `S Manpage.s_options; `S OpamArg.package_selection_section; `S OpamArg.package_listing_section; ] in let cmd global_options package_selection disjunction state_selection - package_listing env packages = - OpamArg.apply_global_options global_options; + package_listing env packages () = + OpamArg.apply_global_options cli global_options; let format = let force_all_versions = match packages with @@ -823,38 +860,38 @@ in OpamListCommand.display st format results in - Term.(const cmd $ OpamArg.global_options $ OpamArg.package_selection $ - or_arg $ state_selection_arg $ OpamArg.package_listing $ env_arg $ - pattern_list_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ OpamArg.package_selection cli $ + or_arg cli $ state_selection_arg cli $ OpamArg.package_listing cli $ + env_arg cli $ pattern_list_arg) let filter_command_doc = "Filters a repository to only keep selected packages" -let filter_command = +let filter_command cli = let command = "filter" in let doc = filter_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command removes all package definitions that don't match the \ search criteria (specified similarly to 'opam admin list') from a \ repository."; - `S "ARGUMENTS"; - `S "OPTIONS"; + `S Manpage.s_arguments; + `S Manpage.s_options; `S OpamArg.package_selection_section; ] in let remove_arg = - OpamArg.mk_flag ["remove"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["remove"] "Invert the behaviour and remove the matching packages, keeping the ones \ that don't match." in let dryrun_arg = - OpamArg.mk_flag ["dry-run"] + OpamArg.mk_flag ~cli OpamArg.cli_original ["dry-run"] "List the removal commands, without actually performing them" in let cmd global_options package_selection disjunction state_selection env - remove dryrun packages = - OpamArg.apply_global_options global_options; + remove dryrun packages () = + OpamArg.apply_global_options cli global_options; let repo_root = OpamFilename.cwd () in let pattern_selector = OpamListCommand.pattern_selector packages in let join = @@ -899,8 +936,7 @@ if not (dryrun || OpamConsole.confirm "Confirm?") then OpamStd.Sys.exit_because `Aborted else - let repo = OpamRepositoryBackend.local repo_root in - let pkg_prefixes = OpamRepository.packages_with_prefixes repo in + let pkg_prefixes = OpamRepository.packages_with_prefixes repo_root in OpamPackage.Map.iter (fun nv prefix -> if OpamPackage.Set.mem nv packages then let d = OpamRepositoryPath.packages repo_root prefix nv in @@ -911,26 +947,27 @@ OpamFilename.rmdir_cleanup d)) pkg_prefixes in - Term.(const cmd $ OpamArg.global_options $ OpamArg.package_selection $ or_arg $ - state_selection_arg $ env_arg $ remove_arg $ dryrun_arg $ - pattern_list_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(const cmd $ global_options cli $ OpamArg.package_selection cli $ + or_arg cli $ state_selection_arg cli $ env_arg cli $ remove_arg $ + dryrun_arg $ + pattern_list_arg) let add_constraint_command_doc = "Adds version constraints on all dependencies towards a given package" -let add_constraint_command = +let add_constraint_command cli = let command = "add-constraint" in let doc = add_constraint_command_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command searches to all dependencies towards a given package, and \ adds a version constraint to them. It is particularly useful to add \ upper bounds to existing dependencies when a new, incompatible major \ version of a library is added to a repository. The new version \ constraint is merged with the existing one, and simplified if \ possible (e.g. $(b,>=3 & >5) becomes $(b,>5))."; - `S "ARGUMENTS"; - `S "OPTIONS"; + `S Manpage.s_arguments; + `S Manpage.s_options; ] in let atom_arg = @@ -942,32 +979,26 @@ package.") in let force_arg = - Arg.(value & flag & info ["force"] ~doc: - "Force updating of constraints even if the resulting constraint is \ - unsatisfiable (e.g. when adding $(b,>3) to the constraint \ - $(b,<2)). The default in this case is to print a warning and keep \ - the existing constraint unchanged.") - in - let cmd global_options force atom = - OpamArg.apply_global_options global_options; - let repo_root = OpamFilename.cwd () in - if not (OpamFilename.exists_dir OpamFilename.Op.(repo_root / "packages")) - then - OpamConsole.error_and_exit `Not_found - "No repository found in current directory.\n\ - Please make sure there is a \"packages\" directory"; - let repo = OpamRepositoryBackend.local repo_root in - let pkg_prefixes = OpamRepository.packages_with_prefixes repo in - let name, cstr = atom in - let cstr = match cstr with + OpamArg.mk_flag ~cli OpamArg.cli_original ["force"] + "Force updating of constraints even if the resulting constraint is \ + unsatisfiable (e.g. when adding $(b,>3) to the constraint \ + $(b,<2)). The default in this case is to print a warning and keep \ + the existing constraint unchanged." + in + let cmd global_options force atom () = + OpamArg.apply_global_options cli global_options; + let repo_root = checked_repo_root () in + let pkg_prefixes = OpamRepository.packages_with_prefixes repo_root in + let name, cstr_opt = atom in + let cstr = match cstr_opt with | Some (relop, v) -> OpamFormula.Atom (Constraint (relop, FString (OpamPackage.Version.to_string v))) | None -> OpamFormula.Empty in - let add_cstr nv n c = - let f = OpamFormula.ands [c; cstr] in + let add_cstr op cstr nv n c = + let f = op [ cstr; c] in match OpamFilter.simplify_extended_version_formula f with | Some f -> f | None -> (* conflicting constraint *) @@ -989,23 +1020,50 @@ let deps = OpamFormula.map (function | (n,c as atom) -> - if n = name then Atom (n, (add_cstr nv n c)) + if n = name then Atom (n, (add_cstr OpamFormula.ands cstr nv n c)) else Atom atom) deps0 in - if deps <> deps0 then + let depopts0 = OpamFile.OPAM.depopts opam in + let conflicts0 = OpamFile.OPAM.conflicts opam in + let contains name = + OpamFormula.fold_left (fun contains (n,_) -> + contains || n = name) false + in + let conflicts = + if contains name depopts0 then + match cstr_opt with + | Some (relop, v) -> + let icstr = + OpamFormula.Atom + (Constraint (OpamFormula.neg_relop relop, + FString (OpamPackage.Version.to_string v))) + in + if contains name conflicts0 then + OpamFormula.map (function + | (n,c as atom) -> + if n = name then Atom (n, (add_cstr OpamFormula.ors icstr nv n c)) + else Atom atom) + conflicts0 + else + OpamFormula.ors [ conflicts0; Atom (name, icstr) ] + | None -> conflicts0 + else conflicts0 + in + if deps <> deps0 || conflicts <> conflicts0 then OpamFile.OPAM.write_with_preserved_format opam_file - (OpamFile.OPAM.with_depends deps opam)) + (OpamFile.OPAM.with_depends deps opam + |> OpamFile.OPAM.with_conflicts conflicts)) pkg_prefixes in - Term.(pure cmd $ OpamArg.global_options $ force_arg $ atom_arg), - OpamArg.term_info command ~doc ~man + OpamArg.mk_command ~cli OpamArg.cli_original command ~doc ~man + Term.(pure cmd $ global_options cli $ force_arg $ atom_arg) (* HELP *) let help = let doc = "Display help about opam admin and opam admin subcommands." in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Prints help about opam admin commands."; `P "Use `$(mname) help topics' to get the full list of help topics."; ] in @@ -1027,34 +1085,36 @@ Term.(ret (const help $Term.man_format $Term.choice_names $topic)), Term.info "help" ~doc ~man -let admin_subcommands = [ - index_command; OpamArg.make_command_alias index_command "make"; - cache_command; - upgrade_command; - lint_command; - check_command; - list_command; - filter_command; - add_constraint_command; - add_hashes_command; - help; -] +let admin_subcommands cli = + let index_command = index_command cli in + [ + index_command; OpamArg.make_command_alias ~cli index_command "make"; + cache_command cli; + upgrade_command cli; + lint_command cli; + check_command cli; + list_command cli; + filter_command cli; + add_constraint_command cli; + add_hashes_command cli; + help; + ] -let default_subcommand = +let default_subcommand cli = let man = admin_command_man @ [ - `S "COMMANDS"; + `S Manpage.s_commands; `S "COMMAND ALIASES"; - ] @ OpamArg.help_sections + ] @ OpamArg.help_sections cli in let usage global_options = - OpamArg.apply_global_options global_options; + OpamArg.apply_global_options cli global_options; OpamConsole.formatted_msg "usage: opam admin [--version]\n\ \ [--help]\n\ \ []\n\ \n\ - The most commonly used opam commands are:\n\ + The most commonly used opam admin commands are:\n\ \ index %s\n\ \ cache %s\n\ \ upgrade-format %s\n\ @@ -1065,9 +1125,12 @@ cache_command_doc upgrade_command_doc in - Term.(const usage $ OpamArg.global_options), + Term.(const usage $ global_options cli), Term.info "opam admin" ~version:(OpamVersion.to_string OpamVersion.current) ~sdocs:OpamArg.global_option_section ~doc:admin_command_doc ~man + +let get_cmdliner_parser cli = + default_subcommand cli, admin_subcommands cli diff -Nru opam-2.0.10/src/client/opamAdminCommand.mli opam-2.1.2/src/client/opamAdminCommand.mli --- opam-2.0.10/src/client/opamAdminCommand.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAdminCommand.mli 2021-12-07 16:09:27.000000000 +0000 @@ -11,6 +11,6 @@ val admin_command_doc: string -val admin_subcommands: (unit Cmdliner.Term.t * Cmdliner.Term.info) list +type command = unit Cmdliner.Term.t * Cmdliner.Term.info -val default_subcommand: unit Cmdliner.Term.t * Cmdliner.Term.info +val get_cmdliner_parser: OpamCLIVersion.Sourced.t -> command * command list diff -Nru opam-2.0.10/src/client/opamAdminRepoUpgrade.ml opam-2.1.2/src/client/opamAdminRepoUpgrade.ml --- opam-2.0.10/src/client/opamAdminRepoUpgrade.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAdminRepoUpgrade.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -149,20 +149,18 @@ OpamFilename.of_string "~/.cache/opam-compilers-to-packages/url-hashes" let do_upgrade repo_root = - let repo = OpamRepositoryBackend.local repo_root in - let write_opam ?(add_files=[]) opam = let nv = O.package opam in let pfx = Some (OpamPackage.name_to_string nv) in - let files_dir = OpamRepositoryPath.files repo.repo_root pfx nv in - O.write (OpamRepositoryPath.opam repo.repo_root pfx nv) opam; + let files_dir = OpamRepositoryPath.files repo_root pfx nv in + O.write (OpamRepositoryPath.opam repo_root pfx nv) opam; List.iter (fun (base,contents) -> OpamFilename.(write Op.(files_dir // base) contents)) add_files in let compilers = - let compilers_dir = OpamFilename.Op.(repo.repo_root / "compilers") in + let compilers_dir = OpamFilename.Op.(repo_root / "compilers") in if OpamFilename.exists_dir compilers_dir then ( List.fold_left (fun map f -> if OpamFilename.check_suffix f ".comp" then @@ -179,7 +177,10 @@ let url_md5 = Hashtbl.create 187 in let () = OpamFile.Lines.read_opt cache_file +! [] |> List.iter @@ function - | [url; md5] -> Hashtbl.add url_md5 (OpamUrl.of_string url) (OpamHash.of_string md5) + | [url; md5] -> + OpamStd.Option.iter + (fun url -> Hashtbl.add url_md5 url (OpamHash.of_string md5)) + (OpamUrl.parse_opt ~handle_suffix:false url) | _ -> failwith "Bad cache, run 'opam admin upgrade --clear-cache'" in (fun url -> @@ -188,7 +189,14 @@ OpamFilename.with_tmp_dir_job @@ fun dir -> OpamProcess.Job.ignore_errors ~default:None (fun () -> - OpamDownload.download ~overwrite:false url dir @@| fun f -> + (* Download to package.patch, rather than allowing the name to be + guessed since, on Windows, some of the characters which are + valid in URLs are not valid in filenames *) + let f = + let base = OpamFilename.Base.of_string "package.patch" in + OpamFilename.create dir base + in + OpamDownload.download_as ~overwrite:false url f @@| fun () -> let hash = OpamHash.compute (OpamFilename.to_string f) in Hashtbl.add url_md5 url hash; Some hash)), @@ -397,14 +405,14 @@ in OpamStd.String.Set.iter gen_ocaml_wrapper ocaml_versions; - let packages = OpamRepository.packages_with_prefixes repo in + let packages = OpamRepository.packages_with_prefixes repo_root in OpamConsole.log "REPO_UPGRADE" "Will not update base packages: %s\n" (OpamPackage.Name.Set.to_string all_base_packages); OpamPackage.Map.iter (fun package prefix -> - let opam_file = OpamRepositoryPath.opam repo.repo_root prefix package in + let opam_file = OpamRepositoryPath.opam repo_root prefix package in let opam0 = OpamFile.OPAM.read opam_file in OpamFile.OPAM.print_errors ~file:opam_file opam0; let nv = OpamFile.OPAM.package opam0 in @@ -418,16 +426,16 @@ (OpamFile.OPAM.write_with_preserved_format opam_file opam; List.iter OpamFilename.remove [ OpamFile.filename - (OpamRepositoryPath.descr repo.repo_root prefix package); + (OpamRepositoryPath.descr repo_root prefix package); OpamFile.filename - (OpamRepositoryPath.url repo.repo_root prefix package); + (OpamRepositoryPath.url repo_root prefix package); ]; OpamConsole.status_line "Updated %s" (OpamFile.to_string opam_file)) ) packages; OpamConsole.clear_status (); - let repo_file = OpamRepositoryPath.repo repo.repo_root in + let repo_file = OpamRepositoryPath.repo repo_root in OpamFile.Repo.write repo_file (OpamFile.Repo.with_opam_version upgradeto_version (OpamFile.Repo.safe_read repo_file)) @@ -482,7 +490,7 @@ FString (upgradeto_version_string ^ "~")))) in repo0 |> - OpamFile.Repo.with_opam_version (OpamVersion.current_nopatch) |> + OpamFile.Repo.with_opam_version OpamFile.Repo.format_version |> OpamFile.Repo.with_redirect (redir :: OpamFile.Repo.redirect repo0) in OpamFile.Repo.write repo_file repo_12; diff -Nru opam-2.0.10/src/client/opamArg.ml opam-2.1.2/src/client/opamArg.ml --- opam-2.0.10/src/client/opamArg.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamArg.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -14,14 +14,427 @@ open Cmdliner open OpamStd.Op -(* Global options *) +include OpamArgTools + + +(** Utils *) + +let when_enum = + [ "always", `Always; "never", `Never; "auto", `Auto ] + |> List.map (fun (s,v) -> cli_original, s, v) + +let confirm_enum = [ + cli_from cli2_1, "ask", `ask; + cli_from cli2_1, "no", `all_no; + cli_from cli2_1, "yes", `all_yes; + cli_from cli2_1, "unsafe-yes", `unsafe_yes; +] + +(* Windows directory separators need to be escaped for manpages *) +let dir_sep, escape_path = + match Filename.dir_sep with + | "\\" -> + let esc = "\\\\" in + esc, + fun p -> + OpamStd.List.concat_map esc (fun x -> x) + (OpamStd.String.split_delim p '\\') + | ds -> ds, fun x -> x + + +(** Opam environment variables *) + +(* Environment variables that need to be initialised before config init, see + [OpamCliMain.run]. *) +let preinit_environment_variables = + let open OpamStd.Config in + let core = + let open OpamCoreConfig.E in [ + "DEBUG", (fun v -> DEBUG (env_int v)), + "see options `--debug' and `--debug-level'."; + "YES", (fun v -> YES (env_bool v)), + "see options `--yes' and `--confirm-level`. \ + $(b,OPAMYES) has has priority over $(b,OPAMNO) and is \ + ignored if $(b,OPAMCONFIRMLEVEL) is set."; + ] in + let client = + let open OpamClientConfig.E in [ + "CLI", (fun v -> CLI (env_string v)), "see option `--cli'."; + "NOSELFUPGRADE",(fun v -> NOSELFUPGRADE (env_string v)), + "see option `--no-self-upgrade'"; + "ROOTISOK", (fun v -> ROOTISOK (env_bool v)), + "don't complain when running as root."; + ] in + core @ client + +let preinit_opam_env_variables, doc_opam_env_variables_pre = + let preinit () = + OpamStd.Config.E.updates @@ + List.map (fun (var, cons, _doc) -> cons var) + preinit_environment_variables + in + let doc = + List.map (fun (var, _cons, doc) -> + `P (Printf.sprintf "$(i,OPAM%s) %s" var doc)) + preinit_environment_variables + in + preinit, doc + +(* Environment variables with their doc and their validity OPAMVAR_var and + OPAMPACKAGE_var are defined and documented static in [help_sections]. +*) +let environment_variables = + let open OpamStd.Config in + let core = + let open OpamCoreConfig.E in [ + "COLOR", cli_original, (fun v -> COLOR (env_when v)), + "when set to $(i,always) or $(i,never), sets a default value for the \ + `--color' option."; + "CONFIRMLEVEL", cli_from cli2_1, (fun v -> CONFIRMLEVEL (env_answer v)), + "see option `--confirm-level`. \ + $(b,OPAMCONFIRMLEVEL) has priority over $(b,OPAMYES) \ + and $(b,OPAMNO)."; + "DEBUGSECTIONS", cli_from cli2_1, (fun v -> DEBUGSECTIONS (env_sections v)), + "if set, limits debug messages to the space-separated list of \ + sections. Sections can optionally have a specific debug level (for \ + example, $(b,CLIENT:2) or $(b,CLIENT CUDF:2)), but otherwise use \ + `--debug-level'."; + "ERRLOGLEN", cli_original, (fun v -> ERRLOGLEN (env_int v)), + "sets the number of log lines printed when a sub-process fails. 0 to \ + print all."; + "KEEPLOGS", cli_original, (fun v -> KEEPLOGS (env_bool v)), + "tells opam to not remove some temporary command logs and some \ + backups. This skips some finalisers and may also help to get more \ + reliable backtraces."; + "LOGS", cli_original, (fun v -> LOGS (env_string v)), + ("$(i,logdir) sets log directory, default is a temporary directory in \ + " ^ (if Sys.win32 then "%TEMP%" else "/tmp")); + "MERGEOUT", cli_original, (fun v -> MERGEOUT (env_bool v)), + "merge process outputs, stderr on stdout."; + "NO", cli_original, (fun v -> NO (env_bool v)), + "answer no to any question asked, see options `--no` and `--confirm-level`. \ + $(b,OPAMNO) is ignored if either $(b,OPAMCONFIRMLEVEL) or $(b,OPAMYES) \ + is set."; + "PRECISETRACKING", cli_original, (fun v -> PRECISETRACKING (env_bool v)), + "fine grain tracking of directories."; + "SAFE", cli_original, (fun v -> SAFE (env_bool v)), + "see option `--safe'."; + "STATUSLINE", cli_original, (fun v -> STATUSLINE (env_when v)), + ("display a dynamic status line showing what's currently going on on \ + the terminal. (one of "^string_of_enum when_enum^")"); + "USEOPENSSL", cli_original, (fun v -> USEOPENSSL (env_bool v)), + "force openssl use for hash computing."; + "UTF8", cli_original, (fun v -> UTF8 (env_when_ext v)), + (Printf.sprintf "use UTF8 characters in output (one of %s). By default \ + `auto', which is determined from the locale)." + (string_of_enum when_enum)); + "UTF8MSGS", cli_original, (fun v -> UTF8MSGS (env_bool v)), + "use extended UTF8 characters (camels) in opam messages. Implies \ + $(i,OPAMUTF8). This is set by default on OSX only."; + "VERBOSE", cli_original, (fun v -> VERBOSE (env_level v)), + "see option `--verbose'."; + ] in + let format = + let open OpamFormatConfig.E in [ + "ALLPARENS", cli_original, (fun v -> ALLPARENS (env_bool v)), + "surround all filters with parenthesis."; + "SKIPVERSIONCHECKS", cli_original, + (fun v -> SKIPVERSIONCHECKS (env_bool v)), + "bypasses some version checks. Unsafe, for compatibility testing only."; + "STRICT", cli_original, (fun v -> STRICT (env_bool v)), + "fail on inconsistencies (file reading, switch import, etc.)."; + ] in + let solver = + let open OpamSolverConfig.E in [ + "BESTEFFORT", cli_original, (fun v -> BESTEFFORT (env_bool v)), + "see option `--best-effort'."; + "BESTEFFORTPREFIXCRITERIA", cli_original, + (fun v -> BESTEFFORTPREFIXCRITERIA (env_string v)), + "sets the string that must be prepended to the criteria when the \ + `--best-effort' option is set, and is expected to maximise the \ + `opam-query' property in the solution."; + "CRITERIA", cli_original, (fun v -> CRITERIA (env_string v)), + "specifies user $(i,preferences) for dependency solving. The default \ + value depends on the solver version, use `config report' to know the \ + current setting. See also option --criteria."; + "CUDFFILE", cli_original, (fun v -> CUDFFILE (env_string v)), + "save the cudf graph to $(i,file)-actions-explicit.dot."; + "CUDFTRIM", cli_original, (fun v -> CUDFTRIM (env_string v)), + "controls the filtering of unrelated packages during CUDF preprocessing."; + "DIGDEPTH", cli_original, (fun v -> DIGDEPTH (env_int v)), + "defines how aggressive the lookup for conflicts during CUDF \ + preprocessing is."; + "EXTERNALSOLVER", cli_original, (fun v -> EXTERNALSOLVER (env_string v)), + "see option `--solver'."; + "FIXUPCRITERIA", cli_original, (fun v -> FIXUPCRITERIA (env_string v)), + "same as $(i,OPAMUPGRADECRITERIA), but specific to fixup."; + "NOASPCUD", cli_original, (fun v -> NOASPCUD (env_bool v)), + "Deprecated."; + "PREPRO", cli_original, (fun v -> PREPRO (env_bool v)), + "set this to false to disable CUDF preprocessing. Less efficient, but \ + might help debugging solver issue."; + "SOLVERALLOWSUBOPTIMAL", cli_from cli2_1, + (fun v -> SOLVERALLOWSUBOPTIMAL (env_bool v)), + "(default `true') allows some solvers to still return a solution when \ + they reach timeout; while the solution remains assured to be \ + consistent, there is no guarantee in this case that it fits the \ + expected optimisation criteria. If `true', opam willcontinue with a \ + warning, if `false' a timeout is an error. Currently only \ + the builtin-z3 backend handles this degraded case."; + "SOLVERTIMEOUT", cli_original, (fun v -> SOLVERTIMEOUT (env_float v)), + (Printf.sprintf + "change the time allowance of the solver. Default is %.1f, set to 0 \ + for unlimited. Note that all solvers may not support this option." + (OpamStd.Option.default 0. OpamSolverConfig.(default.solver_timeout))); + "UPGRADECRITERIA", cli_original, + (fun v -> UPGRADECRITERIA (env_string v)), + "specifies user $(i,preferences) for dependency solving when performing \ + an upgrade. Overrides $(i,OPAMCRITERIA) in upgrades if both are set. \ + See also option --criteria."; + "USEINTERNALSOLVER", cli_original, + (fun v -> USEINTERNALSOLVER (env_bool v)), + "see option `--use-internal-solver'."; + "VERSIONLAGPOWER", cli_original, (fun v -> VERSIONLAGPOWER (env_int v)), + "do not use."; + ] in + let repository = + let open OpamRepositoryConfig.E in [ + "CURL", cli_original, (fun v -> CURL (env_string v)), + "can be used to select a given 'curl' program. See $(i,OPAMFETCH) for \ + more options."; + "FETCH", cli_original, (fun v -> FETCH (env_string v)), + "specifies how to download files: either `wget', `curl' or a custom \ + command where variables $(b,%{url}%), $(b,%{out}%), $(b,%{retry}%), \ + $(b,%{compress}%) and $(b,%{checksum}%) will be replaced. Overrides the \ + 'download-command' value from the main config file."; + "NOCHECKSUMS", cli_original, (fun v -> NOCHECKSUMS (env_bool v)), + "enables option --no-checksums when available."; + "REQUIRECHECKSUMS", cli_original, + (fun v -> REQUIRECHECKSUMS (env_bool v)), + "Enables option `--require-checksums' when available \ + (e.g. for `opam install')."; + "RETRIES", cli_original, (fun v -> RETRIES (env_int v)), + "sets the number of tries before failing downloads."; + "VALIDATIONHOOK", cli_original, (fun v -> VALIDATIONHOOK (env_string v)), + "if set, uses the `%{hook%}' command to validate \ + an opam repository update."; + ] in + let state = + let open OpamStateConfig.E in [ + "BUILDDOC", cli_between cli2_0 cli2_1, + (fun v -> BUILDDOC (env_bool v)), "see option `--build-doc'."; + "BUILDTEST", cli_between cli2_0 cli2_1, + (fun v -> BUILDTEST (env_bool v)), "see option `--build-test'."; + "DOWNLOADJOBS", cli_original, (fun v -> DOWNLOADJOBS (env_int v)), + "sets the maximum number of simultaneous downloads."; + "DRYRUN", cli_original, (fun v -> DRYRUN (env_bool v)), + "see option `--dry-run'."; + "IGNORECONSTRAINTS", cli_original, + (fun v -> IGNORECONSTRAINTS (env_string v)), + "see install option `--ignore-constraints-on'."; + "JOBS", cli_original, (fun v -> JOBS (env_int v)), + "sets the maximum number of parallel workers to run."; + "LOCKED", cli_original, (fun v -> LOCKED (env_string v)), + "combination of `--locked' and `--lock-suffix' options."; + "MAKECMD", cli_original, (fun v -> MAKECMD (env_string v)), + "set the system make command to use."; + "NODEPEXTS", cli_from cli2_1, (fun v -> NODEPEXTS (env_bool v)), + "disables system dependencies handling, see option `--no-depexts'."; + "NOENVNOTICE", cli_original, (fun v -> NOENVNOTICE (env_bool v)), + "Internal."; + "ROOT", cli_original, (fun v -> ROOT (env_string v)), + "see option `--root'. This is automatically set \ + by `opam env --root=DIR --set-root'."; + "SWITCH", cli_original, (fun v -> SWITCH (env_string v)), + "see option `--switch'. Automatically set \ + by `opam env --switch=SWITCH --set-switch'."; + "UNLOCKBASE", cli_original, (fun v -> UNLOCKBASE (env_bool v)), + "see install option `--unlock-base'."; + "WITHDOC", cli_original, (fun v -> WITHDOC (env_bool v)), + "see install option `--with-doc'."; + "WITHTEST", cli_original, (fun v -> WITHTEST (env_bool v)), + "see install option `--with-test."; + ] in + let client = + let open OpamClientConfig.E in [ + "ASSUMEDEPEXTS", cli_from cli2_1, (fun v -> ASSUMEDEPEXTS (env_bool v)), + "see option `--assume-depexts'."; + "AUTOREMOVE", cli_original, (fun v -> AUTOREMOVE (env_bool v)), + "see remove option `--auto-remove'."; + "DROPWORKINGDIR", cli_from cli2_1, (fun v -> DROPWORKINGDIR (env_bool v)), + "overrides packages previously updated with $(b,--working-dir) on \ + update. Without this variable set, opam would keep them unchanged \ + unless explicitly named on the command-line."; + "EDITOR", cli_original, (fun v -> EDITOR (env_string v)), + "sets the editor to use for opam file editing, overrides $(i,\\$EDITOR) \ + and $(i,\\$VISUAL)."; + "FAKE", cli_original, (fun v -> FAKE (env_bool v)), + "see option `--fake'."; + "IGNOREPINDEPENDS", cli_original, + (fun v -> IGNOREPINDEPENDS (env_bool v)), + "see option `--ignore-pin-depends'."; + "INPLACEBUILD", cli_original, (fun v -> INPLACEBUILD (env_bool v)), + "see option `--inplace-build'."; + "JSON", cli_original, (fun v -> JSON (env_string v)), + "log json output to the given file \ + (use character `%' to index the files)."; + "KEEPBUILDDIR", cli_original, (fun v -> KEEPBUILDDIR (env_bool v)), + "see install option `--keep-build-dir'."; + "NOAUTOUPGRADE", cli_original, (fun v -> NOAUTOUPGRADE (env_bool v)), + "disables automatic internal upgrade of repositories in an earlier \ + format to the current one, on 'update' or 'init'."; + "NOAGGREGATE", cli_original, (fun v -> NOAGGREGATE (env_bool v)), + "with `opam admin check', don't aggregate packages."; + "PINKINDAUTO", cli_original, (fun v -> PINKINDAUTO (env_bool v)), + "sets whether version control systems should be detected when pinning \ + to a local path. Enabled by default since 1.3.0."; + "REUSEBUILDDIR", cli_original, (fun v -> REUSEBUILDDIR (env_bool v)), + "see option `--reuse-build-dir'."; + "SHOW", cli_original, (fun v -> SHOW (env_bool v)), + "see option `--show'."; + "SKIPUPDATE", cli_original, (fun v -> SKIPUPDATE (env_bool v)), + "see option `--skip-updates'."; + "STATS", cli_original, (fun v -> STATS (env_bool v)), + "display stats at the end of command."; + "WORKINGDIR", cli_original, (fun v -> WORKINGDIR (env_bool v)), + "see option `--working-dir'."; + ] in + core @ format @ solver @ repository @ state @ client + +let scrubbed_environment_variables = + let f (name, validity, _, _) = + if is_original_cli validity then + None + else + Some ("OPAM" ^ name) + in + OpamStd.List.filter_map f environment_variables + +let doc_opam_env_variables, init_opam_env_variabes = + env_with_cli environment_variables + +(** Help sections common to all commands *) + +let global_option_section = Manpage.s_common_options +let help_sections cli = + [ + `S global_option_section; + `P "These options are common to all commands."; + + `S Manpage.s_environment; + `P "Opam makes use of the environment variables listed here. Boolean \ + variables should be set to \"0\", \"no\", \"false\" or the empty \ + string to disable, \"1\", \"yes\" or \"true\" to enable."; + ] @ + List.sort compare (doc_opam_env_variables_pre @ doc_opam_env_variables cli) + @ [ + `P "$(i,OPAMVAR_var) overrides the contents of the variable $(i,var) when \ + substituting `%{var}%` strings in `opam` files."; + `P "$(i,OPAMVAR_package_var) overrides the contents of the variable \ + $(i,package:var) when substituting `%{package:var}%` strings in \ + `opam` files."; + + `S "CLI VERSION"; + `P "All scripts and programmatic invocations of opam should use `--cli' in \ + order to ensure that they work seamlessly with future versions of the \ + opam client. Additionally, blog posts or other documentation can \ + benefit, as it prevents information from becoming stale."; + `P (Printf.sprintf + "Although opam only supports roots ($(i,~%s.opam%s)) for the current \ + version, it does provide backwards compatibility for its \ + command-line interface." dir_sep dir_sep); + `P "Since CLI version support was only added in opam 2.1, use $(i,OPAMCLI) \ + to select 2.0 support (as opam 2.0 will just ignore it), \ + and `--cli=2.1' for 2.1 (or later) versions, since an environment variable \ + controlling the parsing of syntax is brittle. To this end, opam \ + displays a warning if $(i,OPAMCLI) specifies a valid version other \ + than 2.0, and also if `--cli=2.0' is specified."; + `P "The command-line version is selected by using the `--cli' option or \ + the $(i,OPAMCLI) environment variable. `--cli' may be specified more\ + than once, where the last instance takes precedence. $(i,OPAMCLI) is \ + only inspected if `--cli' is not given."; + + `S Manpage.s_exit_status; + `P "As an exception to the following, the `exec' command returns 127 if the \ + command was not found or couldn't be executed, and the command's exit \ + value otherwise." + ] @ + List.map (fun (reason, code) -> + `I (string_of_int code, match reason with + | `Success -> + "Success, or true for boolean queries." + | `False -> + "False. Returned when a boolean return value is expected, e.g. when \ + running with $(b,--check), or for queries like $(b,opam lint)." + | `Bad_arguments -> + "Bad command-line arguments, or command-line arguments pointing to \ + an invalid context (e.g. file not following the expected format)." + | `Not_found -> + "Not found. You requested something (package, version, repository, \ + etc.) that couldn't be found." + | `Aborted -> + "Aborted. The operation required confirmation, which wasn't given." + | `Locked -> + "Could not acquire the locks required for the operation." + | `No_solution -> + "There is no solution to the user request. This can be caused by \ + asking to install two incompatible packages, for example." + | `File_error -> + "Error in package definition, or other metadata files. Using \ + $(b,--strict) raises this error more often." + | `Package_operation_error -> + "Package script error. Some package operations were unsuccessful. \ + This may be an error in the packages or an incompatibility with \ + your system. This can be a partial error." + | `Sync_error -> + "Sync error. Could not fetch some remotes from the network. This can \ + be a partial error." + | `Configuration_error -> + "Configuration error. Opam or system configuration doesn't allow \ + operation, and needs fixing." + | `Solver_failure -> + "Solver failure. The solver failed to return a sound answer. It can \ + be due to a broken external solver, or an error in solver \ + configuration." + | `Internal_error -> + "Internal error. Something went wrong, likely due to a bug in opam \ + itself." + | `User_interrupt -> + "User interrupt. SIGINT was received, generally due to the user \ + pressing Ctrl-C." + )) + OpamStd.Sys.exit_codes + @ [ + `S "FURTHER DOCUMENTATION"; + `P (Printf.sprintf "See https://opam.ocaml.org/doc."); + + `S Manpage.s_authors; + `P "Vincent Bernardoff "; `Noblank; + `P "Raja Boujbel "; `Noblank; + `P "Roberto Di Cosmo "; `Noblank; + `P "Thomas Gazagnaire "; `Noblank; + `P "Louis Gesbert "; `Noblank; + `P "Fabrice Le Fessant "; `Noblank; + `P "Anil Madhavapeddy "; `Noblank; + `P "Guillem Rieu "; `Noblank; + `P "Ralf Treinen "; `Noblank; + `P "Frederic Tuong "; + + `S Manpage.s_bugs; + `P "Check bug reports at https://github.com/ocaml/opam/issues."; + ] + + +(** Global options *) + type global_options = { debug_level: int option; verbose: int; quiet : bool; - color : [ `Always | `Never | `Auto ] option; + color : OpamStd.Config.when_ option; opt_switch : string option; - yes : bool; + confirm_level : OpamStd.Config.answer option; + yes: bool option; strict : bool; opt_root : dirname option; git_version : bool; @@ -35,33 +448,36 @@ no_auto_upgrade : bool; working_dir : bool; ignore_pin_depends : bool; + cli : OpamCLIVersion.t; } -let deprecated_option option absent name instead = - if option <> absent then - OpamConsole.warning - "Option %s is deprecated, ignoring it.%s" name - (match instead with - | None -> "" - | Some instead -> Printf.sprintf " You can use %s instead." instead) - +(* The --cli passed by cmdliner is ignored (it's only there for --help) *) let create_global_options - git_version debug debug_level verbose quiet color opt_switch yes strict + git_version debug debug_level verbose quiet color opt_switch + yes confirm_level + strict opt_root external_solver use_internal_solver cudf_file solver_preferences best_effort safe_mode json no_auto_upgrade working_dir ignore_pin_depends - d_no_aspcud = - deprecated_option d_no_aspcud false "no-aspcud" None; + d_no_aspcud _ = + if d_no_aspcud then + OpamConsole.warning + "Option %s is deprecated, ignoring it." + (OpamConsole.colorise `bold "--no-aspcud"); let debug_level = OpamStd.Option.Op.( debug_level >>+ fun () -> if debug then Some 1 else None ) in + let get_last l = match List.rev l with [] -> None | x::_ -> Some x in + let yes = get_last yes in + let confirm_level = get_last confirm_level in let verbose = List.length verbose in - { git_version; debug_level; verbose; quiet; color; opt_switch; yes; + let cli = OpamCLIVersion.current in + { git_version; debug_level; verbose; quiet; color; opt_switch; confirm_level; yes; strict; opt_root; external_solver; use_internal_solver; cudf_file; solver_preferences; best_effort; safe_mode; json; - no_auto_upgrade; working_dir; ignore_pin_depends; } + no_auto_upgrade; working_dir; ignore_pin_depends; cli } -let apply_global_options o = +let apply_global_options cli o = if o.git_version then ( begin match OpamGitVersion.version with | None -> () @@ -80,6 +496,8 @@ o.external_solver >>| fun s -> lazy (OpamCudfSolver.solver_of_string s) in let solver_prefs = o.solver_preferences >>| fun p -> lazy (Some p) in + let yes = OpamStd.Option.(map some o.yes) in + init_opam_env_variabes cli; OpamClientConfig.opam_init (* - format options - *) ?strict:(flag o.strict) @@ -92,7 +510,8 @@ ?color:o.color (* ?utf8:[ `Extended | `Always | `Never | `Auto ] *) (* ?disp_status_line:[ `Always | `Never | `Auto ] *) - ?answer:(some (flag o.yes)) + ?confirm_level:o.confirm_level + ?yes ?safe_mode:(flag o.safe_mode) (* ?lock_retries:int *) (* ?log_dir:OpamTypes.dirname *) @@ -135,6 +554,7 @@ (* ?pin_kind_auto:bool *) (* ?autoremove:bool *) (* ?editor:string *) + ~cli:o.cli (); if OpamClientConfig.(!r.json_out <> None) then ( OpamJson.append "opam-version" (`String OpamVersion.(to_string (full ()))); @@ -142,7 +562,9 @@ (`A (List.map (fun s -> `String s) (Array.to_list Sys.argv))) ) -(* Build options *) + +(** Build options *) + type build_options = { keep_build_dir: bool; reuse_build_dir: bool; @@ -159,21 +581,28 @@ jobs : int option; ignore_constraints_on: name list option; unlock_base : bool; - locked: string option; + locked : bool; + lock_suffix : string; + assume_depexts: bool; + no_depexts : bool; } let create_build_options keep_build_dir reuse_build_dir inplace_build make no_checksums req_checksums build_test build_doc show dryrun skip_update - fake jobs ignore_constraints_on unlock_base locked = { - keep_build_dir; reuse_build_dir; inplace_build; make; - no_checksums; req_checksums; build_test; build_doc; show; dryrun; - skip_update; fake; jobs; ignore_constraints_on; unlock_base; locked; -} + fake jobs ignore_constraints_on unlock_base locked lock_suffix + assume_depexts no_depexts + = + { + keep_build_dir; reuse_build_dir; inplace_build; make; no_checksums; + req_checksums; build_test; build_doc; show; dryrun; skip_update; fake; + jobs; ignore_constraints_on; unlock_base; locked; lock_suffix; + assume_depexts; no_depexts; + } -let apply_build_options b = +let apply_build_options cli b = + let open OpamStd.Option.Op in let flag f = if f then Some true else None in - let some x = match x with None -> None | some -> Some some in OpamRepositoryConfig.update (* ?download_tool:(OpamTypes.arg list * dl_tool_kind) Lazy.t *) (* ?retries:int *) @@ -183,18 +612,19 @@ (); OpamStateConfig.update (* ?root: -- handled globally *) - ?jobs:OpamStd.Option.Op.(b.jobs >>| fun j -> lazy j) + ?jobs:(b.jobs >>| fun j -> lazy j) (* ?dl_jobs:int *) (* ?no_base_packages:(flag o.no_base_packages) -- handled globally *) ?build_test:(flag b.build_test) ?build_doc:(flag b.build_doc) ?dryrun:(flag b.dryrun) - ?makecmd:OpamStd.Option.Op.(b.make >>| fun m -> lazy m) + ?makecmd:(b.make >>| fun m -> lazy m) ?ignore_constraints_on: - OpamStd.Option.Op.(b.ignore_constraints_on >>| - OpamPackage.Name.Set.of_list) + (b.ignore_constraints_on >>| + OpamPackage.Name.Set.of_list) ?unlock_base:(flag b.unlock_base) - ?locked:(some b.locked) + ?locked:(if b.locked then Some (Some b.lock_suffix) else None) + ?no_depexts:(flag (b.no_depexts || OpamCLIVersion.Op.(cli @= cli2_0))) (); OpamClientConfig.update ?keep_build_dir:(flag b.keep_build_dir) @@ -203,205 +633,12 @@ ?show:(flag b.show) ?fake:(flag b.fake) ?skip_dev_update:(flag b.skip_update) + ?assume_depexts:(flag (b.assume_depexts || b.no_depexts)) + ~scrubbed_environment_variables () -let when_enum = [ "always", `Always; "never", `Never; "auto", `Auto ] -(* Help sections common to all commands *) -let global_option_section = "COMMON OPTIONS" -let help_sections = [ - `S global_option_section; - `P "These options are common to all commands."; - - `S "ENVIRONMENT VARIABLES"; - `P "Opam makes use of the environment variables listed here. Boolean \ - variables should be set to \"0\", \"no\", \"false\" or the empty string \ - to disable, \"1\", \"yes\" or \"true\" to enable."; - - (* Alphabetical order *) - `P "$(i,OPAMALLPARENS) surround all filters with parenthesis"; - `P "$(i,OPAMAUTOREMOVE) see remove option `--auto-remove`"; - `P "$(i,OPAMBESTEFFORT) see option `--best-effort`"; - `P "$(i,OPAMBESTEFFORTPREFIXCRITERIA) sets the string that must be prepended \ - to the criteria when the `--best-effort` option is set, and is expected \ - to maximise the `opam-query` property in the solution "; - `P "$(i,OPAMCOLOR), when set to $(i,always) or $(i,never), sets a default \ - value for the --color option."; - `P "$(i,OPAMCRITERIA) specifies user $(i,preferences) for dependency \ - solving. The default value depends on the solver version, use `config \ - report` to know the current setting. See also option --criteria"; - `P "$(i,OPAMCUDFFILE file) save the cudf graph to \ - $(i,file)-actions-explicit.dot"; - `P "$(i,OPAMCURL) can be used to select a given 'curl' program. See \ - $(i,OPAMFETCH) for more options."; - `P "$(i,OPAMDEBUG) see options `--debug' and `--debug-level'."; - `P "$(i,OPAMDOWNLOADJOBS) sets the maximum number of simultaneous downloads."; - `P "$(i,OPAMDRYRUN) see option `--dry-run`"; - `P "$(i,OPAMEDITOR) sets the editor to use for opam file editing, overrides \ - $(i,\\$EDITOR) and $(i,\\$VISUAL)"; - `P "$(i,OPAMERRLOGLEN) sets the number of log lines printed when a \ - sub-process fails. 0 to print all."; - `P "$(i,OPAMEXTERNALSOLVER) see option `--solver'."; - `P "$(i,OPAMFAKE) see option `--fake`"; - `P "$(i,OPAMFETCH) specifies how to download files: either `wget', `curl' or \ - a custom command where variables $(b,%{url}%), $(b,%{out}%), \ - $(b,%{retry}%), $(b,%{compress}%) and $(b,%{checksum}%) will \ - be replaced. Overrides the \ - 'download-command' value from the main config file."; - `P "$(i,OPAMFIXUPCRITERIA) same as $(i,OPAMUPGRADECRITERIA), but specific \ - to fixup"; - `P "$(i,OPAMIGNORECONSTRAINTS) see install option `--ignore-constraints-on`"; - `P "$(i,OPAMIGNOREPINDEPENDS) see option `--ignore-pin-depends`"; - `P "$(i,OPAMJOBS) sets the maximum number of parallel workers to run."; - `P "$(i,OPAMJSON) log json output to the given file (use character `%' to \ - index the files)"; - `P "$(i,OPAMLOCKED) see install option `--locked`"; - `P "$(i,OPAMLOGS logdir) sets log directory, default is a temporary directory \ - in /tmp"; - `P "$(i,OPAMMAKECMD) set the system make command to use"; - `P "$(i,OPAMNOAUTOUPGRADE) disables automatic internal upgrade of \ - repositories in an earlier format to the current one, on 'update' or \ - 'init'."; - `P "$(i,OPAMKEEPLOGS) tells opam to not remove some temporary command logs \ - and some backups. This skips some finalisers and may also help to get \ - more reliable backtraces"; - `P "$(i,OPAMLOCKRETRIES) sets the number of tries after which opam gives up \ - acquiring its lock and fails. <= 0 means infinite wait."; - `P "$(i,OPAMMERGEOUT) merge process outputs, stderr on stdout"; - `P "$(i,OPAMNO) answer no to any question asked."; - `P "$(i,OPAMNOASPCUD) Deprecated."; - `P "$(i,OPAMNOCHECKSUMS) enables option --no-checksums when available."; - `P "$(i,OPAMNOSELFUPGRADE) see option `--no-self-upgrade'."; - `P "$(i,OPAMPINKINDAUTO) sets whether version control systems should be \ - detected when pinning to a local path. Enabled by default since 1.3.0."; - `P "$(i,OPAMPRECISETRACKING) fine grain tracking of directories"; - `P "$(i,OPAMREQUIRECHECKSUMS) Enables option `--require-checksums' when \ - available (e.g. for `opam install`)."; - `P "$(i,OPAMRETRES) sets the number of tries before failing downloads."; - `P "$(i,OPAMROOT) see option `--root'. This is automatically set by \ - `opam env --root=DIR --set-root'."; - `P "$(i,OPAMROOTISOK) don't complain when running as root."; - `P "$(i,OPAMSAFE) see option `--safe'"; - `P "$(i,OPAMSHOW) see option `--show`"; - `P "$(i,OPAMSKIPUPDATE) see option `--skip-updates`"; - `P "$(i,OPAMSKIPVERSIONCHECKS) bypasses some version checks. Unsafe, for \ - compatibility testing only."; - `P (Printf.sprintf - "$(i,OPAMSOLVERTIMEOUT) change the time allowance of the solver. \ - Default is %.1f, set to 0 for unlimited. Note that all solvers may \ - not support this option." - (OpamStd.Option.default 0. OpamSolverConfig.(default.solver_timeout))); - `P ("$(i,OPAMSTATUSLINE) display a dynamic status line showing what's \ - currently going on on the terminal. \ - (one of "^Arg.doc_alts_enum when_enum^")"); - `P "$(i,OPAMSTATS) display stats at the end of command"; - `P "$(i,OPAMSTRICT) fail on inconsistencies (file reading, switch import, etc.)"; - `P "$(i,OPAMSWITCH) see option `--switch'. Automatically set by \ - `opam env --switch=SWITCH --set-switch'."; - `P "$(i,OPAMUNLOCKBASE) see install option `--unlock-base`"; - `P ("$(i,OPAMUPGRADECRITERIA) specifies user $(i,preferences) for dependency \ - solving when performing an upgrade. Overrides $(i,OPAMCRITERIA) in \ - upgrades if both are set. See also option --criteria"); - `P "$(i,OPAMUSEINTERNALSOLVER) see option `--use-internal-solver'."; - `P "$(i,OPAMUSEOPENSSL) force openssl use for hash computing"; - `P ("$(i,OPAMUTF8) use UTF8 characters in output \ - (one of "^Arg.doc_alts_enum when_enum^ - "). By default `auto', which is determined from the locale)."); - `P "$(i,OPAMUTF8MSGS) use extended UTF8 characters (camels) in opam \ - messages. Implies $(i,OPAMUTF8). This is set by default on OSX only."; - `P "$(i,OPAMVALIDATIONHOOK hook) if set, uses the `%{hook%}` command to \ - validate an opam repository update"; - `P "$(i,OPAMVAR_var) overrides the contents of the variable $(i,var) when \ - substituting `%{var}%` strings in `opam` files."; - `P "$(i,OPAMVAR_package_var) overrides the contents of the variable \ - $(i,package:var) when substituting `%{package:var}%` strings in \ - `opam` files."; - `P "$(i,OPAMVERBOSE) see option `--verbose'."; - `P "$(i,OPAMWORKINGDIR) see option `--working-dir`"; - `P "$(i,OPAMYES) see option `--yes'."; - - `S "EXIT STATUS"; - `P "As an exception to the following, the `exec' command returns 127 if the \ - command was not found or couldn't be executed, and the command's exit \ - value otherwise." -] @ - List.map (fun (reason, code) -> - `I (string_of_int code, match reason with - | `Success -> - "Success, or true for boolean queries." - | `False -> - "False. Returned when a boolean return value is expected, e.g. when \ - running with $(b,--check), or for queries like $(b,opam lint)." - | `Bad_arguments -> - "Bad command-line arguments, or command-line arguments pointing to \ - an invalid context (e.g. file not following the expected format)." - | `Not_found -> - "Not found. You requested something (package, version, repository, \ - etc.) that couldn't be found." - | `Aborted -> - "Aborted. The operation required confirmation, which wasn't given." - | `Locked -> - "Could not acquire the locks required for the operation." - | `No_solution -> - "There is no solution to the user request. This can be caused by \ - asking to install two incompatible packages, for example." - | `File_error -> - "Error in package definition, or other metadata files. Using \ - $(b,--strict) raises this error more often." - | `Package_operation_error -> - "Package script error. Some package operations were unsuccessful. \ - This may be an error in the packages or an incompatibility with \ - your system. This can be a partial error." - | `Sync_error -> - "Sync error. Could not fetch some remotes from the network. This can \ - be a partial error." - | `Configuration_error -> - "Configuration error. Opam or system configuration doesn't allow \ - operation, and needs fixing." - | `Solver_failure -> - "Solver failure. The solver failed to return a sound answer. It can \ - be due to a broken external solver, or an error in solver \ - configuration." - | `Internal_error -> - "Internal error. Something went wrong, likely due to a bug in opam \ - itself." - | `User_interrupt -> - "User interrupt. SIGINT was received, generally due to the user \ - pressing Ctrl-C." - )) - OpamStd.Sys.exit_codes -@ [ - `S "FURTHER DOCUMENTATION"; - `P (Printf.sprintf "See https://opam.ocaml.org/doc."); - - `S "AUTHORS"; - `P "Vincent Bernardoff "; `Noblank; - `P "Raja Boujbel "; `Noblank; - `P "Roberto Di Cosmo "; `Noblank; - `P "Thomas Gazagnaire "; `Noblank; - `P "Louis Gesbert "; `Noblank; - `P "Fabrice Le Fessant "; `Noblank; - `P "Anil Madhavapeddy "; `Noblank; - `P "Guillem Rieu "; `Noblank; - `P "Ralf Treinen "; `Noblank; - `P "Frederic Tuong "; - - `S "BUGS"; - `P "Check bug reports at https://github.com/ocaml/opam/issues."; -] - -(* Converters *) - -(* Windows directory separator need to be escaped for manpage *) -let dir_sep, escape_path = - match Filename.dir_sep with - | "\\" -> - let esc = "\\\\" in - esc, - fun p -> - OpamStd.List.concat_map esc (fun x -> x) - (OpamStd.String.split_delim p '\\') - | ds -> ds, fun x -> x +(** Converters *) let pr_str = Format.pp_print_string @@ -411,7 +648,11 @@ parse, print let url = - let parse str = `Ok (OpamUrl.parse str) in + let parse str = + match OpamUrl.parse_opt ~from_file:false str with + | Some url -> `Ok url + | None -> `Error ("malformed url "^str) + in let print ppf url = pr_str ppf (OpamUrl.to_string url) in parse, print @@ -456,6 +697,9 @@ in parse, print +let _subpath_conv = + (fun str -> `Ok (OpamStd.String.remove_prefix ~prefix:"./" str)), pr_str + let package_name = let parse str = try `Ok (OpamPackage.Name.of_string str) @@ -464,6 +708,14 @@ let print ppf pkg = pr_str ppf (OpamPackage.Name.to_string pkg) in parse, print +let package_version = + let parse str = + try `Ok (OpamPackage.Version.of_string str) + with Failure msg -> `Error msg + in + let print ppf ver = pr_str ppf (OpamPackage.Version.to_string ver) in + parse, print + let positive_integer : int Arg.converter = let (parser, printer) = Arg.int in let parser s = @@ -485,9 +737,9 @@ ]) in try let sub = Re.exec re str in - let name = OpamPackage.Name.of_string (Re.get sub 1) in + let name = OpamPackage.Name.of_string (Re.Group.get sub 1) in let version_opt = - try Some (OpamPackage.Version.of_string (Re.get sub 2)) + try Some (OpamPackage.Version.of_string (Re.Group.get sub 2)) with Not_found -> None in `Ok (name, version_opt) with Not_found | Failure _ -> `Error "bad package format" @@ -513,27 +765,8 @@ (* name * version constraint *) let atom = let parse str = - let re = Re.(compile @@ seq [ - bos; - group @@ rep1 @@ diff any (set ">=<.!"); - group @@ alt [ seq [ set "<>"; opt @@ char '=' ]; - set "=."; str "!="; ]; - group @@ rep1 any; - eos; - ]) in - try - let sub = Re.exec re str in - let sname = Re.get sub 1 in - let sop = Re.get sub 2 in - let sversion = Re.get sub 3 in - let name = OpamPackage.Name.of_string sname in - let sop = if sop = "." then "=" else sop in - let op = OpamLexer.relop sop in - let version = OpamPackage.Version.of_string sversion in - `Ok (name, Some (op, version)) - with Not_found | Failure _ | OpamLexer.Error _ -> - try `Ok (OpamPackage.Name.of_string str, None) - with Failure msg -> `Error msg + try `Ok (OpamFormula.atom_of_string str) + with Failure msg -> `Error msg in let print ppf atom = pr_str ppf (OpamFormula.short_string_of_atom atom) in @@ -575,6 +808,21 @@ let print ppf = snd atom_or_local ppf in parse, print +let dep_formula = + let module OpamParser = OpamParser.FullPos in + let module OpamPrinter = OpamPrinter.FullPos in + let pp = OpamFormat.V.(package_formula `Conj (constraints version)) in + let parse str = + try + let v = OpamParser.value_from_string str "" in + `Ok (OpamPp.parse pp ~pos:pos_null v) + with e -> OpamStd.Exn.fatal e; `Error (Printexc.to_string e) + in + let print ppf f = + pr_str ppf (OpamPrinter.value (OpamPp.print pp f)) + in + parse, print + let variable_bindings = let parse str = try @@ -631,8 +879,29 @@ in parse, print -type 'a default = [> `default of string] as 'a +let _selector = + let parse str = + let r = + List.fold_left (fun (plus, minus) elem -> + match OpamStd.String.sub_at 1 elem with + | "+" as prefix -> + (OpamStd.String.remove_prefix ~prefix elem)::plus, minus + | "-" as prefix -> + plus, (OpamStd.String.remove_prefix ~prefix elem)::minus + | _ -> elem::plus, minus) + ([],[]) (OpamStd.String.split str ',') + in + `Ok r + in + let print ppf (plus,minus) = + let concat c = + OpamStd.List.concat_map ~nil:"" "," (fun x -> c^x) + in + pr_str ppf @@ Printf.sprintf "%s,%s" (concat "+" plus) (concat "-" minus) + in + parse, print +(* unused let enum_with_default sl: 'a Arg.converter = let parse, print = Arg.enum sl in let parse s = @@ -640,6 +909,7 @@ | `Ok _ as x -> x | _ -> `Ok (`default s) in parse, print +*) let opamlist_column = let parse str = @@ -704,109 +974,35 @@ in parse, print -(* Helpers *) -let mk_flag ?section flags doc = - let doc = Arg.info ?docs:section ~doc flags in - Arg.(value & flag & doc) - -let mk_opt ?section ?vopt flags value doc kind default = - let doc = Arg.info ?docs:section ~docv:value ~doc flags in - Arg.(value & opt ?vopt kind default & doc) - -let mk_opt_all ?section ?vopt ?(default=[]) flags value doc kind = - let doc = Arg.info ?docs:section ~docv:value ~doc flags in - Arg.(value & opt_all ?vopt kind default & doc) - -let mk_tristate_opt ?section flags value doc = - let doc = Arg.info ?docs:section ~docv:value ~doc flags in - Arg.(value & opt (some (enum when_enum)) None & doc) - -type 'a subcommand = string * 'a * string list * string - -type 'a subcommands = 'a subcommand list - -let mk_subdoc ?(defaults=[]) commands = - let bold s = Printf.sprintf "$(b,%s)" s in - let it s = Printf.sprintf "$(i,%s)" s in - `S "COMMANDS" :: - (List.map (function - | "", name -> - `P (Printf.sprintf "Without argument, defaults to %s." - (bold name)) - | arg, default -> - `I (it arg, Printf.sprintf "With a %s argument, defaults to %s %s." - (it arg) (bold default) (it arg)) - ) defaults) @ - List.map (fun (c,_,args,d) -> - let cmds = bold c ^ " " ^ OpamStd.List.concat_map " " it args in - `I (cmds, d) - ) commands - -let mk_subcommands_aux my_enum commands = - let command = - let doc = Arg.info ~docv:"COMMAND" [] in - let commands = - List.fold_left - (fun acc (c,f,_,_) -> (c,f) :: acc) - [] commands in - Arg.(value & pos 0 (some & my_enum commands) None & doc) in - let params = - let doc = Arg.info ~doc:"Optional parameters." [] in - Arg.(value & pos_right 0 string [] & doc) in - command, params +let term_info ~cli title ~doc ~man = + let man = man @ help_sections cli in + Term.info ~sdocs:global_option_section ~docs:Manpage.s_commands + ~doc ~man title -let mk_subcommands commands = - mk_subcommands_aux Arg.enum commands +let mk_command ~cli validity name ~doc ~man = + mk_command ~cli validity term_info name ~doc ~man -let mk_subcommands_with_default commands = - mk_subcommands_aux enum_with_default commands +let mk_command_ret ~cli validity name ~doc ~man = + mk_command_ret ~cli validity term_info name ~doc ~man -let make_command_alias cmd ?(options="") name = +let make_command_alias ~cli cmd ?(options="") name = let term, info = cmd in let orig = Term.name info in let doc = Printf.sprintf "An alias for $(b,%s%s)." orig options in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P (Printf.sprintf "$(mname)$(b, %s) is an alias for $(mname)$(b, %s%s)." name orig options); `P (Printf.sprintf "See $(mname)$(b, %s --help) for details." orig); - `S "OPTIONS"; - ] @ help_sections + `S Manpage.s_options; + ] @ help_sections cli in term, Term.info name - ~docs:"COMMAND ALIASES" + ~docs:"COMMAND ALIASES" ~sdocs:global_option_section ~doc ~man -let bad_subcommand subcommands (command, usersubcommand, userparams) = - match usersubcommand with - | None -> - `Error (false, Printf.sprintf "Missing subcommand. Valid subcommands are %s." - (OpamStd.Format.pretty_list - (List.map (fun (a,_,_,_) -> a) subcommands))) - | Some (`default cmd) -> - `Error (true, Printf.sprintf "Invalid %s subcommand %S" command cmd) - | Some usersubcommand -> - let exe = Filename.basename Sys.executable_name in - match List.find_all (fun (_,cmd,_,_) -> cmd = usersubcommand) subcommands with - | [name, _, args, _doc] -> - let usage = - Printf.sprintf "%s %s [OPTION]... %s %s" - exe command name (String.concat " " args) in - if List.length userparams < List.length args then - `Error (false, Printf.sprintf "%s: Missing argument.\nUsage: %s\n" - exe usage) - else - `Error (false, Printf.sprintf "%s: Too many arguments.\nUsage: %s\n" - exe usage) - | _ -> - `Error (true, Printf.sprintf "Invalid %s subcommand" command) - -let term_info title ~doc ~man = - let man = man @ help_sections in - Term.info ~sdocs:global_option_section ~docs:"COMMANDS" ~doc ~man title - let arg_list name doc kind = let doc = Arg.info ~docv:name ~doc [] in Arg.(value & pos_all kind [] & doc) @@ -815,32 +1011,31 @@ let doc = Arg.info ~docv:name ~doc [] in Arg.(non_empty & pos_all kind [] & doc) -(* Common flags *) -let print_short_flag = - mk_flag ["s";"short"] - "Output raw lists of names, one per line, skipping any details." -let installed_roots_flag = - mk_flag ["installed-roots"] "Display only the installed roots." +(** Common flags *) + +let print_short_flag cli validity = + mk_flag ~cli validity ["s";"short"] + "Output raw lists of names, one per line, skipping any details." -let shell_opt = +let shell_opt cli validity = let enum = [ - "bash",SH_bash; - "sh",SH_sh; - "csh",SH_csh; - "zsh",SH_zsh; - "fish",SH_fish; - ] in - mk_opt ["shell"] "SHELL" + "bash",SH_bash; + "sh",SH_sh; + "csh",SH_csh; + "zsh",SH_zsh; + "fish",SH_fish; + ] |> List.map (fun (s,v) -> cli_original, s, v) + in + mk_enum_opt ~cli validity ["shell"] "SHELL" enum (Printf.sprintf "Sets the configuration mode for opam environment appropriate for \ $(docv). One of %s. Guessed from the parent processes and the \\$SHELL \ variable by default." - (Arg.doc_alts_enum enum)) - (Arg.some (Arg.enum enum)) None + (string_of_enum enum)) -let dot_profile_flag = - mk_opt ["dot-profile"] +let dot_profile_flag cli validity = + mk_opt ~cli validity ["dot-profile"] "FILENAME" (Printf.sprintf "Name of the configuration file to update instead of \ @@ -848,26 +1043,27 @@ dir_sep dir_sep) (Arg.some filename) None -let repo_kind_flag = +let repo_kind_flag cli validity = let main_kinds = [ "http" , `http; "local", `rsync; "git" , `git; "darcs", `darcs; "hg" , `hg; - ] in + ] |> List.map (fun (s,v) -> cli_original, s, v) + in let aliases_kinds = [ "wget" , `http; "curl" , `http; "rsync", `rsync; - ] in - mk_opt ["k";"kind"] "KIND" + ] |> List.map (fun (s,v) -> cli_original, s, v) + in + mk_enum_opt ~cli validity ["k";"kind"] "KIND" (main_kinds @ aliases_kinds) (Printf.sprintf "Specify the kind of the repository to be used (%s)." - (Arg.doc_alts_enum main_kinds)) - Arg.(some (enum (main_kinds @ aliases_kinds))) None + (string_of_enum main_kinds)) -let jobs_flag = - mk_opt ["j";"jobs"] "JOBS" +let jobs_flag cli validity = + mk_opt ~cli validity ["j";"jobs"] "JOBS" "Set the maximal number of concurrent jobs to use. The default value is \ calculated from the number of cores. You can also set it using the \ $(b,\\$OPAMJOBS) environment variable." @@ -908,19 +1104,19 @@ arg_list "PARAMS" "List of parameters." Arg.string (* Options common to all commands *) -let global_options = +let global_options cli = let section = global_option_section in let git_version = - mk_flag ~section ["git-version"] + mk_flag ~cli cli_original ~section ["git-version"] "Print the git version of opam, if set (i.e. you are using a development \ version), and exit." in let debug = - mk_flag ~section ["debug"] + mk_flag ~cli cli_original ~section ["debug"] "Print debug message to stderr. \ This is equivalent to setting $(b,\\$OPAMDEBUG) to \"true\"." in let debug_level = - mk_opt ~section ["debug-level"] "LEVEL" + mk_opt ~cli cli_original ~section ["debug-level"] "LEVEL" "Like $(b,--debug), but allows specifying the debug level ($(b,--debug) \ sets it to 1). Equivalent to setting $(b,\\$OPAMDEBUG) to a positive \ integer." @@ -932,41 +1128,69 @@ $(i,patch) etc.) Repeating $(i,n) times is equivalent to setting \ $(b,\\$OPAMVERBOSE) to \"$(i,n)\".") in let quiet = - mk_flag ~section ["q";"quiet"] "Disables $(b,--verbose)." in + mk_flag ~cli cli_original ~section ["q";"quiet"] "Disables $(b,--verbose)." in let color = - mk_tristate_opt ~section ["color"] "WHEN" + mk_enum_opt ~cli cli_original ~section ["color"] "WHEN" when_enum (Printf.sprintf "Colorize the output. $(docv) must be %s." - (Arg.doc_alts_enum when_enum)) in + (string_of_enum when_enum)) in + (* The --cli option is pre-processed, because it has to be able to appear + before sub-commands. The one here is present only for --help. *) + let cli_arg = + mk_opt ~cli cli_original ~section ["cli"] "MAJOR.MINOR" + "Use the command-line interface syntax and semantics of $(docv). \ + Intended for any persistent use of opam (scripts, blog posts, etc.), \ + any version of opam in the same MAJOR series will behave as for the \ + specified MINOR release. The flag was not available in opam 2.0, so to \ + select the 2.0 CLI, set the $(b,OPAMCLI) environment variable to \ + $(i,2.0) instead of using this parameter." + Arg.string (OpamCLIVersion.to_string OpamCLIVersion.current) in let switch = - mk_opt ~section ["switch"] + mk_opt ~cli cli_original ~section ["switch"] "SWITCH" "Use $(docv) as the current compiler switch. \ This is equivalent to setting $(b,\\$OPAMSWITCH) to $(i,SWITCH)." Arg.(some string) None in let yes = - mk_flag ~section ["y";"yes"] - "Answer yes to all yes/no questions without prompting. \ - This is equivalent to setting $(b,\\$OPAMYES) to \"true\"." in + mk_vflag_all ~cli [ + cli_original, true, ["y";"yes"], + "Answer yes to all opam yes/no questions without prompting. \ + See also $(b,--confirm-level). \ + This is equivalent to setting $(b,\\$OPAMYES) to \"true\"."; + cli_from cli2_1, false, ["no"], + "Answer no to all opam yes/no questions without prompting. \ + See also $(b,--confirm-level). \ + This is equivalent to setting $(b,\\$OPAMNO) to \"true\"."; + ] + in + let confirm_level = + mk_enum_opt_all ~cli (cli_from cli2_1) ~section ["confirm-level"] "LEVEL" + confirm_enum + (Printf.sprintf + "Confirmation level, $(docv) must be %s. Can be specified more than \ + once. If $(b,--yes) or $(b,--no) are also given, the value of the \ + last $(b,--confirm-level) is taken into account. This is equivalent \ + to setting $(b, \\$OPAMCONFIRMLEVEL)`." + (string_of_enum confirm_enum)) + in let strict = - mk_flag ~section ["strict"] + mk_flag ~cli cli_original ~section ["strict"] "Fail whenever an error is found in a package definition \ or a configuration file. The default is to continue silently \ if possible." in let root = - mk_opt ~section ["root"] + mk_opt ~cli cli_original ~section ["root"] "ROOT" "Use $(docv) as the current root path. \ This is equivalent to setting $(b,\\$OPAMROOT) to $(i,ROOT)." Arg.(some dirname) None in let d_no_aspcud = - mk_flag ~section ["no-aspcud"] - "Deprecated." + mk_flag ~cli (cli_between cli2_0 cli2_1) ~section ["no-aspcud"] "Deprecated" in let use_internal_solver = - mk_flag ~section ["use-internal-solver"] + mk_flag ~cli cli_original ~section ["use-internal-solver"] "Disable any external solver, and use the built-in one (this requires \ that opam has been compiled with a built-in solver). This is equivalent \ to setting $(b,\\$OPAMNOASPCUD) or $(b,\\$OPAMUSEINTERNALSOLVER)." in let external_solver = - mk_opt ~section ["solver"] "CMD" + mk_opt ~cli cli_original ~section ["solver"] "CMD" (Printf.sprintf "Specify the CUDF solver to use for resolving package installation \ problems. This is either a predefined solver (this version of opam \ @@ -978,7 +1202,7 @@ (OpamCudfSolver.default_solver_selection))) Arg.(some string) None in let solver_preferences = - mk_opt ~section ["criteria"] "CRITERIA" + mk_opt ~cli cli_original ~section ["criteria"] "CRITERIA" ("Specify user $(i,preferences) for dependency solving for this run. \ Overrides both $(b,\\$OPAMCRITERIA) and $(b,\\$OPAMUPGRADECRITERIA). \ For details on the supported language, and the external solvers available, see \ @@ -987,19 +1211,19 @@ $(i, http://www.dicosmo.org/Articles/usercriteria.pdf).") Arg.(some string) None in let cudf_file = - mk_opt ~section ["cudf"] "FILENAME" + mk_opt ~cli cli_original ~section ["cudf"] "FILENAME" "Debug option: Save the CUDF requests sent to the solver to \ $(docv)-.cudf." Arg.(some string) None in let best_effort = - mk_flag ~section ["best-effort"] + mk_flag ~cli cli_original ~section ["best-effort"] "Don't fail if all requested packages can't be installed: try to install \ as many as possible. Note that not all external solvers may support \ this option (recent versions of $(i,aspcud) or $(i,mccs) should). This \ is equivalent to setting $(b,\\$OPAMBESTEFFORT) environment variable." in let safe_mode = - mk_flag ~section ["readonly"; "safe"] + mk_flag ~cli cli_original ~section ["readonly"; "safe"] "Make sure nothing will be automatically updated or rewritten. Useful \ for calling from completion scripts, for example. Will fail whenever \ such an operation is needed ; also avoids waiting for locks, skips \ @@ -1007,7 +1231,7 @@ This is equivalent to set environment variable $(b,\\$OPAMSAFE)." in let json_flag = - mk_opt ~section ["json"] "FILENAME" + mk_opt ~cli cli_original ~section ["json"] "FILENAME" "Save the results of the opam run in a computer-readable file. If the \ filename contains the character `%', it will be replaced by an index \ that doesn't overwrite an existing file. Similar to setting the \ @@ -1015,7 +1239,7 @@ Arg.(some string) None in let no_auto_upgrade = - mk_flag ~section ["no-auto-upgrade"] + mk_flag ~cli cli_original ~section ["no-auto-upgrade"] "When configuring or updating a repository that is written for an \ earlier opam version (1.2), opam internally converts it to the current \ format. This disables this behaviour. Note that repositories should \ @@ -1025,38 +1249,61 @@ upgrade [--mirror URL]) when possible." in let working_dir = - mk_flag ~section ["working-dir"; "w"] + mk_flag ~cli cli_original ~section ["working-dir"; "w"] "Whenever updating packages that are bound to a local, \ version-controlled directory, update to the current working state of \ their source instead of the last committed state, or the ref they are \ - pointing to. \ + pointing to. As source directory is copied as it is, if it isn't clean \ + it may result on a opam build failure.\ This only affects packages explicitly listed on the command-line.\ It can also be set with $(b,\\$OPAMWORKINGDIR). " in let ignore_pin_depends = - mk_flag ~section ["ignore-pin-depends"] + mk_flag ~cli cli_original ~section ["ignore-pin-depends"] "Ignore extra pins required by packages that get pinned, either manually \ through $(i,opam pin) or through $(i,opam install DIR). This is \ equivalent to setting $(b,IGNOREPINDEPENDS=true)." in Term.(const create_global_options - $git_version $debug $debug_level $verbose $quiet $color $switch $yes + $git_version $debug $debug_level $verbose $quiet $color $switch + $yes $confirm_level $strict $root $external_solver $use_internal_solver $cudf_file $solver_preferences $best_effort $safe_mode $json_flag $no_auto_upgrade $working_dir $ignore_pin_depends - $d_no_aspcud) + $d_no_aspcud $cli_arg) + +(* lock options *) +let locked ?(section=Manpage.s_options) cli = + mk_flag ~cli cli_original ~section ["locked"] + "In commands that use opam files found from pinned sources, if a variant \ + of the file with an added .$(i,locked) extension is found (e.g. \ + $(b,foo.opam.locked) besides $(b,foo.opam)), that will be used instead. \ + This is typically useful to offer a more specific set of dependencies \ + and reproduce similar build contexts, hence the name. The $(i, lock)\ + option can be used to generate such files, based on the versions \ + of the dependencies currently installed on the host. This is equivalent \ + to setting the $(b,\\$OPAMLOCKED) environment variable. Note that this \ + option doesn't generally affect already pinned packages." +let lock_suffix ?(section=Manpage.s_options) cli = + mk_opt ~cli (cli_from cli2_1) ~section ["lock-suffix"] "SUFFIX" + "Set locked files suffix to $(i,SUFFIX)." + Arg.(string) ("locked") (* Options common to all build commands *) let build_option_section = "PACKAGE BUILD OPTIONS" -let build_options = +let man_build_option_section = + [ + `S build_option_section; + ] +let build_options cli = let section = build_option_section in let keep_build_dir = - mk_flag ~section ["b";"keep-build-dir"] + mk_flag ~cli cli_original ~section ["b";"keep-build-dir"] "Keep the build directories after compiling packages. \ This is equivalent to setting $(b,\\$OPAMKEEPBUILDDIR) to \"true\"." in let reuse_build_dir = - mk_flag ~section ["reuse-build-dir"] + mk_flag ~cli cli_original ~section ["reuse-build-dir"] "Reuse existing build directories (kept by using $(b,--keep-build-dir)), \ instead of compiling from a fresh clone of the source. This can be \ faster, but also lead to failures if the build systems of the packages \ @@ -1064,7 +1311,7 @@ setting $(b,\\$OPAMREUSEBUILDDIR) to \"true\"." in let inplace_build = - mk_flag ~section ["inplace-build"] + mk_flag ~cli cli_original ~section ["inplace-build"] "When compiling a package which has its source bound to a local \ directory, process the build and install actions directly in that \ directory, rather than in a clean copy handled by opam. This only \ @@ -1072,130 +1319,158 @@ This is equivalent to setting $(b,\\$OPAMINPLACEBUILD) to \"true\"." in let no_checksums = - mk_flag ~section ["no-checksums"] + mk_flag ~cli cli_original ~section ["no-checksums"] "Do not verify the checksum of downloaded archives.\ This is equivalent to setting $(b,\\$OPAMNOCHECKSUMS) to \"true\"." in let req_checksums = - mk_flag ~section ["require-checksums"] + mk_flag ~cli cli_original ~section ["require-checksums"] "Reject the installation of packages that don't provide a checksum for the upstream archives. \ This is equivalent to setting $(b,\\$OPAMREQUIRECHECKSUMS) to \"true\"." in let build_test = - mk_flag ~section ["t";"with-test";"build-test"] - "Build and $(b,run) the package unit-tests. This only affects packages \ - listed on the command-line. The $(b,--build-test) form is deprecated as \ - this also affects installation. This is equivalent to setting \ + mk_flag_replaced ~cli ~section [ + cli_between cli2_0 cli2_1 ~replaced:"--with-test", ["build-test"]; + cli_original, ["t";"with-test"]; + ] "Build and $(b,run) the package unit-tests. This only affects packages \ + listed on the command-line. This is equivalent to setting \ $(b,\\$OPAMWITHTEST) (or the deprecated $(b,\\$OPAMBUILDTEST)) to \ - \"true\"." in + \"true\"." + in let build_doc = - mk_flag ~section ["d";"with-doc";"build-doc"] - "Build the package documentation. This only affects packages listed on \ - the command-line. The $(b,--build-doc) form is deprecated as this does \ - also installation. This is equivalent to setting $(b,\\$OPAMWITHDOC) \ - (or the deprecated $(b,\\$OPAMBUILDDOC)) to \"true\"." in + mk_flag_replaced ~cli ~section [ + cli_between cli2_0 cli2_1 ~replaced:"--with-doc", ["build-doc"]; + cli_original, ["d";"with-doc"]; + ] "Build the package documentation. This only affects packages listed on \ + the command-line. This is equivalent to setting $(b,\\$OPAMWITHDOC) \ + (or the deprecated $(b,\\$OPAMBUILDDOC)) to \"true\"." + in let make = - mk_opt ~section ["m";"make"] "MAKE" - "Use $(docv) as the default 'make' command. Deprecated: use $(b,opam \ - config set[-global] make MAKE) instead. Has no effect if the $(i,make) \ - variable is defined." - Arg.(some string) None in + mk_opt ~cli (cli_between cli2_0 cli2_1 + ~replaced:"opam config set[-global] make MAKE") + ~section ["m";"make"] "MAKE" + "Use $(docv) as the default 'make' command. Has no effect if the \ + $(i,make) variable is defined." + Arg.(some string) None + in let show = - mk_flag ~section ["show-actions"] + mk_flag ~cli cli_original ~section ["show-actions"] "Call the solver and display the actions. Don't perform any changes. \ This is equivalent to setting $(b,\\$OPAMSHOW)." in let dryrun = - mk_flag ~section ["dry-run"] + mk_flag ~cli cli_original ~section ["dry-run"] "Simulate the command, but don't actually perform any changes. This also \ can be set with environment variable $(b,\\$OPAMDEBUG)." in let skip_update = - mk_flag ~section ["skip-updates"] + mk_flag ~cli cli_original ~section ["skip-updates"] "When running an install, upgrade or reinstall on source-pinned \ packages, they are normally updated from their origin first. This flag \ disables that behaviour and will keep them to their version in cache. \ This is equivalent to setting $(b,\\$OPAMSKIPUPDATE)." in let fake = - mk_flag ~section ["fake"] + mk_flag ~cli cli_original ~section ["fake"] "This option registers the actions into the opam database, without \ actually performing them. \ WARNING: This option is dangerous and likely to break your opam \ - environment. You probably want `--dry-run'. You've been $(i,warned)." in + environment. You probably want $(b,--dry-run). You've been $(i,warned)." + in let ignore_constraints_on = - mk_opt ~section ["ignore-constraints-on"] "PACKAGES" + mk_opt ~cli cli_original ~section ["ignore-constraints-on"] "PACKAGES" "Forces opam to ignore version constraints on all dependencies to the \ listed packages. This can be used to test compatibility, but expect \ builds to break when using this. Note that version constraints on \ optional dependencies and conflicts are unaffected. This is equivalent \ to setting $(b,\\$OPAMIGNORECONSTRAINTS)." - Arg.(some (list package_name)) None ~vopt:(Some []) in + Arg.(some (list package_name)) None ~vopt:(Some []) + in let unlock_base = - mk_flag ~section ["unlock-base"] - "Allow changes to the packages set as switch base (typically, the main \ + mk_flag_replaced ~cli ~section [ + cli_between cli2_0 cli2_1 ~replaced:"--update-invariant", ["unlock-base"]; + cli_from cli2_1, ["update-invariant"] + ] "Allow changes to the packages set as switch base (typically, the main \ compiler). Use with caution. This is equivalent to setting the \ - $(b,\\$OPAMUNLOCKBASE) environment variable" in - let locked = - let open Arg in - value & opt ~vopt:(Some "locked") (some string) None & - info ~docs:section ~docv:"SUFFIX" ["locked"] ~doc: - "In commands that use opam files found from pinned sources, if a variant \ - of the file with an added .$(i,SUFFIX) extension is found (e.g. \ - $(b,foo.opam.locked) besides $(b,foo.opam)), that will be used instead. \ - This is typically useful to offer a more specific set of dependencies \ - and reproduce similar build contexts, hence the name. The $(i,opam \ - lock) plugin can be used to generate such files, based on the versions \ - of the dependencies currently installed on the host. This is equivalent \ - to setting the $(b,\\$OPAMLOCKED) environment variable. Note that this \ - option doesn't generally affect already pinned packages." + $(b,\\$OPAMUNLOCKBASE) environment variable" + in + let locked = locked cli ~section in + let lock_suffix = lock_suffix cli ~section in + let assume_depexts = + mk_flag ~cli (cli_from cli2_1) ~section ["assume-depexts"] + "Skip the installation step for any missing system packages, and attempt \ + to proceed with compilation of the opam packages anyway. If the \ + installation is successful, opam won't prompt again about these system \ + packages. Only meaningful if external dependency handling is enabled." + in + let no_depexts = + mk_flag ~cli (cli_from cli2_1) ~section ["no-depexts"] + "Temporarily disables handling of external dependencies. This can be \ + used if a package is not available on your system package manager, but \ + you installed the required dependency by hand. Implies \ + $(b,--assume-depexts), and stores the exceptions upon success as well." in Term.(const create_build_options - $keep_build_dir $reuse_build_dir $inplace_build $make - $no_checksums $req_checksums $build_test $build_doc $show $dryrun - $skip_update $fake $jobs_flag $ignore_constraints_on - $unlock_base $locked) + $keep_build_dir $reuse_build_dir $inplace_build $make + $no_checksums $req_checksums $build_test $build_doc $show $dryrun + $skip_update $fake $jobs_flag cli cli_original $ignore_constraints_on + $unlock_base $locked $lock_suffix $assume_depexts $no_depexts) (* Option common to install commands *) -let assume_built = - Arg.(value & flag & info ["assume-built"] - ~doc:"For use on locally-pinned packages: assume they have already \ - been correctly built, and only run their installation \ - instructions, directly from their source directory. This \ - skips the build instructions and can be useful to install \ - packages that are being worked on. Implies $(i,--inplace-build). \ - No locally-pinned packages will be skipped.") +let assume_built cli = + mk_flag ~cli cli_original ["assume-built"] + "For use on locally-pinned packages: assume they have already \ + been correctly built, and only run their installation \ + instructions, directly from their source directory. This \ + skips the build instructions and can be useful to install \ + packages that are being worked on. Implies $(i,--inplace-build). \ + No locally-pinned packages will be skipped." + +(* Options common to all path based/related commands, e.g. (un)pin, upgrade, + remove, (re)install. + Disabled *) +let recurse _cli = Term.const false + +(* + mk_flag ~cli (cli_from cli2_2) ["recursive"] + "Allow recursive lookups of (b,*.opam) files. Cf. $(i,--subpath) also." +*) + +let subpath _cli = Term.const None +(* + mk_opt ~cli (cli_from cli2_2) ["subpath"] "PATH" + "$(b,*.opam) files are retrieved from the given sub directory instead of \ + top directory. Sources are then taken from the targeted sub directory, \ + internally only this subdirectory is copied/fetched. It can be combined \ + with $(i,--recursive) to have a recursive lookup on the subpath." + Arg.(some subpath_conv) None +*) let package_selection_section = "PACKAGE SELECTION OPTIONS" -let package_selection = +let package_selection cli = let section = package_selection_section in - let docs = section in let depends_on = - let doc = - "List only packages that depend on one of (comma-separated) $(docv)." - in - Arg.(value & opt_all (list atom) [] & - info ~doc ~docs ~docv:"PACKAGES" ["depends-on"]) + mk_opt_all ~cli cli_original ["depends-on"] "PACKAGES" ~section + "List only packages that depend on one of (comma-separated) $(b,PACKAGES)." + Arg.(list atom) in let required_by = - let doc = "List only the dependencies of (comma-separated) $(docv)." in - Arg.(value & opt_all (list atom) [] & - info ~doc ~docs ~docv:"PACKAGES" ["required-by"]) + mk_opt_all ~cli cli_original ["required-by"] "PACKAGES" ~section + "List only the dependencies of (comma-separated) $(b,PACKAGES)." + Arg.(list atom) in let conflicts_with = - let doc = + mk_opt_all ~cli cli_original ["conflicts-with"] "PACKAGES" ~section "List packages that have declared conflicts with at least one of the \ given list. This includes conflicts defined from the packages in the \ list, from the other package, or by a common $(b,conflict-class:) \ field." - in - Arg.(value & opt_all (list package_with_version) [] & - info ~doc ~docs ~docv:"PACKAGES" ["conflicts-with"]) + Arg.(list package_with_version) in let coinstallable_with = - let doc = "Only list packages that are compatible with all of $(docv)." in - Arg.(value & opt_all (list package_with_version) [] & - info ~doc ~docs ~docv:"PACKAGES" ["coinstallable-with"]) + mk_opt_all ~cli cli_original ["coinstallable-with"] "PACKAGES" ~section + "Only list packages that are compatible with all of $(b,PACKAGES)." + Arg.(list package_with_version) in let resolve = - let doc = + mk_opt_all ~cli cli_original ["resolve"] "PACKAGES" ~section "Restrict to a solution to install (comma-separated) $(docv), $(i,i.e.) \ a consistent set of packages including those. This is subtly different \ from `--required-by --recursive`, which is more predictable and can't \ @@ -1207,47 +1482,45 @@ `--no-switch` further makes the solution independent from the \ currently pinned packages, architecture, and compiler version. \ The combination with `--depopts' is not supported." - in - Arg.(value & opt_all (list atom) [] & - info ~doc ~docs ~docv:"PACKAGES" ["resolve"]) + Arg.(list atom) in let recursive = - mk_flag ["recursive"] ~section + mk_flag ~cli cli_original ["recursive"] ~section "With `--depends-on' and `--required-by', display all transitive \ dependencies rather than just direct dependencies." in let depopts = - mk_flag ["depopts"] ~section + mk_flag ~cli cli_original ["depopts"] ~section "Include optional dependencies in dependency requests." in let nobuild = - mk_flag ["nobuild"] ~section + mk_flag ~cli cli_original ["nobuild"] ~section "Exclude build dependencies (they are included by default)." in let post = - mk_flag ["post"] ~section + mk_flag ~cli cli_original ["post"] ~section "Include dependencies tagged as $(i,post)." in let dev = - mk_flag ["dev"] ~section + mk_flag ~cli cli_original ["dev"] ~section "Include development packages in dependencies." in let doc_flag = - mk_flag ["doc";"with-doc"] ~section + mk_flag ~cli cli_original ["doc";"with-doc"] ~section "Include doc-only dependencies." in let test = - mk_flag ["t";"test";"with-test"] ~section + mk_flag ~cli cli_original ["t";"test";"with-test"] ~section "Include test-only dependencies." in let field_match = - mk_opt_all ["field-match"] "FIELD:PATTERN" ~section + mk_opt_all ~cli cli_original ["field-match"] "FIELD:PATTERN" ~section "Filter packages with a match for $(i,PATTERN) on the given $(i,FIELD)" Arg.(pair ~sep:':' string string) in let has_flag = - mk_opt_all ["has-flag"] "FLAG" ~section - ("Only include packages which have the given flag set. Package flags are \ - one of: "^ + mk_opt_all ~cli cli_original ["has-flag"] "FLAG" ~section + ("Only include packages which have the given flag set. \ + Package flags are one of: "^ (OpamStd.List.concat_map " " (Printf.sprintf "$(b,%s)" @* string_of_pkg_flag) all_package_flags)) @@ -1261,7 +1534,7 @@ Format.pp_print_string fmt (string_of_pkg_flag flag)) in let has_tag = - mk_opt_all ["has-tag"] "TAG" ~section + mk_opt_all ~cli cli_original ["has-tag"] "TAG" ~section "Only includes packages which have the given tag set" Arg.string in @@ -1305,9 +1578,10 @@ let package_listing_section = "OUTPUT FORMAT OPTIONS" -let package_listing = +let package_listing cli = let section = package_listing_section in - let all_versions = mk_flag ["all-versions";"V"] ~section + let all_versions = mk_flag ~cli cli_original ["all-versions";"V"] + ~section "Normally, when multiple versions of a package match, only one is shown \ in the output (the installed one, the pinned-to one, or, failing that, \ the highest one available or the highest one). This flag disables this \ @@ -1318,39 +1592,39 @@ command-line." in let print_short = - mk_flag ["short";"s"] ~section + mk_flag ~cli cli_original ["short";"s"] ~section "Don't print a header, and sets the default columns to $(b,name) only. \ If you need package versions included, use $(b,--columns=package) \ instead" in let sort = - mk_flag ["sort";"S"] ~section + mk_flag ~cli cli_original ["sort";"S"] ~section "Sort the packages in dependency order (i.e. an order in which they \ could be individually installed.)" in let columns = - mk_opt ["columns"] "COLUMNS" ~section + mk_opt ~cli cli_original ["columns"] "COLUMNS" ~section (Printf.sprintf "Select the columns to display among: %s.\n\ The default is $(b,name) when $(i,--short) is present \ and %s otherwise." (OpamStd.List.concat_map ", " (fun (_,f) -> Printf.sprintf "$(b,%s)" f) - OpamListCommand.field_names) + OpamListCommand.raw_field_names) (OpamStd.List.concat_map ", " (fun f -> Printf.sprintf "$(b,%s)" (OpamListCommand.string_of_field f)) OpamListCommand.default_list_format)) Arg.(some & opamlist_columns) None in - let normalise = mk_flag ["normalise"] ~section + let normalise = mk_flag ~cli cli_original ["normalise"] ~section "Print the values of opam fields normalised" in - let wrap = mk_flag ["wrap"] ~section + let wrap = mk_flag ~cli cli_original ["wrap"] ~section "Wrap long lines, the default being to truncate when displaying on a \ terminal, or to keep as is otherwise" in let separator = - Arg.(value & opt string " " & info ["separator"] - ~docv:"STRING" ~docs:package_listing_section - ~doc:"Set the column-separator string") + mk_opt ~cli cli_original ["separator"] "STRING" ~section + "Set the column-separator string" + Arg.string " " in let format all_versions short sort columns normalise wrap separator = fun ~force_all_versions -> diff -Nru opam-2.0.10/src/client/opamArg.mli opam-2.1.2/src/client/opamArg.mli --- opam-2.0.10/src/client/opamArg.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamArg.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -16,17 +16,70 @@ (** {2 Helpers and argument constructors} *) -val mk_flag: ?section:string -> string list -> string -> bool Term.t +(** {3 CLI versioning} *) + +(* Type of the validity of a flag *) +type validity + +val cli2_0: OpamCLIVersion.t +val cli2_1: OpamCLIVersion.t + +(* [cli_from since] validity flag since [since], and no removal version *) +val cli_from: OpamCLIVersion.t -> validity + +(* [cli_between since until ?replaced] a validity flags introduced in + [since], removed in [until], [replaced] is the replacement helper + message *) +val cli_between: + OpamCLIVersion.t -> ?default:bool -> ?replaced:string -> OpamCLIVersion.t -> + validity + +(* Original cli options : [validity] from 2.0 and no removal. + No new options should use this. *) +val cli_original: validity + +(** {3 Common helphers} *) + +(* Helpers function takes [cli] as first argument, which is the requested cli + (via [--cli] or [OPAMCLI]), and a [validity] argument, the validity of the + flag. + All arguments must be defined using [mk_*] function, they embed cli + validation. *) + +val mk_flag: + cli:OpamCLIVersion.Sourced.t -> validity -> + ?section:string -> string list -> string -> + bool Term.t + +(* Deprecate and replace a [flags]. Constructs a [vflag] with the deprecated + option and the new one *) +val mk_flag_replaced: + cli:OpamCLIVersion.Sourced.t -> ?section:string -> (validity * string list) list -> + string -> bool Term.t val mk_opt: + cli:OpamCLIVersion.Sourced.t -> validity -> ?section:string -> ?vopt:'a -> string list -> string -> string -> - 'a Arg.converter -> 'a -> 'a Term.t + 'a Arg.converter -> 'a -> + 'a Term.t val mk_opt_all: + cli:OpamCLIVersion.Sourced.t -> validity -> ?section:string -> ?vopt:'a -> ?default:'a list -> string list -> string -> string -> 'a Arg.converter -> 'a list Term.t +val mk_vflag: + cli:OpamCLIVersion.Sourced.t -> + ?section:string -> 'a -> (validity * 'a * string list * string) list -> + 'a Term.t + +val mk_vflag_all: + cli:OpamCLIVersion.Sourced.t -> + ?section:string -> ?default:'a list -> + (validity * 'a * string list * string) list -> + 'a list Term.t + (* Escaped Windows directory separator. To use instead of [Filename.dir_sep] for manpage strings *) val dir_sep: string @@ -37,22 +90,24 @@ (** {2 Flags} *) (** --short *) -val print_short_flag: bool Term.t - -(** --installed-root *) -val installed_roots_flag: bool Term.t +val print_short_flag: + OpamCLIVersion.Sourced.t -> validity -> bool Term.t (** --shell *) -val shell_opt: shell option Term.t +val shell_opt: + OpamCLIVersion.Sourced.t -> validity -> shell option Term.t (** --dot-profile *) -val dot_profile_flag: filename option Term.t +val dot_profile_flag: + OpamCLIVersion.Sourced.t -> validity -> filename option Term.t (** --http/ --git/ --local *) -val repo_kind_flag: OpamUrl.backend option Term.t +val repo_kind_flag: + OpamCLIVersion.Sourced.t -> validity -> OpamUrl.backend option Term.t (** --jobs *) -val jobs_flag: int option Term.t +val jobs_flag: + OpamCLIVersion.Sourced.t -> validity -> int option Term.t (** package names *) val name_list: name list Term.t @@ -79,6 +134,9 @@ (** Generic argument list builder *) val nonempty_arg_list: string -> string -> 'a Arg.converter -> 'a list Term.t +(** Confirmation level enum *) +val confirm_enum: (validity * string * OpamStd.Config.answer) list + (** {3 Global options} *) (** Type for global options *) @@ -86,9 +144,10 @@ debug_level: int option; verbose: int; quiet : bool; - color : [ `Always | `Never | `Auto ] option; + color : OpamStd.Config.when_ option; opt_switch : string option; - yes : bool; + confirm_level : OpamStd.Config.answer option; + yes: bool option; strict : bool; opt_root : dirname option; git_version : bool; @@ -102,13 +161,14 @@ no_auto_upgrade : bool; working_dir : bool; ignore_pin_depends : bool; + cli : OpamCLIVersion.t; } (** Global options *) -val global_options: global_options Term.t +val global_options: OpamCLIVersion.Sourced.t -> global_options Term.t (** Apply global options *) -val apply_global_options: global_options -> unit +val apply_global_options: OpamCLIVersion.Sourced.t -> global_options -> unit (** {3 Build options} *) @@ -116,17 +176,26 @@ (** Abstract type for build options *) type build_options -val build_option_section: string +val man_build_option_section: Manpage.block list (** Build options *) -val build_options: build_options Term.t +val build_options: OpamCLIVersion.Sourced.t -> build_options Term.t + +(** Install and reinstall options *) +val assume_built: OpamCLIVersion.Sourced.t -> bool Term.t -(** Instal and reinstall options *) -val assume_built: bool Term.t +(* Options common to all path based/related commands, e.g. (un)pin, upgrade, + remove, (re)install + Disabled *) +val recurse: OpamCLIVersion.Sourced.t -> bool Term.t +val subpath: OpamCLIVersion.Sourced.t -> string option Term.t (** Applly build options *) -val apply_build_options: build_options -> unit +val apply_build_options: OpamCLIVersion.Sourced.t -> build_options -> unit +(** Lock options *) +val locked: ?section:string -> OpamCLIVersion.Sourced.t -> bool Term.t +val lock_suffix: ?section:string -> OpamCLIVersion.Sourced.t -> string Term.t (** {3 Package listing and filtering options} *) @@ -134,7 +203,7 @@ val package_selection_section: string (** Build a package selection filter *) -val package_selection: OpamListCommand.selector list Term.t +val package_selection: OpamCLIVersion.Sourced.t -> OpamListCommand.selector list Term.t (** Man section name *) val package_listing_section: string @@ -142,6 +211,7 @@ (** Package selection filter based on the current state of packages (installed, available, etc.) *) val package_listing: + OpamCLIVersion.Sourced.t -> (force_all_versions:bool -> OpamListCommand.package_listing_format) Term.t (** {3 Converters} *) @@ -169,6 +239,9 @@ (** Package name converter *) val package_name: name Arg.converter +(** Package version converter *) +val package_version: version Arg.converter + (** [name{.version}] (or [name=version]) *) val package: (name * version option) Arg.converter @@ -185,63 +258,93 @@ val atom_or_dir: [ `Atom of atom | `Dirname of dirname ] Arg.converter +(** Formula, in the same format as [depends:] in opam files *) +val dep_formula: formula Arg.converter + (** [var=value,...] argument *) val variable_bindings: (OpamVariable.t * string) list Arg.converter (** Warnings string ["+3..10-4"] *) val warn_selector: (int * bool) list Arg.converter -type 'a default = [> `default of string] as 'a - -(** Enumeration with a default command *) -val enum_with_default: (string * 'a default) list -> 'a Arg.converter - val opamlist_columns: OpamListCommand.output_format list Arg.converter (** {2 Subcommands} *) -type 'a subcommand = string * 'a * string list * string +type 'a subcommand = validity * string * 'a * string list * string (** A subcommand [cmds, v, args, doc] is the subcommand [cmd], using the documentation [doc] and the list of documentation parameters - [args]. If the subcommand is selected, return [v]. *) + [args]. If the subcommand is selected, return [v] value. *) type 'a subcommands = 'a subcommand list -val mk_subcommands: 'a subcommands -> 'a option Term.t * string list Term.t +val mk_subcommands: + cli:OpamCLIVersion.Sourced.t -> + 'a subcommands -> 'a option Term.t * string list Term.t (** [subcommands cmds] are the terms [cmd] and [params]. [cmd] parses which sub-commands in [cmds] is selected and [params] parses the remaining of the command-line parameters as a list of strings. *) +type 'a default = [> `default of string] as 'a + +(* unused +(** Enumeration with a default command *) +val enum_with_default: (string * 'a default) list -> 'a Arg.converter +*) + val mk_subcommands_with_default: + cli:OpamCLIVersion.Sourced.t -> 'a default subcommands -> 'a option Term.t * string list Term.t (** Same as {!mk_subcommand} but use the default value if no sub-command is selected. *) -val make_command_alias: - 'a Term.t * Term.info -> ?options:string -> string -> - 'a Term.t * Term.info -(** Create an alias for an existing command. [options] can be used to add extra - options after the original command in the doc (eg like `unpin` is an alias - for `pin remove`). *) - val bad_subcommand: + cli:OpamCLIVersion.Sourced.t -> 'a default subcommands -> (string * 'a option * string list) -> 'b Term.ret (** [bad_subcommand cmds cmd] is a command return value denoting a parsing error of sub-commands. *) val mk_subdoc : + cli:OpamCLIVersion.Sourced.t -> ?defaults:(string * string) list -> 'a subcommands -> Manpage.block list (** [mk_subdoc cmds] is the documentation block for [cmds]. *) -(** {2 Misc} *) +val make_command_alias: + cli:OpamCLIVersion.Sourced.t -> + 'a Term.t * Term.info -> ?options:string -> string -> + 'a Term.t * Term.info +(** Create an alias for an existing command. [options] can be used to add extra + options after the original command in the doc (eg like `unpin` is an alias + for `pin remove`). *) + +(** {2 Commands} *) + +(* All commands must be defined using [mk_command] and [mk_command_ret] for + prior cli validation. *) -val deprecated_option: 'a -> 'a -> string -> string option -> unit -(** [deprecated_option option default name instead] displays a message if - [option] if set to its non [default] value. [instead], if present, is the new - option/command to launch *) +type command = unit Term.t * Term.info + +val mk_command: + cli:OpamCLIVersion.Sourced.t -> validity -> string -> doc:string -> + man:Manpage.block list -> (unit -> unit) Term.t -> command + (* [mk_command cli validity name doc man term] is the command [name] with its + [doc] and [man], and using [term]. Its [validity] is checked at runtime + against requested [cli], updates its documentation and errors if not + valid. *) + +val mk_command_ret: + cli:OpamCLIVersion.Sourced.t -> validity -> string -> doc:string -> + man:Manpage.block list -> (unit -> unit Term.ret) Term.t -> command + (* Same as {!mk_command} but [term] returns a [Cmdliner.Term.ret] *) (** {2 Documentation} *) val global_option_section: string -val help_sections: Manpage.block list -val term_info: string -> doc:string -> man:Manpage.block list -> Term.info +val help_sections: OpamCLIVersion.Sourced.t -> Manpage.block list + + +(** {2 Environment variables} *) + +val preinit_opam_env_variables: unit -> unit +val init_opam_env_variabes: OpamCLIVersion.Sourced.t -> unit +val scrubbed_environment_variables: string list diff -Nru opam-2.0.10/src/client/opamArgTools.ml opam-2.1.2/src/client/opamArgTools.ml --- opam-2.0.10/src/client/opamArgTools.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamArgTools.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,618 @@ +(**************************************************************************) +(* *) +(* Copyright 2021 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamStd.Op +open Cmdliner +open OpamCLIVersion.Op + +let cli2_0 = OpamCLIVersion.of_string "2.0" +let cli2_1 = OpamCLIVersion.of_string "2.1" + +type 'b validity_and_content = { + valid: OpamCLIVersion.t; + removed: (OpamCLIVersion.t * string option) option; + content: 'b; + default: bool; +} + +type 'a content = Valid of 'a | Removed of 'a +type 'a contented_validity = 'a content validity_and_content + +type validity = unit validity_and_content + +let elem_of_vr = function Valid e | Removed e -> e + +let contented_validity (validity:validity) content : 'a contented_validity = + match validity.removed with + | None -> { validity with content = Valid content} + | Some _ -> { validity with content = Removed content} + +let is_original_cli validity = + OpamCLIVersion.compare validity.valid cli2_0 = 0 + +let cli_from valid = { valid ; removed = None; content = (); default = false } +let cli_between since ?(default=false) ?replaced removal = + if since >= removal then + OpamConsole.error_and_exit `Internal_error + "An option can't be added in %s and removed in %s" + (OpamCLIVersion.to_string since) + (OpamCLIVersion.to_string removal); + { valid = since ; removed = Some (removal, replaced); + content = (); default } +let cli_original = cli_from cli2_0 + +let bold = OpamConsole.colorise `bold +let string_of_sourced_cli (c,_) = OpamCLIVersion.to_string c +let string_of_cli_option cli = + if cli = cli2_0 then + Printf.sprintf "set %s environment variable to %s" + (bold "OPAMCLI") (bold "2.0") + else + Printf.sprintf "use --cli=%s" + (bold @@ OpamCLIVersion.to_string cli) + +let update_doc_w_cli doc ~cli = function + | { valid = c ; removed = None; _} -> + if cli @< c then + Printf.sprintf "(Since $(b,%s)) %s" + (OpamCLIVersion.to_string c) doc + else doc + | { removed = Some (since, instead); _} -> + if cli @< since then doc else + Printf.sprintf "Removed in $(b,%s)%s" + (OpamCLIVersion.to_string since) + (match instead with + | Some instead -> + Printf.sprintf ", use $(i,%s) instead." instead + | None -> ".") + +(* Error messages *) +type target = + | Flags of string list + | Option of string + | Verbatim of string + +let get_long_form flags = + List.fold_left (fun (lgth,long) f -> + let flgth = String.length f in + if flgth > lgth then (flgth, f) else (lgth, long)) + (0,"") flags |> snd + +let string_of_target = function + | Flags flags -> bold "--"^get_long_form flags + | Option o -> bold o + | Verbatim s -> s + +let newer_flag_msg cli valid_since target = + Printf.sprintf + "%s was added in version %s of the opam CLI, \ + but version %s has been requested, which is older." + target (OpamCLIVersion.to_string valid_since) + (string_of_sourced_cli cli) + +let newer_flag_error cli valid_since targets = + let target = string_of_target targets in + let msg = newer_flag_msg cli valid_since target in + `Error (false, msg) + +let previously_str removal instead = + let previous = + string_of_cli_option (OpamCLIVersion.previous removal) + in + match instead with + | Some ist -> + Printf.sprintf ". Use %s instead or %s" + (bold ist) previous + | None -> Printf.sprintf ", %s" previous + +let older_flag_msg cli removal instead target = + Printf.sprintf + "%s was removed in version %s of the opam CLI, \ + but version %s has been requested%s." + target (OpamCLIVersion.to_string removal) + (string_of_sourced_cli cli) + (previously_str removal instead) + +let older_flag_error cli removal instead targets = + let target = string_of_target targets in + let msg = older_flag_msg cli removal instead target in + `Error (false, msg) + +let deprecated_warning removal instead targets = + let target = string_of_target targets in + OpamConsole.warning + "%s was deprecated in version %s of the opam CLI%s." + target (OpamCLIVersion.to_string removal) + (previously_str removal instead) + +(* Cli version check *) +let cond_new cli c = cli @< c +let cond_removed cli removal = cli @>= removal + +let check_cli_validity_t ~newer ~default_cli ~older ~valid ?(cond=fun x -> x) + cli = function + | { removed = None ; valid = c; _ } when cond (cond_new cli c) -> + newer c + | { removed = Some (removal, instead); default = true; _ } + when (snd cli = `Default) && OpamCLIVersion.default < removal -> + (* default cli case : we dont even check if the condition is required *) + default_cli removal instead + | { removed = Some (removal, instead); _ } + when cond (cond_removed cli removal) -> + older removal instead + | _ -> valid () + +let check_cli_validity cli validity ?cond elem targets = + check_cli_validity_t cli validity ?cond + ~newer:(fun c -> + newer_flag_error cli c targets) + ~default_cli:(fun removal instead -> + deprecated_warning removal instead targets; + `Ok elem) + ~older:(fun removal instead -> + older_flag_error cli removal instead targets) + ~valid:(fun () -> `Ok elem) + +let term_cli_check ~check arg = + Term.(ret ((const check) $ (Arg.value arg))) + +(* Arguments *) + +let mk_flag ~cli validity ?section flags doc = + let doc = update_doc_w_cli doc ~cli validity in + let doc = Arg.info ?docs:section ~doc flags in + let check elem = + check_cli_validity cli validity ~cond:(fun c -> c && elem) elem + (Flags flags) + in + term_cli_check ~check Arg.(flag & doc) + +let mk_opt ~cli validity ?section ?vopt flags value doc kind default = + let doc = update_doc_w_cli doc ~cli validity in + let doc = Arg.info ?docs:section ~docv:value ~doc flags in + let check elem = + check_cli_validity cli validity + ~cond:(fun c -> c && default <> elem) elem (Flags flags) + in + term_cli_check ~check Arg.(opt ?vopt kind default & doc) + +let mk_opt_all ~cli validity ?section ?vopt ?(default=[]) + flags value doc kind = + let doc = update_doc_w_cli doc ~cli validity in + let doc = Arg.info ?docs:section ~docv:value ~doc flags in + let check elem = + check_cli_validity cli validity + ~cond:(fun c -> c && default <> elem) elem (Flags flags) + in + term_cli_check ~check Arg.(opt_all ?vopt kind default & doc) + +let mk_vflag ~cli ?section default flags = + let flags = List.map (fun (v,c,f,d) -> contented_validity v c, f, d) flags in + let info_flags = + List.map (fun (validity, flag, doc) -> + let doc = update_doc_w_cli doc ~cli validity in + validity.content, Arg.info ?docs:section flag ~doc) + flags + in + let check elem = + match + OpamStd.List.find_opt (fun (validity, _, _) -> + validity.content = elem) + flags + with + | Some (validity, flags, _) -> + check_cli_validity cli validity (elem_of_vr elem) (Flags flags) + | None -> `Ok (elem_of_vr elem) + in + term_cli_check ~check Arg.(vflag (Valid default) info_flags) + +let mk_flag_replaced ~cli ?section flags doc = + let flags = List.map (fun (c,f) -> c, true, f, doc) flags in + mk_vflag ~cli ?section false flags + +let mk_vflag_all ~cli ?section ?(default=[]) flags = + let flags = List.map (fun (v,c,f,d) -> contented_validity v c, f, d) flags in + let info_flags = + List.map (fun (validity, flag, doc) -> + let doc = update_doc_w_cli doc ~cli validity in + validity.content, Arg.info ?docs:section flag ~doc) + flags + in + let check selected = + let newer_cli, older_cli = + List.fold_left (fun (newer_cli,older_cli as acc) elem -> + match OpamStd.List.find_opt (fun (validity, _, _) -> + validity.content = elem) flags with + | Some (validity, flags, _) -> + check_cli_validity_t cli validity + ~newer:(fun c -> (flags, c)::newer_cli, older_cli) + ~default_cli:(fun _ _ -> acc) + ~older:(fun removal instead -> + newer_cli, (flags, (removal, instead))::older_cli) + ~valid:(fun () -> acc) + | None -> acc) + ([],[]) selected + in + let max_cli clis = + OpamCLIVersion.to_string @@ + match clis with + | [] -> assert false + | c::cl -> List.fold_left max c cl + in + let string_of_options options = + OpamStd.Format.pretty_list + (List.map (fun o -> string_of_target (Flags o)) options) + in + match newer_cli, older_cli with + | [], [] -> `Ok (List.map elem_of_vr selected) + | [flags, c], [] -> + newer_flag_error cli c (Flags flags) + | [], [flags, (c, instead)] -> + older_flag_error cli c instead (Flags flags) + | _::_, []-> + let options, clis = List.split newer_cli in + let msg = + Printf.sprintf + "%s can only be used with at least version %s of the opam \ + CLI, but version %s has been requested." + (string_of_options options) + (max_cli clis) + (string_of_sourced_cli cli) + in + `Error (false, msg) + | [], _::_-> + let options, clis = List.split older_cli in + let clis = List.split clis |> fst in + let in_all = + match clis with + | c::cs when List.for_all ((=) c) cs -> Some c + | _ -> None + in + let msg = + Printf.sprintf + "%s %swere all removed by version %s of the opam CLI, \ + but version %s has been requested." + (string_of_options options) + (OpamStd.Option.to_string + (OpamCLIVersion.to_string + @> Printf.sprintf "were all in %s, and ") in_all) + (max_cli clis) + (string_of_sourced_cli cli) + in + `Error (false, msg) + | _,_ -> + let newer, nclis = List.split newer_cli in + let older, rclis_ist = List.split older_cli in + let rclis, insteads = List.split rclis_ist in + let msg = + if List.for_all ((<>) None) insteads then + Printf.sprintf + "This combination of options is not possible: %s require \ + at least version %s of the opam CLI and the newer %s \ + flags must be used for %s respectively!" + (string_of_options newer) (max_cli nclis) + (string_of_options older) + (OpamStd.Format.pretty_list + (List.map (function Some f -> f | None -> assert false) + insteads)) + else + Printf.sprintf + "This combination of options is not possible: %s require \ + at least version %s of the opam CLI but %s were all \ + removed by version %s of the opam CLI!" + (string_of_options newer) (max_cli nclis) + (string_of_options older) (max_cli rclis) + in + `Error (false, msg) + in + let default = List.map (fun x -> Valid x) default in + term_cli_check ~check Arg.(vflag_all default info_flags) + +let string_of_enum enum = + Arg.doc_alts_enum (List.map (fun (_, s, v) -> s,v) enum) + +let mk_enum_opt ~cli validity ?section flags value states doc = + let doc = update_doc_w_cli doc ~cli validity in + let doc = Arg.info ?docs:section ~docv:value ~doc flags in + let check elem = + (* first check validity of flag *) + let flag_validity = + check_cli_validity cli validity ~cond:(fun c -> c && elem <> None) + elem (Flags flags) + in + (* then check validity of the argument *) + match flag_validity with + | `Ok (Some elem) -> + let validity, str, _ = List.find (fun (_,_,v) -> v = elem) states in + check_cli_validity cli validity (Some elem) + (Verbatim + (Printf.sprintf "the %s option for %s" + (bold str) (bold "--"^get_long_form flags))) + | _ -> flag_validity + in + let states = List.map (fun (_, s, v) -> s,v) states in + term_cli_check ~check Arg.(opt (some (enum states)) None & doc) + +let mk_enum_opt_all ~cli validity ?section flags value states doc = + let doc = update_doc_w_cli doc ~cli validity in + let doc = Arg.info ?docs:section ~docv:value ~doc flags in + let check elems = + (* first check validity of flag *) + let flag_validity = + check_cli_validity cli validity ~cond:(fun c -> c && elems <> []) + elems (Flags flags) + in + (* then check validity of the argument *) + match flag_validity with + | `Error _ -> flag_validity + | `Ok elems -> + let newer_cli, older_cli, valid = + List.fold_left (fun (newer_cli,older_cli,valid) elem -> + let (validity, str, _) = + List.find (fun (_,_,v) -> v = elem) states + in + check_cli_validity_t cli validity + ~newer:(fun c -> (str, c)::newer_cli, older_cli, valid) + ~default_cli:(fun _ _ -> newer_cli, older_cli, elem::valid) + ~older:(fun removal instead -> + newer_cli, (str, (removal, instead))::older_cli, valid) + ~valid:(fun () -> newer_cli, older_cli, elem::valid)) + ([],[],[]) elems + in + let max_cli clis = + OpamCLIVersion.to_string @@ + match clis with + | [] -> assert false + | c::cl -> List.fold_left max c cl + in + let long_form_flags = "--"^get_long_form flags in + let to_str states = + Printf.sprintf "the option%s %s for %s" + (match states with [_] -> "s" | _ -> "") + (bold @@ OpamStd.Format.pretty_list states) + (bold long_form_flags) + in + match newer_cli, older_cli, List.rev valid with + | [], [], elems -> `Ok elems + | [str, c], [], [] -> + newer_flag_error cli c (Verbatim (to_str [str])) + | [str, c], [], elems -> + (OpamConsole.warning "%s" + (newer_flag_msg cli c (to_str [str])); + `Ok elems) + | [], [str, (c, instead)], [] -> + older_flag_error cli c instead (Verbatim (to_str [str])) + | [], [str, (c, instead)], elems -> + (OpamConsole.warning "%s" + (older_flag_msg cli c instead (to_str [str])); + `Ok elems) + | _::_, [], elems -> + let states, clis = List.split newer_cli in + let msg = + Printf.sprintf + "%s can only be used with at least version %s of the opam \ + CLI, but version %s has been requested." + (to_str states) (max_cli clis) + (string_of_sourced_cli cli) + in + if elems = [] then `Error (false, msg) else + (OpamConsole.warning "%s" msg; `Ok elems) + | [], _::_, elems-> + let states, clis = List.split older_cli in + let clis = List.split clis |> fst in + let in_all = + match clis with + | c::cs when List.for_all ((=) c) cs -> Some c + | _ -> None + in + let msg = + Printf.sprintf + "%s %swere all removed by version %s of the opam CLI, \ + but version %s has been requested." + (to_str states) + (OpamStd.Option.to_string + (OpamCLIVersion.to_string + @> Printf.sprintf "were all in %s, and ") in_all) + (max_cli clis) + (string_of_sourced_cli cli) + in + if elems = [] then `Error (false, msg) else + (OpamConsole.warning "%s" msg; `Ok elems) + | _, _, elems -> + let newer, nclis = List.split newer_cli in + let older, rclis_ist = List.split older_cli in + let rclis, insteads = List.split rclis_ist in + let msg = + if List.for_all ((<>) None) insteads then + Printf.sprintf + "This combination of %s options is not possible: %s require \ + at least version %s of the opam CLI and the newer %s \ + flags must be used for %s respectively!" + (bold long_form_flags) + (bold @@ OpamStd.Format.pretty_list newer) + (max_cli nclis) + (bold @@ OpamStd.Format.pretty_list older) + (OpamStd.Format.pretty_list + (List.map (function Some f -> f | None -> assert false) + insteads)) + else + Printf.sprintf + "This combination of %s options is not possible: %s require \ + at least version %s of the opam CLI but %s were all \ + removed by version %s of the opam CLI!" + (bold long_form_flags) + (bold @@ OpamStd.Format.pretty_list newer) (max_cli nclis) + (bold @@ OpamStd.Format.pretty_list older) (max_cli rclis) + in + if elems = [] then `Error (false, msg) else + (OpamConsole.warning "%s" msg; `Ok elems) + in + let states = List.map (fun (_, s, v) -> s,v) states in + term_cli_check ~check Arg.(opt_all (enum states) [] & doc) + +(* Subcommands *) +type 'a subcommand = validity * string * 'a * string list * string +type 'a subcommands = 'a subcommand list + +let mk_subdoc ~cli ?(defaults=[]) commands = + let bold s = Printf.sprintf "$(b,%s)" s in + let it s = Printf.sprintf "$(i,%s)" s in + `S Manpage.s_commands :: + (List.map (function + | "", name -> + `P (Printf.sprintf "Without argument, defaults to %s." + (bold name)) + | arg, default -> + `I (it arg, Printf.sprintf "With a %s argument, defaults to %s %s." + (it arg) (bold default) (it arg)) + ) defaults) @ + List.map (fun (validity, c, _, args,d) -> + let cmds = bold c ^ " " ^ OpamStd.List.concat_map " " it args in + let d = update_doc_w_cli d ~cli validity in + `I (cmds, d) + ) commands + +let mk_subcommands_aux ~cli my_enum commands = + let commands = + List.map (fun (v,n,c,a,d) -> contented_validity v c, n, a, d) commands + in + let command = + let doc = Arg.info ~docv:"COMMAND" [] in + let scommand = List.rev_map (fun (v,f,_,_) -> f,v.content) commands in + let check = function + | None -> `Ok None + | Some elem -> + match OpamStd.List.find_opt (fun (validity, _, _, _) -> + validity.content = elem) commands with + | Some (validity, sbcmd, _,_) -> + check_cli_validity cli validity (Some (elem_of_vr elem)) + (Option sbcmd) + | None -> `Ok (Some (elem_of_vr elem)) + in + + term_cli_check ~check Arg.(pos 0 (some & my_enum scommand) None & doc) + in + let params = + let doc = Arg.info ~doc:"Optional parameters." [] in + Arg.(value & pos_right 0 string [] & doc) + in + command, params + +let mk_subcommands ~cli commands = + mk_subcommands_aux ~cli Arg.enum commands + +type 'a default = [> `default of string] as 'a + +let mk_subcommands_with_default ~cli commands = + let enum_with_default_valrem sl = + let parse, print = Arg.enum sl in + let parse s = + match parse s with + | `Ok x -> `Ok (x) + | _ -> `Ok (Valid (`default s)) in + parse, print + in + mk_subcommands_aux ~cli enum_with_default_valrem commands + +let bad_subcommand ~cli subcommands (command, usersubcommand, userparams) = + match usersubcommand with + | None -> + `Error (false, + Printf.sprintf "Missing subcommand. Valid subcommands are %s." + (OpamStd.Format.pretty_list + (OpamStd.List.filter_map (fun (validity,sb,_,_,_) -> + match validity with + | {valid = c; removed = None; _} when cli @>= c -> None + | {removed = Some (c,_); _} when cli @< c -> None + | _ -> Some sb) + subcommands))) + | Some (`default cmd) -> + `Error (true, Printf.sprintf "Invalid %s subcommand %S" command cmd) + | Some usersubcommand -> + let exe = Filename.basename Sys.executable_name in + match + List.find_all (fun (_,_,cmd,_,_) -> + cmd = usersubcommand) subcommands + with + | [ _, name, _, args, _doc] -> + let usage = + Printf.sprintf "%s %s [OPTION]... %s %s" + exe command name (String.concat " " args) in + if List.length userparams < List.length args then + `Error (false, Printf.sprintf "%s: Missing argument.\nUsage: %s\n" + exe usage) + else + `Error (false, Printf.sprintf "%s: Too many arguments.\nUsage: %s\n" + exe usage) + | _ -> + `Error (true, Printf.sprintf "Invalid %s subcommand" command) + +(* Commands *) + +type command = unit Term.t * Term.info + +(* As [term_info] is defined later, we need to have it as argument *) +let mk_command ~cli validity term_info name ~doc ~man cmd = + let doc = update_doc_w_cli doc ~cli validity in + let info = term_info ~cli name ~doc ~man in + let check = + check_cli_validity cli validity () (Option name) + |> Term.const + |> Term.ret + in + Term.(cmd $ check), info + +let mk_command_ret ~cli validity term_info name ~doc ~man cmd = + let doc = update_doc_w_cli doc ~cli validity in + let info = term_info ~cli name ~doc ~man in + let check = + check_cli_validity cli validity () (Option name) + |> Term.const + |> Term.ret + in + Term.(ret (cmd $ check)), info + +(* Environment variables *) + +let check_cli_env_validity cli validity var cons = + let is_defined () = OpamStd.Config.env (fun x -> x) var <> None in + let ovar = "OPAM"^var in + match validity with + | { removed = None ; valid = c; _ } when cond_new cli c -> + if is_defined () then + OpamConsole.warning + "%s was ignored because CLI %s \ + was requested and it was introduced in %s." + ovar (string_of_sourced_cli cli) (OpamCLIVersion.to_string c); + None + | { removed = Some (removal, instead); _ } when cond_removed cli removal -> + if is_defined () then + OpamConsole.warning + "%s was ignored because CLI %s \ + was requested and it was removed in %s%s." + ovar (string_of_sourced_cli cli) (OpamCLIVersion.to_string removal) + (previously_str removal instead); + None + | _ -> Some (cons var) + +let env_with_cli environment = + let doc_env cli = + List.map (fun (var, validity, _cons, doc) -> + let doc = update_doc_w_cli doc ~cli validity in + `P (Printf.sprintf "$(i,OPAM%s) %s" var doc)) + environment + in + let init_env cli = + OpamStd.List.filter_map (fun (var, validity, cons, _doc) -> + check_cli_env_validity cli validity var cons) + environment + |> OpamStd.Config.E.updates + in + doc_env, init_env diff -Nru opam-2.0.10/src/client/opamArgTools.mli opam-2.1.2/src/client/opamArgTools.mli --- opam-2.0.10/src/client/opamArgTools.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamArgTools.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,112 @@ +(**************************************************************************) +(* *) +(* Copyright 2021 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** CLI version helpers *) + +(* Defines Cmdliner optional argument function-helpers, with the cli + version. *) + +open Cmdliner + +type validity + +val cli_from: OpamCLIVersion.t -> validity +val cli_between: + OpamCLIVersion.t -> ?default:bool -> ?replaced:string -> + OpamCLIVersion.t -> validity +val cli_original: validity + +val cli2_0: OpamCLIVersion.t +val cli2_1: OpamCLIVersion.t + +val mk_flag: + cli:OpamCLIVersion.Sourced.t -> validity -> ?section:string -> string list -> + string -> bool Term.t + +val mk_flag_replaced: + cli:OpamCLIVersion.Sourced.t -> ?section:string -> + (validity * string list) list -> + string -> bool Term.t + +val mk_opt: + cli:OpamCLIVersion.Sourced.t -> validity -> ?section:string -> ?vopt:'a -> + string list -> string -> string -> 'a Arg.converter -> 'a -> 'a Term.t + +val mk_opt_all: + cli:OpamCLIVersion.Sourced.t -> validity -> ?section:string -> ?vopt:'a -> + ?default:'a list -> string list -> string -> string -> 'a Arg.converter -> + 'a list Term.t + +val mk_vflag: + cli:OpamCLIVersion.Sourced.t -> ?section:string -> 'a -> + (validity * 'a * string list * string) list -> 'a Term.t + +val mk_vflag_all: + cli:OpamCLIVersion.Sourced.t -> ?section:string -> ?default:'a list -> + (validity * 'a * string list * string) list -> 'a list Term.t + +val mk_enum_opt: + cli:OpamCLIVersion.Sourced.t -> validity -> ?section:string -> string list -> + string -> (validity * string * 'a) list -> string -> 'a option Term.t + +(** [opt_all] with enums. Check each flag content cli, purge non corresponding + ones from the final result. If after purge the resulting list is empty (all + removed or newer flag contents), it raises an error ; otherwise only + display warnings on wrong cli contents. *) +val mk_enum_opt_all: + cli:OpamCLIVersion.Sourced.t -> validity -> ?section:string -> string list -> + string -> (validity * string * 'a) list -> string -> 'a list Term.t + +val string_of_enum: (validity * string * 'a) list -> string + +type 'a subcommand = validity * string * 'a * string list * string +type 'a subcommands = 'a subcommand list + +val mk_subcommands: + cli:OpamCLIVersion.Sourced.t -> 'a subcommands -> + 'a option Term.t * string list Term.t + +type 'a default = [> `default of string] as 'a + +val mk_subcommands_with_default: + cli:OpamCLIVersion.Sourced.t -> 'a default subcommands -> + 'a option Term.t * string list Term.t + +val bad_subcommand: + cli:OpamCLIVersion.Sourced.t -> 'a default subcommands -> + (string * 'a option * string list) -> 'b Term.ret + +val mk_subdoc : + cli:OpamCLIVersion.Sourced.t -> ?defaults:(string * string) list -> + 'a subcommands -> Manpage.block list + +type command = unit Term.t * Term.info + +val mk_command: + cli:OpamCLIVersion.Sourced.t -> validity -> + (cli:OpamCLIVersion.Sourced.t -> string -> doc:string -> + man:Manpage.block list -> Term.info) -> + string -> doc:string -> + man:Manpage.block list -> (unit -> unit) Term.t -> command + +val mk_command_ret: + cli:OpamCLIVersion.Sourced.t -> validity -> + (cli:OpamCLIVersion.Sourced.t -> string -> doc:string -> + man:Manpage.block + list -> Term.info) -> + string -> doc:string -> man:Manpage.block list -> + (unit -> unit Term.ret) Term.t -> command + +val env_with_cli: + (string * validity * (string -> OpamStd.Config.E.t) * string) list -> + (OpamCLIVersion.Sourced.t -> Manpage.block list) * + (OpamCLIVersion.Sourced.t -> unit) + +val is_original_cli: validity -> bool diff -Nru opam-2.0.10/src/client/opamAuxCommands.ml opam-2.1.2/src/client/opamAuxCommands.ml --- opam-2.0.10/src/client/opamAuxCommands.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAuxCommands.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -88,9 +88,9 @@ | None -> url) | url -> url -let opams_of_dir d = - let files = OpamPinned.files_in_source d in - List.fold_left (fun acc (n, f) -> +let opams_of_dir ?recurse ?subpath d = + let files = OpamPinned.files_in_source ?recurse ?subpath d in + List.fold_left (fun acc (n, f, subp) -> let name = let open OpamStd.Option.Op in n >>+ fun () -> @@ -101,7 +101,7 @@ | [_] -> name_from_project_dirname d in match name with - | Some n -> (n, f) :: acc + | Some n -> (n, f, subp) :: acc | None -> OpamConsole.warning "Ignoring file at %s: could not infer package name" @@ -109,6 +109,42 @@ acc) [] files +let opams_of_dir_w_target ?(recurse=false) ?subpath + ?(same_kind=fun _ -> OpamClientConfig.(!r.pin_kind_auto)) url dir = + OpamStd.List.filter_map (fun (name, file, subp) -> + let url = + match url.OpamUrl.backend with + | #OpamUrl.version_control as vc -> + let module VCS = + (val match vc with + | `git -> (module OpamGit.VCS: OpamVCS.VCS) + | `hg -> (module OpamHg.VCS: OpamVCS.VCS) + | `darcs -> (module OpamDarcs.VCS: OpamVCS.VCS) + : OpamVCS.VCS) + in + let open OpamProcess.Job.Op in + let versioned_files = + OpamProcess.Job.run @@ + VCS.versioned_files dir @@| fun files -> files + in + let opamfile = + OpamFilename.remove_prefix dir (OpamFile.filename file) + in + if List.mem opamfile versioned_files + || not (OpamStd.String.contains opamfile ~sub:Filename.dir_sep) then + url + else + { url with + transport = "file"; + hash = None; + backend = `rsync } + | _ -> url + in + if same_kind url then + Some (name, file, url, subp) + else None) + (opams_of_dir ~recurse ?subpath dir) + let name_and_dir_of_opam_file f = let srcdir = OpamFilename.dirname f in let srcdir = @@ -126,13 +162,29 @@ in name, srcdir -let resolve_locals_pinned st atom_or_local_list = +let resolve_locals_pinned st ?(recurse=false) ?subpath atom_or_local_list = let pinned_packages_of_dir st dir = OpamPackage.Set.filter (fun nv -> - OpamStd.Option.Op.(OpamSwitchState.primary_url st nv >>= - OpamUrl.local_dir) - = Some dir) + let open OpamStd.Option.Op in + match subpath with + | Some sp -> + let dir_sp = OpamFilename.Op.(dir / sp) in + let url_sp_dir = + OpamSwitchState.primary_url_with_subpath st nv >>= OpamUrl.local_dir + in + if recurse then + (url_sp_dir >>| OpamFilename.dir_starts_with dir_sp) +! false + else + url_sp_dir = Some dir_sp + | None -> + if recurse then + (OpamSwitchState.primary_url st nv >>= OpamUrl.local_dir) + = Some dir + else + (OpamSwitchState.primary_url_with_subpath st nv >>= OpamUrl.local_dir) + = Some dir + ) st.pinned in let atoms = @@ -143,7 +195,7 @@ if OpamPackage.Set.is_empty pkgs then OpamConsole.warning "No pinned packages found at %s" (OpamFilename.Dir.to_string d); - List.rev_append (OpamSolution.eq_atoms_of_packages pkgs) acc + List.rev_append (OpamSolution.atoms_of_packages pkgs) acc | `Filename f -> OpamConsole.error_and_exit `Bad_arguments "This command doesn't support specifying a file name (%S)" @@ -152,33 +204,33 @@ in List.rev atoms -let resolve_locals ?(quiet=false) atom_or_local_list = +let resolve_locals ?(quiet=false) ?recurse ?subpath atom_or_local_list = let target_dir dir = let d = OpamFilename.Dir.to_string dir in let backend = OpamUrl.guess_version_control d in - OpamUrl.parse ?backend d |> + OpamUrl.parse ?backend ~from_file:false d |> url_with_local_branch in let to_pin, atoms = List.fold_left (fun (to_pin, atoms) -> function | `Atom a -> to_pin, a :: atoms | `Dirname d -> - let names_files = opams_of_dir d in + let target = target_dir d in + let names_files = opams_of_dir_w_target ?recurse ?subpath target d in if names_files = [] && not quiet then OpamConsole.warning "No package definitions found at %s" (OpamFilename.Dir.to_string d); - let target = target_dir d in let to_pin = - List.map (fun (n,f) -> n, target, f) names_files @ to_pin + List.map (fun (n,f,u,b) -> n, u, b, f) names_files @ to_pin in let atoms = - List.map (fun (n,_) -> n, None) names_files @ atoms + List.map (fun (n,_,_,_) -> n, None) names_files @ atoms in to_pin, atoms | `Filename f -> match name_and_dir_of_opam_file f with | Some n, srcdir -> - (n, target_dir srcdir, OpamFile.make f) :: to_pin, + (n, target_dir srcdir, None, OpamFile.make f) :: to_pin, (n, None) :: atoms | None, _ -> OpamConsole.error_and_exit `Not_found @@ -188,8 +240,8 @@ atom_or_local_list in let duplicates = - List.filter (fun (n, _, f) -> - List.exists (fun (n1, _, f1) -> n = n1 && f <> f1) to_pin) + List.filter (fun (n, _, _, f) -> + List.exists (fun (n1, _, _, f1) -> n = n1 && f <> f1) to_pin) to_pin in match duplicates with @@ -197,7 +249,7 @@ | _ -> OpamConsole.error_and_exit `Bad_arguments "Multiple files for the same package name were specified:\n%s" - (OpamStd.Format.itemize (fun (n, t, f) -> + (OpamStd.Format.itemize (fun (n, t, _, f) -> Printf.sprintf "Package %s with definition %s %s %s" (OpamConsole.colorise `bold @@ OpamPackage.Name.to_string n) (OpamFile.to_string f) @@ -205,43 +257,52 @@ (OpamUrl.to_string t)) duplicates) -let autopin_aux st ?quiet ?(for_view=false) atom_or_local_list = - let to_pin, atoms = resolve_locals ?quiet atom_or_local_list in +let autopin_aux st ?quiet ?(for_view=false) ?recurse ?subpath atom_or_local_list = + let to_pin, atoms = resolve_locals ?quiet ?recurse ?subpath atom_or_local_list in if to_pin = [] then atoms, to_pin, OpamPackage.Set.empty, OpamPackage.Set.empty else let pinning_dirs = OpamStd.List.filter_map (function - | `Dirname d -> Some d + | `Dirname d -> + (match subpath with + | Some s -> Some OpamFilename.Op.(d/s) + | None -> Some d) | _ -> None) atom_or_local_list in log "autopin: %a" - (slog @@ OpamStd.List.to_string (fun (name, target, _) -> - Printf.sprintf "%s => %s" + (slog @@ OpamStd.List.to_string (fun (name, target, subpath, _) -> + Printf.sprintf "%s => %s%s" (OpamPackage.Name.to_string name) - (OpamUrl.to_string target))) + (OpamUrl.to_string target) + (match subpath with None -> "" | Some s -> " ("^s^")"))) to_pin; let obsolete_pins = (* Packages not current but pinned to the same dirs *) OpamPackage.Set.filter (fun nv -> - not (List.exists (fun (n,_,_) -> n = nv.name) to_pin) && - match OpamStd.Option.Op.(OpamSwitchState.primary_url st nv >>= - OpamUrl.local_dir) - with - | Some d -> List.mem d pinning_dirs + not (List.exists (fun (n,_,_,_) -> n = nv.name) to_pin) && + let primary_url = + if recurse = Some true then + OpamSwitchState.primary_url + else + OpamSwitchState.primary_url_with_subpath + in + match OpamStd.Option.Op.( primary_url st nv >>= OpamUrl.local_dir) with + | Some d -> + List.mem d pinning_dirs | None -> false) st.pinned in let already_pinned, to_pin = - List.partition (fun (name, target, opam) -> + List.partition (fun (name, target, _, opam) -> try (* check of the target to avoid repin of pin to update with `opam install .` and loose edited opams *) let pinned_pkg = OpamPinned.package st name in OpamSwitchState.primary_url st pinned_pkg = Some target && - (* For `opam show`, we need to check is the opam file changed to + (* For `opam show`, we need to check does the opam file changed to perform a simulated pin if so *) (not for_view || match @@ -253,7 +314,7 @@ to_pin in let already_pinned_set = - List.fold_left (fun acc (name, _, _) -> + List.fold_left (fun acc (name, _, _, _) -> OpamPackage.Set.add (OpamPinned.package st name) acc) OpamPackage.Set.empty already_pinned in @@ -263,12 +324,12 @@ assert (not (for_view && OpamSystem.get_lock_flag st.switch_lock = `Lock_write)); let local_names = - List.fold_left (fun set (name, _, _) -> + List.fold_left (fun set (name, _, _, _) -> OpamPackage.Name.Set.add name set) OpamPackage.Name.Set.empty to_pin in let local_opams = - List.fold_left (fun map (name, target, file) -> + List.fold_left (fun map (name, target, subpath, file) -> match OpamPinCommand.read_opam_file_for_pinning ?quiet name file target with @@ -276,8 +337,8 @@ | Some opam -> let opam = OpamFile.OPAM.with_name name opam in let opam = - if for_view then opam - else OpamFile.OPAM.with_url (OpamFile.URL.create target) opam + if for_view then opam else + OpamFile.OPAM.with_url (OpamFile.URL.create ?subpath target) opam in let opam, version = match OpamFile.OPAM.version_opt opam with | Some v -> opam, v @@ -320,9 +381,9 @@ } in st, local_packages -let simulate_autopin st ?quiet ?(for_view=false) atom_or_local_list = +let simulate_autopin st ?quiet ?(for_view=false) ?recurse ?subpath atom_or_local_list = let atoms, to_pin, obsolete_pins, already_pinned_set = - autopin_aux st ?quiet ~for_view atom_or_local_list + autopin_aux st ?quiet ~for_view ?recurse ?subpath atom_or_local_list in if to_pin = [] then st, atoms else let st = @@ -352,12 +413,12 @@ OpamConsole.msg "\n")); st, atoms -let autopin st ?(simulate=false) ?quiet atom_or_local_list = +let autopin st ?(simulate=false) ?quiet ?recurse ?subpath atom_or_local_list = if OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show) then simulate_autopin st ?quiet atom_or_local_list else let atoms, to_pin, obsolete_pins, already_pinned_set = - autopin_aux st ?quiet atom_or_local_list + autopin_aux st ?quiet ?recurse ?subpath atom_or_local_list in if to_pin = [] && OpamPackage.Set.is_empty obsolete_pins && OpamPackage.Set.is_empty already_pinned_set @@ -371,26 +432,29 @@ (OpamPackage.Name.Set.elements (OpamPackage.names_of_packages obsolete_pins)) in - let st = - let working_dir = - if OpamClientConfig.(!r.working_dir) then already_pinned_set - else OpamPackage.Set.empty - in - let _result, st, _updated = - OpamUpdate.dev_packages st ~working_dir already_pinned_set - in - st + let already_pinned_diff_url = + (* is pinned but no in already pinned because not same url *) + List.fold_left (fun set (n,_,_,_) -> + match + OpamStd.Option.map + (fun nv -> OpamPackage.Set.mem nv already_pinned_set) + (OpamPinned.package_opt st n) + with + | Some false -> OpamPackage.Name.Set.add n set + | _ -> set + ) OpamPackage.Name.Set.empty to_pin in + let st, pins = if simulate then simulate_local_pinnings ?quiet st to_pin else try - List.fold_left (fun (st, pins) (name, target, file) -> + List.fold_left (fun (st, pins) (name, target, subpath, file) -> match OpamPinCommand.read_opam_file_for_pinning ?quiet name file target with | None -> st, pins | Some opam -> let st = try - OpamPinCommand.source_pin st name ~quiet:true ~opam + OpamPinCommand.source_pin st name ~quiet:true ~opam ?subpath (Some target) with OpamPinCommand.Nothing_to_do -> st in @@ -399,165 +463,85 @@ with OpamPinCommand.Aborted -> OpamStd.Sys.exit_because `Aborted in + let _result, st, _updated = + let already_pinned = + OpamPackage.Set.union already_pinned_set + (OpamPackage.packages_of_names pins already_pinned_diff_url) + in + if OpamClientConfig.(!r.working_dir || !r.inplace_build) then + OpamUpdate.dev_packages st ~working_dir:pins pins + else + OpamUpdate.dev_packages st ~working_dir:OpamPackage.Set.empty already_pinned + in let st = if OpamClientConfig.(!r.ignore_pin_depends) then st else - OpamPackage.Set.fold (fun nv st -> - OpamPinCommand.handle_pin_depends st nv (OpamSwitchState.opam st nv)) - (OpamPackage.Set.union pins already_pinned_set) st + OpamPackage.Set.fold (fun nv st -> + OpamPinCommand.handle_pin_depends st nv (OpamSwitchState.opam st nv)) + (OpamPackage.Set.union pins already_pinned_set) st in st, atoms -let get_compatible_compiler ?repos rt dir = - let gt = rt.repos_global in - let virt_st = - OpamSwitchState.load_virtual ?repos_list:repos gt rt - in - let local_files = opams_of_dir dir in - let local_opams = - List.fold_left (fun acc (name, f) -> - let opam = OpamFile.OPAM.safe_read f in - let opam = OpamFormatUpgrade.opam_file ~filename:f opam in - let nv, opam = - match OpamFile.OPAM.version_opt opam with - | Some v -> OpamPackage.create name v, opam - | None -> - let v = OpamPinCommand.default_version virt_st name in - OpamPackage.create name v, - OpamFile.OPAM.with_version v opam - in - OpamPackage.Map.add nv opam acc) - OpamPackage.Map.empty - local_files - in - let local_packages = OpamPackage.keys local_opams in - let pin_depends = - OpamPackage.Map.fold (fun _nv opam acc -> - List.fold_left (fun acc (nv,_) -> OpamPackage.Set.add nv acc) - acc (OpamFile.OPAM.pin_depends opam)) - local_opams OpamPackage.Set.empty - in - let virt_st = - let opams = - OpamPackage.Map.union (fun _ x -> x) virt_st.opams local_opams +let check_and_revert_sandboxing root config = + let sdbx_wrappers = + let w = OpamFile.Config.wrappers config in + let init_sdbx_cmds = + List.map (function `build cmd | `install cmd | `remove cmd -> cmd) + OpamInitDefaults.sandbox_wrappers + |> List.flatten in - let available = lazy ( - OpamPackage.Map.filter (fun package opam -> - OpamFilter.eval_to_bool ~default:false - (OpamPackageVar.resolve_switch_raw ~package gt - (OpamSwitch.of_dirname dir) - OpamFile.Switch_config.empty) - (OpamFile.OPAM.available opam)) - opams - |> OpamPackage.keys - ) in - let open OpamPackage.Set.Op in - { virt_st with - opams = - OpamPackage.Set.fold (fun nv acc -> - OpamPackage.Map.add nv (OpamFile.OPAM.create nv) acc) - pin_depends opams; - packages = - virt_st.packages ++ local_packages ++ pin_depends; - available_packages = - lazy (Lazy.force available ++ local_packages ++ pin_depends); - } - in - let univ = - OpamSwitchState.universe virt_st - ~requested:(OpamPackage.names_of_packages local_packages) - Query - in - (* Find if there is a single possible dependency having Pkgflag_Compiler *) - let alldeps = - OpamSolver.dependencies - ~depopts:false ~build:true ~post:true ~installed:false - univ local_packages + List.filter (fun cmd -> List.mem cmd init_sdbx_cmds) + OpamFile.Wrappers.(wrap_build w @ wrap_install w @ wrap_remove w) in - let compilers = - OpamPackage.Set.filter (fun nv -> - OpamFile.OPAM.has_flag Pkgflag_Compiler - (OpamSwitchState.opam virt_st nv)) - (OpamPackage.Set.of_list alldeps) - in - let installable = - OpamSolver.installable_subset - {univ with u_base = local_packages; u_installed = local_packages} - (OpamPackage.Set.union local_packages compilers) - in - if not (OpamPackage.Set.is_empty local_packages) && - OpamPackage.Set.is_empty installable - then - (OpamConsole.error - "The following local packages don't appear to be installable:\n%s" - (OpamStd.Format.itemize OpamPackage.to_string - (OpamPackage.Set.elements local_packages)); - if OpamConsole.confirm "Do you want to create an empty switch regardless?" - then [], false - else OpamStd.Sys.exit_because `Aborted) - else - let compilers = OpamPackage.Set.inter compilers installable in - try - [OpamSolution.eq_atom_of_package - (OpamPackage.Set.choose_one compilers)], true - with - | Not_found when not (OpamPackage.Set.is_empty local_packages) -> - OpamConsole.warning - "No possible installation was found including a compiler and the \ - selected packages."; - if OpamClientConfig.(!r.show) || - OpamConsole.confirm - "Create the switch with no specific compiler selected, and attempt to \ - continue?" - then [], false - else OpamStd.Sys.exit_because `Aborted - | Failure _ | Not_found -> - (* Find a matching compiler from the default selection *) - let default_compiler = - OpamFile.Config.default_compiler gt.config - in - if default_compiler = Empty then - (OpamConsole.warning "No compiler selected"; [], false) - else - let candidates = OpamFormula.to_dnf default_compiler in - try - OpamStd.List.find_map - (fun atoms -> - let has_all compiler_packages = - List.for_all (fun at -> - OpamPackage.Set.exists (OpamFormula.check at) compiler_packages) - atoms - in - let compiler = - OpamFormula.packages_of_atoms - (Lazy.force virt_st.available_packages) - atoms - in - if not (has_all compiler) then None else - if OpamPackage.Set.is_empty local_packages then - Some (OpamSolution.eq_atoms_of_packages compiler) - else - (* fake universe with `local_packages` as base, just to check - coinstallability *) - let univ = - { univ with u_base = local_packages; u_installed = local_packages } - in - let compiler = OpamSolver.installable_subset univ compiler in - if has_all compiler then - Some (OpamSolution.eq_atoms_of_packages compiler) - else None - ) - candidates, false - with Not_found -> - OpamConsole.warning - "The default compiler selection: %s\n\ - is not compatible with the local packages found at %s, and the \ - packages don't specify an unambiguous compiler.\n\ - You can manually specify a compiler with \ - `opam switch create switch compiler`." - (OpamFormula.to_string default_compiler) - (OpamFilename.Dir.to_string dir); - if OpamConsole.confirm - "You may also proceed, with no specific compiler selected. \ - Do you want to?" - then [], false - else OpamStd.Sys.exit_because `Aborted + let env = fun v -> + let fv = OpamVariable.Full.variable v in + match OpamVariable.Map.find_opt fv (OpamEnv.hook_env root) with + | Some c -> c + | None -> + OpamStd.Option.Op.(OpamStd.Option.of_Not_found (List.assoc fv) + OpamSysPoll.variables >>= Lazy.force) + in + match OpamFilter.commands env sdbx_wrappers with + | [] -> config + | cmd::_ -> + (* All the provided sandboxing scripts are expected to define [TMPDIR] *) + let test_file = "$TMPDIR/opam-sandbox-check-out" in + let test_cmd = + [ "sh"; "-c"; + Printf.sprintf "echo SUCCESS >%s && cat %s; rm -f %s" + test_file test_file test_file ] + in + let working_or_noop = + let env = + Array.append [| "OPAM_SWITCH_PREFIX=/dev/null" |] (Unix.environment ()) + in + try + (* Don't assume that we can mount the CWD *) + OpamSystem.in_tmp_dir @@ fun () -> + OpamSystem.read_command_output ~env ~allow_stdin:false (cmd @ test_cmd) + = ["SUCCESS"] + with e -> + (OpamConsole.error "Sandboxing is not working on your platform%s:\n%s" + (OpamStd.Option.to_string (fun os -> " "^os) + (OpamSysPoll.os_distribution ())) + (Printexc.to_string e); + not (OpamConsole.confirm ~default:false + "Do you want to disable it? Note that this will result in \ + less secure package builds, so please ensure that you have \ + some other isolation mechanisms in place (such as running \ + within a container or virtual machine).")) + in + if working_or_noop then config else + let wrappers = + let filter sdbx_cmd = + List.filter (fun cmd_l -> not (List.mem cmd_l sdbx_cmd)) + in + List.fold_left OpamFile.Wrappers.(fun w -> function + | `build sdbx_build -> + { w with wrap_build = filter sdbx_build w.wrap_build } + | `install sdbx_install -> + { w with wrap_install = filter sdbx_install w.wrap_install } + | `remove sdbx_remove -> + { w with wrap_remove = filter sdbx_remove w.wrap_remove }) + (OpamFile.Config.wrappers config) OpamInitDefaults.sandbox_wrappers + in + OpamFile.Config.with_wrappers wrappers config diff -Nru opam-2.0.10/src/client/opamAuxCommands.mli opam-2.1.2/src/client/opamAuxCommands.mli --- opam-2.0.10/src/client/opamAuxCommands.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamAuxCommands.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -33,15 +33,31 @@ be found, and the corresponding source directory *) val name_and_dir_of_opam_file: filename -> name option * dirname +(** From a directory, retrieve its opam files and returns packages name, opam + file and subpath option *) +val opams_of_dir: + ?recurse:bool -> ?subpath:string -> + OpamFilename.Dir.t -> (name * OpamFile.OPAM.t OpamFile.t * string option) list + +(** Like [opam_of_dirs], but changes the pinning_url if needed. If given [url] + is local dir with vcs backend, and opam files not versioned, its pinning url + is changed to rsync path-pin. If [ame_kind the_new_url] returns true, + package information (name, opam file, new_url, subpath) are added to the + returned list, otherwise it is discarded. *) +val opams_of_dir_w_target: + ?recurse:bool -> ?subpath:string -> + ?same_kind:(OpamUrl.t -> bool) -> OpamUrl.t -> OpamFilename.Dir.t -> + (name * OpamFile.OPAM.t OpamFile.t * OpamUrl.t * string option) list + (** Resolves the opam files and directories in the list to package name and location, and returns the corresponding pinnings and atoms. May fail and exit if package names for provided [`Filename] could not be inferred, or if the same package name appears multiple times. *) val resolve_locals: - ?quiet:bool -> + ?quiet:bool -> ?recurse:bool -> ?subpath:string -> [ `Atom of atom | `Filename of filename | `Dirname of dirname ] list -> - (name * OpamUrl.t * OpamFile.OPAM.t OpamFile.t) list * atom list + (name * OpamUrl.t * string option * OpamFile.OPAM.t OpamFile.t) list * atom list (** Resolves the opam files and directories in the list to package name and location, according to what is currently pinned, and returns the @@ -49,7 +65,7 @@ is pinned, or opam files corresponding to no pinned package. *) val resolve_locals_pinned: - 'a switch_state -> + 'a switch_state -> ?recurse:bool -> ?subpath:string -> [ `Atom of atom | `Dirname of dirname ] list -> atom list @@ -68,6 +84,8 @@ rw switch_state -> ?simulate:bool -> ?quiet:bool -> + ?recurse:bool -> + ?subpath:string -> [ `Atom of atom | `Filename of filename | `Dirname of dirname ] list -> rw switch_state * atom list @@ -81,14 +99,15 @@ 'a switch_state -> ?quiet:bool -> ?for_view:bool -> + ?recurse:bool -> + ?subpath:string -> [ `Atom of atom | `Filename of filename | `Dirname of dirname ] list -> 'a switch_state * atom list -(** Scans for package definition files in a directory, and selects a compiler - that is compatible with them from the configured default compiler list, or - that is unambiguously selected by the package definitions. - Returns the corresponding atoms. If no compiler matches, prints a - warning, and returns the empty list after user confirmation. *) -val get_compatible_compiler: - ?repos:repository_name list -> - 'a repos_state -> dirname -> atom list * bool +(* Check sandboxing script call. If it errors or unattended output, disable + sandboxing by removing [OpamInitDefaults.sandbox_wrappers] commands in + config file. + Only one script is checked (init script default one), and tested on an + `echo SUCCESS' call. *) +val check_and_revert_sandboxing: + OpamPath.t -> OpamFile.Config.t -> OpamFile.Config.t diff -Nru opam-2.0.10/src/client/opamClientConfig.ml opam-2.1.2/src/client/opamClientConfig.ml --- opam-2.0.10/src/client/opamClientConfig.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamClientConfig.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2020 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -8,6 +8,54 @@ (* *) (**************************************************************************) +module E = struct + + type OpamStd.Config.E.t += + | ASSUMEDEPEXTS of bool option + | AUTOREMOVE of bool option + | CLI of string option + | DROPWORKINGDIR of bool option + | EDITOR of string option + | FAKE of bool option + | IGNOREPINDEPENDS of bool option + | INPLACEBUILD of bool option + | JSON of string option + | KEEPBUILDDIR of bool option + | NOAGGREGATE of bool option + | NOAUTOUPGRADE of bool option + | NOSELFUPGRADE of string option + | PINKINDAUTO of bool option + | REUSEBUILDDIR of bool option + | ROOTISOK of bool option + | SHOW of bool option + | SKIPUPDATE of bool option + | STATS of bool option + | WORKINGDIR of bool option + + open OpamStd.Config.E + let assumedepexts = value (function ASSUMEDEPEXTS b -> b | _ -> None) + let autoremove = value (function AUTOREMOVE b -> b | _ -> None) + let cli = value (function CLI s -> s | _ -> None) + let dropworkingdir = value (function DROPWORKINGDIR b -> b | _ -> None) + let editor = value (function EDITOR s -> s | _ -> None) + let fake = value (function FAKE b -> b | _ -> None) + let ignorepindepends = value (function IGNOREPINDEPENDS b -> b | _ -> None) + let inplacebuild = value (function INPLACEBUILD b -> b | _ -> None) + let json = value (function JSON s -> s | _ -> None) + let keepbuilddir = value (function KEEPBUILDDIR b -> b | _ -> None) + let noaggregate = value (function NOAGGREGATE b -> b | _ -> None) + let noautoupgrade = value (function NOAUTOUPGRADE b -> b | _ -> None) + let noselfupgrade = value (function NOSELFUPGRADE s -> s | _ -> None) + let pinkindauto = value (function PINKINDAUTO b -> b | _ -> None) + let reusebuilddir = value (function REUSEBUILDDIR b -> b | _ -> None) + let rootisok = value (function ROOTISOK b -> b | _ -> None) + let show = value (function SHOW b -> b | _ -> None) + let skipupdate = value (function SKIPUPDATE b -> b | _ -> None) + let stats = value (function STATS b -> b | _ -> None) + let workingdir = value (function WORKINGDIR b -> b | _ -> None) + +end + type t = { print_stats: bool; pin_kind_auto: bool; @@ -17,6 +65,7 @@ reuse_build_dir: bool; inplace_build: bool; working_dir: bool; + drop_working_dir: bool; ignore_pin_depends: bool; show: bool; fake: bool; @@ -24,6 +73,9 @@ json_out: string option; root_is_ok: bool; no_auto_upgrade: bool; + assume_depexts: bool; + cli: OpamCLIVersion.t; + scrubbed_environment_variables: string list; } let default = { @@ -35,6 +87,7 @@ reuse_build_dir = false; inplace_build = false; working_dir = false; + drop_working_dir = false; ignore_pin_depends = false; show = false; fake = false; @@ -42,6 +95,9 @@ json_out = None; root_is_ok = false; no_auto_upgrade = false; + assume_depexts = false; + cli = OpamCLIVersion.current; + scrubbed_environment_variables = []; } type 'a options_fun = @@ -53,6 +109,7 @@ ?reuse_build_dir:bool -> ?inplace_build:bool -> ?working_dir:bool -> + ?drop_working_dir:bool -> ?ignore_pin_depends:bool -> ?show:bool -> ?fake:bool -> @@ -60,6 +117,9 @@ ?json_out:string option -> ?root_is_ok:bool -> ?no_auto_upgrade:bool -> + ?assume_depexts:bool -> + ?cli:OpamCLIVersion.t -> + ?scrubbed_environment_variables:string list -> 'a let setk k t @@ -71,6 +131,7 @@ ?reuse_build_dir ?inplace_build ?working_dir + ?drop_working_dir ?ignore_pin_depends ?show ?fake @@ -78,6 +139,9 @@ ?json_out ?root_is_ok ?no_auto_upgrade + ?assume_depexts + ?cli + ?scrubbed_environment_variables = let (+) x opt = match opt with Some x -> x | None -> x in k { @@ -89,6 +153,7 @@ reuse_build_dir = t.reuse_build_dir + reuse_build_dir; inplace_build = t.inplace_build + inplace_build; working_dir = t.working_dir + working_dir; + drop_working_dir = t.drop_working_dir + drop_working_dir; ignore_pin_depends = t.ignore_pin_depends + ignore_pin_depends; show = t.show + show; fake = t.fake + fake; @@ -96,6 +161,9 @@ json_out = t.json_out + json_out; root_is_ok = t.root_is_ok + root_is_ok; no_auto_upgrade = t.no_auto_upgrade + no_auto_upgrade; + assume_depexts = t.assume_depexts + assume_depexts; + cli = t.cli + cli; + scrubbed_environment_variables = t.scrubbed_environment_variables + scrubbed_environment_variables } let set t = setk (fun x () -> x) t @@ -105,27 +173,31 @@ let update ?noop:_ = setk (fun cfg () -> r := cfg) !r let initk k = - let open OpamStd.Config in let open OpamStd.Option.Op in + Random.self_init (); let editor = - env_string "EDITOR" ++ OpamStd.Env.(getopt "VISUAL" ++ getopt "EDITOR") + E.editor () ++ OpamStd.Env.(getopt "VISUAL" ++ getopt "EDITOR") in setk (setk (fun c -> r := c; k)) !r - ?print_stats:(env_bool "STATS") - ?pin_kind_auto:(env_bool "PINKINDAUTO") - ?autoremove:(env_bool "AUTOREMOVE") + ?print_stats:(E.stats ()) + ?pin_kind_auto:(E.pinkindauto ()) + ?autoremove:(E.autoremove ()) ?editor - ?keep_build_dir:(env_bool "KEEPBUILDDIR") - ?reuse_build_dir:(env_bool "REUSEBUILDDIR") - ?inplace_build:(env_bool "INPLACEBUILD") - ?working_dir:(env_bool "WORKINGDIR") - ?ignore_pin_depends:(env_bool "IGNOREPINDEPENDS") - ?show:(env_bool "SHOW") - ?fake:(env_bool "FAKE") - ?skip_dev_update:(env_bool "SKIPUPDATE") - ?json_out:(env_string "JSON" >>| function "" -> None | s -> Some s) - ?root_is_ok:(env_bool "ROOTISOK") - ?no_auto_upgrade:(env_bool "NOAUTOUPGRADE") + ?keep_build_dir:(E.keepbuilddir ()) + ?reuse_build_dir:(E.reusebuilddir ()) + ?inplace_build:(E.inplacebuild ()) + ?working_dir:(E.workingdir ()) + ?drop_working_dir:(E.dropworkingdir ()) + ?ignore_pin_depends:(E.ignorepindepends ()) + ?show:(E.show ()) + ?fake:(E.fake ()) + ?skip_dev_update:(E.skipupdate ()) + ?json_out:(E.json () >>| function "" -> None | s -> Some s) + ?root_is_ok:(E.rootisok ()) + ?no_auto_upgrade:(E.noautoupgrade ()) + ?assume_depexts:(E.assumedepexts ()) + ?cli:None + ?scrubbed_environment_variables:None let init ?noop:_ = initk (fun () -> ()) @@ -133,7 +205,7 @@ open OpamStd.Op -let opam_init ?root_dir ?strict = +let opam_init ?root_dir ?strict ?solver = let open OpamStd.Option.Op in (* (i) get root dir *) @@ -148,6 +220,14 @@ (* !X fixme: don't drop the loaded config file to reload it afterwards (when loading the global_state) like that... *) + let solver = + if solver = None && OpamSolverConfig.E.externalsolver () = None then + (* fixme: in order to not revert config file solver value, we need to + check it here *) + (config >>= OpamFile.Config.solver >>| + fun s -> lazy (OpamCudfSolver.custom_solver s)) + else solver + in begin match config with | None -> () | Some conf -> @@ -156,25 +236,30 @@ try Some (List.assoc kind c) with Not_found -> None in OpamSolverConfig.update - ?solver:(OpamFile.Config.solver conf >>| - fun s -> lazy(OpamCudfSolver.custom_solver s)) + ?solver ?solver_preferences_default:(criteria `Default >>| fun s-> lazy(Some s)) ?solver_preferences_upgrade:(criteria `Upgrade >>| fun s-> lazy(Some s)) ?solver_preferences_fixup:(criteria `Fixup >>| fun s -> lazy (Some s)) ?solver_preferences_best_effort_prefix: (OpamFile.Config.best_effort_prefix conf >>| fun s -> lazy (Some s)) + (); + OpamStateConfig.update () end; (* (iii) load from env and options using OpamXxxConfig.init *) let log_dir = + OpamStd.Option.map OpamFilename.Dir.to_string @@ if log_dir = None && initialised - then Some OpamFilename.(Dir.to_string (OpamPath.log root)) - else None + && OpamCoreConfig.E.logs () = None then + (* fixme: in order to not revert [OPAMLOGS] value, + we need to check it here *) + Some (OpamPath.log root) + else log_dir in (fun () -> ()) |> - OpamStd.Config.initk ?log_dir |> + OpamCoreConfig.initk ?log_dir |> OpamRepositoryConfig.initk |> - OpamSolverConfig.initk |> + OpamSolverConfig.initk ?solver |> OpamStateConfig.initk ~root_dir:root |> initk diff -Nru opam-2.0.10/src/client/opamClientConfig.mli opam-2.1.2/src/client/opamClientConfig.mli --- opam-2.0.10/src/client/opamClientConfig.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamClientConfig.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2020 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -11,6 +11,34 @@ (** Configuration options for the client lib (record, global reference, setter, initialisation), plus helper for global setup *) +module E: sig + type OpamStd.Config.E.t += + | ASSUMEDEPEXTS of bool option + | AUTOREMOVE of bool option + | CLI of string option + | DROPWORKINGDIR of bool option + | EDITOR of string option + | FAKE of bool option + | IGNOREPINDEPENDS of bool option + | INPLACEBUILD of bool option + | JSON of string option + | KEEPBUILDDIR of bool option + | NOAGGREGATE of bool option + | NOAUTOUPGRADE of bool option + | NOSELFUPGRADE of string option + | PINKINDAUTO of bool option + | REUSEBUILDDIR of bool option + | ROOTISOK of bool option + | SHOW of bool option + | SKIPUPDATE of bool option + | STATS of bool option + | WORKINGDIR of bool option + val cli: unit -> string option + val rootisok: unit -> bool option + val noaggregate: unit -> bool option + val noselfupgrade: unit -> string option +end + type t = private { print_stats: bool; pin_kind_auto: bool; @@ -20,6 +48,7 @@ reuse_build_dir: bool; inplace_build: bool; working_dir: bool; + drop_working_dir: bool; ignore_pin_depends: bool; show: bool; fake: bool; @@ -27,6 +56,9 @@ json_out: string option; root_is_ok: bool; no_auto_upgrade: bool; + assume_depexts: bool; + cli: OpamCLIVersion.t; + scrubbed_environment_variables: string list; } type 'a options_fun = @@ -39,6 +71,7 @@ ?reuse_build_dir:bool -> ?inplace_build:bool -> ?working_dir:bool -> + ?drop_working_dir:bool -> ?ignore_pin_depends:bool -> ?show:bool -> ?fake:bool -> @@ -46,6 +79,9 @@ ?json_out:string option -> ?root_is_ok:bool -> ?no_auto_upgrade:bool -> + ?assume_depexts:bool -> + ?cli:OpamCLIVersion.t -> + ?scrubbed_environment_variables:string list -> 'a (* constraint 'a = 'b -> 'c *) @@ -61,6 +97,7 @@ val opam_init: ?root_dir:OpamTypes.dirname -> ?strict:bool -> + ?solver:(module OpamCudfSolver.S) Lazy.t -> ?skip_version_checks:bool -> ?all_parens:bool -> ?log_dir:OpamTypes.dirname -> @@ -72,6 +109,7 @@ ?reuse_build_dir:bool -> ?inplace_build:bool -> ?working_dir:bool -> + ?drop_working_dir:bool -> ?ignore_pin_depends:bool -> ?show:bool -> ?fake:bool -> @@ -79,8 +117,11 @@ ?json_out:string option -> ?root_is_ok:bool -> ?no_auto_upgrade:bool -> + ?assume_depexts:bool -> + ?cli:OpamCLIVersion.t -> + ?scrubbed_environment_variables:string list -> ?current_switch:OpamSwitch.t -> - ?switch_from:[ `Command_line | `Default | `Env ] -> + ?switch_from:OpamStateTypes.provenance -> ?jobs:int Lazy.t -> ?dl_jobs:int -> ?build_test:bool -> @@ -91,24 +132,31 @@ ?unlock_base:bool -> ?no_env_notice:bool -> ?locked:string option -> + ?no_depexts:bool -> ?cudf_file:string option -> - ?solver:(module OpamCudfSolver.S) Lazy.t -> ?best_effort:bool -> ?solver_preferences_default:string option Lazy.t -> ?solver_preferences_upgrade:string option Lazy.t -> ?solver_preferences_fixup:string option Lazy.t -> ?solver_preferences_best_effort_prefix: string option Lazy.t -> ?solver_timeout:float option -> + ?solver_allow_suboptimal:bool -> + ?cudf_trim:string option -> + ?dig_depth:int -> + ?preprocess:bool -> + ?version_lag_power:int -> ?download_tool:(OpamTypes.arg list * OpamRepositoryConfig.dl_tool_kind) Lazy.t -> ?validation_hook:OpamTypes.arg list option -> ?retries:int -> ?force_checksums:bool option -> ?debug_level:int -> - ?verbose_level:int -> - ?color:[ `Always | `Auto | `Never ] -> - ?utf8:[ `Always | `Auto | `Extended | `Never ] -> - ?disp_status_line:[ `Always | `Auto | `Never ] -> - ?answer:bool option -> + ?debug_sections:OpamStd.Config.sections -> + ?verbose_level:OpamStd.Config.level -> + ?color:OpamStd.Config.when_ -> + ?utf8:OpamStd.Config.when_ext -> + ?disp_status_line:OpamStd.Config.when_ -> + ?confirm_level:OpamStd.Config.answer -> + ?yes:bool option -> ?safe_mode:bool -> ?keep_log_dir:bool -> ?errlog_length:int -> diff -Nru opam-2.0.10/src/client/opamClient.ml opam-2.1.2/src/client/opamClient.ml --- opam-2.0.10/src/client/opamClient.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamClient.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -23,20 +23,28 @@ - they are checked for conflicts with the user request - they are re-added to the universe if (transitively) unrelated to the request (the [changes] parameter) - - they are otherwise put in [wish_remove] in case we use the internal - solver This function separates full orphans (no version of the package available anymore) from orphan versions, because they have a different impact on the request (needs version change VS needs uninstall). - See also preprocess_request and check_conflicts *) + + Orphan packages include both installed packages that are no longer available, + and packages that are "invalidated", i.e. their system dependencies are no + longer up-to-date: in this case they might still be available for reinstall.. + + See also check_conflicts. *) let orphans ?changes ?(transitive=false) t = let all = t.packages ++ t.installed in + let available = Lazy.force t.available_packages in let allnames = OpamPackage.names_of_packages all in + let invalidated = Lazy.force t.invalidated in let universe = OpamSwitchState.universe t ~requested:OpamPackage.Name.Set.empty Reinstall in (* Basic definition of orphan packages *) - let orphans = t.installed -- Lazy.force t.available_packages in + let orphans = + t.installed -- available + in + log "Base orphans: %a" (slog OpamPackage.Set.to_string) orphans; (* Restriction to the request-related packages *) let changes = match changes with | None -> None @@ -55,7 +63,6 @@ | Some ch -> if OpamPackage.Set.is_empty orphans then orphans else let recompile_cone = - OpamPackage.Set.of_list @@ OpamSolver.reverse_dependencies ~depopts:true ~installed:true ~unavailable:true ~build:true ~post:false @@ -63,14 +70,17 @@ in orphans %% recompile_cone in + (* invalidated packages forbid changes of their reverse dependencies, while + basic orphans do not *) + let orphans = orphans ++ invalidated in (* Pinned versions of packages remain always available *) let orphans = orphans -- OpamPinned.packages t in (* Splits between full orphans (no version left) and partial ones *) let full_partition orphans = - let orphan_names = (* names for which there is no version left *) + let orphan_names = (* names for which there is no available version left *) OpamPackage.Name.Set.diff allnames - (OpamPackage.names_of_packages (all -- orphans)) in + (OpamPackage.names_of_packages (available -- orphans)) in OpamPackage.Set.partition (fun nv -> OpamPackage.Name.Set.mem nv.name orphan_names) orphans @@ -82,7 +92,6 @@ let rec add_trans full_orphans orphan_versions = (* fixpoint to check all packages with no available version *) let new_orphans = - OpamPackage.Set.of_list @@ OpamSolver.reverse_dependencies ~depopts:false ~installed:false ~unavailable:true ~build:true ~post:false @@ -99,10 +108,9 @@ re-add them to the universe *) let t = if changes = None then t else - let available_packages = - lazy (Lazy.force t.available_packages ++ - (t.installed -- orphans)) in - { t with available_packages } in + let available_packages = lazy (available ++ (t.installed -- orphans)) in + { t with available_packages } + in log "Orphans: (changes: %a, transitive: %b) -> full %a, versions %a" (slog @@ OpamStd.Option.to_string OpamPackage.Set.to_string) changes transitive @@ -124,26 +132,18 @@ (* Check atoms for pinned packages, and update them. Returns the state that may have been reloaded if there were changes *) -let update_dev_packages_t atoms t = - (* Check last update of the repo *) - let last_update = - (Unix.stat (OpamFilename.to_string - (OpamPath.state_cache - (OpamStateConfig.(!r.root_dir))))).Unix.st_mtime - in - let too_old = float_of_int (3600*24*21) in - if (Unix.time () -. last_update) > too_old then - OpamConsole.note "It seems you have not updated your repositories \ - for a while. Consider updating them with:\n%s\n" - (OpamConsole.colorise `bold "opam update"); - +let update_dev_packages_t ?(only_installed=false) atoms t = + OpamRepositoryState.check_last_update (); if OpamClientConfig.(!r.skip_dev_update) then t else - let working_dir = OpamClientConfig.(!r.working_dir) in + let working_dir = OpamClientConfig.(!r.working_dir || !r.inplace_build) in let to_update = List.fold_left (fun to_update (name,_) -> try let nv = OpamPackage.package_of_name t.pinned name in - if OpamSwitchState.is_dev_package t nv then + if OpamSwitchState.is_dev_package t nv && + ( not only_installed || + OpamPackage.Set.exists (fun nv -> nv.name = name) t.installed ) + then OpamPackage.Set.add nv to_update else to_update with Not_found -> to_update) @@ -167,7 +167,8 @@ ) let compute_upgrade_t - ?(strict_upgrade=true) ?(auto_install=false) ~all atoms t = + ?(strict_upgrade=true) ?(auto_install=false) ?(only_installed=false) + ~all atoms t = let names = OpamPackage.Name.Set.of_list (List.rev_map fst atoms) in let atoms = List.map (function @@ -179,11 +180,15 @@ let nv = OpamSwitchState.find_installed_package_by_name t n in if OpamSwitchState.is_dev_package t nv || OpamPackage.has_name t.pinned n || - OpamPackage.Set.mem nv t.reinstall + OpamPackage.Set.mem nv (Lazy.force t.reinstall) then (n, None) else let atom = (n, Some (`Gt, nv.version)) in - if OpamPackage.Set.exists (OpamFormula.check atom) + if OpamPackage.Set.exists + (fun nv -> + OpamFormula.check atom nv && + (not (OpamFile.OPAM.has_flag Pkgflag_AvoidVersion (OpamSwitchState.opam t nv)) || + OpamSwitchState.can_upgrade_to_avoid_version (OpamPackage.name nv) t)) (Lazy.force t.available_packages) then atom else (n, None) @@ -201,7 +206,7 @@ packages, atom :: not_installed) (OpamPackage.Set.empty,[]) atoms in let to_install = - if not_installed = [] then [] else + if only_installed || not_installed = [] then [] else if auto_install || OpamConsole.confirm "%s %s not installed. Install %s?" (OpamStd.Format.pretty_list @@ -211,6 +216,15 @@ then not_installed else [] in + let upgrade_atoms to_upgrade = + (* packages corresponds to the currently installed versions. + Not what we are interested in, recover the original atom constraints *) + List.map (fun nv -> + let name = nv.name in + try name, List.assoc name atoms + with Not_found -> name, None) + (OpamPackage.Set.elements to_upgrade) + in if all then let t, full_orphans, orphan_versions = orphans ~transitive:true t in let to_upgrade = t.installed -- full_orphans in @@ -218,10 +232,10 @@ OpamSolution.resolve t Upgrade ~orphans:(full_orphans ++ orphan_versions) ~requested:names - ~reinstall:t.reinstall + ~reinstall:(Lazy.force t.reinstall) (OpamSolver.request ~install:to_install - ~upgrade:(OpamSolution.atoms_of_packages to_upgrade) + ~upgrade:(upgrade_atoms to_upgrade) ~criteria:`Upgrade ()) else let changes = @@ -230,14 +244,6 @@ let t, full_orphans, orphan_versions = orphans ~changes t in let to_remove = requested_installed %% full_orphans in let to_upgrade = requested_installed -- full_orphans in - let upgrade_atoms = - (* packages corresponds to the currently installed versions. - Not what we are interested in, recover the original atom constraints *) - List.map (fun nv -> - let name = nv.name in - try name, List.assoc name atoms - with Not_found -> name, None) - (OpamPackage.Set.elements to_upgrade) in names, OpamSolution.resolve t Upgrade ~orphans:(full_orphans ++ orphan_versions) @@ -245,26 +251,27 @@ (OpamSolver.request ~install:to_install ~remove:(OpamSolution.atoms_of_packages to_remove) - ~upgrade:upgrade_atoms + ~upgrade:(upgrade_atoms to_upgrade) ()) let upgrade_t - ?strict_upgrade ?auto_install ?ask ?(check=false) ?(terse=false) ~all - atoms t + ?strict_upgrade ?auto_install ?ask ?(check=false) ?(terse=false) + ?only_installed ~all atoms t = log "UPGRADE %a" (slog @@ function [] -> "" | a -> OpamFormula.string_of_atoms a) atoms; - match compute_upgrade_t ?strict_upgrade ?auto_install ~all atoms t with + match compute_upgrade_t ?strict_upgrade ?auto_install ?only_installed ~all atoms t with | requested, Conflicts cs -> log "conflict!"; if not (OpamPackage.Name.Set.is_empty requested) then - (OpamConsole.msg "%s" - (OpamCudf.string_of_conflict t.packages + (OpamConsole.error "Package conflict!"; + OpamConsole.errmsg "%s" + (OpamCudf.string_of_conflicts t.packages (OpamSwitchState.unavailable_reason t) cs); OpamStd.Sys.exit_because `No_solution); - let reasons, chains, cycles = - OpamCudf.strings_of_conflict t.packages + let reasons, cycles = + OpamCudf.conflict_explanations t.packages (OpamSwitchState.unavailable_reason t) cs in if cycles <> [] then begin OpamConsole.error @@ -278,11 +285,9 @@ OpamConsole.warning "Upgrade is not possible because of conflicts or packages that \ are no longer available:"; - OpamConsole.errmsg "%s" (OpamStd.Format.itemize (fun x -> x) reasons); - if chains <> [] then - OpamConsole.errmsg - "The following dependencies are the cause:\n%s" - (OpamStd.Format.itemize (fun x -> x) chains); + OpamConsole.errmsg " %s" + (OpamStd.Format.itemize (OpamCudf.string_of_conflict ~start_column:2) + reasons); OpamConsole.errmsg "\nYou may run \"opam upgrade --fixup\" to let opam fix the \ current state.\n" @@ -295,7 +300,7 @@ then `False else `Success) else - let t, result = OpamSolution.apply ?ask t Upgrade ~requested solution in + let t, result = OpamSolution.apply ?ask t ~requested solution in if result = Nothing_to_do then ( let to_check = if OpamPackage.Name.Set.is_empty requested then t.installed @@ -436,13 +441,13 @@ ) ); - OpamSolution.check_solution t result; + OpamSolution.check_solution t (Success result); t -let upgrade t ?check ~all names = +let upgrade t ?check ?only_installed ~all names = let atoms = OpamSolution.sanitize_atom_list t names in - let t = update_dev_packages_t atoms t in - upgrade_t ?check ~strict_upgrade:(not all) ~all atoms t + let t = update_dev_packages_t ?only_installed atoms t in + upgrade_t ?check ~strict_upgrade:(not all) ?only_installed ~all atoms t let fixup t = log "FIXUP"; @@ -462,7 +467,7 @@ | _, Success _ -> true | _, Conflicts cs -> log "conflict: %a" - (slog (OpamCudf.string_of_conflict t.packages @@ + (slog (OpamCudf.string_of_conflicts t.packages @@ OpamSwitchState.unavailable_reason t)) cs; false @@ -495,18 +500,20 @@ let t, result = match solution with | Conflicts cs -> (* ouch... *) OpamConsole.error - "It appears that the base packages for this switch are no longer \ - available. Either fix their prerequisites or change them through \ - 'opam list --base' and 'opam switch set-base'."; + "It appears that the switch invariant is no longer satisfiable. \ + Either fix the package prerequisites or change the invariant \ + with 'opam switch set-invariant'."; OpamConsole.errmsg "%s" - (OpamCudf.string_of_conflict t.packages + (OpamCudf.string_of_conflicts t.packages (OpamSwitchState.unavailable_reason t) cs); - t, No_solution + t, Conflicts cs | Success solution -> let _, req_rm, _ = orphans ~transitive:false t in - OpamSolution.apply ~ask:true t Upgrade - ~requested:(OpamPackage.names_of_packages (requested ++ req_rm)) - solution + let t, res = + OpamSolution.apply ~ask:true t + ~requested:(OpamPackage.names_of_packages (requested ++ req_rm)) + solution in + t, Success res in OpamSolution.check_solution t result; t @@ -567,7 +574,9 @@ nondev_packages) in let dirty_dev_packages, dev_packages = - if names <> [] then OpamPackage.Set.empty, dev_packages else + if names <> [] || OpamClientConfig.(!r.drop_working_dir) then + OpamPackage.Set.empty, dev_packages + else OpamPackage.Set.partition (fun nv -> let src_cache = OpamSwitchState.source_dir st nv in @@ -576,8 +585,12 @@ in match OpamSwitchState.primary_url st nv with | Some { OpamUrl.backend = #OpamUrl.version_control as vc; _ } -> - OpamProcess.Job.run @@ - OpamRepository.is_dirty { cache_url with OpamUrl.backend = vc } + (try + OpamProcess.Job.run @@ + OpamRepository.is_dirty { cache_url with OpamUrl.backend = vc } + with OpamSystem.Process_error _ -> + log "Skipping %s, not a git repo" (OpamPackage.to_string nv); + false) | _ -> false) dev_packages in @@ -621,18 +634,25 @@ (* st is still based on the old rt, it's not a problem at this point, but don't return it *) - let (dev_update_success, dev_changed), _st = + let (dev_update_success, dev_changed), st = if OpamPackage.Set.is_empty packages then (true, false), st else OpamSwitchState.with_write_lock st @@ fun st -> + let working_dir = + if OpamClientConfig.(!r.working_dir) && names <> [] then + Some (OpamPackage.packages_of_names packages + (OpamPackage.Name.(Set.of_list (List.map of_string names)))) + else None + in OpamConsole.header_msg "Synchronising development packages"; - let success, st, updates = OpamUpdate.dev_packages st packages in + let success, st, updates = OpamUpdate.dev_packages st ?working_dir packages in if OpamClientConfig.(!r.json_out <> None) then OpamJson.append "dev-packages-updates" (OpamPackage.Set.to_json updates); (success, not (OpamPackage.Set.is_empty updates)), st in + OpamSwitchState.drop st; repo_update_failure = [] && dev_update_success && remaining = [] && OpamPackage.Set.is_empty ignore_packages, repo_changed || dev_changed, @@ -732,9 +752,9 @@ else conf in config |> - setifnew C.jobs C.with_jobs (match I.jobs init_config with - | Some j -> j - | None -> Lazy.force OpamStateConfig.(default.jobs)) |> + match I.jobs init_config with + | Some j -> setifnew C.jobs C.with_jobs j + | None -> fun c -> c |> setifnew C.dl_tool C.with_dl_tool_opt (I.dl_tool init_config) |> setifnew C.dl_jobs C.with_dl_jobs (OpamStd.Option.default OpamStateConfig.(default.dl_jobs) @@ -747,14 +767,20 @@ setifnew C.eval_variables C.with_eval_variables (I.eval_variables init_config) |> setifnew C.default_compiler C.with_default_compiler - (I.default_compiler init_config) + (I.default_compiler init_config) |> + setifnew C.default_invariant C.with_default_invariant + (I.default_invariant init_config) let reinit ?(init_config=OpamInitDefaults.init_config()) ~interactive - ?dot_profile ?update_config ?env_hook ?completion config shell = + ?dot_profile ?update_config ?env_hook ?completion ?inplace + ?(check_sandbox=true) ?(bypass_checks=false) + config shell = let root = OpamStateConfig.(!r.root_dir) in let config = update_with_init_config config init_config in - let _all_ok = init_checks ~hard_fail_exn:false init_config in - OpamFile.Config.write (OpamPath.config root) config; + let _all_ok = + if bypass_checks then false else + init_checks ~hard_fail_exn:false init_config + in let custom_init_scripts = let env v = let vs = OpamVariable.Full.variable v in @@ -768,8 +794,14 @@ (OpamFile.InitConfig.init_scripts init_config) in OpamEnv.write_custom_init_scripts root custom_init_scripts; + let config = + if check_sandbox then + OpamAuxCommands.check_and_revert_sandboxing root config + else config + in + OpamFile.Config.write (OpamPath.config root) config; OpamEnv.setup root ~interactive - ?dot_profile ?update_config ?env_hook ?completion shell; + ?dot_profile ?update_config ?env_hook ?completion ?inplace shell; let gt = OpamGlobalState.load `Lock_write in let rt = OpamRepositoryState.load `Lock_write gt in OpamConsole.header_msg "Updating repositories"; @@ -777,13 +809,13 @@ OpamRepositoryCommand.update_with_auto_upgrade rt (OpamRepositoryName.Map.keys rt.repos_definitions) in - let _rt = OpamRepositoryState.unlock rt in - () + OpamRepositoryState.drop rt let init ~init_config ~interactive ?repo ?(bypass_checks=false) ?dot_profile ?update_config ?env_hook ?(completion=true) + ?(check_sandbox=true) shell = log "INIT %a" (slog @@ OpamStd.Option.to_string OpamRepositoryBackend.to_string) repo; @@ -796,7 +828,11 @@ if OpamFile.exists config_f then ( OpamConsole.msg "Opam has already been initialized.\n"; let gt = OpamGlobalState.load `Lock_write in - gt, OpamRepositoryState.load `Lock_none gt, OpamFormula.Empty + if OpamFile.Config.installed_switches gt.config = [] then + OpamConsole.msg + "... but you have no switches installed, use `opam switch \ + create ' to get started."; + gt, OpamRepositoryState.load `Lock_none gt, [] ) else ( if not root_empty then ( OpamConsole.warning "%s exists and is not empty" @@ -810,10 +846,11 @@ | None -> OpamFile.InitConfig.repositories init_config in let config = - update_with_init_config OpamFile.Config.empty init_config |> + update_with_init_config + OpamFile.Config.(with_opam_root_version root_version empty) + init_config |> OpamFile.Config.with_repositories (List.map fst repos) in - OpamFile.Config.write config_f config; let dontswitch = if bypass_checks then false else @@ -836,6 +873,12 @@ Some (nam,scr) else None) scripts in OpamEnv.write_custom_init_scripts root custom_scripts; + let config = + if check_sandbox then + OpamAuxCommands.check_and_revert_sandboxing root config + else config + in + OpamFile.Config.write config_f config; let repos_config = OpamRepositoryName.Map.of_list repos |> OpamRepositoryName.Map.map OpamStd.Option.some @@ -852,11 +895,38 @@ (List.map fst repos) in if failed <> [] then - OpamConsole.error_and_exit `Sync_error - "Initial download of repository failed"; - gt, OpamRepositoryState.unlock rt, - (if dontswitch then OpamFormula.Empty - else OpamFile.InitConfig.default_compiler init_config) + (if root_empty then + (try OpamFilename.rmdir root with _ -> ()); + OpamConsole.error_and_exit `Sync_error + "Initial download of repository failed."); + let default_compiler = + if dontswitch then [] else + let chrono = OpamConsole.timer () in + let alternatives = + OpamFormula.to_dnf + (OpamFile.InitConfig.default_compiler init_config) + in + let invariant = OpamFile.InitConfig.default_invariant init_config in + let virt_st = + OpamSwitchState.load_virtual ~avail_default:false gt rt + in + let univ = + OpamSwitchState.universe virt_st + ~requested:OpamPackage.Name.Set.empty Query + in + let univ = { univ with u_invariant = invariant } in + let default_compiler = + OpamStd.List.find_opt + (OpamSolver.atom_coinstallability_check univ) + alternatives + |> OpamStd.Option.default [] + in + log "Selected default compiler %s in %0.3fs" + (OpamFormula.string_of_atoms default_compiler) + (chrono ()); + default_compiler + in + gt, OpamRepositoryState.unlock ~cleanup:false rt, default_compiler with e -> OpamStd.Exn.finalise e @@ fun () -> if not (OpamConsole.debug ()) && root_empty then begin @@ -873,83 +943,163 @@ let check_conflicts t atoms = let changes = OpamSwitchState.packages_of_atoms t atoms in let t, full_orphans, orphan_versions = orphans ~changes t in - let available_changes = changes %% Lazy.force t.available_packages in - (* packages which still have local data are OK for install/reinstall *) - let has_no_local_data nv = - not (OpamFile.exists - (OpamPath.Switch.installed_opam t.switch_global.root t.switch nv)) - in - let full_orphans, full_orphans_with_local_data = - OpamPackage.Set.partition has_no_local_data - full_orphans in - let orphan_versions, orphan_versions_with_local_data = - OpamPackage.Set.partition - (fun nv -> has_no_local_data nv || - OpamPackage.has_name available_changes nv.name) - orphan_versions in - let available = lazy (t.packages -- full_orphans -- orphan_versions) in + let available = Lazy.force t.available_packages in + let available_changes = changes %% available in + (* packages which still have local data are OK for install/reinstall if still + "available" *) + let full_orphans_reinstallable, full_orphans = + OpamPackage.Set.partition (fun nv -> + match OpamPackage.Map.find_opt nv t.opams with + | None -> false + | Some opam -> + OpamFilter.eval_to_bool ~default:false + (OpamPackageVar.resolve_switch ~package:nv t) + (OpamFile.OPAM.available opam)) + full_orphans + in + let orphan_versions_reinstallable, orphan_versions = + OpamPackage.Set.partition (fun nv -> + not (OpamPackage.has_name available_changes nv.name) && + match OpamPackage.Map.find_opt nv t.opams with + | None -> false + | Some opam -> + OpamFilter.eval_to_bool ~default:false + (OpamPackageVar.resolve_switch ~package:nv t) + (OpamFile.OPAM.available opam)) + orphan_versions + in let orphans = full_orphans ++ orphan_versions in let conflict_atoms = + let non_orphans = lazy (t.packages -- full_orphans -- orphan_versions) in List.filter (fun (name,_ as a) -> not (OpamPackage.has_name t.pinned name) && OpamPackage.Set.exists (OpamFormula.check a) orphans && (*optim*) not (OpamPackage.Set.exists (OpamFormula.check a) (* real check *) - (Lazy.force available))) - atoms in + (Lazy.force non_orphans))) + atoms + in if conflict_atoms <> [] then + (* Atoms that were unavailable to begin with should be already filtered out + at this point (by [sanitize_atom_list]) *) OpamConsole.error_and_exit `Not_found - "Sorry, these packages are no longer available \ - from the repositories: %s" + "Sorry, these packages are no longer available from the repositories: \ + %s" (OpamStd.Format.pretty_list (List.map OpamFormula.string_of_atom conflict_atoms)) else {t with available_packages = lazy - (Lazy.force t.available_packages ++ - full_orphans_with_local_data ++ - orphan_versions_with_local_data )}, + (available ++ + full_orphans_reinstallable ++ + orphan_versions_reinstallable)}, full_orphans, orphan_versions -let assume_built_restrictions t atoms = - let installed_fixed, not_installed_fixed = - let rec all_deps set pkgs = - let universe = - OpamSwitchState.universe t - ~requested:(OpamPackage.names_of_packages pkgs) - Install - in - let deps = - OpamPackage.Set.of_list - (OpamSolver.dependencies ~build:false ~post:true - ~depopts:false ~installed:false ~unavailable:true universe pkgs) +let check_installed ~build ~post t atoms = + let available = (Lazy.force t.available_packages) in + let uninstalled = OpamPackage.Set.Op.(available -- t.installed) in + let pkgs = + OpamPackage.to_map + (OpamFormula.packages_of_atoms available atoms) + in + let test = OpamStateConfig.(!r.build_test) in + let doc = OpamStateConfig.(!r.build_doc) in + let env p = + OpamFilter.deps_var_env ~build ~post ~test ~doc + ~dev:(OpamSwitchState.is_dev_package t p) + in + OpamPackage.Name.Map.fold (fun name versions map -> + let compliant, missing_opt = + OpamPackage.Version.Set.fold (fun version (found, missing) -> + if found then (found, missing) + else + let pkg = OpamPackage.create name version in + let cnf_formula = + OpamSwitchState.opam t pkg + |> OpamFile.OPAM.depends + |> OpamFilter.filter_formula (env pkg) + |> OpamFormula.to_cnf + in + let missing_conj = + List.filter + (List.for_all (fun ((n,_vc) as atom) -> + OpamPackage.Set.for_all + (fun p -> not (OpamFormula.check atom p)) + (OpamPackage.packages_of_name t.installed n))) + cnf_formula + in + if missing_conj = [] then true, None + else false, Some (pkg,missing_conj)) + versions (false,None) in - let deps = deps -- pkgs in - if OpamPackage.Set.is_empty deps then set - else all_deps (set ++ deps) deps - in - let pkg_of_atoms = - OpamPackage.Set.filter - (fun p -> List.exists (fun a -> OpamFormula.check a p) atoms) - t.packages - in - let all_fixed = all_deps OpamPackage.Set.empty pkg_of_atoms in - OpamSolution.eq_atoms_of_packages all_fixed |> get_installed_atoms t - in + if compliant then map else + match missing_opt with + | None -> assert false (* version set can't be empty *) + | Some (pkg, missing_conj) -> + OpamPackage.Map.add pkg + (OpamPackage.names_of_packages + (List.fold_left (fun names disj -> + OpamPackage.Set.union names + (OpamFormula.packages_of_atoms uninstalled disj)) + OpamPackage.Set.empty missing_conj)) + map + ) pkgs OpamPackage.Map.empty + +let assume_built_restrictions ?available_packages t atoms = + let missing = check_installed ~build:false ~post:false t atoms in let atoms = - atoms @ OpamSolution.eq_atoms_of_packages - (OpamPackage.Set.of_list installed_fixed) + if OpamPackage.Map.is_empty missing then atoms else + (OpamConsole.warning + "You specified '--assume-built' but the following dependencies \ + aren't installed, skipping\n%s\ + Launch 'opam install %s --deps-only' (and recompile) to \ + install them.\n" + (OpamStd.Format.itemize (fun (nv, names) -> + Printf.sprintf "%s: %s" (OpamPackage.name_to_string nv) + (OpamStd.List.concat_map " " OpamPackage.Name.to_string + (OpamPackage.Name.Set.elements names))) + (OpamPackage.Map.bindings missing)) + (OpamStd.List.concat_map " " OpamPackage.name_to_string + (OpamPackage.Map.keys missing)); + List.filter (fun (n,_) -> + not (OpamPackage.Map.exists (fun nv _ -> + OpamPackage.name nv = n) missing)) + atoms) + in + let pinned = + (* Not pinned atoms already removed. *) + OpamPackage.Set.filter + (fun p -> List.exists (fun a -> OpamFormula.check a p) atoms) + t.pinned + in + let installed_dependencies = + OpamSolver.dependencies ~build:false ~post:false + ~depopts:false ~installed:true ~unavailable:false + (OpamSwitchState.universe t + ~requested:(OpamPackage.names_of_packages pinned) Query) + pinned in - let t = - let avp = - OpamPackage.Set.filter - (fun p -> not (List.exists (fun a -> OpamFormula.check a p) - not_installed_fixed)) - (Lazy.force t.available_packages) - in - { t with available_packages = lazy avp} + let available_packages = + match available_packages with + | Some a -> a + | None -> Lazy.force t.available_packages + in + let uninstalled_dependencies = + (OpamPackage.Map.values missing + |> List.fold_left OpamPackage.Name.Set.union OpamPackage.Name.Set.empty + |> OpamPackage.packages_of_names available_packages) + -- installed_dependencies + in + let available_packages = lazy ( + (available_packages -- uninstalled_dependencies) ++ t.installed ++ pinned + ) in + let fixed_atoms = + List.map (fun nv -> + (OpamPackage.name nv , Some (`Eq, OpamPackage.version nv))) + (OpamPackage.Set.elements pinned @ + OpamPackage.Set.elements installed_dependencies) in - t, atoms + { t with available_packages }, fixed_atoms let filter_unpinned_locally t atoms f = OpamStd.List.filter_map (fun at -> @@ -967,7 +1117,8 @@ None)) atoms -let install_t t ?ask atoms add_to_roots ~deps_only ~assume_built = +let install_t t ?ask ?(ignore_conflicts=false) ?(depext_only=false) + ?(download_only=false) atoms add_to_roots ~deps_only ~assume_built = log "INSTALL %a" (slog OpamFormula.string_of_atoms) atoms; let names = OpamPackage.Name.Set.of_list (List.rev_map fst atoms) in @@ -988,7 +1139,7 @@ get_installed_atoms t atoms in let pkg_reinstall = if assume_built then OpamPackage.Set.of_list pkg_skip - else t.reinstall %% OpamPackage.Set.of_list pkg_skip + else Lazy.force t.reinstall %% OpamPackage.Set.of_list pkg_skip in (* Add the packages to the list of package roots and display a warning for already installed package roots. *) @@ -1013,16 +1164,15 @@ { t with installed_roots = OpamPackage.Set.add nv t.installed_roots } | Some false -> - if OpamPackage.Set.mem nv t.compiler_packages then - (OpamConsole.note - "Package %s is part of the compiler base and can't be set \ - as 'installed automatically'" - (OpamPackage.name_to_string nv); - t) - else if OpamPackage.Set.mem nv t.installed_roots then + if OpamPackage.Set.mem nv t.installed_roots then begin + if OpamPackage.Set.mem nv t.compiler_packages then + OpamConsole.note + "Package %s is part of the switch invariant and won't be uninstalled \ + unless the invariant is updated." + (OpamPackage.name_to_string nv); { t with installed_roots = OpamPackage.Set.remove nv t.installed_roots } - else + end else (OpamConsole.note "Package %s is already marked as 'installed automatically'." (OpamPackage.Name.to_string nv.name); @@ -1063,13 +1213,26 @@ available_packages atoms else (OpamSolution.check_availability t available_packages atoms; - available_packages) in - let t = {t with available_packages = lazy available_packages} in + available_packages) + in + let opams = + if deps_only && ignore_conflicts then + (let pkgs = OpamFormula.packages_of_atoms available_packages atoms in + log "removing conflicts from %s" (OpamPackage.Set.to_string pkgs); + OpamPackage.Set.fold (fun pkg opams -> + let opam = + OpamFile.OPAM.with_conflicts Empty (OpamSwitchState.opam t pkg) + in + OpamPackage.Map.add pkg opam opams) + pkgs t.opams) + else t.opams + in + let t = {t with available_packages = lazy available_packages; opams} in if pkg_new = [] && OpamPackage.Set.is_empty pkg_reinstall then t else let t, atoms = if assume_built then - assume_built_restrictions t atoms + assume_built_restrictions ~available_packages t atoms else t, atoms in let request = OpamSolver.request ~install:atoms () in @@ -1083,48 +1246,97 @@ let t, solution = match solution with | Conflicts cs -> log "conflict!"; - OpamConsole.msg "%s" - (OpamCudf.string_of_conflict t.packages - (OpamSwitchState.unavailable_reason t) cs); - t, No_solution - | Success solution -> + OpamConsole.error "Package conflict!"; + let (conflicts, _cycles) as explanations = + OpamCudf.conflict_explanations_raw t.packages cs + in + let has_missing_depexts = + let check = function + | `Missing (_, _, fdeps) -> + OpamFormula.fold_right (fun a x -> + match OpamSwitchState.unavailable_reason_raw t x with + | `MissingDepexts _ -> true + | _ -> a) + false fdeps + | _ -> false + in + List.exists check conflicts + in + let extra_message = + if has_missing_depexts then + OpamStd.Option.map_default (fun s -> s ^ ".\n\n") "" + (OpamSysInteract.repo_enablers ()) + else + "" + in + OpamConsole.errmsg "%s%s" + (OpamCudf.string_of_explanations + (OpamSwitchState.unavailable_reason t) explanations) + extra_message; + t, if depext_only then None else Some (Conflicts cs) + | Success full_solution -> let solution = if deps_only then OpamSolver.filter_solution (fun nv -> not (OpamPackage.Name.Set.mem nv.name names)) - solution - else solution in + full_solution + else full_solution in + if depext_only then + (OpamSolution.install_depexts ~force_depext:true ~confirm:false t + (OpamSolver.all_packages solution)), None + else let add_roots = - OpamStd.Option.map (function - | true -> names - | false -> OpamPackage.Name.Set.empty) - add_to_roots + if deps_only && add_to_roots <> Some false then + let requested_deps = + OpamPackage.Set.fold (fun nv acc -> + OpamFormula.ors [ + OpamPackageVar.all_depends t (OpamSwitchState.opam t nv) + ~depopts:false ~build:true ~post:false; + acc + ]) + (OpamPackage.packages_of_names + (OpamSolver.all_packages full_solution) + names) + OpamFormula.Empty + in + Some (OpamPackage.names_of_packages + (OpamFormula.packages + (OpamSolver.all_packages solution) requested_deps)) + else + OpamStd.Option.map (function + | true -> names + | false -> OpamPackage.Name.Set.empty) + add_to_roots in - OpamSolution.apply ?ask t Install ~requested:names ?add_roots - ~assume_built solution + let t, res = + OpamSolution.apply ?ask t ~requested:names ?add_roots + ~download_only ~assume_built solution in + t, Some (Success res) in - OpamSolution.check_solution t solution; + OpamStd.Option.iter (OpamSolution.check_solution t) solution; t let install t ?autoupdate ?add_to_roots - ?(deps_only=false) ?(assume_built=false) names = + ?(deps_only=false) ?(ignore_conflicts=false) ?(assume_built=false) + ?(download_only=false) ?(depext_only=false) names = let atoms = OpamSolution.sanitize_atom_list ~permissive:true t names in let autoupdate_atoms = match autoupdate with | None -> atoms | Some a -> OpamSolution.sanitize_atom_list ~permissive:true t a in let t = update_dev_packages_t autoupdate_atoms t in - install_t t atoms add_to_roots ~deps_only ~assume_built + install_t t atoms add_to_roots + ~ignore_conflicts ~depext_only ~deps_only ~download_only ~assume_built let remove_t ?ask ~autoremove ~force atoms t = log "REMOVE autoremove:%b %a" autoremove (slog OpamFormula.string_of_atoms) atoms; let t, full_orphans, orphan_versions = - let changes = - if autoremove then None - else Some (OpamSwitchState.packages_of_atoms t atoms) in - orphans ?changes t + if atoms = [] then t, OpamPackage.Set.empty, OpamPackage.Set.empty + else + let changes = OpamSwitchState.packages_of_atoms t atoms in + orphans ~changes t in let nothing_to_do = ref true in @@ -1160,18 +1372,17 @@ Remove in let to_remove = - OpamPackage.Set.of_list - (OpamSolver.reverse_dependencies ~build:true ~post:true - ~depopts:false ~installed:true universe packages) in + OpamSolver.reverse_dependencies ~build:true ~post:true + ~depopts:false ~installed:true universe packages + in let to_keep = (if autoremove then t.installed_roots %% t.installed else t.installed) ++ universe.u_base - -- to_remove -- full_orphans -- orphan_versions + -- to_remove in let to_keep = - OpamPackage.Set.of_list - (OpamSolver.dependencies ~build:true ~post:true - ~depopts:true ~installed:true universe to_keep) in + OpamSolver.dependencies ~build:true ~post:true + ~depopts:true ~installed:true universe to_keep in (* to_keep includes the depopts, because we don't want to autoremove them. But that may re-include packages that we wanted removed, so we need to remove them again *) @@ -1179,17 +1390,17 @@ let requested = OpamPackage.names_of_packages packages in let to_remove = if autoremove then - let to_remove = t.installed -- to_keep in - if atoms = [] then to_remove + let to_remove1 = t.installed -- to_keep in + if atoms = [] then to_remove1 else (* restrict to the dependency cone of removed pkgs *) - to_remove %% - (OpamPackage.Set.of_list - (OpamSolver.dependencies ~build:true ~post:true - ~depopts:true ~installed:true universe to_remove)) + to_remove1 %% + (OpamSolver.dependencies ~build:true ~post:true + ~depopts:true ~installed:true universe to_remove) else to_remove in let t, solution = - OpamSolution.resolve_and_apply ?ask t Remove ~requested - ~orphans:(full_orphans ++ orphan_versions) + OpamSolution.resolve_and_apply ?ask t Remove + ~force_remove:force + ~requested ~orphans:(full_orphans ++ orphan_versions) (OpamSolver.request ~install:(OpamSolution.eq_atoms_of_packages to_keep) ~remove:(OpamSolution.atoms_of_packages to_remove) @@ -1268,16 +1479,39 @@ open OpamPinCommand let post_pin_action st was_pinned names = - let names = - OpamPackage.Set.Op.(st.pinned -- was_pinned) - |> OpamPackage.names_of_packages - |> (fun s -> - List.fold_left - (fun s p -> OpamPackage.Name.Set.add p s) - s names) - |> OpamPackage.Name.Set.elements + let pkgs = + let newly = st.pinned -- was_pinned in + let old = + OpamPackage.packages_of_names was_pinned + OpamPackage.Name.Set.Op.( + OpamPackage.Name.Set.of_list names + -- OpamPackage.names_of_packages newly) + in + newly ++ old + in + let no_depexts = + not (OpamFile.Config.depext st.switch_global.config) + || OpamSysPkg.Set.is_empty + ((OpamPackage.Set.fold (fun pkg acc -> + OpamSysPkg.Set.union acc (OpamSwitchState.depexts st pkg))) + pkgs OpamSysPkg.Set.empty) in try + let st = + if no_depexts then st else + let st = + { st with sys_packages = lazy ( + OpamPackage.Map.union (fun _ n -> n) + (Lazy.force st.sys_packages) + (OpamSwitchState.depexts_status_of_packages st pkgs) + )} + in + { st with available_packages = lazy ( + OpamPackage.Set.filter (fun nv -> + OpamSwitchState.depexts_unavailable st nv = None) + (Lazy.force st.available_packages) + )} + in upgrade_t ~strict_upgrade:false ~auto_install:true ~ask:true ~terse:true ~all:false @@ -1307,18 +1541,38 @@ "No package named %S found" (OpamPackage.Name.to_string name) - let pin st name ?(edit=false) ?version ?(action=true) target = + let pin st name ?(edit=false) ?version ?(action=true) ?subpath ?locked target = try let pinned = st.pinned in let st = match target with - | `Source url -> source_pin st name ?version ~edit (Some url) + | `Source url -> source_pin st name ?version ~edit ?subpath ?locked (Some url) | `Version v -> let st = version_pin st name v in if edit then OpamPinCommand.edit st name else st + | `Source_version (srcv, version) -> + let url = + let nv = (OpamPackage.create name srcv) in + match OpamPackage.Map.find_opt nv st.repos_package_index with + | Some opam -> + (match + OpamStd.Option.Op.(OpamFile.OPAM.url opam >>| OpamFile.URL.url) + with + | Some u -> u + | None -> + OpamConsole.error_and_exit `Not_found + "Package %s has no url defined in its opam file description" + (OpamPackage.to_string nv)) + | None -> + OpamConsole.error_and_exit `Not_found + "Package %s has no known version %s in the repositories" + (OpamPackage.Name.to_string name) + (OpamPackage.Version.to_string version) + in + source_pin st name ~version ~edit ?locked (Some url) | `Dev_upstream -> - source_pin st name ?version ~edit (Some (get_upstream st name)) - | `None -> source_pin st name ?version ~edit None + source_pin st name ?version ~edit ?locked (Some (get_upstream st name)) + | `None -> source_pin st name ?version ~edit ?locked None in if action then (OpamConsole.msg "\n"; post_pin_action st pinned [name]) else st @@ -1326,7 +1580,44 @@ | OpamPinCommand.Aborted -> OpamStd.Sys.exit_because `Aborted | OpamPinCommand.Nothing_to_do -> st - let edit st ?(action=true) ?version name = + let url_pins st ?edit ?(action=true) ?locked ?(pre=fun _ -> ()) pins = + let names = List.map (fun (n,_,_,_,_) -> n) pins in + (match names with + | _::_::_ -> + if not (OpamConsole.confirm + "This will pin the following packages: %s. Continue?" + (OpamStd.List.concat_map ", " OpamPackage.Name.to_string names)) + then + OpamStd.Sys.exit_because `Aborted + | _ -> ()); + let pins = + let urls_ok = + OpamPinCommand.fetch_all_pins st + (List.map (fun (name, _, _, url, subpath) -> + name, url, subpath) pins) + in + List.filter (fun (_,_,_, url, subpath) -> + List.mem (url, subpath) urls_ok) + pins + in + let pinned = st.pinned in + let st = + List.fold_left (fun st (name, version, opam, url, subpath as pin) -> + pre pin; + try + OpamPinCommand.source_pin st name ?version ?opam + ?edit ?subpath ?locked (Some url) + with + | OpamPinCommand.Aborted -> OpamStd.Sys.exit_because `Aborted + | OpamPinCommand.Nothing_to_do -> st) + st pins + in + if action then + (OpamConsole.msg "\n"; + post_pin_action st pinned names) + else st + + let edit st ?(action=true) ?version ?locked name = let pinned = st.pinned in let st = if OpamPackage.has_name st.pinned name then @@ -1351,7 +1642,7 @@ OpamStd.Option.Op.(OpamSwitchState.url st nv >>| OpamFile.URL.url) in let opam = OpamPackage.Map.find_opt nv st.repos_package_index in - try source_pin st name ~edit:true ?version ?opam target + try source_pin st name ~edit:true ?version ?opam ?locked target with OpamPinCommand.Aborted -> OpamStd.Sys.exit_because `Aborted | OpamPinCommand.Nothing_to_do -> st else @@ -1366,14 +1657,10 @@ let unpin st ?(action=true) names = let pinned_before = st.pinned in let st = unpin st names in - let available = Lazy.force st.available_packages in let installed_unpinned = (pinned_before -- st.pinned) %% st.installed in if action && not (OpamPackage.Set.is_empty installed_unpinned) then let atoms = - OpamPackage.Set.fold (fun nv acc -> - if OpamPackage.Set.mem nv available then - (nv.name, Some (`Eq, nv.version)) :: acc - else (nv.name, None) :: acc) + OpamPackage.Set.fold (fun nv acc -> (nv.name, None) :: acc) installed_unpinned [] in upgrade_t diff -Nru opam-2.0.10/src/client/opamClient.mli opam-2.1.2/src/client/opamClient.mli --- opam-2.0.10/src/client/opamClient.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamClient.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -15,7 +15,9 @@ open OpamTypes open OpamStateTypes -(** Initialize the client to a consistent state. *) +(** Initialize the client to a consistent state. + Returns the initial state and, in case a switch is to be created, its + initial set of packages *) val init: init_config:OpamFile.InitConfig.t -> interactive:bool -> @@ -25,8 +27,9 @@ ?update_config:bool -> ?env_hook:bool -> ?completion:bool -> + ?check_sandbox:bool -> shell -> - rw global_state * unlocked repos_state * formula + rw global_state * unlocked repos_state * atom list (* (\** Gets the initial config (opamrc) to be used *\) * val get_init_config: @@ -38,9 +41,9 @@ (defaults to [OpamInitDefaults.init_config]) for the settings that are unset, and updates all repositories *) val reinit: - ?init_config:OpamFile.InitConfig.t -> - interactive:bool -> - ?dot_profile:filename -> ?update_config:bool -> ?env_hook:bool -> ?completion:bool -> + ?init_config:OpamFile.InitConfig.t -> interactive:bool -> ?dot_profile:filename -> + ?update_config:bool -> ?env_hook:bool -> ?completion:bool -> ?inplace:bool -> + ?check_sandbox:bool -> ?bypass_checks:bool -> OpamFile.Config.t -> shell -> unit (** Install the given list of packages. [add_to_roots], if given, specifies that @@ -50,15 +53,24 @@ val install: rw switch_state -> ?autoupdate:atom list -> ?add_to_roots:bool -> ?deps_only:bool -> - ?assume_built:bool -> atom list -> rw switch_state + ?ignore_conflicts:bool -> ?assume_built:bool -> ?download_only:bool -> + ?depext_only:bool -> atom list -> + rw switch_state (** Low-level version of [reinstall], bypassing the package name sanitization and dev package update, and offering more control *) val install_t: - rw switch_state -> ?ask:bool -> + rw switch_state -> + ?ask:bool -> ?ignore_conflicts:bool -> ?depext_only:bool -> ?download_only:bool -> atom list -> bool option -> deps_only:bool -> assume_built:bool -> rw switch_state +(** Check that the given list of packages [atoms] have their dependencies + satisfied, without calling the solver. Returns missing dependencies. *) +val check_installed: + build:bool -> post:bool -> rw switch_state -> atom list -> + OpamPackage.Name.Set.t OpamPackage.Map.t + (** Reinstall the given set of packages. *) val reinstall: rw switch_state -> ?assume_built:bool -> atom list -> rw switch_state @@ -83,7 +95,9 @@ versions. The specified atoms are kept installed (or newly installed after a confirmation). The upgrade concerns them only unless [all] is specified. *) val upgrade: - rw switch_state -> ?check:bool -> all:bool -> atom list -> rw switch_state + rw switch_state -> + ?check:bool -> ?only_installed:bool -> + all:bool -> atom list -> rw switch_state (** Low-level version of [upgrade], bypassing the package name sanitization and dev package update, and offering more control. [terse] avoids the verbose @@ -91,6 +105,7 @@ val upgrade_t: ?strict_upgrade:bool -> ?auto_install:bool -> ?ask:bool -> ?check:bool -> ?terse:bool -> + ?only_installed:bool -> all:bool -> atom list -> rw switch_state -> rw switch_state (** Recovers from an inconsistent universe *) @@ -108,12 +123,25 @@ val pin: rw switch_state -> OpamPackage.Name.t -> - ?edit:bool -> ?version:version -> ?action:bool -> - [< `Source of url | `Version of version | `Dev_upstream | `None ] -> + ?edit:bool -> ?version:version -> ?action:bool -> ?subpath:string -> + ?locked:bool -> + [< `Source of url | `Version of version | `Dev_upstream + | `Source_version of version * version + (* the first version is the source one, the second the package one *) + | `None ] -> rw switch_state val edit: - rw switch_state -> ?action:bool -> ?version:version -> OpamPackage.Name.t -> + rw switch_state -> + ?action:bool -> ?version:version -> ?locked:bool -> + OpamPackage.Name.t -> + rw switch_state + + val url_pins: + rw switch_state -> ?edit:bool -> ?action:bool -> ?locked:bool -> + ?pre:((name * version option * OpamFile.OPAM.t option * url * string option) + -> unit) -> + (name * version option * OpamFile.OPAM.t option * url * string option) list -> rw switch_state val unpin: @@ -129,3 +157,25 @@ val post_pin_action: rw switch_state -> package_set -> name list -> rw switch_state end + + +(** {2 Auxiliary functions} + These functions are exposed for advanced uses by external libraries +*) + +(** Orphan packages are installed but no longer available packages; we add + special treatment so that opam doesn't force their removal for consistency + reasons on any action. Returns the "fixed" state, fully orphan packages (no + available version of the package remaining), and orphan package versions. + + Find more technical explanations in the source. *) +val orphans: + ?changes:package_set -> ?transitive:bool -> + 'a switch_state -> + 'a switch_state * package_set * package_set + +(** An extended version of [orphans] that checks for conflicts between a given + request and the orphan packages *) +val check_conflicts: + 'a switch_state -> atom list -> + 'a switch_state * package_set * package_set diff -Nru opam-2.0.10/src/client/opamCliMain.ml opam-2.1.2/src/client/opamCliMain.ml --- opam-2.0.10/src/client/opamCliMain.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamCliMain.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,477 @@ +(**************************************************************************) +(* *) +(* Copyright 2012-2019 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open Cmdliner +open OpamTypes +open OpamStateTypes +open OpamTypesBase +open OpamStd.Op + +exception InvalidCLI of OpamCLIVersion.Sourced.t + +(* [InvalidFlagContent (flag_name, Some (invalid_value, expected_value))] *) +exception InvalidFlagContent of string * (string * string) option + +(* [InvalidNewFlag (requested_cli, flag_name, flag_valid_since)] *) +exception InvalidNewFlag of OpamCLIVersion.Sourced.t * string * OpamCLIVersion.t + +let raise_invalid_cli : + (OpamCLIVersion.Sourced.t, string option) OpamCompat.Result.t -> 'a + = function + | Ok ocli -> raise (InvalidCLI ocli) + | Error None -> raise (InvalidFlagContent ("cli", None)) + | Error (Some invalid) -> + raise (InvalidFlagContent ("cli", Some (invalid, "major.minor"))) + +let raise_invalid_confirm_level invalid = + let invalid = + OpamStd.Option.map (fun i -> + i, "one of " ^ + (OpamArg.confirm_enum + |> List.map (fun (_,s,_) -> Printf.sprintf "`%s'" s) + |> OpamStd.Format.pretty_list ~last:"or")) + invalid + in + raise (InvalidFlagContent ("confirm-level", invalid)) + +(* Filter and parse "--cli=v" or "--cli v" options *) +let rec filter_cli_arg cli acc args = + match args with + | [] + | "--" :: _ -> (cli, List.rev_append acc args) + | "--cl" :: args -> filter_cli_arg cli acc ("--cli"::args) + | ["--cli"] | "--cli" :: "--" :: _ -> raise_invalid_cli (Error None) + | "--cli" :: arg :: args -> + let version = + match OpamCLIVersion.of_string_opt arg with + | Some cli -> + let ocli = cli, `Command_line in + if OpamCLIVersion.is_supported cli then ocli else + raise_invalid_cli (Ok ocli) + | None -> raise_invalid_cli (Error (Some arg)) + in + filter_cli_arg (Some version) acc args + | arg :: args -> + match OpamStd.String.cut_at arg '=' with + | Some ("--cl", value) + | Some ("--cli", value) -> + filter_cli_arg cli acc ("--cli"::value::args) + | _ -> + filter_cli_arg cli (arg::acc) args + +let is_confirm_level = + OpamStd.String.is_prefix_of ~from:4 ~full:"--confirm-level" + +(* Pre-process argv processing the --yes, --confirm-level, and --cli. Returns + Some cli, if --cli was encountered, a boolean indicating if --yes/-y and + the list of arguments to continue with processing. *) +let rec preprocess_argv cli yes_args confirm args = + let yes = yes_args <> [] in + match args with + | [] -> + (cli, yes, confirm, yes_args) + | "--" :: _ -> + (cli, yes, confirm, yes_args @ args) + (* Note that because this is evaluated before a sub-command, all the + prefixes of --yes are assumed to valid at all times. *) + | ("-y" | "--y" | "--ye" | "--yes") as yes_opt :: args -> + preprocess_argv cli [yes_opt] confirm args + | ([c] | c :: "--" :: _) when is_confirm_level c -> + raise_invalid_confirm_level None + | confirm_level :: cl_arg :: args when is_confirm_level confirm_level -> + let answer = + match OpamStd.List.find_opt (fun (_,n,_) -> n = cl_arg) + OpamArg.confirm_enum with + | Some (_, _, a) -> a + | None -> raise_invalid_confirm_level (Some cl_arg) + in + preprocess_argv cli yes_args (Some answer) args + | "--cl" :: args -> preprocess_argv cli yes_args confirm ("--cli"::args) + | ["--cli"] | "--cli" :: "--" :: _ -> raise_invalid_cli (Error None) + | "--cli" :: arg :: args -> + let version = + match OpamCLIVersion.of_string_opt arg with + | Some cli -> + let ocli = cli, `Command_line in + if OpamCLIVersion.is_supported cli then ocli else + raise_invalid_cli (Ok ocli) + | _ -> raise_invalid_cli (Error (Some arg)) + in + preprocess_argv (Some version) yes_args confirm args + | arg :: rest -> + match OpamStd.String.cut_at arg '=' with + | Some ("--cl", value) + | Some ("--cli", value) -> + preprocess_argv cli yes_args confirm ("--cli"::value::rest) + | Some (pre, value) when is_confirm_level pre -> + preprocess_argv cli yes_args confirm ("--confirm-level"::value::rest) + | _ -> + if OpamCommands.is_builtin_command arg then + let (cli, rest) = filter_cli_arg cli [] rest in + (cli, yes, confirm, arg :: (yes_args @ rest)) + else + (cli, yes, confirm, args) + +(* Handle git-like plugins *) +let check_and_run_external_commands () = + (* Pre-process the --yes and --cli options *) + let (cli, yes, confirm_level, argv) = + match Array.to_list Sys.argv with + | prog::args -> + let (ocli, yes, confirm, args) = preprocess_argv None [] None args in + let ocli = + match ocli with + | Some ((cli, _) as ocli) -> + if OpamCLIVersion.(cli < (2, 1)) then begin + let cli = OpamCLIVersion.to_string cli in + OpamConsole.warning + "%s cannot be understood by opam %s; set %s to %s instead." + (OpamConsole.colorise `bold ("--cli=" ^ cli)) cli + (OpamConsole.colorise `bold "OPAMCLI") (OpamConsole.colorise `bold cli) + end; + ocli + | None -> + match OpamCLIVersion.Sourced.env (OpamClientConfig.E.cli ()) with + | Some ((cli, _) as ocli) -> + if OpamCLIVersion.is_supported cli then + let () = + if OpamCLIVersion.(cli >= (2, 1)) then + let flag = "--cli=" ^ OpamCLIVersion.(to_string cli) in + OpamConsole.warning + "OPAMCLI should only ever be set to %s - use '%s' instead." + (OpamConsole.colorise `bold "2.0") + (OpamConsole.colorise `bold flag) + in + ocli + else + raise_invalid_cli (Ok ocli) + | None -> + OpamCLIVersion.Sourced.current + in + let confirm = + (* hardcoded cli validation *) + match confirm with + | Some _ when OpamCLIVersion.(fst ocli < (2,1)) -> + raise (InvalidNewFlag (ocli, "confirm-level", + OpamCLIVersion.of_string "2.1")) + | _ -> confirm + in + (ocli, yes, confirm, prog::args) + | args -> (OpamCLIVersion.Sourced.current, false, None, args) + in + match argv with + | [] | [_] -> (cli, argv) + | _ :: name :: args -> + if String.length name > 0 && name.[0] = '-' + || OpamCommands.is_builtin_command name + then (cli, argv) + else + (* No such command, check if there is a matching plugin *) + let command = OpamPath.plugin_prefix ^ name in + OpamArg.init_opam_env_variabes cli; + (* `--no` is not taken into account, only `--yes/--confirm-lzvel` are + preprocessed *) + let yes = if yes then Some (Some true) else None in + OpamCoreConfig.init ?yes ?confirm_level (); + OpamFormatConfig.init (); + let root_dir = OpamStateConfig.opamroot () in + let has_init, root_upgraded = + match OpamStateConfig.load_defaults ~lock_kind:`Lock_read root_dir with + | None -> (false, false) + | Some config -> + let root_upgraded = + let cmp = + OpamVersion.compare OpamFile.Config.root_version + (OpamFile.Config.opam_root_version config) + in + if cmp < 0 then + OpamConsole.error_and_exit `Configuration_error + "%s reports a newer opam version, aborting." + (OpamFilename.Dir.to_string root_dir) + else + cmp = 0 + in + (true, root_upgraded) + in + let plugins_bin = OpamPath.plugins_bin root_dir in + let plugin_symlink_present = + OpamFilename.is_symlink (OpamPath.plugin_bin root_dir (OpamPackage.Name.of_string name)) + in + let env = + if has_init then + let updates = + ["PATH", OpamParserTypes.PlusEq, + OpamFilename.Dir.to_string plugins_bin, None] + in + OpamStateConfig.init ~root_dir (); + match OpamStateConfig.get_switch_opt () with + | None -> env_array (OpamEnv.get_pure ~updates ()) + | Some sw -> + env_array + (OpamEnv.full_with_path ~force_path:false ~updates root_dir sw) + else + Unix.environment () + in + match OpamSystem.resolve_command ~env command with + | Some command when plugin_symlink_present && root_upgraded -> + let argv = Array.of_list (command :: args) in + raise (OpamStd.Sys.Exec (command, argv, env)) + | None when not has_init -> (cli, argv) + | cmd -> + (* Look for a corresponding package *) + match OpamStateConfig.get_switch_opt () with + | None -> (cli, argv) + | Some sw -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamSwitchState.with_ `Lock_none gt ~switch:sw @@ fun st -> + let prefixed_name = OpamPath.plugin_prefix ^ name in + let candidates = + OpamPackage.packages_of_names + (Lazy.force st.available_packages) + (OpamPackage.Name.Set.of_list @@ + (OpamStd.List.filter_map + (fun s -> + try Some (OpamPackage.Name.of_string s) + with Failure _ -> None) + [ prefixed_name; name ])) + in + let plugins = + OpamPackage.Set.filter (fun nv -> + OpamFile.OPAM.has_flag Pkgflag_Plugin (OpamSwitchState.opam st nv)) + candidates + in + let installed = OpamPackage.Set.inter plugins st.installed in + if OpamPackage.Set.is_empty candidates then (cli, argv) + else if not OpamPackage.Set.(is_empty installed) && cmd = None then + (OpamConsole.error + "Plugin %s is already installed, but no %s command was found.\n\ + Try upgrading, and report to the package maintainer if \ + the problem persists." + (OpamPackage.to_string (OpamPackage.Set.choose installed)) + command; + exit (OpamStd.Sys.get_exit_code `Package_operation_error)) + else if OpamPackage.Set.is_empty plugins then + (OpamConsole.error + "%s is not a known command or plugin (package %s does \ + not have the 'plugin' flag set)." + name + (OpamPackage.to_string (OpamPackage.Set.max_elt candidates)); + exit (OpamStd.Sys.get_exit_code `Bad_arguments)) + else if + (if cmd = None then + OpamConsole.confirm "Opam plugin \"%s\" is not installed. \ + Install it on the current switch?" + else + OpamConsole.confirm "Opam plugin \"%s\" may require upgrading/reinstalling. \ + Reinstall the plugin on the current switch?") name + then + let nv = + try + (* If the command was resolved, attempt to find the package to reinstall. *) + if cmd = None then + raise Not_found + else + OpamPackage.package_of_name installed (OpamPackage.Name.of_string prefixed_name) + with Not_found -> + try + OpamPackage.max_version plugins + (OpamPackage.Name.of_string prefixed_name) + with Not_found -> + OpamPackage.max_version plugins + (OpamPackage.Name.of_string name) + in + OpamRepositoryConfig.init (); + OpamSolverConfig.init (); + OpamClientConfig.init (); + OpamSwitchState.with_ `Lock_write gt (fun st -> + OpamSwitchState.drop @@ ( + if cmd = None then + OpamClient.install st [OpamSolution.eq_atom_of_package nv] + else if root_upgraded then + OpamClient.reinstall st [OpamSolution.eq_atom_of_package nv] + else + OpamClient.upgrade st ~all:false [OpamSolution.eq_atom_of_package nv]) + ); + match OpamSystem.resolve_command ~env command with + | None -> + OpamConsole.error_and_exit `Package_operation_error + "Plugin %s was installed, but no %s command was found.\n\ + This is probably an error in the plugin package." + (OpamPackage.to_string nv) + command + | Some command -> + OpamConsole.header_msg "Carrying on to \"%s\"" + (String.concat " " (Array.to_list Sys.argv)); + OpamConsole.msg "\n"; + let argv = Array.of_list (command :: args) in + raise (OpamStd.Sys.Exec (command, argv, env)) + else (cli, argv) + +let display_cli_error msg = + Format.eprintf + "@[opam: @[%a@]@,@[Usage: @[opam COMMAND ...@]@]@,\ + Try `opam --help' for more information.@]@." + Format.pp_print_text msg + +let display_cli_error fmt = + Format.ksprintf display_cli_error fmt + +let rec main_catch_all f = + try f () with + | OpamStd.Sys.Exit 0 -> () + | OpamStd.Sys.Exec (cmd,args,env) -> + OpamStd.Sys.exec_at_exit (); + if Sys.win32 then + OpamProcess.create_process_env cmd args env + Unix.stdin Unix.stdout Unix.stderr + |> Unix.waitpid [] + |> function + | _, Unix.WEXITED n -> exit n + | _, (Unix.WSIGNALED n | Unix.WSTOPPED n) -> exit (128 - n) + (* This is not how you should handle `WSTOPPED` ; but it doesn't happen on + Windows anyway. *) + else + Unix.execvpe cmd args env + | OpamFormatUpgrade.Upgrade_done (conf, reinit) -> + main_catch_all @@ fun () -> + OpamConsole.header_msg "Rerunning init and update"; + (match reinit with + | Some reinit -> + reinit conf; + OpamConsole.msg "Update done.\n"; + exit (OpamStd.Sys.get_exit_code `Success) + | None -> + OpamClient.reinit ~interactive:true ~update_config:false + ~bypass_checks:true conf (OpamStd.Sys.guess_shell_compat ()); + OpamConsole.msg + "Update done, please now retry your command.\n"; + exit (OpamStd.Sys.get_exit_code `Aborted)) + | e -> + flush stdout; + flush stderr; + if (OpamConsole.verbose ()) then + OpamConsole.errmsg "'%s' failed.\n" + (String.concat " " (Array.to_list Sys.argv)); + let exit_code = match e with + | OpamStd.Sys.Exit i -> + if (OpamConsole.debug ()) && i <> 0 then + OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); + i + | OpamSystem.Internal_error _ -> + OpamConsole.errmsg "%s\n" (Printexc.to_string e); + OpamStd.Sys.get_exit_code `Internal_error + | OpamSystem.Process_error result -> + OpamConsole.errmsg "%s Command %S failed:\n%s\n" + (OpamConsole.colorise `red "[ERROR]") + (try List.assoc "command" result.OpamProcess.r_info with + | Not_found -> "") + (Printexc.to_string e); + OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); + OpamStd.Sys.get_exit_code `Internal_error + | Sys.Break + | OpamParallel.Errors (_, (_, Sys.Break)::_, _) -> + OpamStd.Sys.get_exit_code `User_interrupt + | Sys_error e when e = "Broken pipe" -> + (* workaround warning 52, this is a fallback (we already handle the + signal) and there is no way around at the moment *) + 141 + | InvalidCLI (cli, source) -> + (* Unsupported CLI version *) + let suffix = + if source = `Env then + " Please fix the value of the OPAMCLI environment variable, \ + or use the '--cli .' flag" + else + "" + in + OpamConsole.error "opam command-line version %s is not supported.%s" + (OpamCLIVersion.to_string cli) suffix; + OpamStd.Sys.get_exit_code `Bad_arguments + | InvalidFlagContent (flag, None) -> + (* No argument given to flag *) + display_cli_error "option `--%s' needs an argument" flag; + OpamStd.Sys.get_exit_code `Bad_arguments + | InvalidFlagContent (flag, Some (invalid, expected)) -> + (* Wrong argument kind given to flag *) + display_cli_error + "option `--%s': invalid value `%s', expected %s" + flag invalid expected; + OpamStd.Sys.get_exit_code `Bad_arguments + | InvalidNewFlag ((req_cli, _), flag, flag_cli) -> + (* Requested cli is older than flag introduction *) + display_cli_error + "--%s was added in version %s of the opam CLI, \ + but version %s has been requested, which is older." + flag (OpamCLIVersion.to_string flag_cli) + (OpamCLIVersion.to_string req_cli); + OpamStd.Sys.get_exit_code `Bad_arguments + | Failure msg -> + OpamConsole.errmsg "Fatal error: %s\n" msg; + OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); + OpamStd.Sys.get_exit_code `Internal_error + | _ -> + OpamConsole.errmsg "Fatal error:\n%s\n" (Printexc.to_string e); + OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); + OpamStd.Sys.get_exit_code `Internal_error + in + exit exit_code + +let run () = + OpamStd.Option.iter OpamVersion.set_git OpamGitVersion.version; + OpamSystem.init (); + OpamArg.preinit_opam_env_variables (); + main_catch_all @@ fun () -> + let cli, argv = check_and_run_external_commands () in + let (default, commands), argv1 = + match argv with + | prog :: command :: argv when OpamCommands.is_admin_subcommand command -> + OpamAdminCommand.get_cmdliner_parser cli, prog::argv + | _ -> + OpamCommands.get_cmdliner_parser cli, argv + in + let argv = Array.of_list argv1 in + match Term.eval_choice ~catch:false ~argv default commands with + | `Error _ -> exit (OpamStd.Sys.get_exit_code `Bad_arguments) + | _ -> exit (OpamStd.Sys.get_exit_code `Success) + +let json_out () = + match OpamClientConfig.(!r.json_out) with + | None -> () + | Some s -> + let file_name () = + match OpamStd.String.cut_at s '%' with + | None -> OpamFilename.of_string s + | Some (pfx, sfx) -> + let rec getname i = + let f = OpamFilename.of_string (Printf.sprintf "%s%d%s" pfx i sfx) in + if OpamFilename.exists f then getname (i+1) else f + in + getname 1 + in + try + let f = OpamFilename.open_out (file_name ()) in + OpamJson.flush f; + close_out f + with e -> + OpamConsole.warning "Couldn't write json log: %s" + (Printexc.to_string e) + +let main () = + OpamStd.Sys.at_exit (fun () -> + flush stderr; + flush stdout; + if OpamClientConfig.(!r.print_stats) then ( + OpamFile.Stats.print (); + OpamSystem.print_stats (); + ); + json_out () + ); + run () diff -Nru opam-2.0.10/src/client/opamCliMain.mli opam-2.1.2/src/client/opamCliMain.mli --- opam-2.0.10/src/client/opamCliMain.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamCliMain.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,34 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** Handles calling opam plugins (à la git). E.g. [opam publish] runs + [opam-publish] from PATH, with specific addition of OpamPath.plugins_bin and + the current switch bin directory). + + Note that this does load some configuration and env, but only handles a + leading [--yes] argument. + @raise InvalidCLI *) +val check_and_run_external_commands: + unit -> OpamCLIVersion.Sourced.t * string list + +(** Handles flushing buffers and catching exceptions from the main call, + including special cases like [OpamStd.Sys.Exec] that is expected to do a + [Unix.exec], but after all proper cleanup has been done. *) +val main_catch_all: (unit -> unit) -> unit + +(** Handling of debug JSON output, according to [OpamClientConfig.json_out] *) +val json_out: unit -> unit + +(** [run default command_list] runs command-line argument parsing and processing + of the command *) +val run: unit -> unit + +(** Default entry point with handling of debug finalisers *) +val main: unit -> unit diff -Nru opam-2.0.10/src/client/opamCLIVersion.ml opam-2.1.2/src/client/opamCLIVersion.ml --- opam-2.0.10/src/client/opamCLIVersion.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamCLIVersion.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,83 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 David Allsopp Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamCompat + +type t = int * int + +let supported_versions = [(2, 0); (2, 1)] + +let is_supported v = List.mem v supported_versions + +let of_string s = + match String.index s '.' with + | i when s.[0] <> '0' && (i >= String.length s - 2 || s.[i + 1] <> '0') -> + begin + try Scanf.sscanf s "%u.%u%!" (fun major minor -> (major, minor)) + with Scanf.Scan_failure _ -> failwith "OpamVersion.CLI.of_string" + end + | exception Not_found -> failwith "OpamVersion.CLI.of_string" + | _ -> failwith "OpamVersion.CLI.of_string" + +let current = of_string @@ OpamVersion.(to_string current_nopatch) + +(* This line is checked on CI to ensure that default cli version + matches release opam version *) +let default = (2,0) + +let of_string_opt s = try Some (of_string s) with Failure _ -> None + +let to_string (major, minor) = Printf.sprintf "%d.%d" major minor + +let to_json v = `String (to_string v) +let of_json = function + | `String x -> of_string_opt x + | _ -> None + +let ( >= ) = Stdlib.( >= ) +let ( < ) = Stdlib.( < ) +let compare = Stdlib.compare + +let previous cli = + let f previous version = + if version > previous && cli > version then version else previous + in + let zero = (0, 0) in + let previous = List.fold_left f zero supported_versions in + if previous = zero then raise Not_found + else previous + +(* CLI version extended with provenance *) +module Sourced = struct + type nonrec t = t * OpamStateTypes.provenance + + let current = current, `Default + + let env s = + OpamStd.Option.Op.(s >>= of_string_opt >>| (fun c -> c, `Env)) + +end + +module Op = struct + let ( @>= ) (c,_) = Stdlib.( >= ) c + let ( @< ) (c,_) = Stdlib.( < ) c + let ( @= ) (c,_) = Stdlib.( = ) c +end + +module O = struct + type nonrec t = t + let to_string = to_string + let to_json = to_json + let of_json = of_json + let compare = compare +end + +module Set = OpamStd.Set.Make(O) +module Map = OpamStd.Map.Make(O) diff -Nru opam-2.0.10/src/client/opamCLIVersion.mli opam-2.1.2/src/client/opamCLIVersion.mli --- opam-2.0.10/src/client/opamCLIVersion.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamCLIVersion.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,58 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 David Allsopp Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** CLI Versions *) + +include OpamStd.ABSTRACT + +(** The current version of the CLI (major and minor of OpamVersion.current *) +val current : t + +(* Default CLI version, currently 2.0. + This value is checked in CI. *) +val default : t + +(** Tests whether a valid CLI version is supported by the client library *) +val is_supported : t -> bool + +(** ['a option] version of {!to_string} *) +val of_string_opt : string -> t option +val of_string : string -> t + +(** Comparison [>]] with [(major, minor)] *) +val ( >= ) : t -> int * int -> bool + +(** Comparison [<] with [(major, minor)] *) +val ( < ) : t -> int * int -> bool + +val compare : t -> t -> int + +(** Returns previous supported version. + @raise Not_found if there isn't one. *) +val previous: t -> t + +(* CLI version extended with provenance *) +module Sourced : sig + + type nonrec t = t * OpamStateTypes.provenance + + (** The current version of the CLI (major and minor of OpamVersion.current *) + val current : t + + (** Parse the given environment variable result as MAJOR.MINOR *) + val env: string option -> t option + +end + +module Op : sig + val (@<) : Sourced.t -> t -> bool + val (@=) : Sourced.t -> t -> bool + val (@>=) : Sourced.t -> t -> bool +end diff -Nru opam-2.0.10/src/client/opamCommands.ml opam-2.1.2/src/client/opamCommands.ml --- opam-2.0.10/src/client/opamCommands.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamCommands.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -48,7 +48,7 @@ (OpamVersion.to_string update_version) (OpamVersion.to_string OpamVersion.current) else ( - if OpamVersion.git () <> None then + if OpamVersion.is_dev_version () then OpamConsole.warning "Using opam self-upgrade to %s while the system \ opam is a development version (%s)" (OpamVersion.to_string update_version) @@ -72,39 +72,44 @@ updated_self_str (OpamVersion.to_string OpamVersion.current))) -let global_options = +let global_options cli = let no_self_upgrade = - mk_flag ~section:global_option_section ["no-self-upgrade"] + mk_flag ~cli cli_original ~section:global_option_section ["no-self-upgrade"] (Printf.sprintf "Opam will replace itself with a newer binary found \ at $(b,OPAMROOT%sopam) if present. This disables this behaviour." OpamArg.dir_sep) in let self_upgrade no_self_upgrade options = let self_upgrade_status = - if OpamStd.Config.env_string "NOSELFUPGRADE" = + if OpamClientConfig.E.noselfupgrade () = Some self_upgrade_bootstrapping_value then `Running else if no_self_upgrade then `Disable - else if OpamStd.Config.env_bool "NOSELFUPGRADE" = Some true then `Disable + else if OpamStd.Option.Op.((OpamClientConfig.E.noselfupgrade ()) + >>= OpamStd.Config.bool_of_string) + = Some true + then `Disable else `None in if self_upgrade_status = `None then switch_to_updated_self OpamStd.Option.Op.(options.debug_level ++ - OpamStd.Config.env_level "DEBUG" +! 0 > 0) + OpamCoreConfig.E.debug () +! 0 |> abs > 0) (OpamStateConfig.opamroot ?root_dir:options.opt_root ()); let root_is_ok = - OpamStd.Option.default false (OpamStd.Config.env_bool "ROOTISOK") + OpamStd.Option.default false (OpamClientConfig.E.rootisok ()) in if not (options.safe_mode || root_is_ok) && Unix.getuid () = 0 then OpamConsole.warning "Running as root is not recommended"; - options, self_upgrade_status + {options with cli = fst cli}, self_upgrade_status in - Term.(const self_upgrade $ no_self_upgrade $ global_options) + Term.(const self_upgrade $ no_self_upgrade $ global_options cli) -let apply_global_options (options,self_upgrade) = - apply_global_options options; +let apply_global_options cli (options,self_upgrade) = + apply_global_options cli options; + OpamConsole.log "CLI" "Parsing CLI version %s" + (OpamCLIVersion.to_string options.cli); try let argv0 = OpamFilename.of_string Sys.executable_name in if self_upgrade <> `Running && @@ -119,8 +124,6 @@ let self_upgrade_status global_options = snd global_options -type command = unit Term.t * Term.info - let get_init_config ~no_sandboxing ~no_default_config_file ~add_config_file = let builtin_config = OpamInitDefaults.init_config ~sandboxing:(not no_sandboxing) () @@ -150,10 +153,13 @@ | [] -> "" | [file] -> OpamFile.to_string file ^ " and then from " | _ -> - (OpamStd.List.concat_map ~nil:"" ~right:", and finally from " ", then " + (OpamStd.List.concat_map ~right:", and finally from " ", then " OpamFile.to_string (List.rev config_files)) in - OpamConsole.note "Will configure from %sbuilt-in defaults." others; + if config_files = [] then + OpamConsole.msg "No configuration file found, using built-in defaults.\n" + else + OpamConsole.msg "Configuring from %sbuilt-in defaults.\n" others; List.fold_left (fun acc f -> OpamFile.InitConfig.add acc (OpamFile.InitConfig.read f)) builtin_config config_files @@ -166,10 +172,10 @@ (* INIT *) let init_doc = "Initialize opam state, or set init options." -let init = +let init cli = let doc = init_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Initialise the opam state, or update opam init options"; `P (Printf.sprintf "The $(b,init) command initialises a local \"opam root\" (by default, \ @@ -187,8 +193,8 @@ `P "Additionally, this command allows one to customise some aspects of opam's \ shell integration, when run initially (avoiding the interactive \ dialog), but also at any later time."; - `S "ARGUMENTS"; - `S "OPTIONS"; + `S Manpage.s_arguments; + `S Manpage.s_options; `S "CONFIGURATION FILE"; `P (Printf.sprintf "Any field from the built-in initial configuration can be overridden \ @@ -196,15 +202,15 @@ $(i,--config). The default configuration for this version of opam \ can be obtained using $(b,--show-default-opamrc)." OpamArg.dir_sep); - `S OpamArg.build_option_section; - ] in + ] @ OpamArg.man_build_option_section + in let compiler = - mk_opt ["c";"compiler"] "PACKAGE" + mk_opt ~cli cli_original ["c";"compiler"] "PACKAGE" "Set the compiler to install (when creating an initial switch)" Arg.(some string) None in let no_compiler = - mk_flag ["bare"] + mk_flag ~cli cli_original ["bare"] "Initialise the opam state, but don't setup any compiler switch yet." in let repo_name = @@ -224,47 +230,47 @@ Arg.(value & pos ~rev:true 0 (some string) None & doc) in let interactive = - Arg.(value & vflag None [ - Some false, info ["a";"auto-setup"] ~doc: + mk_vflag ~cli None [ + cli_original, (Some false), ["a";"auto-setup"], "Automatically do a full setup, including adding a line to your \ shell init files."; - Some true, info ["i";"interactive"] ~doc: + cli_original, (Some true), ["i";"interactive"], "Run the setup interactively (this is the default for an initial \ run, or when no more specific options are specified)"; - ]) + ] in let update_config = - Arg.(value & vflag None [ - Some true, info ["shell-setup"] ~doc: + mk_vflag ~cli None [ + cli_original, (Some true), ["shell-setup"], "Automatically setup the user shell configuration for opam, e.g. \ adding a line to the `~/.profile' file."; - Some false, info ["n";"no-setup"] ~doc: + cli_original, (Some false), ["n";"no-setup"], "Do not update the user shell configuration to setup opam. Also \ implies $(b,--disable-shell-hook), unless $(b,--interactive) or \ specified otherwise"; - ]) + ] in let setup_completion = - Arg.(value & vflag None [ - Some true, info ["enable-completion"] ~doc: + mk_vflag ~cli None [ + cli_original, (Some true), ["enable-completion"], "Setup shell completion in opam init scripts, for supported \ shells."; - Some false, info ["disable-completion"] ~doc: + cli_original, (Some false), ["disable-completion"], "Disable shell completion in opam init scripts."; - ]) + ] in let env_hook = - Arg.(value & vflag None [ - Some true, info ["enable-shell-hook"] ~doc: + mk_vflag ~cli None [ + cli_original, (Some true), ["enable-shell-hook"], "Setup opam init scripts to register a shell hook that will \ automatically keep the shell environment up-to-date at every \ prompt."; - Some false, info ["disable-shell-hook"] ~doc: + cli_original, (Some false), ["disable-shell-hook"], "Disable registration of a shell hook in opam init scripts."; - ]) + ] in let config_file = - mk_opt_all ["config"] "FILE" + mk_opt_all ~cli cli_original ["config"] "FILE" "Use the given init config file. If repeated, latest has the highest \ priority ($(b,i.e.) each field gets its value from where it was defined \ last). Specifying a URL pointing to a config file instead is \ @@ -272,27 +278,27 @@ OpamArg.url in let no_config_file = - mk_flag ["no-opamrc"] + mk_flag ~cli cli_original ["no-opamrc"] (Printf.sprintf "Don't read `/etc/opamrc' or `~%s.opamrc': use the default settings and \ the files specified through $(b,--config) only" OpamArg.dir_sep) in let reinit = - mk_flag ["reinit"] + mk_flag ~cli cli_original ["reinit"] "Re-run the initial checks and setup, according to opamrc, even if this \ is not a new opam root" in let show_default_opamrc = - mk_flag ["show-default-opamrc"] + mk_flag ~cli cli_original ["show-default-opamrc"] "Print the built-in default configuration to stdout and exit" in let bypass_checks = - mk_flag ["bypass-checks"] + mk_flag ~cli cli_original ["bypass-checks"] "Skip checks on required or recommended tools, and assume everything is \ fine" in let no_sandboxing = - mk_flag ["disable-sandboxing"] + mk_flag ~cli cli_original ["disable-sandboxing"] "Use a default configuration with sandboxing disabled (note that this \ may be overridden by `opamrc' if $(b,--no-opamrc) is not specified or \ $(b,--config) is used). Use this at your own risk, without sandboxing \ @@ -302,9 +308,10 @@ build_options repo_kind repo_name repo_url interactive update_config completion env_hook no_sandboxing shell dot_profile_o compiler no_compiler config_file no_config_file reinit - show_opamrc bypass_checks = - apply_global_options global_options; - apply_build_options build_options; + show_opamrc bypass_checks + () = + apply_global_options cli global_options; + apply_build_options cli build_options; (* If show option is set, dump opamrc and exit *) if show_opamrc then (OpamFile.InitConfig.write_to_channel stdout @@ @@ -318,6 +325,11 @@ let root = OpamStateConfig.(!r.root_dir) in let config_f = OpamPath.config root in let already_init = OpamFile.exists config_f in + (* handling of `-ni` option *) + let inplace = + interactive = Some true && update_config = Some false + && env_hook = None && completion = None + in let interactive, update_config, completion, env_hook = match interactive with | Some false -> @@ -336,11 +348,11 @@ true, None, None, None else let reconfirm = function - | None | Some false -> Some false - | Some true -> None + | None | Some false -> None + | Some true -> Some true in true, - reconfirm update_config, + (if update_config = Some true then update_config else Some false), reconfirm completion, reconfirm env_hook in @@ -359,12 +371,32 @@ get_init_config ~no_sandboxing ~no_default_config_file:no_config_file ~add_config_file:config_file in - OpamClient.reinit ~init_config ~interactive ~dot_profile - ?update_config ?env_hook ?completion - (OpamStateConfig.safe_load ~lock_kind:`Lock_write root) shell; + let reinit conf = + OpamClient.reinit ~init_config ~interactive ~dot_profile + ?update_config ?env_hook ?completion ~inplace ~bypass_checks + ~check_sandbox:(not no_sandboxing) + conf shell + in + let config = + match OpamStateConfig.load ~lock_kind:`Lock_write root with + | Some c -> c + | exception (OpamPp.Bad_version _ as e) -> + OpamFormatUpgrade.hard_upgrade_from_2_1_intermediates ~reinit root; + raise e + | None -> OpamFile.Config.empty + in + reinit config else - OpamEnv.setup root - ~interactive ~dot_profile ?update_config ?env_hook ?completion shell + (if not interactive + && update_config <> Some true + && completion <> Some true + && env_hook <> Some true then + OpamConsole.msg + "Opam was already initialised. If you want to set it up again, \ + use `--interactive', `--reinit', or choose a different \ + `--root'.\n"; + OpamEnv.setup root ~interactive ~dot_profile ?update_config ?env_hook + ?completion ~inplace shell) else let init_config = get_init_config ~no_sandboxing @@ -372,76 +404,74 @@ in let repo = OpamStd.Option.map (fun url -> - let repo_url = OpamUrl.parse ?backend:repo_kind url in - let repo_root = - OpamRepositoryPath.create (OpamStateConfig.(!r.root_dir)) - repo_name - in - { repo_root; repo_name; repo_url; repo_trust = None }) + let repo_url = OpamUrl.parse ?backend:repo_kind ~from_file:false url in + { repo_name; repo_url; repo_trust = None }) repo_url in let gt, rt, default_compiler = OpamClient.init ~init_config ~interactive ?repo ~bypass_checks ~dot_profile - ?update_config ?env_hook ?completion shell + ?update_config ?env_hook ?completion + ~check_sandbox:(not no_sandboxing) + shell in + OpamStd.Exn.finally (fun () -> OpamRepositoryState.drop rt) + @@ fun () -> if no_compiler then () else - match compiler with - | Some comp when String.length comp <> 0-> - let packages = - OpamSwitchCommand.guess_compiler_package rt comp - in - OpamConsole.header_msg "Creating initial switch (%s)" - (OpamFormula.string_of_atoms packages); - OpamSwitchCommand.install - gt ~rt ~packages ~update_config:true (OpamSwitch.of_string comp) - |> ignore - | _ as nocomp -> - if nocomp <> None then - OpamConsole.warning - "No compiler specified, a default compiler will be selected."; - let candidates = OpamFormula.to_dnf default_compiler in - let all_packages = OpamSwitchCommand.get_compiler_packages rt in - let compiler_packages = - try - Some (List.find (fun atoms -> - let names = List.map fst atoms in - let pkgs = OpamFormula.packages_of_atoms all_packages atoms in - List.for_all (OpamPackage.has_name pkgs) names) - candidates) - with Not_found -> None - in - match compiler_packages with - | Some packages -> - OpamConsole.header_msg "Creating initial switch (%s)" - (OpamFormula.string_of_atoms packages); - OpamSwitchCommand.install - gt ~rt ~packages ~update_config:true - (OpamSwitch.of_string "default") - |> ignore - | None -> + let invariant, default_compiler, name = + match compiler with + | Some comp when String.length comp > 0 -> + OpamSwitchCommand.guess_compiler_invariant rt [comp], + [], + comp + | _ -> + OpamFile.Config.default_invariant gt.config, + default_compiler, "default" + in + OpamConsole.header_msg "Creating initial switch '%s' (invariant %s%s)" + name + (match invariant with + | OpamFormula.Empty -> "empty" + | c -> OpamFileTools.dep_formula_to_string c) + (match default_compiler with + | [] -> "" + | comp -> " - initially with "^ (OpamFormula.string_of_atoms comp)); + let (), st = + try + OpamSwitchCommand.create + gt ~rt ~invariant ~update_config:true (OpamSwitch.of_string name) @@ + (fun st -> + (), + OpamSwitchCommand.install_compiler st + ~ask:false + ~additional_installs:default_compiler) + with e -> + OpamStd.Exn.finalise e @@ fun () -> OpamConsole.note - "No compiler selected, and no available default switch found: \ - no switch has been created.\n\ + "Opam has been initialised, but the initial switch creation \ + failed.\n\ Use 'opam switch create ' to get started." + in + OpamSwitchState.drop st in - Term.(const init - $global_options $build_options $repo_kind_flag $repo_name $repo_url - $interactive $update_config $setup_completion $env_hook $no_sandboxing - $shell_opt $dot_profile_flag - $compiler $no_compiler - $config_file $no_config_file $reinit $show_default_opamrc $bypass_checks), - term_info "init" ~doc ~man + mk_command ~cli cli_original "init" ~doc ~man + Term.(const init + $global_options cli $build_options cli $repo_kind_flag cli + cli_original $repo_name $repo_url $interactive $update_config + $setup_completion $env_hook $no_sandboxing $shell_opt cli + cli_original $dot_profile_flag cli cli_original $compiler + $no_compiler $config_file $no_config_file $reinit $show_default_opamrc + $bypass_checks) (* LIST *) let list_doc = "Display the list of available packages." -let list ?(force_search=false) () = +let list ?(force_search=false) cli = let doc = list_doc in let selection_docs = OpamArg.package_selection_section in let display_docs = OpamArg.package_listing_section in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "List selections of opam packages."; `P "Without argument, the command displays the list of currently installed \ packages. With pattern arguments, lists all available packages \ @@ -457,7 +487,7 @@ `P "For a more detailed description of packages, see $(b,opam show). For \ extended search capabilities within the packages' metadata, see \ $(b,opam search)."; - `S "ARGUMENTS"; + `S Manpage.s_arguments; `S selection_docs; `S display_docs; ] in @@ -468,63 +498,61 @@ Arg.string in let state_selector = - let docs = selection_docs in - Arg.(value & vflag_all [] [ - OpamListCommand.Any, info ~docs ["A";"all"] - ~doc:"Include all, even uninstalled or unavailable packages"; - OpamListCommand.Installed, info ~docs ["i";"installed"] - ~doc:"List installed packages only. This is the default when no \ + mk_vflag_all ~cli ~section:selection_docs [ + cli_original, OpamListCommand.Any, ["A";"all"], + "Include all, even uninstalled or unavailable packages"; + cli_original, OpamListCommand.Installed, ["i";"installed"], + "List installed packages only. This is the default when no \ further arguments are supplied"; - OpamListCommand.Root, info ~docs ["roots";"installed-roots"] - ~doc:"List only packages that were explicitly installed, excluding \ + cli_original, OpamListCommand.Root, ["roots";"installed-roots"], + "List only packages that were explicitly installed, excluding \ the ones installed as dependencies"; - OpamListCommand.Available, info ~docs ["a";"available"] - ~doc:"List only packages that are available on the current system"; - OpamListCommand.Installable, info ~docs ["installable"] - ~doc:"List only packages that can be installed on the current switch \ + cli_original, OpamListCommand.Available, ["a";"available"], + "List only packages that are available on the current system"; + cli_original, OpamListCommand.Installable, ["installable"], + "List only packages that can be installed on the current switch \ (this calls the solver and may be more costly; a package \ depending on an unavailable package may be available, but is \ never installable)"; - OpamListCommand.Compiler, info ~docs ["base"] - ~doc:"List only the immutable base of the current switch (i.e. \ + cli_original, OpamListCommand.Compiler, ["base"], + "List only the immutable base of the current switch (i.e. \ compiler packages)"; - OpamListCommand.Pinned, info ~docs ["pinned"] - ~doc:"List only the pinned packages"; - ]) + cli_original, OpamListCommand.Pinned, ["pinned"], + "List only the pinned packages"; + ] in + let section = selection_docs in let search = if force_search then Term.const true else - mk_flag ["search"] ~section:selection_docs + mk_flag ~cli cli_original ["search"] ~section "Match $(i,PATTERNS) against the full descriptions of packages, and \ require all of them to match, instead of requiring at least one to \ match against package names (unless $(b,--or) is also specified)." in let repos = - mk_opt ["repos"] "REPOS" ~section:selection_docs + mk_opt ~cli cli_original ["repos"] "REPOS" ~section "Include only packages that took their origin from one of the given \ repositories (unless $(i,no-switch) is also specified, this excludes \ pinned packages)." Arg.(some & list & repository_name) None in let owns_file = - let doc = + mk_opt ~cli cli_original ["owns-file"] "FILE" ~section "Finds installed packages responsible for installing the given file" - in - Arg.(value & opt (some OpamArg.filename) None & - info ~doc ~docv:"FILE" ~docs:selection_docs ["owns-file"]) + Arg.(some OpamArg.filename) None in let no_switch = - mk_flag ["no-switch"] ~section:selection_docs + mk_flag ~cli cli_original ["no-switch"] ~section:selection_docs "List what is available from the repositories, without consideration for \ the current (or any other) switch (installed or pinned packages, etc.)" in let disjunction = - mk_flag ["or"] ~section:selection_docs + mk_flag ~cli cli_original ["or"] ~section:selection_docs "Instead of selecting packages that match $(i,all) the criteria, select \ packages that match $(i,any) of them" in let depexts = - mk_flag ["e";"external";"depexts"] ~section:display_docs + mk_flag ~cli cli_original ["e";"external";"depexts"] ~section:display_docs "Instead of displaying the packages, display their external dependencies \ that are associated with the current system. This excludes other \ display options. Rather than using this directly, you should probably \ @@ -533,21 +561,29 @@ `opam depext'." in let vars = - mk_opt ["vars"] "[VAR=STR,...]" ~section:display_docs + mk_opt ~cli cli_original ["vars"] "[VAR=STR,...]" ~section:display_docs "Define the given variable bindings. Typically useful with \ $(b,--external) to override the values for $(i,arch), $(i,os), \ $(i,os-distribution), $(i,os-version), $(i,os-family)." OpamArg.variable_bindings [] in let silent = - mk_flag ["silent"] - "Don't write anything in the output, exit with return code 0 if the list \ + mk_flag_replaced ~cli [ + cli_between ~default:true cli2_0 cli2_1 ~replaced:"--check", ["silent"]; + cli_from cli2_1, ["check"] + ] "Don't write anything in the output, exit with return code 0 if the list \ is not empty, 1 otherwise." in + let no_depexts = + mk_flag ~cli cli_original ["no-depexts"] + "Disable external dependencies handling for the query. This can be used \ + to include packages that are marked as unavailable because of an unavailable \ + system dependency." + in let list global_options selection state_selector no_switch depexts vars repos - owns_file disjunction search silent format packages = - apply_global_options global_options; + owns_file disjunction search silent no_depexts format packages () = + apply_global_options cli global_options; let no_switch = no_switch || OpamStateConfig.get_switch_opt () = None in @@ -601,8 +637,9 @@ ] in OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamRepositoryState.with_ `Lock_none gt @@ fun rt -> + if no_depexts then OpamStateConfig.update ~no_depexts:true (); let st = - let rt = OpamRepositoryState.load `Lock_none gt in if no_switch then OpamSwitchState.load_virtual ?repos_list:repos gt rt else OpamSwitchState.load `Lock_none gt rt (OpamStateConfig.get_switch ()) in @@ -624,6 +661,39 @@ let results = OpamListCommand.filter ~base:all st filter in + if not no_depexts && not silent then + (let drop_by_depexts = + List.fold_left (fun missing str -> + let is_missing pkgs = + if OpamStd.String.contains_char str '.' then + let nv = OpamPackage.of_string str in + if OpamPackage.Set.mem nv results then None else + OpamPackage.Set.find_opt (OpamPackage.equal nv) pkgs + else + let n = OpamPackage.Name.of_string str in + if OpamPackage.has_name results n then None else + let exist = OpamPackage.packages_of_name pkgs n in + if OpamPackage.Set.is_empty exist then None else + Some (OpamPackage.Set.max_elt exist) + in + match OpamStd.Option.Op.( + is_missing OpamPackage.Set.Op.(st.packages ++ st.pinned) + >>= OpamSwitchState.depexts_unavailable st) with + | Some nf -> OpamStd.String.Map.add str nf missing + | None -> missing + | exception Failure _ -> missing (* invalid package *) + ) OpamStd.String.Map.empty packages + in + if not (OpamStd.String.Map.is_empty drop_by_depexts) then + OpamConsole.note + "Some packages are unavailable because of their external dependencies. \ + Use `--no-depexts' to show them anyway.\n%s" + (OpamStd.Format.itemize (fun (n, spkgs) -> + Printf.sprintf "%s: %s" n + (OpamStd.Format.pretty_list + (List.map OpamSysPkg.to_string + (OpamSysPkg.Set.elements spkgs)))) + (OpamStd.String.Map.bindings drop_by_depexts))); if not depexts then (if not silent then OpamListCommand.display st format results @@ -633,21 +703,21 @@ let results_depexts = OpamListCommand.get_depexts st results in if not silent then OpamListCommand.print_depexts results_depexts - else if OpamStd.String.Set.is_empty results_depexts then + else if OpamSysPkg.Set.is_empty results_depexts then OpamStd.Sys.exit_because `False in - Term.(const list $global_options $package_selection $state_selector - $no_switch $depexts $vars $repos $owns_file $disjunction $search - $silent $package_listing $pattern_list), - term_info "list" ~doc ~man + mk_command ~cli cli_original "list" ~doc ~man + Term.(const list $global_options cli $package_selection cli $state_selector + $no_switch $depexts $vars $repos $owns_file $disjunction $search + $silent $no_depexts $package_listing cli $pattern_list) (* SHOW *) let show_doc = "Display information about specific packages." -let show = +let show cli = let doc = show_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command displays the information block for the selected \ package(s)."; `P "The information block consists of the name of the package, \ @@ -661,138 +731,403 @@ metadata will be shown." ] in let fields = - let doc = - Arg.info - ~docv:"FIELDS" - ~doc:("Only display the values of these fields. Fields can be selected \ - among "^ - OpamStd.List.concat_map ", " (Printf.sprintf "$(i,%s)" @* snd) - OpamListCommand.field_names - ^". Multiple fields can be separated with commas, in which case \ - field titles will be printed; the raw value of any opam-file \ - field can be queried by suffixing a colon character (:), e.g. \ - $(b,--field=depopts:).") - ["f";"field"] in - Arg.(value & opt (list string) [] & doc) in + mk_opt ~cli cli_original ["f";"field"] "FIELDS" + (Printf.sprintf + "Only display the values of these fields. Fields can be selected \ + among %s. Multiple fields can be separated with commas, in which case \ + field titles will be printed; the raw value of any opam-file \ + field can be queried by combinig with $(b,--raw)" + (OpamStd.List.concat_map ", " (Printf.sprintf "$(i,%s)" @* snd) + OpamListCommand.field_names)) + Arg.(list string) [] + in let show_empty = - mk_flag ["empty-fields"] + mk_flag ~cli cli_original ["empty-fields"] "Show fields that are empty. This is implied when $(b,--field) is \ given." in let raw = - mk_flag ["raw"] "Print the raw opam file for this package" in + mk_flag ~cli cli_original ["raw"] "Print the raw opam file for this package" in let where = - mk_flag ["where"] + mk_flag ~cli cli_original ["where"] "Print the location of the opam file used for this package" in let list_files = - mk_flag ["list-files"] + mk_flag ~cli cli_original ["list-files"] "List the files installed by the package. Equivalent to \ $(b,--field=installed-files), and only available for installed \ packages" in let file = - let doc = - Arg.info - ~docv:"FILE" - ~doc:"DEPRECATED: use an explicit path argument as package instead. \ - Get package information from the given FILE instead of from \ - known packages. This implies $(b,--raw) unless $(b,--fields) is \ - used. Only raw opam-file fields can be queried." - ["file"] in - Arg.(value & opt (some existing_filename_or_dash) None & doc) in - let normalise = mk_flag ["normalise"] + mk_opt ~cli (cli_between cli2_0 cli2_1 ~replaced:"--just-file") + ["file"] "FILE" + "DEPRECATED: use an explicit path argument as package instead. \ + Get package information from the given FILE instead of from \ + known packages. This implies $(b,--raw) unless $(b,--fields) is \ + used. Only raw opam-file fields can be queried." + existing_filename_or_dash None + in + let normalise = + mk_flag ~cli cli_original ["normalise"] "Print the values of opam fields normalised (no newlines, no implicit \ brackets)" in - let no_lint = mk_flag ["no-lint"] + let no_lint = + mk_flag ~cli cli_original ["no-lint"] "Don't output linting warnings or errors when reading from files" in - let pkg_info global_options fields show_empty raw where - list_files file normalise no_lint atom_locs = - apply_global_options global_options; - match file, atom_locs with - | None, [] -> - `Error (true, "required argument PACKAGES is missing") - | Some _, _::_ -> - `Error (true, - "arguments PACKAGES and `--file' can't be specified together") - | None, atom_locs -> - let fields, show_empty = - if list_files then - fields @ [OpamListCommand.(string_of_field Installed_files)], - show_empty - else fields, show_empty || fields <> [] - in - OpamGlobalState.with_ `Lock_none @@ fun gt -> - let st = OpamListCommand.get_switch_state gt in - let st, atoms = - OpamAuxCommands.simulate_autopin ~quiet:no_lint ~for_view:true st - atom_locs - in - OpamListCommand.info st - ~fields ~raw_opam:raw ~where ~normalise ~show_empty atoms; - `Ok () - | Some f, [] -> - let opam = match f with - | Some f -> OpamFile.OPAM.read (OpamFile.make f) - | None -> OpamFile.OPAM.read_from_channel stdin - in + let just_file = + mk_flag ~cli (cli_from cli2_1) ["just-file"] + "Load and display information from the given files (allowed \ + $(i,PACKAGES) are file or directory paths), without consideration for \ + the repositories or state of the package. This implies $(b,--raw) unless \ + $(b,--fields) is used. Only raw opam-file fields can be queried. If no \ + PACKAGES argument is given, read opam file from stdin." + in + let all_versions = + mk_flag ~cli (cli_from cli2_1) ["all-versions"] + "Display information of all packages matching $(i,PACKAGES), not \ + restrained to a single package matching $(i,PACKAGES) constraints." + in + let sort = mk_flag ~cli (cli_from cli2_1) ["sort"] "Sort opam fields" in + let opam_files_in_dir d = + match OpamPinned.files_in_source d with + | [] -> [] + | l -> List.map (fun (_,f,_) -> f) l + in + let show global_options fields show_empty raw where + list_files file normalise no_lint just_file all_versions sort atom_locs + () = + let print_just_file opamf opam = if not no_lint then OpamFile.OPAM.print_errors opam; + let opam = + if not sort then opam else + OpamFileTools.sort_opam opam + in if where then - (OpamConsole.msg "%s\n" - (match f with Some f -> OpamFilename.(Dir.to_string (dirname f)) - | None -> "."); - `Ok ()) + OpamConsole.msg "%s\n" + (match opamf with + | Some opamf -> + OpamFilename.(Dir.to_string (dirname (OpamFile.filename opamf))) + | None -> ".") else let opam_content_list = OpamFile.OPAM.to_list opam in let get_field f = - let f = OpamStd.String.remove_suffix ~suffix:":" f in try OpamListCommand.mini_field_printer ~prettify:true ~normalise (List.assoc f opam_content_list) with Not_found -> "" in match fields with | [] -> - OpamFile.OPAM.write_to_channel stdout opam; `Ok () + OpamFile.OPAM.write_to_channel stdout opam | [f] -> - OpamConsole.msg "%s\n" (get_field f); `Ok () + OpamConsole.msg "%s\n" (get_field f) | flds -> let tbl = List.map (fun fld -> - [ OpamConsole.colorise `blue - (OpamStd.String.remove_suffix ~suffix:":" fld ^ ":"); - get_field fld ]) + [ OpamConsole.colorise `blue (fld ^ ":"); get_field fld ]) flds in OpamStd.Format.align_table tbl |> - OpamConsole.print_table stdout ~sep:" "; - `Ok () + OpamConsole.print_table stdout ~sep:" " + in + apply_global_options cli global_options; + match file with + | Some file -> + let opamf = OpamFile.make file in + print_just_file (Some opamf) (OpamFile.OPAM.safe_read opamf); + `Ok () + | None -> + (match atom_locs, just_file with + | [], false -> + `Error (true, "required argument PACKAGES is missing") + | [], true -> + (try + let opam = OpamFile.OPAM.read_from_channel stdin in + print_just_file None opam; + `Ok () + with + | Parsing.Parse_error | OpamLexer.Error _ + | OpamPp.Bad_version _ | OpamPp.Bad_format _ as exn -> + OpamConsole.error_and_exit `File_error + "Stdin parsing failed:\n%s" (Printexc.to_string exn)) + | atom_locs, false -> + let fields, show_empty = + if list_files then + fields @ [OpamListCommand.(string_of_field Installed_files)], + show_empty + else fields, show_empty || fields <> [] + in + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamRepositoryState.with_ `Lock_none gt @@ fun rt -> + let st = OpamListCommand.get_switch_state gt rt in + let st, atoms = + OpamAuxCommands.simulate_autopin ~quiet:no_lint ~for_view:true st + atom_locs + in + if atoms = [] then + OpamConsole.error_and_exit `Not_found "No package found" + else + OpamListCommand.info st + ~fields ~raw ~where ~normalise ~show_empty ~all_versions ~sort atoms; + `Ok () + | atom_locs, true -> + if List.exists (function `Atom _ -> true | _ -> false) atom_locs then + `Error (true, "packages can't be specified with --just-file") + else + let opamfs = + List.fold_left (fun acc al -> + match al with + | `Filename f -> (OpamFile.make f) :: acc + | `Dirname d -> opam_files_in_dir d @ acc + | _ -> acc) + [] atom_locs + in + if opamfs = [] then + let dirnames = + OpamStd.List.filter_map (function + | `Dirname d -> Some (OpamFilename.Dir.to_string d) + | _ -> None) + atom_locs + in + OpamConsole.error_and_exit `Not_found "No opam files found at %s" + (OpamStd.List.concat_map ", " ~last_sep:" and " + (fun x -> x) dirnames ) + else + let errors, opams = + List.fold_left (fun (errors,opams) opamf -> + try + errors, (Some opamf, (OpamFile.OPAM.read opamf))::opams + with + | Parsing.Parse_error | OpamLexer.Error _ + | OpamPp.Bad_version _ | OpamPp.Bad_format _ as exn -> + (opamf, exn)::errors, opams) + ([],[]) opamfs + in + List.iter (fun (f,o) -> print_just_file f o) opams; + (if errors <> [] then + let sgl = match errors with [_] -> true | _ -> false in + let tostr (opamf, error) = + (OpamFilename.to_string (OpamFile.filename opamf)) + ^ ":\n" ^ Printexc.to_string error + in + OpamConsole.error "Parsing error on%s:%s" + (if sgl then "" else "some opam files") + (match errors with + | [f] -> tostr f + | fs -> OpamStd.Format.itemize tostr fs)); + if opams = [] then + OpamStd.Sys.exit_because `File_error + else + `Ok () + ) + in + mk_command_ret ~cli cli_original "show" ~doc ~man + Term.(const show $global_options cli $fields $show_empty $raw $where + $list_files $file $normalise $no_lint $just_file $all_versions + $sort $atom_or_local_list) + +(* Shared between [option] and [var] *) +module Var_Option_Common = struct + + let global cli = + mk_flag ~cli (cli_from cli2_1) ["global"] "Act on global configuration" + + let var_option global global_options cmd var = + let switch_set = (fst global_options).opt_switch <> None in + if global && switch_set then + `Error (true, "--global and --switch sw option can't be used together") + else + let scope = + if global then `Global + else if switch_set then `Switch + else match var with + | None -> `All + | Some f -> match cmd with + | `var -> `All_var + | `option -> OpamConfigCommand.get_scope f + in + let apply = + match var with + | None -> `empty + | Some var -> + try `value_eq (OpamConfigCommand.parse_update var) + with Invalid_argument _ -> `value_wo_eq var + in + match scope with + | `None field -> + (* must be an option command *) + OpamConsole.error_and_exit `Bad_arguments + "No option named '%s' found. Use 'opam option [--global]' to list them" + field + | `All -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + (match cmd with + | `var -> OpamConfigCommand.vars_list gt + | `option -> OpamConfigCommand.options_list gt); + `Ok () + | `All_var -> + (match apply with + | `value_wo_eq v -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamConfigCommand.var_show gt v; + `Ok () + | `empty -> assert false (* can't happen *) + | `value_eq _ -> + `Error (true, "variable setting needs a scope, \ + use '--global' or '--switch '")) + | (`Global | `Switch) as scope -> + match cmd, apply with + | _ , `empty -> + (match scope with + | `Switch -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + (match cmd with + | `var -> OpamConfigCommand.vars_list_switch gt + | `option -> OpamConfigCommand.options_list_switch gt); + `Ok () + | `Global -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + (match cmd with + | `var -> OpamConfigCommand.vars_list_global gt + | `option -> OpamConfigCommand.options_list_global gt); + `Ok ()) + | _, `value_wo_eq v -> + (match scope with + | `Switch -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + (match cmd with + | `var -> OpamConfigCommand.var_show_switch gt v + | `option -> OpamConfigCommand.option_show_switch gt v); + `Ok () + | `Global -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + (match cmd with + | `var -> OpamConfigCommand.var_show_global gt v + | `option -> OpamConfigCommand.option_show_global gt v); + `Ok ()) + | `var, `value_eq (_,#OpamConfigCommand.append_op) -> + `Error (true, "var: append operation are not permitted") + | _, `value_eq (v,u) -> + match scope with + | `Switch -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + let _st = + match cmd with + | `var -> + OpamConfigCommand.set_var_switch gt v + (OpamConfigCommand.whole_of_update_op u) + | `option -> OpamConfigCommand.set_opt_switch gt v u + in + `Ok () + | `Global -> + OpamGlobalState.with_ `Lock_write @@ fun gt -> + let _st = + match cmd with + | `var -> OpamConfigCommand.set_var_global gt v + (OpamConfigCommand.whole_of_update_op u) + | `option -> OpamConfigCommand.set_opt_global gt v u + in + `Ok () + +end + +(* VAR *) +let var_doc = "Display and update the value associated with a given variable" +let var cli = + let doc = var_doc in + let man = [ + `S Manpage.s_description; + `P "Without argument, lists the opam variables currently defined. With a \ + $(i,VAR) argument, prints the value associated with $(i,VAR). \ + Otherwise, sets or updates $(i,VAR)'s value. \ + If no scope is given, it acts on switch variables by default. \ + This command does not perform any variable expansion."; + ] in + let open Var_Option_Common in + let varvalue = + let docv = "VAR[=[VALUE]]" in + let doc = + "If only $(i,VAR) is given, displays its associated value. \ + If $(i,VALUE) is absent, $(i,VAR)'s value is removed. Otherwise, its \ + value is overwritten." + in + Arg.(value & pos 0 (some string) None & info ~docv ~doc []) + in + let package = + mk_opt ~cli cli_original ["package"] "PACKAGE" + "List all variables defined for the given package" + Arg.(some package_name) None + in + let print_var global_options package varvalue global () = + apply_global_options cli global_options; + match varvalue, package with + | _, None -> + var_option global global_options `var varvalue + | None, Some pkg -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamSwitchState.with_ `Lock_none gt @@ fun st -> + (try `Ok (OpamConfigCommand.list st [pkg]) + with Failure msg -> `Error (false, msg)) + | _, _ -> + `Error (true, "--package can't be specified with a var argument, use \ + 'pkg:var' instead.") in - Term.(ret - (const pkg_info $global_options $fields $show_empty $raw $where $list_files - $file $normalise $no_lint $atom_or_local_list)), - term_info "show" ~doc ~man + mk_command_ret ~cli cli_original "var" ~doc ~man + Term.(const print_var + $global_options cli $package $varvalue $global cli) + +(* OPTION *) +let option_doc = "Global and switch configuration options settings" +let option cli = + let doc = option_doc in + let man = [ + `S Manpage.s_description; + `P "Without argument, list all configurable fields. If a field name \ + $(i,FIELD) is given, display its content. Otherwise, sets or updates \ + the given field in the global/switch configuration file. \ + For global configuration, $(i,FIELD) is reset to its default initial \ + value, as after a fresh init (use `opam init show-default-opamrc` to \ + display it)." + ] in + let open Var_Option_Common in + let fieldvalue = + let docv = "FIELD[(=|+=|-=)[VALUE]]" in + let doc = + "If only $(i,FIELD) is given, displays its associated value. If \ + $(i,VALUE) is absent, $(i,FIELD)'s value is reverted. Otherwise, its \ + value is updated: overwrite (=), append (+=), or remove of an element \ + (-=)." + in + Arg.(value & pos 0 (some string) None & info ~docv ~doc []) + in + let option global_options fieldvalue global () = + apply_global_options cli global_options; + var_option global global_options `option fieldvalue + in + mk_command_ret ~cli (cli_from cli2_1) "option" ~doc ~man + Term.(const option + $global_options cli $fieldvalue $global cli) module Common_config_flags = struct - let sexp = - mk_flag ["sexp"] + let sexp cli = + mk_flag ~cli cli_original ["sexp"] "Print environment as an s-expression rather than in shell format" - let inplace_path = - mk_flag ["inplace-path"] + let inplace_path cli = + mk_flag ~cli cli_original ["inplace-path"] "When updating the $(i,PATH) variable, replace any pre-existing opam \ path in-place rather than putting the new path in front. This means \ programs installed in opam that were shadowed will remain so after \ $(b,opam env)" - let set_opamroot = - mk_flag ["set-root"] + let set_opamroot cli = + mk_flag ~cli cli_original ["set-root"] "With the $(b,env) and $(b,exec) subcommands, also sets the \ $(i,OPAMROOT) variable, making sure further calls to opam will use the \ same root." - let set_opamswitch = - mk_flag ["set-switch"] + let set_opamswitch cli = + mk_flag ~cli cli_original ["set-switch"] "With the $(b,env) and $(b,exec) subcommands, also sets the \ $(i,OPAMSWITCH) variable, making sure further calls to opam will use \ the same switch as this one." @@ -801,78 +1136,83 @@ (* CONFIG *) let config_doc = "Display configuration options for packages." -let config = +let config cli = + let shell = OpamStd.Sys.guess_shell_compat () in let doc = config_doc in let commands = [ - "env", `env, [], - "Returns the bindings for the environment variables set in the current \ - switch, e.g. PATH, in a format intended to be evaluated by a shell. With \ - $(i,-v), add comments documenting the reason or package of origin for \ - each binding. This is most usefully used as $(b,eval \\$(opam config \ - env\\)) to have further shell commands be evaluated in the proper opam \ - context. Can also be accessed through $(b,opam env)."; - "revert-env", `revert_env, [], - "Reverts environment changes made by opam, e.g. $(b,eval \\$(opam config \ - revert-env)) undoes what $(b,eval \\$(opam config env\\)) did, as much as \ - possible."; - "exec", `exec, ["[--] COMMAND"; "[ARG]..."], - "Execute $(i,COMMAND) with the correct environment variables. This command \ - can be used to cross-compile between switches using $(b,opam config exec \ - --switch=SWITCH -- COMMAND ARG1 ... ARGn). Opam expansion takes place in \ - command and args. If no switch is present on the command line or in the \ - $(i,OPAMSWITCH) environment variable, $(i,OPAMSWITCH) is not set in \ - $(i,COMMAND)'s environment. Can also be accessed through $(b,opam exec)."; - "var", `var, ["VAR"], - "Return the value associated with variable $(i,VAR). Package variables can \ - be accessed with the syntax $(i,pkg:var). Can also be accessed through \ - $(b,opam var)"; - "list", `list, ["[PACKAGE]..."], + cli_original, "env", `env, [], + Printf.sprintf + "Returns the bindings for the environment variables set in the \ + current switch, e.g. PATH, in a format intended to be evaluated by \ + a shell. With $(i,-v), add comments documenting the reason or \ + package of origin for each binding. This is most usefully used as \ + $(b,%s) to have further shell commands be evaluated in the proper \ + opam context. Can also be accessed through $(b,opam env)." + OpamEnv.(shell_eval_invocation shell "opam config env" |> Manpage.escape); + cli_original, "revert-env", `revert_env, [], + Printf.sprintf + "Reverts environment changes made by opam, e.g. $(b,%s) undoes what \ + $(b,%s) did, as much as possible." + OpamEnv.(shell_eval_invocation shell "opam config revert-env" + |> Manpage.escape) + OpamEnv.(shell_eval_invocation shell "opam config env" |> Manpage.escape); + cli_original, "list", `list, ["[PACKAGE]..."], "Without argument, prints a documented list of all available variables. \ With $(i,PACKAGE), lists all the variables available for these packages. \ Use $(i,-) to include global configuration variables for this switch."; - "set", `set, ["VAR";"VALUE"], - "Set the given opam variable for the current switch. Warning: changing a \ - configured path will not move any files! This command does not perform \ - any variable expansion."; - "unset", `unset, ["VAR"], - "Unset the given opam variable for the current switch. Warning: \ - unsetting built-in configuration variables can cause problems!"; - "set-global", `set_global, ["VAR";"VALUE"], - "Set the given variable globally in the opam root, to be visible in all \ - switches"; - "unset-global", `unset_global, ["VAR"], - "Unset the given global variable"; - "expand", `expand, ["STRING"], + cli_original, "expand", `expand, ["STRING"], "Expand variable interpolations in the given string"; - "subst", `subst, ["FILE..."], + cli_original, "subst", `subst, ["FILE..."], "Substitute variables in the given files. The strings $(i,%{var}%) are \ replaced by the value of variable $(i,var) (see $(b,var))."; - "report", `report, [], + cli_original, "report", `report, [], "Prints a summary of your setup, useful for bug-reports."; - "cudf-universe",`cudf, ["[FILE]"], + cli_original, "cudf-universe", `cudf, ["[FILE]"], "Outputs the current available package universe in CUDF format."; - "pef-universe", `pef, ["[FILE]"], + cli_original, "pef-universe", `pef, ["[FILE]"], "Outputs the current package universe in PEF format."; + (* Deprecated options *) + cli_between ~default:true cli2_0 cli2_1 ~replaced:"opam exec", "exec", + `exec, ["[--] COMMAND"; "[ARG]..."], + "Execute $(i,COMMAND) with the correct environment variables. This command \ + can be used to cross-compile between switches using $(b,opam config exec \ + --switch=SWITCH -- COMMAND ARG1 ... ARGn). Opam expansion takes place in \ + command and args. If no switch is present on the command line or in the \ + $(i,OPAMSWITCH) environment variable, $(i,OPAMSWITCH) is not set in \ + $(i,COMMAND)'s environment. Can also be accessed through $(b,opam exec)."; + cli_between ~default:true cli2_0 cli2_1 ~replaced:"opam var", "set", `set, + ["VAR";"VALUE"], "Set switch variable"; + cli_between ~default:true cli2_0 cli2_1 ~replaced:"opam var", "unset", + `unset, ["VAR"], "Unset switch variable"; + cli_between ~default:true cli2_0 cli2_1 ~replaced:"opam var", "set-global", + `set_global, ["VAR";"VALUE"], "Set global variable"; + cli_between ~default:true cli2_0 cli2_1 ~replaced:"opam var", + "unset-global", `unset_global, ["VAR"], "Unset global variable"; + cli_between ~default:true cli2_0 cli2_1 ~replaced:"opam var", "var", `var, + ["VAR"], + "Return the value associated with variable $(i,VAR), looking in switch \ + first, global if not found. Package variables can be accessed with the \ + syntax $(i,pkg:var). Can also be accessed through $(b,opam var VAR)"; ] in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command uses opam state to output information on how to use \ installed libraries, update the $(b,PATH), and substitute \ variables used in opam packages."; `P "Apart from $(b,opam config env), most of these commands are used \ by opam internally, and are of limited interest for the casual \ user."; - ] @ mk_subdoc commands - @ [`S "OPTIONS"] + ] @ mk_subdoc ~cli commands + @ [`S Manpage.s_options] in - let command, params = mk_subcommands commands in + let command, params = mk_subcommands ~cli commands in let open Common_config_flags in let config global_options command shell sexp inplace_path - set_opamroot set_opamswitch params = - apply_global_options global_options; + set_opamroot set_opamswitch params () = + apply_global_options cli global_options; let shell = match shell with | Some s -> s | None -> OpamStd.Sys.guess_shell_compat () @@ -887,37 +1227,34 @@ ~set_opamroot ~set_opamswitch ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) ~inplace_path)) | Some `revert_env, [] -> - `Ok (OpamConfigCommand.print_eval_env - ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) - (OpamEnv.add [] [])) - | Some `exec, (_::_ as c) -> OpamGlobalState.with_ `Lock_none @@ fun gt -> - `Ok (OpamConfigCommand.exec - gt ~set_opamroot ~set_opamswitch ~inplace_path c) + (match OpamStateConfig.get_switch_opt () with + | None -> `Ok () + | Some sw -> + `Ok (OpamConfigCommand.ensure_env gt sw; + OpamConfigCommand.print_eval_env + ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) + (OpamEnv.add [] []))) + | Some `list, [] -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + `Ok (OpamConfigCommand.vars_list gt) | Some `list, params -> OpamGlobalState.with_ `Lock_none @@ fun gt -> - (try `Ok (OpamConfigCommand.list gt (List.map OpamPackage.Name.of_string params)) + OpamSwitchState.with_ `Lock_none gt @@ fun st -> + (try `Ok (OpamConfigCommand.list st + (List.map OpamPackage.Name.of_string params)) with Failure msg -> `Error (false, msg)) - | Some `set, [var; value] -> - `Ok (OpamConfigCommand.set (OpamVariable.Full.of_string var) (Some value)) - | Some `unset, [var] -> - `Ok (OpamConfigCommand.set (OpamVariable.Full.of_string var) None) - | Some `set_global, [var; value] -> - `Ok (OpamConfigCommand.set_global - (OpamVariable.Full.of_string var) (Some value)) - | Some `unset_global, [var] -> - `Ok (OpamConfigCommand.set_global - (OpamVariable.Full.of_string var) None) | Some `expand, [str] -> OpamGlobalState.with_ `Lock_none @@ fun gt -> `Ok (OpamConfigCommand.expand gt str) | Some `var, [var] -> OpamGlobalState.with_ `Lock_none @@ fun gt -> - (try `Ok (OpamConfigCommand.variable gt (OpamVariable.Full.of_string var)) + (try `Ok (OpamConfigCommand.var_show gt var) with Failure msg -> `Error (false, msg)) | Some `subst, (_::_ as files) -> OpamGlobalState.with_ `Lock_none @@ fun gt -> - `Ok (OpamConfigCommand.subst gt (List.map OpamFilename.Base.of_string files)) + `Ok (OpamConfigCommand.subst gt + (List.map OpamFilename.Base.of_string files)) | Some `pef, params -> OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_none gt @@ fun st -> @@ -928,7 +1265,7 @@ OpamSwitchState.dump_pef_state st oc; close_out oc; `Ok () - | _ -> bad_subcommand commands ("config", command, params)) + | _ -> bad_subcommand ~cli commands ("config", command, params)) | Some `cudf, params -> OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_none gt @@ fun opam_state -> @@ -941,9 +1278,9 @@ (match params with | [] -> `Ok (dump stdout) | [file] -> let oc = open_out file in dump oc; close_out oc; `Ok () - | _ -> bad_subcommand commands ("config", command, params)) + | _ -> bad_subcommand ~cli commands ("config", command, params)) | Some `report, [] -> ( - let print label fmt = OpamConsole.msg ("# %-17s "^^fmt^^"\n") label in + let print label fmt = OpamConsole.msg ("# %-20s "^^fmt^^"\n") label in OpamConsole.msg "# opam config report\n"; print "opam-version" "%s " (OpamVersion.to_string (OpamVersion.full ())); @@ -980,8 +1317,9 @@ if OpamUrl.root repo.repo_url = OpamUrl.root OpamInitDefaults.repository_url then - OpamFile.Repo.safe_read - (OpamRepositoryPath.repo repo.repo_root) |> + OpamRepositoryName.Map.find + repo.repo_name + state.switch_repos.repos_definitions |> OpamFile.Repo.stamp else dft in @@ -1022,71 +1360,95 @@ ); print "current-switch" "%s" (OpamSwitch.to_string state.switch); + let process nv = + try + let conf = OpamSwitchState.package_config state nv.name in + let bindings = + let f (name, value) = + (OpamVariable.Full.create nv.name name, + OpamVariable.string_of_variable_contents value) + in + List.map f (OpamFile.Dot_config.bindings conf) + in + let print (name, value) = + let name = OpamVariable.Full.to_string name in + print name "%s" value + in + List.iter print bindings + with Not_found -> () + in + state.installed + |> OpamPackage.Set.filter (fun p -> + match OpamSwitchState.opam_opt state p with + | Some o -> OpamFile.OPAM.has_flag Pkgflag_Compiler o + | None -> false) + |> OpamSolver.dependencies ~depopts:true ~post:true ~build:true + ~installed:true + (OpamSwitchState.universe ~test:true ~doc:true + ~requested:OpamPackage.Name.Set.empty state Query) + |> OpamPackage.Set.iter process; if List.mem "." (OpamStd.Sys.split_path_variable (Sys.getenv "PATH")) then OpamConsole.warning "PATH contains '.' : this is a likely cause of trouble."; `Ok () with e -> print "read-state" "%s" (Printexc.to_string e); `Ok ()) - | command, params -> bad_subcommand commands ("config", command, params) + (* deprecated *) + | Some `exec, (_::_ as c) -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + `Ok (OpamConfigCommand.exec + gt ~set_opamroot ~set_opamswitch ~inplace_path c) + | Some (`set | `unset as cmd), var::value -> + let args = + match cmd,value with + | `unset, [] -> Some None + | `set, v::_ -> Some (Some v) + | _, _ -> None + in + (match args with + | None -> + bad_subcommand ~cli commands ("config", command, params) + | Some opt_value -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + let value = + OpamStd.Option.map_default (fun v -> `Overwrite v) + `Revert opt_value + in + let _ = OpamConfigCommand.set_var_switch gt var value in + `Ok ()) + | Some (`set_global | `unset_global as cmd), var::value -> + let args = + match cmd,value with + |`unset_global, [] -> Some None + | `set_global, v::_ -> Some (Some v) + | _, _ -> None + in + (match args with + | None -> + bad_subcommand ~cli commands ("config", command, params) + | Some opt_value -> + OpamGlobalState.with_ `Lock_write @@ fun gt -> + let value = + OpamStd.Option.map_default (fun v -> `Overwrite v) + `Revert opt_value + in + let _gt = OpamConfigCommand.set_var_global gt var value in + `Ok ()) + | command, params -> bad_subcommand ~cli commands ("config", command, params) in - Term.ret ( + mk_command_ret ~cli cli_original "config" ~doc ~man Term.(const config - $global_options $command $shell_opt $sexp - $inplace_path - $set_opamroot $set_opamswitch + $global_options cli $command $shell_opt cli cli_original $sexp cli + $inplace_path cli + $set_opamroot cli $set_opamswitch cli $params) - ), - term_info "config" ~doc ~man - -(* VAR *) -let var_doc = "Prints the value associated with a given variable" -let var = - let doc = var_doc in - let man = [ - `S "DESCRIPTION"; - `P "With a $(i,VAR) argument, prints the value associated with $(i,VAR). \ - Without argument, lists the opam variables currently defined. This \ - command is a shortcut to `opam config var` and `opam config list`."; - ] in - let varname = - Arg.(value & pos 0 (some string) None & info ~docv:"VAR" []) - in - let package = - Arg.(value & opt (some package_name) None & - info ~docv:"PACKAGE" ["package"] - ~doc:"List all variables defined for the given package") - in - let print_var global_options package var = - apply_global_options global_options; - match var, package with - | None, None -> - OpamGlobalState.with_ `Lock_none @@ fun gt -> - (try `Ok (OpamConfigCommand.list gt []) - with Failure msg -> `Error (false, msg)) - | None, Some pkg -> - OpamGlobalState.with_ `Lock_none @@ fun gt -> - (try `Ok (OpamConfigCommand.list gt [pkg]) - with Failure msg -> `Error (false, msg)) - | Some v, None -> - OpamGlobalState.with_ `Lock_none @@ fun gt -> - (try `Ok (OpamConfigCommand.variable gt (OpamVariable.Full.of_string v)) - with Failure msg -> `Error (false, msg)) - | Some _, Some _ -> - `Error (true, "--package can't be specified with a var argument, use \ - 'pkg:var' instead.") - in - Term.ret ( - Term.(const print_var $global_options $package $varname) - ), - term_info "var" ~doc ~man (* EXEC *) let exec_doc = "Executes a command in the proper opam environment" -let exec = +let exec cli = let doc = exec_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Execute $(i,COMMAND) with the correct environment variables. This \ command can be used to cross-compile between switches using $(b,opam \ config exec --switch=SWITCH -- COMMAND ARG1 ... ARGn). Opam expansion \ @@ -1098,40 +1460,56 @@ let cmd = Arg.(non_empty & pos_all string [] & info ~docv:"COMMAND [ARG]..." []) in - let exec global_options inplace_path set_opamroot set_opamswitch cmd = - apply_global_options global_options; + let exec global_options inplace_path set_opamroot set_opamswitch cmd () = + apply_global_options cli global_options; OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamConfigCommand.exec gt ~set_opamroot ~set_opamswitch ~inplace_path cmd in let open Common_config_flags in - Term.(const exec $global_options $inplace_path - $set_opamroot $set_opamswitch - $cmd), - term_info "exec" ~doc ~man + mk_command ~cli cli_original "exec" ~doc ~man + Term.(const exec $global_options cli $inplace_path cli + $set_opamroot cli $set_opamswitch cli + $cmd) (* ENV *) let env_doc = "Prints appropriate shell variable assignments to stdout" -let env = +let env cli = + let shell = OpamStd.Sys.guess_shell_compat () in let doc = env_doc in let man = [ - `S "DESCRIPTION"; - `P "Returns the bindings for the environment variables set in the current \ - switch, e.g. PATH, in a format intended to be evaluated by a shell. \ - With $(i,-v), add comments documenting the reason or package of origin \ - for each binding. This is most usefully used as $(b,eval \\$(opam \ - env\\)) to have further shell commands be evaluated in the proper opam \ - context."; + `S Manpage.s_description; + `P (Printf.sprintf + "Returns the bindings for the environment variables set in the current \ + switch, e.g. PATH, in a format intended to be evaluated by a shell. \ + With $(i,-v), add comments documenting the reason or package of origin \ + for each binding. This is most usefully used as $(b,%s) \ + to have further shell commands be evaluated in the proper opam \ + context." + OpamEnv.( + shell_eval_invocation shell (opam_env_invocation ()) + |> Manpage.escape)); `P "This is a shortcut, and equivalent to $(b,opam config env)."; ] in let revert = - mk_flag ["revert"] + mk_flag ~cli cli_original ["revert"] "Output the environment with updates done by opam reverted instead." in + let check = + mk_flag ~cli (cli_from cli2_1) ["check"] + "Exits with 0 if the environment is already up-to-date, 1 otherwise, \ + after printing the list of not up-to-date variables." + in let env global_options shell sexp inplace_path set_opamroot set_opamswitch - revert = - apply_global_options global_options; + revert check () = + apply_global_options cli global_options; + if check then + (OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamSwitchState.with_ `Lock_none gt @@ fun st -> + if not (OpamEnv.is_up_to_date ~skip:false st) then + OpamStd.Sys.exit_because `False) + else let shell = match shell with | Some s -> s | None -> OpamStd.Sys.guess_shell_compat () @@ -1144,24 +1522,25 @@ | Some sw -> OpamConfigCommand.env gt sw ~set_opamroot ~set_opamswitch - ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) ~inplace_path) + ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) ~inplace_path); | true -> OpamConfigCommand.print_eval_env ~csh:(shell=SH_csh) ~sexp ~fish:(shell=SH_fish) (OpamEnv.add [] []) in let open Common_config_flags in + mk_command ~cli cli_original "env" ~doc ~man Term.(const env - $global_options $shell_opt $sexp $inplace_path - $set_opamroot $set_opamswitch$revert), - term_info "env" ~doc ~man + $global_options cli $shell_opt cli cli_original $sexp cli + $inplace_path cli $set_opamroot cli $set_opamswitch cli + $revert $check) (* INSTALL *) let install_doc = "Install a list of packages." -let install = +let install cli = let doc = install_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command installs one or more packages inside the currently \ selected switch (see $(b,opam switch)). Once installed, you can remove \ packages with $(b,opam remove), upgrade them with $(b,opam upgrade), \ @@ -1179,30 +1558,38 @@ $(i,opam) files. Then the corresponding packages will be pinned to \ their local directory and installed (unless $(b,--deps-only) was \ specified)."; - `S "ARGUMENTS"; - `S "OPTIONS"; - `S OpamArg.build_option_section; - ] in + `S Manpage.s_arguments; + `S Manpage.s_options; + ] @ OpamArg.man_build_option_section + in let add_to_roots = - let root = - Some true, Arg.info ["set-root"] - ~doc:"Mark given packages as installed roots. This is the default \ - for newly manually-installed packages." in - let unroot = - Some false, Arg.info ["unset-root"] - ~doc:"Mark given packages as \"installed automatically\"." in - Arg.(value & vflag None[root; unroot]) + mk_vflag ~cli None [ + cli_original, (Some true), ["set-root"], + "Mark given packages as installed roots. This is the default \ + for newly manually-installed packages."; + cli_original, (Some false), ["unset-root"], + "Mark given packages as \"installed automatically\"."; + ] in let deps_only = - Arg.(value & flag & info ["deps-only"] - ~doc:"Install all its dependencies, but don't actually install the \ - package.") in + mk_flag ~cli cli_original ["deps-only"] + "Install all its dependencies, but don't actually install the package." + in + let ignore_conflicts = + mk_flag ~cli (cli_from cli2_1) ["ignore-conflicts"] + "Used with $(b,--deps-only), ignores conflicts of given package" + in + let download_only = + mk_flag ~cli (cli_from cli2_1) ["download-only"] + "Fetch the sources of the packages, but don't build or install anything." + in let restore = - Arg.(value & flag & info ["restore"] - ~doc:"Attempt to restore packages that were marked for installation \ - but have been removed due to errors") in + mk_flag ~cli cli_original ["restore"] + "Attempt to restore packages that were marked for installation but have \ + been removed due to errors" + in let destdir = - mk_opt ["destdir"] "DIR" + mk_opt ~cli cli_original ["destdir"] "DIR" "Copy the files installed by the given package within the current opam \ switch below the prefix $(i,DIR), respecting their hierarchy, after \ installation. Caution, calling this can overwrite, but never remove \ @@ -1211,14 +1598,35 @@ --destdir) to revert." Arg.(some dirname) None in + let check = + mk_flag ~cli (cli_from cli2_1) ["check"] + "Exit with 0 if all the dependencies of $(i,PACKAGES) are already \ + installed. If not, output the names of the missing dependencies to \ + stdout, and exits with 1." + in + let depext_only = + mk_flag ~cli (cli_from cli2_1) ["depext-only"] + "Resolves the package installation normally, but only installs \ + the required system dependencies, without affecting the opam switch \ + state or installing opam packages." + in let install - global_options build_options add_to_roots deps_only restore destdir - assume_built atoms_or_locals = - apply_global_options global_options; - apply_build_options build_options; + global_options build_options add_to_roots deps_only ignore_conflicts + restore destdir assume_built check recurse subpath depext_only + download_only atoms_or_locals () = + apply_global_options cli global_options; + apply_build_options cli build_options; if atoms_or_locals = [] && not restore then `Error (true, "required argument PACKAGES is missing") else + if depext_only + && (OpamClientConfig.(!r.assume_depexts) + || OpamStateConfig.(!r.no_depexts)) then + `Error (true, + Printf.sprintf "--depext-only and --%s can't be used together" + (if OpamClientConfig.(!r.assume_depexts) then "assume-depexts" + else "no-depexts")) + else OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> let pure_atoms = @@ -1241,14 +1649,32 @@ in if atoms_or_locals = [] then `Ok () else let st, atoms = - OpamAuxCommands.autopin st ~simulate:deps_only atoms_or_locals + OpamAuxCommands.autopin + st ~recurse ?subpath ~quiet:check ~simulate:(deps_only||check||depext_only) + atoms_or_locals in if atoms = [] then (OpamConsole.msg "Nothing to do\n"; OpamStd.Sys.exit_because `Success); + if check then + (let missing = + OpamPackage.Map.fold (fun _ -> OpamPackage.Name.Set.union) + (OpamClient.check_installed ~build:true ~post:true st atoms) + (OpamPackage.Name.Set.empty) + in + if OpamPackage.Name.Set.is_empty missing then + (OpamConsole.errmsg "All dependencies installed\n"; + OpamStd.Sys.exit_because `Success) + else + (OpamConsole.errmsg "Missing dependencies:\n"; + OpamConsole.msg "%s\n" + (OpamStd.List.concat_map " " OpamPackage.Name.to_string + (OpamPackage.Name.Set.elements missing)); + OpamStd.Sys.exit_because `False)); let st = OpamClient.install st atoms - ~autoupdate:pure_atoms ?add_to_roots ~deps_only ~assume_built + ~autoupdate:pure_atoms ?add_to_roots ~deps_only ~ignore_conflicts + ~assume_built ~depext_only ~download_only in match destdir with | None -> `Ok () @@ -1257,18 +1683,18 @@ OpamAuxCommands.copy_files_to_destdir st dest packages; `Ok () in - Term.ret - Term.(const install $global_options $build_options - $add_to_roots $deps_only $restore $destdir $assume_built - $atom_or_local_list), - term_info "install" ~doc ~man + mk_command_ret ~cli cli_original "install" ~doc ~man + Term.(const install $global_options cli $build_options cli + $add_to_roots $deps_only $ignore_conflicts $restore $destdir + $assume_built cli $check $recurse cli $subpath cli $depext_only + $download_only $atom_or_local_list) (* REMOVE *) let remove_doc = "Remove a list of packages." -let remove = +let remove cli = let doc = remove_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command uninstalls one or more packages currently \ installed in the currently selected compiler switch. To remove packages \ installed in another compiler, you need to switch compilers using \ @@ -1276,23 +1702,23 @@ inverse of $(b,opam-install)."; `P "If a directory name is specified as package, packages pinned to that \ directory are both unpinned and removed."; - `S "ARGUMENTS"; - `S "OPTIONS"; - `S OpamArg.build_option_section; - ] in + `S Manpage.s_arguments; + `S Manpage.s_options; + ] @ OpamArg.man_build_option_section + in let autoremove = - mk_flag ["a";"auto-remove"] + mk_flag ~cli cli_original ["a";"auto-remove"] "Remove all the packages which have not been explicitly installed and \ which are not necessary anymore. It is possible to prevent the removal \ of an already-installed package by running $(b,opam install \ --set-root). This flag can also be set using the $(b,\\$OPAMAUTOREMOVE) \ configuration variable." in let force = - mk_flag ["force"] + mk_flag ~cli cli_original ["force"] "Execute the remove commands of given packages directly, even if they are \ not considered installed by opam." in let destdir = - mk_opt ["destdir"] "DIR" + mk_opt ~cli cli_original ["destdir"] "DIR" "Instead of uninstalling the packages, reverts the action of $(b,opam \ install --destdir): remove files corresponding to what the listed \ packages installed to the current switch from the given $(i,DIR). Note \ @@ -1302,9 +1728,10 @@ specified." Arg.(some dirname) None in - let remove global_options build_options autoremove force destdir atom_locs = - apply_global_options global_options; - apply_build_options build_options; + let remove global_options build_options autoremove force destdir recurse + subpath atom_locs () = + apply_global_options cli global_options; + apply_build_options cli build_options; OpamGlobalState.with_ `Lock_none @@ fun gt -> match destdir with | Some d -> @@ -1326,7 +1753,9 @@ let pure_atoms, pin_atoms = List.partition (function `Atom _ -> true | _ -> false) atom_locs in - let pin_atoms = OpamAuxCommands.resolve_locals_pinned st pin_atoms in + let pin_atoms = + OpamAuxCommands.resolve_locals_pinned st ~recurse ?subpath pin_atoms + in let st = if OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show) then st else OpamPinCommand.unpin st (List.map fst pin_atoms) @@ -1335,56 +1764,59 @@ List.map (function `Atom a -> a | _ -> assert false) pure_atoms @ pin_atoms in - ignore @@ OpamClient.remove st ~autoremove ~force atoms + let autoremove = autoremove || OpamClientConfig.(!r.autoremove) in + OpamSwitchState.drop (OpamClient.remove st ~autoremove ~force atoms) in - Term.(const remove $global_options $build_options $autoremove $force $destdir - $atom_or_dir_list), - term_info "remove" ~doc ~man + mk_command ~cli cli_original "remove" ~doc ~man + Term.(const remove $global_options cli $build_options cli $autoremove + $force $destdir $recurse cli $subpath cli $atom_or_dir_list) (* REINSTALL *) -let reinstall = +let reinstall cli = let doc = "Reinstall a list of packages." in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command removes the given packages and the ones that depend on \ them, and reinstalls the same versions. Without arguments, assume \ $(b,--pending) and reinstall any package with upstream changes."; `P "If a directory is specified as argument, anything that is pinned to \ that directory is selected for reinstall."; - `S "ARGUMENTS"; - `S "OPTIONS"; - `S OpamArg.build_option_section; - ] in + `S Manpage.s_arguments; + `S Manpage.s_options; + ] @ OpamArg.man_build_option_section + in let cmd = - Arg.(value & vflag `Default [ - `Pending, info ["pending"] - ~doc:"Perform pending reinstallations, i.e. reinstallations of \ + mk_vflag ~cli `Default [ + cli_original, `Pending, ["pending"], + "Perform pending reinstallations, i.e. reinstallations of \ packages that have changed since installed"; - `List_pending, info ["list-pending"] - ~doc:"List packages that have been changed since installed and are \ + cli_original, `List_pending, ["list-pending"], + "List packages that have been changed since installed and are \ marked for reinstallation"; - `Forget_pending, info ["forget-pending"] - ~doc:"Forget about pending reinstallations of listed packages. This \ + cli_original, `Forget_pending, ["forget-pending"], + "Forget about pending reinstallations of listed packages. This \ implies making opam assume that your packages were installed \ with a newer version of their metadata, so only use this if \ you know what you are doing, and the actual changes you are \ overriding." - ]) + ] in - let reinstall global_options build_options assume_built atoms_locs cmd = - apply_global_options global_options; - apply_build_options build_options; + let reinstall global_options build_options assume_built recurse subpath + atoms_locs cmd () = + apply_global_options cli global_options; + apply_build_options cli build_options; + let open OpamPackage.Set.Op in OpamGlobalState.with_ `Lock_none @@ fun gt -> match cmd, atoms_locs with | `Default, (_::_ as atom_locs) -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> - ignore @@ OpamClient.reinstall st ~assume_built - (OpamAuxCommands.resolve_locals_pinned st atom_locs); + OpamSwitchState.drop @@ OpamClient.reinstall st ~assume_built + (OpamAuxCommands.resolve_locals_pinned st ~recurse ?subpath atom_locs); `Ok () | `Pending, [] | `Default, [] -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> - let atoms = OpamSolution.eq_atoms_of_packages st.reinstall in - ignore @@ OpamClient.reinstall st atoms; + let atoms = OpamSolution.eq_atoms_of_packages (Lazy.force st.reinstall) in + OpamSwitchState.drop @@ OpamClient.reinstall st atoms; `Ok () | `List_pending, [] -> OpamSwitchState.with_ `Lock_none gt @@ fun st -> @@ -1396,14 +1828,15 @@ header = false; order = `Dependency; } - st.reinstall; + (Lazy.force st.reinstall); `Ok () | `Forget_pending, atom_locs -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> - let atoms = OpamAuxCommands.resolve_locals_pinned st atom_locs in + let atoms = OpamAuxCommands.resolve_locals_pinned ~recurse ?subpath st atom_locs in + let reinstall = Lazy.force st.reinstall in let to_forget = match atoms with - | [] -> st.reinstall - | atoms -> OpamFormula.packages_of_atoms st.reinstall atoms + | [] -> reinstall + | atoms -> OpamFormula.packages_of_atoms reinstall atoms in OpamPackage.Set.iter (fun nv -> try @@ -1416,22 +1849,23 @@ then OpamSwitchAction.install_metadata st nv with Not_found -> ()) to_forget; - let reinstall = OpamPackage.Set.Op.(st.reinstall -- to_forget) in - ignore @@ OpamSwitchAction.update_switch_state ~reinstall st; + let reinstall = reinstall -- to_forget in + OpamSwitchState.drop @@ OpamSwitchAction.update_switch_state ~reinstall st; `Ok () | _, _::_ -> `Error (true, "Package arguments not allowed with this option") in - Term.(ret (const reinstall $global_options $build_options $assume_built - $atom_or_dir_list $cmd)), - term_info "reinstall" ~doc ~man + mk_command_ret ~cli cli_original "reinstall" ~doc ~man + Term.(const reinstall $global_options cli $build_options cli + $assume_built cli $recurse cli $subpath cli $atom_or_dir_list + $cmd) (* UPDATE *) let update_doc = "Update the list of available packages." -let update = +let update cli = let doc = update_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Update the package definitions. This fetches the newest version of the \ repositories configured through $(b, opam repository), and the sources \ of installed development packages and packages pinned in the current \ @@ -1439,37 +1873,46 @@ $(b,opam upgrade)."; ] in let repos_only = - mk_flag ["R"; "repositories"] + mk_flag ~cli cli_original ["R"; "repositories"] "Update repositories (skipping development packages unless \ $(b,--development) is also specified)." in let dev_only = - mk_flag ["development"] + mk_flag ~cli cli_original ["development"] "Update development packages (skipping repositories unless \ $(b,--repositories) is also specified)." in + let depexts_only = + mk_flag ~cli (cli_from cli2_1) ["depexts"] + "Request the system package manager to update its databases (skipping \ + all opam packages, unless $(b,--development) or $(b,--repositories) is \ + also specified). This generally requires $(b,sudo) rights." in let upgrade = - mk_flag ["u";"upgrade"] + mk_flag ~cli cli_original ["u";"upgrade"] "Automatically run $(b,opam upgrade) after the update." in let name_list = arg_list "NAMES" "List of repository or development package names to update." Arg.string in let all = - mk_flag ["a"; "all"] + mk_flag ~cli cli_original ["a"; "all"] "Update all configured repositories, not only what is set in the current \ switch" in let check = - mk_flag ["check"] + mk_flag ~cli cli_original ["check"] "Do the update, then return with code 0 if there were any upstream \ changes, 1 if there were none. Repositories or development packages \ that failed to update are considered without changes. With \ - $(b,--upgrade), behaves like $(b,opam upgrade --check), that is, \ - returns 0 only if there are currently availbale updates." in - let update global_options jobs names repos_only dev_only all check upgrade = - apply_global_options global_options; + $(b,--upgrade), applies to the upgrade step: that is $(b,opam update \ + --upgrade --check) behaves like $(b,opam update && opam upgrade --check), \ + returning 0 if there are available upgrades, rather than upstream updates." in + let update global_options jobs names repos_only dev_only depexts_only all + check upgrade () = + apply_global_options cli global_options; OpamStateConfig.update ?jobs:OpamStd.Option.Op.(jobs >>| fun j -> lazy j) (); OpamClientConfig.update (); + if depexts_only then OpamSysInteract.update (); + if depexts_only && not (repos_only || dev_only) then () else OpamGlobalState.with_ `Lock_write @@ fun gt -> let success, changed, rt = OpamClient.update gt @@ -1478,54 +1921,61 @@ ~all names in + OpamStd.Exn.finally (fun () -> OpamRepositoryState.drop rt) + @@ fun () -> if upgrade then OpamSwitchState.with_ `Lock_write gt ~rt @@ fun st -> OpamConsole.msg "\n"; - ignore @@ OpamClient.upgrade st ~check ~all:true [] + OpamSwitchState.drop @@ OpamClient.upgrade st ~check ~all:true [] else if check then OpamStd.Sys.exit_because (if changed then `Success else `False) else if changed then OpamConsole.msg "Now run 'opam upgrade' to apply any package updates.\n"; if not success then OpamStd.Sys.exit_because `Sync_error in - Term.(const update $global_options $jobs_flag $name_list - $repos_only $dev_only $all $check $upgrade), - term_info "update" ~doc ~man + mk_command ~cli cli_original "update" ~doc ~man + Term.(const update $global_options cli $jobs_flag cli cli_original $name_list + $repos_only $dev_only $depexts_only $all $check $upgrade) (* UPGRADE *) let upgrade_doc = "Upgrade the installed package to latest version." -let upgrade = +let upgrade cli = let doc = upgrade_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command upgrades the installed packages to their latest available \ versions. More precisely, this command calls the dependency solver to \ find a consistent state where $(i,most) of the installed packages are \ upgraded to their latest versions."; `P "If a directory is specified as argument, anything that is pinned to \ that directory is selected for upgrade."; - `S "ARGUMENTS"; - `S "OPTIONS"; - `S OpamArg.build_option_section; - ] in + `S Manpage.s_arguments; + `S Manpage.s_options; + ] @ OpamArg.man_build_option_section + in let fixup = - mk_flag ["fixup"] + mk_flag ~cli cli_original ["fixup"] "Recover from a broken state (eg. missing dependencies, two conflicting \ packages installed together...)." in let check = - mk_flag ["check"] + mk_flag ~cli cli_original ["check"] "Don't run the upgrade: just check if anything could be upgraded. \ Returns 0 if that is the case, 1 if there is nothing that can be \ upgraded." in let all = - mk_flag ["a";"all"] + mk_flag ~cli cli_original ["a";"all"] "Run an upgrade of all installed packages. This is the default if \ $(i,PACKAGES) was not specified, and can be useful with $(i,PACKAGES) \ to upgrade while ensuring that some packages get or remain installed." in - let upgrade global_options build_options fixup check all atom_locs = - apply_global_options global_options; - apply_build_options build_options; + let installed = + mk_flag ~cli (cli_from cli2_1) ["installed"] + "When a directory is provided as argument, do not install pinned package \ + that are not yet installed." in + let upgrade global_options build_options fixup check only_installed all + recurse subpath atom_locs () = + apply_global_options cli global_options; + apply_build_options cli build_options; let all = all || atom_locs = [] in OpamGlobalState.with_ `Lock_none @@ fun gt -> if fixup then @@ -1533,25 +1983,25 @@ `Error (true, Printf.sprintf "--fixup doesn't allow extra arguments") else OpamSwitchState.with_ `Lock_write gt @@ fun st -> - ignore @@ OpamClient.fixup st; + OpamSwitchState.drop @@ OpamClient.fixup st; `Ok () else OpamSwitchState.with_ `Lock_write gt @@ fun st -> - let atoms = OpamAuxCommands.resolve_locals_pinned st atom_locs in - ignore @@ OpamClient.upgrade st ~check ~all atoms; + let atoms = OpamAuxCommands.resolve_locals_pinned st ~recurse ?subpath atom_locs in + OpamSwitchState.drop @@ OpamClient.upgrade st ~check ~only_installed ~all atoms; `Ok () in - Term.(ret (const upgrade $global_options $build_options $fixup $check $all - $atom_or_dir_list)), - term_info "upgrade" ~doc ~man + mk_command_ret ~cli cli_original "upgrade" ~doc ~man + Term.(const upgrade $global_options cli $build_options cli $fixup $check + $installed $all $recurse cli $subpath cli $atom_or_dir_list) (* REPOSITORY *) let repository_doc = "Manage opam repositories." -let repository = +let repository cli = let doc = repository_doc in let scope_section = "SCOPE SPECIFICATION OPTIONS" in let commands = [ - "add", `add, ["NAME"; "[ADDRESS]"; "[QUORUM]"; "[FINGERPRINTS]"], + cli_original, "add", `add, ["NAME"; "[ADDRESS]"; "[QUORUM]"; "[FINGERPRINTS]"], "Adds under $(i,NAME) the repository at address $(i,ADDRESS) to the list \ of configured repositories, if not already registered, and sets this \ repository for use in the current switch (or the specified scope). \ @@ -1560,35 +2010,36 @@ address. The quorum is a positive integer that determines the validation \ threshold for signed repositories, with fingerprints the trust anchors \ for said validation."; - " ", `add, [], + cli_original, " ", `add, [], (* using an unbreakable space here will indent the text paragraph at the level of the previous labelled paragraph, which is what we want for our note. *) "$(b,Note:) By default, the repository is only added to the current \ - switch. To add a switch to other repositories, you need to use the \ + switch. To add a repository to other switches, you need to use the \ $(b,--all) or $(b,--set-default) options (see below). If you want to \ - enable a repository only to install of of its switches, you may be \ + enable a repository only to install its switches, you may be \ looking for $(b,opam switch create --repositories=REPOS)."; - "remove", `remove, ["NAME..."], + cli_original, "remove", `remove, ["NAME..."], "Unselects the given repositories so that they will not be used to get \ package definitions anymore. With $(b,--all), makes opam forget about \ these repositories completely."; - "set-repos", `set_repos, ["NAME..."], + cli_original, "set-repos", `set_repos, ["NAME..."], "Explicitly selects the list of repositories to look up package \ definitions from, in the specified priority order (overriding previous \ selection and ranks), according to the specified scope."; - "set-url", `set_url, ["NAME"; "ADDRESS"; "[QUORUM]"; "[FINGERPRINTS]"], + cli_original, "set-url", `set_url, + ["NAME"; "ADDRESS"; "[QUORUM]"; "[FINGERPRINTS]"], "Updates the URL and trust anchors associated with a given repository \ name. Note that if you don't specify $(i,[QUORUM]) and \ $(i,[FINGERPRINTS]), any previous settings will be erased."; - "list", `list, [], + cli_original, "list", `list, [], "Lists the currently selected repositories in priority order from rank 1. \ - With $(b,--all), lists all configured repositories and the switches where \ - they are active."; - "priority", `priority, ["NAME"; "RANK"], + With $(b,--all), lists all configured repositories and the switches \ + where they are active."; + cli_original, "priority", `priority, ["NAME"; "RANK"], "Synonym to $(b,add NAME --rank RANK)"; ] in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command is used to manage package repositories. Repositories can \ be registered through subcommands $(b,add), $(b,remove) and \ $(b,set-url), and are updated from their URLs using $(b,opam update). \ @@ -1599,30 +2050,30 @@ explained in $(b,"^scope_section^")."); `P "Without a subcommand, or with the subcommand $(b,list), lists selected \ repositories, or all configured repositories with $(b,--all)."; - ] @ mk_subdoc ~defaults:["","list"] commands @ [ + ] @ mk_subdoc ~cli ~defaults:["","list"] commands @ [ `S scope_section; `P "These flags allow one to choose which selections are changed by $(b,add), \ $(b,remove), $(b,set-repos). If no flag in this section is specified \ the updated selections default to the current switch. Multiple scopes \ can be selected, e.g. $(b,--this-switch --set-default)."; - `S "OPTIONS"; + `S Manpage.s_options; ] in - let command, params = mk_subcommands commands in + let command, params = mk_subcommands ~cli commands in let scope = let scope_info ?docv flags doc = Arg.info ~docs:scope_section ~doc ?docv flags in let flags = - Arg.vflag_all [] [ - `No_selection, scope_info ["dont-select"] + mk_vflag_all ~cli ~section:scope_section [ + cli_original, `No_selection, ["dont-select"], "Don't update any selections"; - `Current_switch, scope_info ["this-switch"] + cli_original, `Current_switch, ["this-switch"], "Act on the selections for the current switch (this is the default)"; - `Default, scope_info ["set-default"] + cli_original, `Default, ["set-default"], "Act on the default repository selection that is used for newly \ created switches"; - `All, scope_info ["all-switches";"a"] + cli_original, `All, ["all-switches";"a"], "Act on the selections of all configured switches"; ] in @@ -1636,19 +2087,19 @@ $ Arg.value switches) in Term.(const (fun l1 l2 -> match l1@l2 with [] -> [`Current_switch] | l -> l) - $ Arg.value flags $ switches) + $ flags $ switches) in let rank = - Arg.(value & opt int 1 & info ~docv:"RANK" ["rank"] ~doc: - "Set the rank of the repository in the list of configured \ - repositories. Package definitions are looked in the repositories \ - in increasing rank order, therefore 1 is the highest priority. \ - Negative ints can be used to select from the lowest priority, -1 \ - being last. $(b,set-repos) can otherwise be used to explicitly \ - set the repository list at once.") + mk_opt ~cli cli_original ["rank"] "RANK" + "Set the rank of the repository in the list of configured repositories. \ + Package definitions are looked in the repositories in increasing rank \ + order, therefore 1 is the highest priority. Negative ints can be used to \ + select from the lowest priority, -1 being last. $(b,set-repos) can \ + otherwise be used to explicitly set the repository list at once." + Arg.(int) 1 in - let repository global_options command kind short scope rank params = - apply_global_options global_options; + let repository global_options command kind short scope rank params () = + apply_global_options cli global_options; let global = List.mem `Default scope in let command, params, rank = match command, params, rank with | Some `priority, [name; rank], 1 -> @@ -1709,7 +2160,7 @@ | Some _ -> kind | None -> OpamUrl.guess_version_control url in - let url = OpamUrl.parse ?backend url in + let url = OpamUrl.parse ?backend ~from_file:false url in let trust_anchors = match security with | [] -> None | quorum::fingerprints -> @@ -1724,13 +2175,11 @@ OpamRepositoryCommand.update_with_auto_upgrade rt [name] in if failed <> [] then - (let _rt = OpamRepositoryCommand.remove rt name in + (OpamRepositoryState.drop @@ OpamRepositoryCommand.remove rt name; OpamConsole.error_and_exit `Sync_error "Initial repository fetch failed")); - let _gt = - OpamRepositoryCommand.update_selection gt ~global ~switches - (update_repos name) - in + OpamGlobalState.drop @@ OpamRepositoryCommand.update_selection gt ~global + ~switches (update_repos name); if scope = [`Current_switch] then OpamConsole.note "Repository %s has been added to the selections of switch %s \ @@ -1756,8 +2205,8 @@ check_for_repos rt names (OpamConsole.warning "No configured repositories by these names found: %s"); - let _rt = List.fold_left OpamRepositoryCommand.remove rt names in - () + OpamRepositoryState.drop @@ + List.fold_left OpamRepositoryCommand.remove rt names else if scope = [`Current_switch] then OpamConsole.msg "Repositories removed from the selections of switch %s. \ @@ -1770,10 +2219,9 @@ check_for_repos rt [name] (OpamConsole.error_and_exit `Not_found "No configured repository '%s' found, you must specify an URL")); - let _gt = - OpamRepositoryCommand.update_selection gt ~global ~switches - (update_repos name) - in + OpamGlobalState.drop @@ + OpamRepositoryCommand.update_selection gt ~global ~switches + (update_repos name); `Ok () | Some `set_url, (name :: url :: security) -> let name = OpamRepositoryName.of_string name in @@ -1782,7 +2230,7 @@ | Some _ -> kind | None -> OpamUrl.guess_version_control url in - let url = OpamUrl.parse ?backend url in + let url = OpamUrl.parse ?backend ~from_file:false url in let trust_anchors = match security with | [] -> None | quorum::fingerprints -> @@ -1808,11 +2256,10 @@ List.filter (fun r -> not (OpamRepositoryName.Map.mem r repos)) names in if not_found = [] then - let _gt = - OpamRepositoryCommand.update_selection gt ~global ~switches - (fun _ -> names) - in - `Ok () + (OpamGlobalState.drop @@ + OpamRepositoryCommand.update_selection gt ~global ~switches + (fun _ -> names); + `Ok ()) else OpamConsole.error_and_exit `Bad_arguments "No configured repositories by these names found: %s" @@ -1836,12 +2283,12 @@ '--all' to see all configured repositories."; OpamRepositoryCommand.list rt ~global ~switches ~short; `Ok () - | command, params -> bad_subcommand commands ("repository", command, params) + | command, params -> bad_subcommand ~cli commands ("repository", command, params) in - Term.ret - Term.(const repository $global_options $command $repo_kind_flag - $print_short_flag $scope $rank $params), - term_info "repository" ~doc ~man + mk_command_ret ~cli cli_original "repository" ~doc ~man + Term.(const repository $global_options cli $command + $repo_kind_flag cli cli_original $print_short_flag cli cli_original + $scope $rank $params) (* SWITCH *) @@ -1849,8 +2296,9 @@ (* From a list of strings (either "repo_name" or "repo_name=URL"), configure the repos with URLs if possible, and return the updated repos_state and selection of repositories *) -let get_repos_rt gt repos = +let with_repos_rt gt repos f = OpamRepositoryState.with_ `Lock_none gt @@ fun rt -> + let repos, rt = match repos with | None -> None, rt | Some repos -> @@ -1864,7 +2312,15 @@ let new_defs = OpamStd.List.filter_map (function | (_, None) -> None - | (n, Some u) -> Some (n, OpamUrl.of_string u)) + | (n, Some url) -> + let repo = + OpamStd.Option.Op.( + OpamUrl.parse_opt ~handle_suffix:false ~from_file:false url + >>| fun u -> n, u) + in + if repo = None then + OpamConsole.warning "Skipping %s, malformed url" url; + repo) repos in if List.for_all @@ -1889,59 +2345,72 @@ (List.map fst new_defs) in if failed <> [] then - let _rt = List.fold_left OpamRepositoryCommand.remove rt failed in - OpamConsole.error_and_exit `Sync_error - "Initial fetch of these repositories failed: %s" - (OpamStd.List.concat_map ", " OpamRepositoryName.to_string failed) + (OpamRepositoryState.drop @@ List.fold_left + OpamRepositoryCommand.remove rt failed; + OpamConsole.error_and_exit `Sync_error + "Initial fetch of these repositories failed: %s" + (OpamStd.List.concat_map ", " OpamRepositoryName.to_string failed)) else Some (List.map fst repos), rt + in + f (repos, rt) let switch_doc = "Manage multiple installation prefixes." -let switch = +let switch cli = + let shell = OpamStd.Sys.guess_shell_compat () in let doc = switch_doc in let commands = [ - "create", `install, ["SWITCH"; "[COMPILER]"], + cli_original, "create", `install, ["SWITCH"; "[COMPILER]"], "Create a new switch, and install the given compiler there. $(i,SWITCH) \ can be a plain name, or a directory, absolute or relative, in which case \ a local switch is created below the given directory. $(i,COMPILER), if \ omitted, defaults to $(i,SWITCH) if it is a plain name, unless \ - $(b,--packages) or $(b,--empty) is specified. When creating a local \ - switch, and none of these options are present, the compiler is chosen \ - according to the configuration default (see opam-init(1)). If the chosen \ - directory contains package definitions, a compatible compiler is searched \ - within the default selection, and the packages will automatically get \ - installed."; - "set", `set, ["SWITCH"], + $(b,--packages), $(b,--formula) or $(b,--empty) is specified. When \ + creating a local switch, and none of these options are present, the \ + compiler is chosen according to the configuration default (see \ + opam-init(1)). If the chosen directory contains package definitions, a \ + compatible compiler is searched within the default selection, and the \ + packages will automatically get installed."; + cli_original, "set", `set, ["SWITCH"], "Set the currently active switch, among the installed switches."; - "remove", `remove, ["SWITCH"], "Remove the given switch from disk."; - "export", `export, ["FILE"], + cli_original, "remove", `remove, ["SWITCH"], + "Remove the given switch from disk."; + cli_original, "export", `export, ["FILE"], "Save the current switch state to a file. If $(b,--full) is specified, it \ - includes the metadata of all installed packages"; - "import", `import, ["FILE"], + includes the metadata of all installed packages, and if $(b,--freeze) is \ + specified, it freezes all vcs to their current commit."; + cli_original, "import", `import, ["FILE"], "Import a saved switch state. If $(b,--switch) is specified and doesn't \ point to an existing switch, the switch will be created for the import."; - "reinstall", `reinstall, ["[SWITCH]"], + cli_original, "reinstall", `reinstall, ["[SWITCH]"], "Reinstall the given compiler switch and all its packages."; - "list", `list, [], + cli_original, "list", `list, [], "Lists installed switches."; - "list-available", `list_available, ["[PATTERN]"], - "Lists base packages that can be used to create a new switch, i.e. \ - packages with the $(i,compiler) flag set. If no pattern is supplied, \ - all versions are shown."; - "show", `current, [], "Prints the name of the current switch."; - "set-base", `set_compiler, ["PACKAGES"], - "Sets the packages forming the immutable base for the selected switch, \ - overriding the current setting."; - "set-description", `set_description, ["STRING"], - "Sets the description for the selected switch"; - "link", `link, ["SWITCH";"[DIR]"], + cli_original, "list-available", `list_available, ["[PATTERN]"], + "Lists all the possible packages that are advised for installation when \ + creating a new switch, i.e. packages with the $(i,compiler) flag set. If \ + no pattern is supplied, all versions are shown."; + cli_original, "show", `current, [], + "Prints the name of the current switch."; + cli_from cli2_1, "invariant", `show_invariant, [], + "Prints the active switch invariant."; + cli_from cli2_1, "set-invariant", `set_invariant, ["PACKAGES"], + "Updates the switch invariant, that is, the formula that the switch must \ + keep verifying throughout all operations. The previous setting is \ + overriden. See also options $(b,--force) and $(b,--no-action). Without \ + arguments, an invariant is chosen automatically."; + cli_original, "set-description", `set_description, ["STRING"], + "Sets the description for the selected switch."; + cli_original, "link", `link, ["SWITCH";"[DIR]"], "Sets a local alias for a given switch, so that the switch gets \ automatically selected whenever in that directory or a descendant."; - "install", `install, ["SWITCH"], - "Deprecated alias for 'create'." + cli_between cli2_0 cli2_1 ~replaced:"create", "install", `install, ["SWITCH"], + "Install a new switch"; + cli_between cli2_0 cli2_1 ~replaced:"set-invariant", "set-base", `set_invariant, ["PACKAGES"], + "Define a set of switch base packages."; ] in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command is used to manage \"switches\", which are independent \ installation prefixes with their own compiler and sets of installed \ and pinned packages. This is typically useful to have different \ @@ -1961,29 +2430,56 @@ prompted to install them after the switch is created unless \ $(b,--no-install) is specified." OpamArg.dir_sep OpamSwitch.external_dirname); - `P "$(b,opam switch set) sets the default switch globally, but it is also \ - possible to select a switch in a given shell session, using the \ - environment. For that, use $(i,eval \\$(opam env \ - --switch=SWITCH --set-switch\\))."; - ] @ mk_subdoc ~defaults:["","list";"SWITCH","set"] commands - @ [`S "OPTIONS"] - @ [`S OpamArg.build_option_section] + `P (Printf.sprintf + "$(b,opam switch set) sets the default switch globally, but it is also \ + possible to select a switch in a given shell session, using the \ + environment. For that, use $(i,%s)." + OpamEnv.( + shell_eval_invocation shell + (opam_env_invocation ~switch:"SWITCH" ~set_opamswitch:true ()) + |> Manpage.escape)); + ] @ mk_subdoc ~cli ~defaults:["","list";"SWITCH","set"] commands + @ [ + `S Manpage.s_examples; + `Pre " opam switch create 4.08.0"; + `P "Create a new switch called \"4.08.0\" and select it, with a compiler \ + automatically selected at version 4.08.0 (note that this can fail in \ + case there is more than one compiler matching that version)."; + `Pre " opam switch create ./ --deps-only"; + `P "Prepare a local switch for building the packages defined in $(i,./). \ + This scans the current directory for package definitions, chooses a \ + compatible compiler, creates a local switch and installs the local \ + package dependencies."; + `Pre " opam switch create trunk --repos \ + default,beta=git+https://github.com/ocaml/ocaml-beta-repository.git \ + ocaml-variants.4.10.0+trunk"; + `P "Create a new switch called \"trunk\", with \ + $(b,ocaml-variants.4.10.0+trunk) as compiler, with a new $(i,beta) \ + repository bound to the given URL selected besides the default one." + ] + @ [`S Manpage.s_options] + @ OpamArg.man_build_option_section in - let command, params = mk_subcommands_with_default commands in + let command, params = mk_subcommands_with_default ~cli commands in let no_switch = - mk_flag ["no-switch"] + mk_flag ~cli cli_original ["no-switch"] "Don't automatically select newly installed switches." in let packages = - mk_opt ["packages"] "PACKAGES" - "When installing a switch, explicitly define the set of packages to set \ - as the compiler." + mk_opt ~cli cli_original ["packages"] "PACKAGES" + "When installing a switch, explicitly define the set of packages to \ + enforce as the switch invariant." Arg.(some (list atom)) None in + let formula = + mk_opt ~cli (cli_from cli2_1) ["formula"] "FORMULA" + "Allows specifying a complete \"dependency formula\", possibly including \ + disjunction cases, as the switch invariant." + Arg.(some OpamArg.dep_formula) None in let empty = - mk_flag ["empty"] - "Allow creating an empty (without compiler) switch." in + mk_flag ~cli cli_original ["empty"] + "Allow creating an empty switch, with no invariant." in let repos = - mk_opt ["repositories"] "REPOS" + mk_opt ~cli cli_original ["repositories"] "REPOS" "When creating a new switch, use the given selection of repositories \ instead of the default. $(i,REPOS) should be a comma-separated list of \ either already registered repository names (configured through e.g. \ @@ -1995,79 +2491,102 @@ Arg.(some (list string)) None in let descr = - mk_opt ["description"] "STRING" + mk_opt ~cli cli_original ["description"] "STRING" "Attach the given description to a switch when creating it. Use the \ $(i,set-description) subcommand to modify the description of an \ existing switch." Arg.(some string) None in let full = - mk_flag ["full"] + mk_flag ~cli cli_original ["full"] "When exporting, include the metadata of all installed packages, \ allowing to re-import even if they don't exist in the repositories (the \ default is to include only the metadata of pinned packages)." in + let freeze = + mk_flag ~cli (cli_from cli2_1) ["freeze"] + "When exporting, locks all VCS urls to their current commit, failing if \ + it can not be retrieved. This ensures that an import will restore the \ + exact state. Implies $(b,--full)." + in let no_install = - mk_flag ["no-install"] + mk_flag ~cli cli_original ["no-install"] "When creating a local switch, don't look for any local package \ definitions to install." in let deps_only = - mk_flag ["deps-only"] + mk_flag ~cli cli_original ["deps-only"] "When creating a local switch in a project directory (i.e. a directory \ containing opam package definitions), install the dependencies of the \ project but not the project itself." in + let force = + mk_flag ~cli (cli_from cli2_1) ["force"] + "Only for $(i,set-invariant): force setting the invariant, bypassing \ + consistency checks." + in + let no_action = + mk_flag ~cli (cli_from cli2_1) ["n"; "no-action"] + "Only for $(i,set-invariant): set the invariant, but don't enforce it \ + right away: wait for the next $(i,install), $(i,upgrade) or similar \ + command." + in (* Deprecated options *) let d_alias_of = - mk_opt ["A";"alias-of"] - "COMP" - "This option is deprecated." - Arg.(some string) None + mk_opt ~cli (cli_between cli2_0 cli2_1) + ["A";"alias-of"] "COMP" "Deprecated" Arg.(some string) None in let d_no_autoinstall = - mk_flag ["no-autoinstall"] - "This option is deprecated." + mk_flag ~cli (cli_between cli2_0 cli2_1) ["no-autoinstall"] "Deprecated" in let switch global_options build_options command print_short - no_switch packages empty descr full no_install deps_only repos - d_alias_of d_no_autoinstall params = - OpamArg.deprecated_option d_alias_of None - "alias-of" (Some "opam switch "); - OpamArg.deprecated_option d_no_autoinstall false "no-autoinstall" None; - apply_global_options global_options; - apply_build_options build_options; - let packages = - match packages, empty with - | None, true -> Some [] - | Some packages, true when packages <> [] -> - OpamConsole.error_and_exit `Bad_arguments - "Options --packages and --empty may not be specified at the same time" - | packages, _ -> packages - in - let compiler_packages rt ?repos switch compiler_opt = - match packages, compiler_opt, OpamSwitch.is_external switch with - | None, None, false -> - OpamSwitchCommand.guess_compiler_package ?repos rt - (OpamSwitch.to_string switch), false - | None, None, true -> - OpamAuxCommands.get_compatible_compiler ?repos rt - (OpamFilename.dirname_dir - (OpamSwitch.get_root rt.repos_global.root switch)) + no_switch packages formula empty descr full freeze no_install deps_only repos + force no_action + d_alias_of d_no_autoinstall params () = + if d_alias_of <> None then + OpamConsole.warning + "Option %s is deprecated, ignoring it. \ + Use instead 'opam switch '" + (OpamConsole.colorise `bold "--alias-of"); + if d_no_autoinstall then + OpamConsole.warning "Option %s is deprecated, ignoring it." + (OpamConsole.colorise `bold "--no-autoinstall"); + apply_global_options cli global_options; + apply_build_options cli build_options; + let invariant_arg ?repos rt args = + match args, packages, formula, empty with + | [], None, None, false -> None + | _::_ as packages, None, None, false -> + Some (OpamSwitchCommand.guess_compiler_invariant ?repos rt packages) + | [], Some atoms, None, false -> + let atoms = List.map (fun p -> Atom p) atoms in + Some (OpamFormula.of_atom_formula (OpamFormula.ands atoms)) + | [], None, (Some f), false -> Some f + | [], None, None, true -> Some OpamFormula.Empty + | _::_ as packages, Some atoms, None, false -> + if fst cli = cli2_0 then + let atoms = List.map (fun p -> Atom p) atoms in + let pkgs_formula = + OpamFormula.of_atom_formula (OpamFormula.ands atoms) + in + let args_formula = + OpamSwitchCommand.guess_compiler_invariant ?repos rt packages + in + Some (OpamFormula.And (args_formula, pkgs_formula)) + else + OpamConsole.error_and_exit `Bad_arguments + "Individual package and option '--packages' can not be specified at \ + the same time. Use just '--packages' instead, e.g.\n\ + opam switch create flambda \ + --packages=ocaml.4.12.0,ocaml-option-flambda\n\ + or '--formula'\n\ + opam switch create flambda \ + --formula='[\"ocaml\" {=\"4.12.0\"} \"ocaml-option-flambda\"]'" | _ -> - OpamStd.Option.Op.( - ((compiler_opt >>| - OpamSwitchCommand.guess_compiler_package ?repos rt) +! []) @ - packages +! []), false - in - let param_compiler = function - | [] -> None - | [comp] -> Some comp - | args -> OpamConsole.error_and_exit `Bad_arguments - "Invalid extra arguments %s" - (String.concat " " args) + "Individual packages, options --packages, --formula and --empty may \ + not be specified at the same time" in match command, params with | None , [] @@ -2077,7 +2596,7 @@ `Ok () | Some `list_available, pattlist -> OpamGlobalState.with_ `Lock_none @@ fun gt -> - let repos, rt = get_repos_rt gt repos in + with_repos_rt gt repos @@ fun (repos, rt) -> let compilers = OpamSwitchCommand.get_compiler_packages ?repos rt in let st = OpamSwitchState.load_virtual ?repos_list:repos gt rt in OpamConsole.msg "# Listing available compilers from repositories: %s\n" @@ -2117,35 +2636,83 @@ `Ok () | Some `install, switch_arg::params -> OpamGlobalState.with_ `Lock_write @@ fun gt -> - let repos, rt = get_repos_rt gt repos in + with_repos_rt gt repos @@ fun (repos, rt) -> let switch = OpamSwitch.of_string switch_arg in - let packages, local_compiler = - compiler_packages rt ?repos switch (param_compiler params) + let use_local = + not no_install && not empty && OpamSwitch.is_external switch in - let _gt, st = - OpamSwitchCommand.install gt ~rt - ?synopsis:descr ?repos - ~update_config:(not no_switch) - ~packages - ~local_compiler - switch - in - let st = - if not no_install && not empty && - OpamSwitch.is_external switch && not local_compiler then - let st, atoms = - OpamAuxCommands.autopin st ~simulate:deps_only ~quiet:true - [`Dirname (OpamFilename.Dir.of_string switch_arg)] - in - OpamClient.install st atoms - ~autoupdate:[] ~add_to_roots:true ~deps_only - else st + let is_implicit = + params = [] && packages = None && formula = None && not empty in - ignore (OpamSwitchState.unlock st); - `Ok () + let pkg_params = + if is_implicit && not (OpamSwitch.is_external switch) then [switch_arg] + else params + in + (match invariant_arg ?repos rt pkg_params with + | exception Failure e -> `Error (false, e) + | invariant_opt -> + let invariant = + OpamStd.Option.default + (OpamFile.Config.default_invariant rt.repos_global.config) + invariant_opt + in + let (), st = + OpamSwitchCommand.create gt ~rt + ?synopsis:descr ?repos + ~update_config:(not no_switch) + ~invariant + switch + @@ fun st -> + let st, additional_installs = + if use_local then + let st, atoms = + OpamAuxCommands.autopin st ~simulate:deps_only ~quiet:true + [`Dirname (OpamFilename.Dir.of_string switch_arg)] + in + let st = + if is_implicit then + let local_compilers = + OpamStd.List.filter_map + (fun (name, _) -> + (* The opam file for the local package might not be + the current pinning (e.g. with deps-only), but it's + guaranteed to be the only available version by + autopin. *) + match + OpamSwitchState.opam st + (OpamPackage.package_of_name + (Lazy.force st.available_packages) + name) + with + | opam -> + if OpamFile.OPAM.has_flag Pkgflag_Compiler opam then + Some (Atom (name, None)) + else None + | exception Not_found -> None) + atoms + in + if local_compilers <> [] then + OpamSwitchCommand.set_invariant_raw st + OpamFormula.(of_atom_formula (ands local_compilers)) + else st + else st + in + st, atoms + else st, [] + in + (), + OpamSwitchCommand.install_compiler st + ~additional_installs + ~deps_only + ~ask:(additional_installs <> []) + in + OpamSwitchState.drop st; + `Ok ()) | Some `export, [filename] -> - OpamSwitchCommand.export - ~full + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamRepositoryState.with_ `Lock_none gt @@ fun rt -> + OpamSwitchCommand.export rt + ~full:(full || freeze) ~freeze (if filename = "-" then None else Some (OpamFile.make (OpamFilename.of_string filename))); `Ok () @@ -2153,33 +2720,37 @@ OpamGlobalState.with_ `Lock_none @@ fun gt -> let switch = OpamStateConfig.get_switch () in let is_new_switch = not (OpamGlobalState.switch_exists gt switch) in - let gt, rt = - if is_new_switch then - let repos, rt = get_repos_rt gt repos in - let (), gt = - OpamGlobalState.with_write_lock gt @@ fun gt -> - (), OpamSwitchAction.create_empty_switch gt ?repos switch + let import_source = + if filename = "-" then None + else Some (OpamFile.make (OpamFilename.of_string filename)) + in + if is_new_switch then + with_repos_rt gt repos @@ fun (repos, rt) -> + let synopsis = "Import from " ^ Filename.basename filename in + let (), gt = + OpamGlobalState.with_write_lock gt @@ fun gt -> + let gt, st = + OpamSwitchCommand.create gt ~rt + ~synopsis ?repos ~invariant:OpamFormula.Empty + ~update_config:(not no_switch) + switch + @@ fun st -> + let st = OpamSwitchCommand.import st import_source in + let invariant = OpamSwitchState.infer_switch_invariant st in + let st = OpamSwitchCommand.set_invariant_raw st invariant in + st.switch_global, st in - gt, rt - else - (if repos <> None then - OpamConsole.warning - "Switch exists, '--repositories' argument ignored"; - gt, OpamRepositoryState.load `Lock_none gt) - in - OpamSwitchState.with_ `Lock_write gt ~rt ~switch @@ fun st -> - let _st = - try - OpamSwitchCommand.import st - (if filename = "-" then None - else Some (OpamFile.make (OpamFilename.of_string filename))) - with e -> - if is_new_switch then - OpamConsole.warning - "Switch %s may have been left partially installed" - (OpamSwitch.to_string switch); - raise e - in + OpamSwitchState.drop st; + (), gt + in + OpamGlobalState.drop gt + else begin + if repos <> None then + OpamConsole.warning + "Switch exists, '--repositories' argument ignored"; + OpamSwitchState.with_ `Lock_write gt ~switch @@ fun st -> + OpamSwitchState.drop @@ OpamSwitchCommand.import st import_source + end; `Ok () | Some `remove, switches -> OpamGlobalState.with_ `Lock_write @@ fun gt -> @@ -2207,7 +2778,7 @@ in OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_write gt ~switch @@ fun st -> - let _st = OpamSwitchCommand.reinstall st in + OpamSwitchState.drop @@ OpamSwitchCommand.reinstall st; `Ok () | Some `current, [] -> OpamSwitchCommand.show (); @@ -2216,20 +2787,38 @@ | Some `default switch, [] -> OpamGlobalState.with_ `Lock_write @@ fun gt -> let switch_name = OpamSwitch.of_string switch in - OpamSwitchCommand.switch `Lock_none gt switch_name |> ignore; + OpamSwitchCommand.switch `Lock_none gt switch_name; `Ok () - | Some `set_compiler, packages -> - (try - let parse_namev s = match fst OpamArg.package s with - | `Ok (name, version_opt) -> name, version_opt - | `Error e -> failwith e + | Some `show_invariant, [] -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamSwitchState.with_ `Lock_none gt @@ fun st -> + OpamConsole.msg "%s\n" + (OpamFileTools.dep_formula_to_string st.switch_invariant); + `Ok () + | Some `set_invariant, params -> + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamRepositoryState.with_ `Lock_none gt @@ fun rt -> + OpamSwitchState.with_ `Lock_write gt @@ fun st -> + let repos = OpamSwitchState.repos_list st in + (match invariant_arg ~repos rt params with + | exception Failure e -> `Error (false, e) + | invariant_opt -> + let invariant = match invariant_opt with + | Some i -> i + | None -> OpamSwitchState.infer_switch_invariant st in - let namesv = List.map parse_namev packages in - OpamGlobalState.with_ `Lock_none @@ fun gt -> - OpamSwitchState.with_ `Lock_write gt @@ fun st -> - let _st = OpamSwitchCommand.set_compiler st namesv in - `Ok () - with Failure e -> `Error (false, e)) + let st = OpamSwitchCommand.set_invariant ~force st invariant in + OpamConsole.msg "The switch invariant was set to %s\n" + (OpamFormula.to_string invariant); + let st = + if no_action || OpamFormula.satisfies_depends st.installed invariant + then st + else OpamClient.install_t + st ~ask:true [] None + ~deps_only:false ~assume_built:false + in + OpamSwitchState.drop st; + `Ok ()) | Some `link, args -> (try let switch, dir = match args with @@ -2277,23 +2866,26 @@ in OpamSwitchAction.install_switch_config gt.root st.switch config; `Ok () - | command, params -> bad_subcommand commands ("switch", command, params) + | command, params -> bad_subcommand ~cli commands ("switch", command, params) in - Term.(ret (const switch - $global_options $build_options $command - $print_short_flag - $no_switch - $packages $empty $descr $full $no_install $deps_only - $repos $d_alias_of $d_no_autoinstall $params)), - term_info "switch" ~doc ~man + mk_command_ret ~cli cli_original "switch" ~doc ~man + Term.(const switch + $global_options cli $build_options cli $command + $print_short_flag cli cli_original + $no_switch + $packages $formula $empty $descr $full $freeze $no_install + $deps_only $repos $force $no_action $d_alias_of $d_no_autoinstall + $params) (* PIN *) let pin_doc = "Pin a given package to a specific version or source." -let pin ?(unpin_only=false) () = +let pin ?(unpin_only=false) cli = let doc = pin_doc in let commands = [ - "list", `list, [], "Lists pinned packages."; - "add", `add, ["PACKAGE"; "TARGET"], + cli_original, "list", `list, [], "Lists pinned packages."; + cli_from cli2_1, "scan", `scan, ["DIR"], + "Lists available packages to pin in directory."; + cli_original, "add", `add, ["PACKAGE"; "TARGET"], "Pins package $(i,PACKAGE) to $(i,TARGET), which may be a version, a path, \ or a URL.\n\ $(i,PACKAGE) can be omitted if $(i,TARGET) contains one or more \ @@ -2310,11 +2902,11 @@ For source pinnings, the package version may be specified by using the \ format $(i,NAME).$(i,VERSION) for $(i,PACKAGE), in the source opam file, \ or with $(b,edit)."; - "remove", `remove, ["NAMES...|TARGET"], + cli_original, "remove", `remove, ["NAMES...|TARGET"], "Unpins packages $(i,NAMES), restoring their definition from the \ repository, if any. With a $(i,TARGET), unpins everything that is \ currently pinned to that target."; - "edit", `edit, ["NAME"], + cli_original, "edit", `edit, ["NAME"], "Opens an editor giving you the opportunity to change the package \ definition that opam will locally use for package $(i,NAME), including \ its version and source URL. Using the format $(i,NAME.VERSION) will \ @@ -2324,7 +2916,7 @@ order."; ] in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "This command allows local customisation of the packages in a given \ switch. A pinning can either just enforce a given version, or provide \ a local, editable version of the definition of the package. It is also \ @@ -2343,22 +2935,21 @@ `P "If $(i,PACKAGE) has the form $(i,name.version), the pinned package \ will be considered as version $(i,version) by opam. Beware that this \ doesn't relate with the version of the source actually used for the \ - package."; + package. See also the $(b,--with-version) option."; `P "The default subcommand is $(i,list) if there are no further arguments, \ and $(i,add) otherwise if unambiguous."; - ] @ mk_subdoc ~defaults:["","list"] commands @ [ - `S "OPTIONS"; - `S OpamArg.build_option_section; - ] + ] @ mk_subdoc ~cli ~defaults:["","list"] commands @ [ + `S Manpage.s_options; + ] @ OpamArg.man_build_option_section in let command, params = if unpin_only then Term.const (Some `remove), Arg.(value & pos_all string [] & Arg.info []) else - mk_subcommands_with_default commands in + mk_subcommands_with_default ~cli commands in let edit = - mk_flag ["e";"edit"] + mk_flag ~cli cli_original ["e";"edit"] "With $(i,opam pin add), edit the opam file as with `opam pin edit' \ after pinning." in let kind = @@ -2389,43 +2980,66 @@ ] in Arg.(value & opt (some & enum kinds) None & doc) in let no_act = - mk_flag ["n";"no-action"] + mk_flag ~cli cli_original ["n";"no-action"] "Just record the new pinning status, and don't prompt for \ (re)installation or removal of affected packages." in let dev_repo = - mk_flag ["dev-repo"] "Pin to the upstream package source for the latest \ - development version" + mk_flag ~cli cli_original ["dev-repo"] + "Pin to the upstream package source for the latest development version" in - let guess_names url k = - let from_opam_files dir = - OpamStd.List.filter_map - (fun (nameopt, f) -> - let opam_opt = OpamFile.OPAM.read_opt f in - let name = - match nameopt with - | None -> OpamStd.Option.replace OpamFile.OPAM.name_opt opam_opt - | some -> some - in - OpamStd.Option.map (fun n -> n, opam_opt) name) - (OpamPinned.files_in_source dir) - in - let basename = - match OpamStd.String.split (OpamUrl.basename url) '.' with - | [] -> - OpamConsole.error_and_exit `Bad_arguments - "Can not retrieve a path from '%s'" - (OpamUrl.to_string url) - | b::_ -> b - in + let normalise = + mk_flag ~cli (cli_from cli2_1) ["normalise"] + (Printf.sprintf + "Print list of available package to pin in format \ + `name.version%curl`, that is comprehensible by `opam pin \ + add`. Available only with the scan subcommand. An example of use is \ + `opam pin scan . --normalise | grep foo | xargs opam pin add`" + OpamPinCommand.scan_sep) + in + let with_version = + mk_opt ~cli (cli_from cli2_1) ["with-version"] "VERSION" + "Set the pinning version to $(i,VERSION) for named $(i,PACKAGES) or \ + packages retrieved from $(i,TARGET). It has priority over any other \ + version specification (opam file version field, $(b,name.vers) \ + argument)). When pinning to a version, the package source from that \ + version is used, but declared as being $(i,VERSION) to opam.\n\ + Using $(b,--with-version) is equivalent to using $(b,--edit) and \ + adjusting the version in the package definition file." + Arg.(some package_version) None + in + let guess_names kind ~recurse ?subpath url k = let found, cleanup = match OpamUrl.local_dir url with - | Some d -> from_opam_files d, None + | Some d -> + let same_kind url = + match kind, url.OpamUrl.backend with + | (None | Some `auto), _ + | Some `rsync, `rsync + | Some `http, `http -> true + | Some (#OpamUrl.version_control as vc1), (#OpamUrl.version_control as vc2) -> + vc1 = vc2 + | Some (`none | `version), _ -> assert false + | _ -> false + in + let pkgs = + OpamAuxCommands.opams_of_dir_w_target ~recurse ?subpath ~same_kind url d + |> List.map (fun (n,o,u,b) -> (n, OpamFile.OPAM.read_opt o, b, u)) + in + pkgs, None | None -> let pin_cache_dir = OpamRepositoryPath.pin_cache url in let cleanup = fun () -> OpamFilename.rmdir @@ OpamRepositoryPath.pin_cache_dir () in + let basename = + match OpamStd.String.split (OpamUrl.basename url) '.' with + | [] -> + OpamConsole.error_and_exit `Bad_arguments + "Can not retrieve a path from '%s'" + (OpamUrl.to_string url) + | b::_ -> b + in try let open OpamProcess.Job.Op in OpamProcess.Job.run @@ @@ -2436,27 +3050,34 @@ | Not_available (_,u) -> OpamConsole.error_and_exit `Sync_error "Could not retrieve %s" u - | Result _ | Up_to_date _ -> from_opam_files pin_cache_dir, Some cleanup + | Result _ | Up_to_date _ -> + let pkgs = + OpamAuxCommands.opams_of_dir ~recurse ?subpath pin_cache_dir + |> List.map (fun (n,o,b) -> (n, OpamFile.OPAM.read_opt o, b, url)) + in + pkgs, Some cleanup with e -> OpamStd.Exn.finalise e cleanup in let finalise = OpamStd.Option.default (fun () -> ()) cleanup in - OpamStd.Exn.finally finalise @@ fun () -> - let names_found = - match found with - | _::_ -> found - | [] -> - try [OpamPackage.Name.of_string basename, None] with - | Failure _ -> - OpamConsole.error_and_exit `Bad_arguments - "Could not infer a package name from %s, please specify it on the \ - command-line, e.g. 'opam pin NAME TARGET'" - (OpamUrl.to_string url) - in - k names_found + OpamStd.Exn.finally finalise @@ fun () -> k found in let pin_target kind target = let looks_like_version_re = - Re.(compile @@ seq [bos; opt @@ char 'v'; digit; rep @@ diff any (set "/\\"); eos]) + Re.(compile @@ + seq [ + bos; + opt @@ char 'v'; + digit; + rep @@ diff any (set "/\\"); + eos]) + in + let parse ?backend ?handle_suffix target = + match OpamUrl.parse_opt ?backend ?handle_suffix ~from_file:false + target with + | Some url -> `Source url + | None -> + OpamConsole.error_and_exit `Bad_arguments + "No package pinned, invalid url" in let auto () = if target = "-" then @@ -2465,17 +3086,16 @@ `Version (OpamPackage.Version.of_string target) else let backend = OpamUrl.guess_version_control target in - `Source (OpamUrl.parse ?backend ~handle_suffix:true target) + parse ?backend ~handle_suffix:true target in let target = match kind with | Some `version -> `Version (OpamPackage.Version.of_string target) - | Some (#OpamUrl.backend as k) -> - `Source (OpamUrl.parse ~backend:k target) + | Some (#OpamUrl.backend as k) -> parse ~backend:k target | Some `none -> `None | Some `auto -> auto () | None when OpamClientConfig.(!r.pin_kind_auto) -> auto () - | None -> `Source (OpamUrl.parse ~handle_suffix:false target) + | None -> parse ~handle_suffix:false target in match target with | `Source url -> `Source (OpamAuxCommands.url_with_local_branch url) @@ -2483,71 +3103,141 @@ in let pin global_options build_options - kind edit no_act dev_repo print_short command params = - apply_global_options global_options; - apply_build_options build_options; + kind edit no_act dev_repo print_short recurse subpath normalise + with_version + command params () = + apply_global_options cli global_options; + apply_build_options cli build_options; + let locked = OpamStateConfig.(!r.locked) <> None in let action = not no_act in - match command, params with - | Some `list, [] | None, [] -> + let get_command = function + | Some `list, [] | None, [] -> + `list + | Some `scan, [url] -> + `scan url + | Some `remove, (_::_ as arg) -> + `remove arg + | Some `edit, [nv] -> + `edit nv + | Some `add, pins when OpamPinCommand.looks_like_normalised pins -> + `add_normalised pins + | Some `default p, pins when + OpamPinCommand.looks_like_normalised (p::pins) -> + `add_normalised (p::pins) + | Some `add, [nv] | Some `default nv, [] when dev_repo -> + `add_dev nv + | Some `add, [arg] | Some `default arg, [] -> + `add_url arg + | Some `add, [n; target] | Some `default n, [target] -> + `add_wtarget (n,target) + | _ -> `incorrect + in + match get_command (command, params) with + | `list -> OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_none gt @@ fun st -> OpamClient.PIN.list st ~short:print_short; `Ok () - | Some `remove, (_::_ as arg) -> + | `scan url -> + let backend, handle_suffix = + match kind with + | Some (#OpamUrl.backend as k) -> Some k, None + | Some `auto -> OpamUrl.guess_version_control url, Some true + | None when OpamClientConfig.(!r.pin_kind_auto) -> + OpamUrl.guess_version_control url, Some true + | _ -> None, None + in + OpamUrl.parse ?backend ?handle_suffix url + |> OpamAuxCommands.url_with_local_branch + |> OpamPinCommand.scan ~normalise ~recurse ?subpath; + `Ok () + | `remove arg -> OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> let err, to_unpin = + let open OpamStd.Option.Op in List.fold_left (fun (err, acc) arg -> let as_url = - let url = OpamUrl.of_string arg in + OpamUrl.parse_opt ~handle_suffix:false ~from_file:false arg + >>| fun url -> OpamPackage.Set.filter (fun nv -> match OpamSwitchState.url st nv with | Some u -> + let spu = OpamFile.URL.subpath u in let u = OpamFile.URL.url u in - OpamUrl.(u.transport = url.transport && u.path = url.path) + let path_equality () = + let open OpamUrl in + match subpath, recurse with + | Some sp, false -> + u.path = url.path && spu = Some sp + | Some sp, true -> + (match spu with + | Some spp -> + OpamUrl.Op.(OpamStd.String.starts_with + ~prefix:(url / sp).path (u / spp).path) + | None -> false) + | None, true -> + u.path = url.path + | None, false -> + spu = None && u.path = url.path + in + OpamUrl.(u.transport = url.transport) && path_equality () | None -> false) st.pinned |> OpamPackage.names_of_packages |> OpamPackage.Name.Set.elements in match as_url with - | _::_ -> err, as_url @ acc - | [] -> + | Some ((_::_) as url) -> err, url @ acc + | _-> match (fst package_name) arg with | `Ok name -> err, name::acc | `Error _ -> OpamConsole.error "No package pinned to this target found, or invalid package \ - name: %s" arg; + name/url: %s" arg; true, acc) (false,[]) arg in if err then OpamStd.Sys.exit_because `Bad_arguments else - (ignore @@ OpamClient.PIN.unpin st ~action to_unpin; + (OpamSwitchState.drop @@ OpamClient.PIN.unpin st ~action to_unpin; `Ok ()) - | Some `edit, [nv] -> + | `edit nv -> (match (fst package) nv with | `Ok (name, version) -> OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> - ignore @@ OpamClient.PIN.edit st ~action ?version name; + let version = OpamStd.Option.Op.(with_version ++ version) in + OpamSwitchState.drop @@ + OpamClient.PIN.edit st ~locked ~action ?version name; `Ok () | `Error e -> `Error (false, e)) - | Some `add, [nv] | Some `default nv, [] when dev_repo -> + | `add_normalised pins -> + let pins = OpamPinCommand.parse_pins pins in + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamSwitchState.with_ `Lock_write gt @@ fun st -> + OpamSwitchState.drop @@ + OpamClient.PIN.url_pins st ~locked ~edit ~action + (List.map (fun (n,v,u,sb) -> + n, OpamStd.Option.Op.(with_version ++ v), None, u, sb) pins); + `Ok () + | `add_dev nv -> (match (fst package) nv with | `Ok (name,version) -> OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> let name = OpamSolution.fuzzy_name st name in - ignore @@ OpamClient.PIN.pin st name ~edit ?version ~action + let version = OpamStd.Option.Op.(with_version ++ version) in + OpamSwitchState.drop @@ + OpamClient.PIN.pin st name ~locked ~edit ?version ~action `Dev_upstream; `Ok () | `Error e -> if command = Some `add then `Error (false, e) - else bad_subcommand commands ("pin", command, params)) - | Some `add, [arg] | Some `default arg, [] -> + else bad_subcommand ~cli commands ("pin", command, params)) + | `add_url arg -> (match pin_target kind arg with | `None | `Version _ -> let msg = @@ -2556,64 +3246,44 @@ in `Error (true, msg) | `Source url -> - guess_names url @@ fun names -> - let names = match names with - | _::_::_ -> - if OpamConsole.confirm - "This will pin the following packages: %s. Continue?" - (OpamStd.List.concat_map ", " (fst @> OpamPackage.Name.to_string) names) - then names - else OpamStd.Sys.exit_because `Aborted - | _ -> names - in + guess_names kind ~recurse ?subpath url @@ fun names -> OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> - let pinned = st.pinned in - let st = - List.fold_left (fun st (name, opam_opt) -> - OpamStd.Option.iter (fun opam -> - let opam_localf = - OpamPath.Switch.Overlay.tmp_opam - st.switch_global.root st.switch name - in - if not (OpamFilename.exists (OpamFile.filename opam_localf)) - then OpamFile.OPAM.write opam_localf opam) - opam_opt; - try OpamPinCommand.source_pin st name ~edit (Some url) with - | OpamPinCommand.Aborted -> OpamStd.Sys.exit_because `Aborted - | OpamPinCommand.Nothing_to_do -> st) - st names - in - if action then - (ignore @@ - OpamClient.PIN.post_pin_action st pinned (List.map fst names); - `Ok ()) - else `Ok ()) - | Some `add, [n; target] | Some `default n, [target] -> + OpamSwitchState.drop @@ + OpamClient.PIN.url_pins st ~locked ~edit ~action + (List.map (fun (n,o,u,sb) -> n,with_version,o,sb,u) names); + `Ok ()) + | `add_wtarget (n, target) -> (match (fst package) n with | `Ok (name,version) -> - let pin = pin_target kind target in + let pin = + match pin_target kind target, with_version with + | `Version v, Some v' -> `Source_version (v, v') + | p, _ -> p + in + let version = OpamStd.Option.Op.(with_version ++ version) in OpamGlobalState.with_ `Lock_none @@ fun gt -> OpamSwitchState.with_ `Lock_write gt @@ fun st -> - ignore @@ - OpamClient.PIN.pin st name ?version ~edit ~action pin; + OpamSwitchState.drop @@ + OpamClient.PIN.pin st name ~locked ?version ~edit ~action ?subpath pin; `Ok () | `Error e -> `Error (false, e)) - | command, params -> bad_subcommand commands ("pin", command, params) + | `incorrect -> bad_subcommand ~cli commands ("pin", command, params) in - Term.ret + mk_command_ret ~cli cli_original "pin" ~doc ~man Term.(const pin - $global_options $build_options - $kind $edit $no_act $dev_repo $print_short_flag - $command $params), - term_info "pin" ~doc ~man + $global_options cli $build_options cli + $kind $edit $no_act $dev_repo $print_short_flag cli cli_original + $recurse cli $subpath cli + $normalise $with_version + $command $params) (* SOURCE *) let source_doc = "Get the source of an opam package." -let source = +let source cli = let doc = source_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Downloads the source for a given package to a local directory \ for development, bug fixing or documentation purposes." ] in @@ -2622,17 +3292,17 @@ ~doc:"A package name with an optional version constraint") in let dev_repo = - mk_flag ["dev-repo"] + mk_flag ~cli cli_original ["dev-repo"] "Get the latest version-controlled source rather than the \ release archive" in let pin = - mk_flag ["pin"] + mk_flag ~cli cli_original ["pin"] "Pin the package to the downloaded source (see `opam pin')." in let dir = - mk_opt ["dir"] "DIR" "The directory where to put the source." + mk_opt ~cli cli_original ["dir"] "DIR" "The directory where to put the source." Arg.(some dirname) None in - let source global_options atom dev_repo pin dir = - apply_global_options global_options; + let source global_options atom dev_repo pin dir () = + apply_global_options cli global_options; OpamGlobalState.with_ `Lock_none @@ fun gt -> (* Fixme: this needs a write lock, because it uses the routines that download to opam's shared switch cache. @@ -2662,6 +3332,10 @@ (see option `--dir')" (Dir.to_string dir); let opam = OpamSwitchState.opam t nv in + let subpath = + OpamStd.Option.map_default OpamFile.URL.subpath + None (OpamFile.OPAM.url opam) + in if dev_repo then ( match OpamFile.OPAM.dev_repo opam with | None -> @@ -2676,6 +3350,7 @@ (OpamRepository.pull_tree ~cache_dir:(OpamRepositoryPath.download_cache OpamStateConfig.(!r.root_dir)) + ?subpath (OpamPackage.to_string nv) dir [] [url]) with @@ -2683,15 +3358,15 @@ OpamConsole.error_and_exit `Sync_error "%s is not available" u | Result _ | Up_to_date _ -> OpamConsole.formatted_msg - "Successfully fetched %s development repo to ./%s/\n" - (OpamPackage.name_to_string nv) (OpamPackage.name_to_string nv) + "Successfully fetched %s development repo to .%s%s%s\n" + (OpamPackage.name_to_string nv) Filename.dir_sep (OpamPackage.name_to_string nv) Filename.dir_sep ) else ( let job = let open OpamProcess.Job.Op in OpamUpdate.download_package_source t nv dir @@+ function - | Some (Not_available (_,s)) -> + | Some (Not_available (_,s)), _ | _, (_, Not_available (_, s)) :: _ -> OpamConsole.error_and_exit `Sync_error "Download failed: %s" s - | None | Some (Result () | Up_to_date ()) -> + | None, _ | Some (Result _ | Up_to_date _), _ -> OpamAction.prepare_package_source t nv dir @@| function | None -> OpamConsole.formatted_msg "Successfully extracted to %s\n" @@ -2701,7 +3376,9 @@ (Dir.to_string dir) (Printexc.to_string e) in OpamProcess.Job.run job; - if OpamPinned.find_opam_file_in_source nv.name dir = None + if OpamPinned.find_opam_file_in_source nv.name + (OpamStd.Option.map_default (fun sp -> Op.(dir / sp)) dir subpath) + = None then let f = if OpamFilename.exists_dir Op.(dir / "opam") @@ -2721,40 +3398,50 @@ else `rsync in let target = - `Source (OpamUrl.parse ~backend + `Source (OpamUrl.parse ~backend ~from_file:false ("file://"^OpamFilename.Dir.to_string dir)) in - ignore @@ OpamClient.PIN.pin t nv.name ~version:nv.version target + OpamSwitchState.drop + (OpamClient.PIN.pin t nv.name ~version:nv.version target) in - Term.(const source - $global_options $atom $dev_repo $pin $dir), - term_info "source" ~doc ~man + mk_command ~cli cli_original "source" ~doc ~man + Term.(const source + $global_options cli $atom $dev_repo $pin $dir) (* LINT *) let lint_doc = "Checks and validate package description ('opam') files." -let lint = +let lint cli = let doc = lint_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Given an $(i,opam) file, performs several quality checks on it and \ - outputs recommendations, warnings or errors on stderr." - ] in + outputs recommendations, warnings or errors on stderr."; + `S Manpage.s_arguments; + `S Manpage.s_options; + `S "LINT CODES" + ] @ + List.map (fun (c,t,s) -> + `P (Printf.sprintf "%s$(b,%d): %s" + (match t with | `Warning -> "W" | `Error -> "$(i,E)") + c s)) + (OpamFileTools.all_lint_warnings ()) + in let files = Arg.(value & pos_all (existing_filename_dirname_or_dash) [] & - info ~docv:"FILES" [] + info ~docv:Manpage.s_files [] ~doc:"Name of the opam files to check, or directory containing \ them. Current directory if unspecified") in let normalise = - mk_flag ["normalise"] + mk_flag ~cli cli_original ["normalise"] "Output a normalised version of the opam file to stdout" in let short = - mk_flag ["short";"s"] + mk_flag ~cli cli_original ["short";"s"] "Only print the warning/error numbers, space-separated, if any" in let warnings = - mk_opt ["warnings";"W"] "WARNS" + mk_opt ~cli cli_original ["warnings";"W"] "WARNS" "Select the warnings to show or hide. $(i,WARNS) should be a \ concatenation of $(b,+N), $(b,-N), $(b,+N..M), $(b,-N..M) to \ respectively enable or disable warning or error number $(b,N) or \ @@ -2764,26 +3451,26 @@ warn_selector [] in let package = - mk_opt ["package"] "PKG" + mk_opt ~cli cli_original ["package"] "PKG" "Lint the current definition of the given package instead of specifying \ an opam file directly." Arg.(some package) None in let check_upstream = - mk_flag ["check-upstream"] + mk_flag ~cli cli_original ["check-upstream"] "Check upstream, archive availability and checksum(s)" in let lint global_options files package normalise short warnings_sel - check_upstream = - apply_global_options global_options; + check_upstream recurse subpath () = + apply_global_options cli global_options; let opam_files_in_dir d = - match OpamPinned.files_in_source d with + match OpamPinned.files_in_source ~recurse ?subpath d with | [] -> OpamConsole.warning "No opam files found in %s" (OpamFilename.Dir.to_string d); [] | l -> - List.map (fun (_name,f) -> Some f) l + List.map (fun (_name,f,_) -> Some f) l in let files = match files, package with | [], None -> (* Lookup in cwd if nothing was specified *) @@ -2806,7 +3493,7 @@ | name, None -> OpamSwitchState.get_package st name in let opam = OpamSwitchState.opam st nv in - match OpamPinned.orig_opam_file (OpamPackage.name nv) opam with + match OpamPinned.orig_opam_file st (OpamPackage.name nv) opam with | None -> raise Not_found | some -> [some] with Not_found -> @@ -2819,14 +3506,20 @@ "--package and a file argument are incompatible" in let msg = if normalise then OpamConsole.errmsg else OpamConsole.msg in - let err = - List.fold_left (fun err opam_f -> + let json = + match OpamClientConfig.(!r.json_out) with + | None -> None + | Some _ -> Some [] + in + let err,json = + List.fold_left (fun (err,json) opam_f -> try let warnings,opam = match opam_f with - | Some f -> OpamFileTools.lint_file ~check_upstream f + | Some f -> + OpamFileTools.lint_file ~check_upstream ~handle_dirname:true f | None -> - OpamFileTools.lint_channel ~check_upstream + OpamFileTools.lint_channel ~check_upstream ~handle_dirname:false (OpamFile.make (OpamFilename.of_string "-")) stdin in let enabled = @@ -2866,69 +3559,78 @@ (OpamFileTools.warns_to_string warnings); if normalise then OpamStd.Option.iter (OpamFile.OPAM.write_to_channel stdout) opam; - err || failed + let json = + OpamStd.Option.map + (OpamStd.List.cons + (OpamFileTools.warns_to_json + ?filename:(OpamStd.Option.map OpamFile.to_string opam_f) + warnings)) + json + in + (err || failed), json with | Parsing.Parse_error | OpamLexer.Error _ | OpamPp.Bad_version _ | OpamPp.Bad_format _ -> msg "File format error\n"; - true) - false files + (true, json)) + (false, json) files in + OpamStd.Option.iter (fun json -> OpamJson.append "lint" (`A json)) json; if err then OpamStd.Sys.exit_because `False in - Term.(const lint $global_options $files $package $normalise $short $warnings - $check_upstream), - term_info "lint" ~doc ~man + mk_command ~cli cli_original "lint" ~doc ~man + Term.(const lint $global_options cli $files $package $normalise $short + $warnings $check_upstream $recurse cli $subpath cli) (* CLEAN *) let clean_doc = "Cleans up opam caches" -let clean = +let clean cli = let doc = clean_doc in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Cleans up opam caches, reclaiming some disk space. If no options are \ specified, the default is $(b,--logs --download-cache \ --switch-cleanup)." ] in let dry_run = - mk_flag ["dry-run"] + mk_flag ~cli cli_original ["dry-run"] "Print the removal commands, but don't execute them" in let download_cache = - mk_flag ["c"; "download-cache"] + mk_flag ~cli cli_original ["c"; "download-cache"] (Printf.sprintf "Clear the cache of downloaded files (\\$OPAMROOT%sdownload-cache), as \ well as the obsolete \\$OPAMROOT%sarchives, if that exists." OpamArg.dir_sep OpamArg.dir_sep) in let repos = - mk_flag ["unused-repositories"] + mk_flag ~cli cli_original ["unused-repositories"] "Clear any configured repository that is not used by any switch nor the \ default." in let repo_cache = - mk_flag ["r"; "repo-cache"] + mk_flag ~cli cli_original ["r"; "repo-cache"] "Clear the repository cache. It will be rebuilt by the next opam command \ that needs it." in let logs = - mk_flag ["logs"] "Clear the logs directory." + mk_flag ~cli cli_original ["logs"] "Clear the logs directory." in let switch = - mk_flag ["s";"switch-cleanup"] + mk_flag ~cli cli_original ["s";"switch-cleanup"] "Run the switch-specific cleanup: clears backups, build dirs, \ uncompressed package sources of non-dev packages, local metadata of \ previously pinned packages, etc." in let all_switches = - mk_flag ["a"; "all-switches"] + mk_flag ~cli cli_original ["a"; "all-switches"] "Run the switch cleanup commands in all switches. Implies $(b,--switch-cleanup)" in let clean global_options dry_run - download_cache repos repo_cache logs switch all_switches = - apply_global_options global_options; + download_cache repos repo_cache logs switch all_switches () = + apply_global_options cli global_options; let logs, download_cache, switch = if logs || download_cache || repos || repo_cache || switch || all_switches then logs, download_cache, switch @@ -2952,6 +3654,13 @@ try OpamFilename.rmdir d with OpamSystem.Internal_error msg -> OpamConsole.warning "Error ignored: %s" msg in + let rm f = + if dry_run then + OpamConsole.msg "rm -f \"%s\"\n" + (OpamFilename.to_string f) + else + OpamFilename.remove f + in let switches = if all_switches then OpamGlobalState.switches gt else if switch then match OpamStateConfig.get_switch_opt () with @@ -2967,6 +3676,7 @@ cleandir (OpamPath.Switch.backup_dir root sw); cleandir (OpamPath.Switch.build_dir root sw); cleandir (OpamPath.Switch.remove_dir root sw); + cleandir (OpamPath.Switch.extra_files_dir root sw); let pinning_overlay_dirs = List.map (fun nv -> OpamPath.Switch.Overlay.package root sw nv.name) @@ -3018,7 +3728,8 @@ OpamRepositoryName.Set.iter (fun r -> OpamConsole.msg "Removing repository %s\n" (OpamRepositoryName.to_string r); - rmdir (OpamRepositoryPath.create root r)) + rmdir (OpamRepositoryPath.root root r); + rm (OpamRepositoryPath.tar root r)) unused_repos; let repos_config = OpamRepositoryName.Map.filter @@ -3034,7 +3745,6 @@ if not dry_run then OpamRepositoryState.Cache.remove ()); if download_cache then (OpamConsole.msg "Clearing cache of downloaded files\n"; - rmdir (OpamPath.archives_dir root); List.iter (fun dir -> match OpamFilename.(Base.to_string (basename_dir dir)) with | "git" -> @@ -3047,15 +3757,91 @@ (OpamConsole.msg "Clearing logs\n"; cleandir (OpamPath.log root)) in - Term.(const clean $global_options $dry_run $download_cache $repos $repo_cache - $logs $switch $all_switches), - term_info "clean" ~doc ~man + mk_command ~cli cli_original "clean" ~doc ~man + Term.(const clean $global_options cli $dry_run $download_cache $repos + $repo_cache $logs $switch $all_switches) + +(* LOCK *) +let lock_doc = "Create locked opam files to share build environments across hosts." +let lock cli = + let doc = lock_doc in + let man = [ + `S Manpage.s_description; + `P "Generates a lock file of a package: checks the current \ + state of their installed dependencies, and outputs modified versions of \ + the opam file with a $(i,.locked) suffix, where all the (transitive) \ + dependencies and pinnings have been bound strictly to the currently \ + installed version."; + `P "By using these locked opam files, it is then possible to recover the \ + precise build environment that was setup when they were generated."; + `P "If paths (filename or directory) are given, those opam files are locked. \ + If package is given, installed one is locked, otherwise its latest \ + version. If a locally pinned package is given, its current local opam \ + file is locked, even if not versioned or uncommitted changes"; + `P "Fails if all mandatory dependencies are not installed in the switch."; + `S "LOCK FILE CHANGED FIELDS"; + `P "- $(i,depends) are fixed to their specific versions, with all filters \ + removed (except for the exceptions below"; + `P "- $(i,depopts) that are installed in the current switch are turned into \ + depends, with their version set. Others are set in the $(i,conflict) field"; + `P "- `{dev}`, `{with-test}, and `{with-doc}` filters are kept if all \ + packages of a specific filters are installed in the switch. Versions are \ + fixed and the same filter is on all dependencies that are added from \ + them"; + `P "- $(i,pin-depends) are kept and new ones are added if in the \ + dependencies some packages are pinned "; + `P "- pins are resolved: if a package is locally pinned, opam tries to get \ + its remote url and branch, and sets this as the target URL"; + `S Manpage.s_arguments; + `S Manpage.s_options; + ] + in + let only_direct_flag = + mk_flag ~cli cli_original ["d"; "direct-only"] + "Only lock direct dependencies, rather than the whole dependency tree." + in + let lock_suffix = OpamArg.lock_suffix cli in + let lock global_options only_direct lock_suffix atom_locs () = + apply_global_options cli global_options; + OpamGlobalState.with_ `Lock_none @@ fun gt -> + OpamSwitchState.with_ `Lock_none gt @@ fun st -> + let st, packages = OpamLockCommand.select_packages atom_locs st in + if OpamPackage.Set.is_empty packages then + OpamConsole.msg "No lock file generated\n" + else + let pkg_done = + OpamPackage.Set.fold (fun nv msgs -> + let opam = OpamSwitchState.opam st nv in + let locked = OpamLockCommand.lock_opam ~only_direct st opam in + let locked_fname = + OpamFilename.add_extension + (OpamFilename.of_string (OpamPackage.name_to_string nv)) + ("opam." ^ lock_suffix) + in + if not (OpamCoreConfig.(!r).OpamCoreConfig.safe_mode + || OpamStateConfig.(!r.dryrun)) then + OpamFile.OPAM.write_with_preserved_format + (OpamFile.make locked_fname) locked; + (nv, locked_fname)::msgs) + packages [] + in + OpamConsole.msg "Generated %slock files for:\n%s" + (if OpamCoreConfig.(!r).safe_mode || OpamStateConfig.(!r.dryrun) then + "(not saved) " else "") + (OpamStd.Format.itemize (fun (nv, file) -> + Printf.sprintf "%s: %s" + (OpamPackage.to_string nv) + (OpamFilename.to_string file)) pkg_done) + in + mk_command ~cli (cli_from cli2_1) "lock" ~doc ~man + Term.(const lock $global_options cli $only_direct_flag $lock_suffix + $atom_or_local_list) (* HELP *) let help = let doc = "Display help about opam and opam commands." in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Prints help about opam commands."; `P "Use `$(mname) help topics' to get the full list of help topics."; ] in @@ -3077,10 +3863,10 @@ Term.(ret (const help $Term.man_format $Term.choice_names $topic)), Term.info "help" ~doc ~man -let default = +let default cli = let doc = "source-based package management" in let man = [ - `S "DESCRIPTION"; + `S Manpage.s_description; `P "Opam is a package manager. It uses the powerful mancoosi tools to \ handle dependencies, including support for version constraints, \ optional dependencies, and conflict management. The default \ @@ -3091,12 +3877,12 @@ different sets of intalled packages."; `P "Use either $(b,opam --help) or $(b,opam help ) \ for more information on a specific command."; - `S "COMMANDS"; + `S Manpage.s_commands; `S "COMMAND ALIASES"; - ] @ help_sections + ] @ help_sections cli in let usage global_options = - apply_global_options global_options; + apply_global_options cli global_options; OpamConsole.formatted_msg "usage: opam [--version]\n\ \ [--help]\n\ @@ -3121,7 +3907,7 @@ upgrade_doc config_doc repository_doc switch_doc pin_doc OpamAdminCommand.admin_command_doc in - Term.(const usage $global_options), + Term.(const usage $global_options cli), Term.info "opam" ~version:(OpamVersion.to_string OpamVersion.current) ~sdocs:global_option_section @@ -3129,28 +3915,57 @@ ~man let admin = - let doc = "Use 'opam admin' instead (abbreviation not supported)" in + (* cmdliner never sees the admin subcommand, so this "can't happen" *) + let doc = "Internal opam error - main admin command invoked" in Term.(ret (const (`Error (true, doc)))), Term.info "admin" ~doc:OpamAdminCommand.admin_command_doc - ~man:[`S "SYNOPSIS"; - `P doc] -let commands = [ - init; - list (); - make_command_alias (list ~force_search:true ()) ~options:" --search" "search"; - show; make_command_alias show "info"; - install; - remove; make_command_alias remove "uninstall"; - reinstall; - update; upgrade; - config; var; exec; env; - repository; make_command_alias repository "remote"; - switch; - pin (); make_command_alias (pin ~unpin_only:true ()) ~options:" remove" "unpin"; - source; - lint; - clean; - admin; - help; -] +(* Note: for cli versionning check, all commands must be constructed with + [OpamArg.mk_command] or [OpamArg.mk_command_ret]. *) +let commands cli = + let show = show cli in + let remove = remove cli in + let repository = repository cli in + (* This list must always include *all* commands, regardless of cli *) + [ + init cli; + list cli; + make_command_alias ~cli (list ~force_search:true cli) ~options:" --search" "search"; + show; make_command_alias ~cli show "info"; + install cli; + remove; make_command_alias ~cli remove "uninstall"; + reinstall cli; + update cli; upgrade cli; + var cli; option cli; + config cli; + exec cli; env cli; + repository; make_command_alias ~cli repository "remote"; + switch cli; + pin cli; make_command_alias ~cli (pin ~unpin_only:true cli) ~options:" remove" "unpin"; + source cli; + lint cli; + clean cli; + lock cli; + admin; + help; + ] + +let current_commands = commands OpamCLIVersion.Sourced.current + +let is_builtin_command prefix = + List.exists (fun (_,info) -> + OpamStd.String.starts_with ~prefix (Term.name info)) + current_commands + +let is_admin_subcommand prefix = + prefix = "admin" || + let matches = + List.filter (fun (_,info) -> + OpamStd.String.starts_with ~prefix (Term.name info)) + current_commands in + match matches with + | [(_,info)] when Term.name info = "admin" -> true + | _ -> false + +let get_cmdliner_parser cli = + (default cli, commands cli) diff -Nru opam-2.0.10/src/client/opamCommands.mli opam-2.1.2/src/client/opamCommands.mli --- opam-2.0.10/src/client/opamCommands.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamCommands.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -11,54 +11,15 @@ (** Opam CLI main entry point *) -open Cmdliner - (** {2 Commands} *) -(** Type of commands *) -type command = unit Term.t * Term.info - -(** The default list of commands *) -val commands: command list - -(** opam *) -val default: command - -(** opam init *) -val init: command - -(** opam list *) -val list: ?force_search:bool -> unit -> command - -(** opam show *) -val show: command - -(** opam install *) -val install: command - -(** opam remove *) -val remove: command - -(** opam reinstall *) -val reinstall: command - -(** opam update *) -val update: command - -(** opam upgrade *) -val upgrade: command - -(** opam config *) -val config: command - -(** opam repository *) -val repository: command - -(** opam switch *) -val switch: command - -(** opam pin *) -val pin: ?unpin_only:bool -> unit -> command +(** [is_builtin_command arg] is [true] if [arg] is a prefix of any built-in + command *) +val is_builtin_command: string -> bool + +(** [is_admin_subcommand arg] is [true] if [arg] is a unique prefix of the admin + sub-command. *) +val is_admin_subcommand: string -> bool -(** opam help *) -val help: command +val get_cmdliner_parser: + OpamCLIVersion.Sourced.t -> OpamArg.command * OpamArg.command list diff -Nru opam-2.0.10/src/client/opamConfigCommand.ml opam-2.1.2/src/client/opamConfigCommand.ml --- opam-2.0.10/src/client/opamConfigCommand.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamConfigCommand.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -12,81 +12,15 @@ let log fmt = OpamConsole.log "CONFIG" fmt let slog = OpamConsole.slog +open OpamParserTypes.FullPos open OpamTypes +open OpamTypesBase open OpamStateTypes -let help t = - let (%) s col = OpamConsole.colorise col s in - OpamConsole.header_msg "Global opam variables"; - let all_global_vars = - List.fold_left (fun acc (v,doc) -> - OpamVariable.Map.add (OpamVariable.of_string v) doc acc) - OpamVariable.Map.empty - OpamPackageVar.global_variable_names - in - let all_global_vars = - OpamVariable.Map.union (fun _ x -> x) - all_global_vars - (OpamVariable.Map.map snd t.switch_global.global_variables) - in - let env = OpamPackageVar.resolve t in - List.map (fun (var, doc) -> - let content = - OpamFilter.ident_string env ~default:"" ([],var,None) - in - let doc = - if doc = OpamGlobalState.inferred_from_system then - match OpamStd.Option.Op.( - OpamVariable.Map.find_opt var t.switch_global.global_variables - >>| fst - >>= Lazy.force) with - | Some c when (OpamVariable.string_of_variable_contents c) <> content -> - "Set through local opam config or env" - | _ -> doc - else doc - in - [ - OpamVariable.to_string var % `bold; - content % `blue; - "#"; doc - ]) - (OpamVariable.Map.bindings all_global_vars) |> - OpamStd.Format.align_table |> - OpamConsole.print_table stdout ~sep:" "; - - OpamConsole.header_msg "Configuration variables from the current switch"; - let global = t.switch_config in - List.map (fun stdpath -> [ - OpamTypesBase.string_of_std_path stdpath % `bold; - OpamPath.Switch.get_stdpath - t.switch_global.root t.switch global stdpath |> - OpamFilename.Dir.to_string |> - OpamConsole.colorise `blue - ]) - OpamTypesBase.all_std_paths @ - List.map (fun (var,value) -> [ - OpamVariable.to_string var % `bold; - OpamVariable.string_of_variable_contents value % `blue; - ]) - (global.OpamFile.Switch_config.variables) |> - OpamStd.Format.align_table |> - OpamConsole.print_table stdout ~sep:" "; - - OpamConsole.header_msg "Package variables ('opam config list PKG' to show)"; - List.map (fun (var, doc) -> [ - ("PKG:"^var) % `bold; - ""; - "#";doc - ]) - OpamPackageVar.package_variable_names |> - OpamStd.Format.align_table |> - OpamConsole.print_table stdout ~sep:" " - (* List all the available variables *) -let list gt ns = +let list t ns = log "config-list"; - OpamSwitchState.with_ `Lock_none gt @@ fun t -> - if ns = [] then help t else + if ns = [] then () else let list_vars name = if OpamPackage.Name.to_string name = "-" then let conf = t.switch_config in @@ -96,12 +30,11 @@ "") (conf.OpamFile.Switch_config.variables) else - try - let nv = OpamSwitchState.get_package t name in - let opam = OpamSwitchState.opam t nv in - let env = OpamPackageVar.resolve ~opam t in - let conf = OpamSwitchState.package_config t name in - let pkg_vars = + let nv = OpamSwitchState.get_package t name in + let pkg_vars = + try + let opam = OpamSwitchState.opam t nv in + let env = OpamPackageVar.resolve ~opam t in OpamStd.List.filter_map (fun (vname, desc) -> let v = OpamVariable.(Full.create name (of_string vname)) in try @@ -109,16 +42,19 @@ Some (v, c, desc) with Failure _ -> None) OpamPackageVar.package_variable_names - in - let conf_vars = + with Not_found -> [] + in + let conf_vars = + try + let conf = OpamSwitchState.package_config t name in List.map (fun (v,c) -> OpamVariable.Full.create name v, OpamVariable.string_of_variable_contents c, "") (OpamFile.Dot_config.bindings conf) - in - pkg_vars @ conf_vars - with Not_found -> [] + with Not_found -> [] + in + pkg_vars @ conf_vars in let vars = List.flatten (List.map list_vars ns) in let (%) s col = OpamConsole.colorise col s in @@ -213,13 +149,32 @@ else print_env env +let ensure_env_aux ?(set_opamroot=false) ?(set_opamswitch=false) ?(force_path=true) gt switch = + let env_file = OpamPath.Switch.environment gt.root switch in + if not (OpamFile.exists env_file) then + Some (OpamSwitchState.with_ `Lock_none gt @@ fun st -> + let upd = + OpamEnv.updates ~set_opamroot ~set_opamswitch ~force_path st + in + log "Missing environment file, regenerate it"; + if not (OpamCoreConfig.(!r.safe_mode)) then + (let _, st = + OpamSwitchState.with_write_lock st @@ fun st -> + (OpamFile.Environment.write env_file upd), st + in OpamSwitchState.drop st); + OpamEnv.add [] upd) + else + None + +let ensure_env gt switch = ignore (ensure_env_aux gt switch) + let env gt switch ?(set_opamroot=false) ?(set_opamswitch=false) ~csh ~sexp ~fish ~inplace_path = log "config-env"; let opamroot_not_current = let current = gt.root in let default = OpamStateConfig.(default.root_dir) in - match OpamStd.Config.env_string "ROOT" with + match OpamStateConfig.E.root () with | None -> current <> default | Some r -> OpamFilename.Dir.of_string r <> current in @@ -229,8 +184,8 @@ (OpamStateConfig.get_current_switch_from_cwd gt.root) (OpamFile.Config.switch gt.config) in - match OpamStd.Config.env_string "SWITCH" with - | None -> + match OpamStateConfig.E.switch () with + | None | Some "" -> Some (OpamStateConfig.resolve_local_switch gt.root switch) <> default | Some s -> OpamStateConfig.resolve_local_switch gt.root (OpamSwitch.of_string s) <> @@ -252,25 +207,15 @@ (OpamConsole.colorise `bold "OPAMSWITCH"); let force_path = not inplace_path in let env = - let env_file = OpamPath.Switch.environment gt.root switch in - if not (OpamFile.exists env_file) then - (OpamSwitchState.with_ `Lock_none gt @@ fun st -> - let upd = - OpamEnv.updates ~set_opamroot ~set_opamswitch ~force_path st - in - log "Missing environment file, regenerates it"; - if not (OpamCoreConfig.(!r.safe_mode)) then - (let _st = - OpamSwitchState.with_write_lock st @@ fun _st -> - (OpamFile.Environment.write env_file upd), _st - in ()); - OpamEnv.add [] upd) - else + match ensure_env_aux ~set_opamroot ~set_opamswitch ~force_path gt switch with + | Some env -> env + | None -> OpamEnv.get_opam_raw ~set_opamroot ~set_opamswitch ~force_path gt.root switch in print_eval_env ~csh ~sexp ~fish env +[@@ocaml.warning "-16"] let subst gt fs = log "config-substitute"; @@ -286,110 +231,819 @@ (OpamFilter.expand_string ~default:(fun _ -> "") (OpamPackageVar.resolve st) str) -let set var value = - if not (OpamVariable.Full.is_global var) then - OpamConsole.error_and_exit `Bad_arguments - "Only global variables may be set using this command"; - let root = OpamStateConfig.(!r.root_dir) in +let exec gt ~set_opamroot ~set_opamswitch ~inplace_path command = + log "config-exec command=%a" (slog (String.concat " ")) command; let switch = OpamStateConfig.get_switch () in - OpamFilename.with_flock `Lock_write (OpamPath.Switch.lock root switch) - @@ fun _ -> - let var = OpamVariable.Full.variable var in - let config_f = OpamPath.Switch.switch_config root switch in - let config = - OpamStateConfig.Switch.safe_load_t ~lock_kind:`Lock_write root switch + let st_lazy = lazy ( + let rt = OpamRepositoryState.load `Lock_none gt in + OpamSwitchState.load `Lock_none gt rt switch + ) in + let env_file = OpamPath.Switch.environment gt.root switch in + let env = + if OpamFile.exists env_file then + let base = List.map (fun (v,va) -> v,va,None) (OpamStd.Env.list ()) in + OpamEnv.get_opam_raw ~base + ~set_opamroot ~set_opamswitch ~force_path:(not inplace_path) + gt.root switch + else + OpamEnv.get_full ~set_opamroot ~set_opamswitch + ~force_path:(not inplace_path) + (Lazy.force st_lazy) + in + let env = OpamTypesBase.env_array env in + let resolve var = + OpamPackageVar.resolve (Lazy.force st_lazy) var + in + let cmd, args = + match + List.map (OpamFilter.expand_string ~default:(fun _ -> "") resolve) command + with + | [] -> OpamSystem.internal_error "Empty command" + | h::_ as l -> h, Array.of_list l + in + (* it's OK not to release [st_lazy] since we are certain everything will be + cleaned up anyway *) + match OpamSystem.resolve_command ~env cmd with + | Some cmd -> raise (OpamStd.Sys.Exec (cmd, args, env)) + | None -> + OpamConsole.error "Command not found '%s'" cmd; + raise (OpamStd.Sys.Exit 127) + + +(** Options and Variables settings *) + +(** Option settings *) + +(* For function that takes two config and update (add or remove) elements in a + field. Used for appending or deleting element in config file fields *) +type 'config fld_updater = ('config -> 'config -> 'config) + +(* Only some field can be modifiied. [Modifiable] is for user modifiable + field, [InModifiable] for fields that can only be modified from inner opam + code (see [set_var_global]). + First argument is the addition function, the second the remove one. *) +type 'config fld_policy = + | Atomic + | Modifiable of 'config fld_updater * 'config fld_updater + | InModifiable of 'config fld_updater * 'config fld_updater + +(* "Configuration" of the [set_opt] function. As modification can be on global + or config switch, on normal fields and sections, adding, removing, or + overwritng values, this record type permits to aggregate all needed inputs. + See [set_opt_global] and [set_opt_switch]. *) +type 'config confset = + { + stg_fields: (string * ('config, value) OpamPp.field_parser) list; + (* Config file fields: field name and parser *) + stg_allwd_fields: + (string * 'config fld_policy * ('config -> 'config)) list; + (* Config file updatable fields: field name, update policy, and function to + revert the given field in config file *) + stg_sections: + (string * ('config, (string option * opamfile_item list) list) + OpamPp.field_parser) list; + (* Same as [stg_field] but for sections *) + stg_allwd_sections: + ((string * 'config fld_policy * ('config -> 'config)) list); + (* Same as [stg_allwd_fields] but for sections *) + stg_config: 'config; + (* The config *) + stg_write_config: 'config -> unit; + (* Function to write the config file *) + stg_doc: string; + (* Global or switch specification, used to print final user message *) + } + +type whole_op = + [ `Overwrite of string + | `Revert ] + +type append_op = + [ `Add of string + | `Remove of string ] + +type update_op = + [ append_op | whole_op ] + +let parse_update fv = + let reg = + Re.(compile @@ seq [ + group @@ seq [ + wordc; + opt @@ (seq [ rep @@ alt [ wordc ; char '-' ]; wordc ]) + ]; + (opt @@ seq [ + (group @@ (alt [ + str "+="; + str "-="; + str "=="; + char '='; + ])); + opt @@ (group @@ rep1 any) + ]); + ]) in - let oldval = OpamFile.Switch_config.variable config var in - let newval = OpamStd.Option.map (fun s -> S s) value in - if oldval = newval then - OpamConsole.note "No change for \"%s\"" (OpamVariable.to_string var) + let grs = Re.exec reg fv in + let var = Re.Group.get grs 1 in + let value = + try + let value = + OpamStd.Option.of_Not_found (fun () -> Re.Group.get grs 3) () + in + match Re.Group.get grs 2, value with + | "+=", Some value -> `Add value + | "-=", Some value -> `Remove value + | ("=" | "=="), Some value -> `Overwrite value + | ("=" | "=="), None -> `Revert + | ("+=" | "-="), None -> raise (Invalid_argument "parse_update: rhs needed") + | _, _ -> raise (Invalid_argument "parse_update: illegal operator") + with Not_found -> raise (Invalid_argument "parse_update: operator needed") + in + var, value + +let whole_of_update_op = function + | #whole_op as w -> w + | _ -> raise Not_found + +let parse_whole fv = + let v, upd = parse_update fv in + try v, (whole_of_update_op upd) + with Not_found -> raise (Invalid_argument "parse_whole: append operator") + +let global_doc = "global configuration" +let switch_doc switch = + Printf.sprintf "switch %s" + (OpamConsole.colorise `bold (OpamSwitch.to_string switch)) + +module OpamParser = OpamParser.FullPos +module OpamPrinter = OpamPrinter.FullPos + +(* General setting option function. Takes the [field] to update, the [value] + operation, [conf] the configuration according the config file (['config + confest]). If [inner] is set, it allows the modification of [InModifiable] + fields *) +let set_opt ?(inner=false) field value conf = + let wrap allowed all parse = + List.map (fun (field, pp) -> + match OpamStd.List.find_opt (fun (x,_,_) -> x = field) allowed with + | None -> field, None + | Some (_, modd, default) -> + let parse elem config = + OpamPp.parse ~pos:OpamTypesBase.pos_null pp + (config, Some (parse elem)) + in + field, + Some (parse, modd, default) + ) all + in + let fields = + (wrap conf.stg_allwd_fields conf.stg_fields + (fun str_value -> + OpamParser.value_from_string str_value "")) + @ (wrap conf.stg_allwd_sections conf.stg_sections + (fun str_value -> + [None, + (OpamParser.string str_value "").file_contents])) + in + let new_config = + match OpamStd.List.assoc_opt field fields, value with + | None, _ -> + OpamConsole.error + "There is no option named '%s'. The allowed options are:" + (OpamConsole.colorise `underline field); + OpamConsole.print_table stderr ~sep:" " + (OpamStd.Format.as_aligned_table + (OpamStd.List.filter_map + (function fl, Some _ -> Some fl | _ -> None) + fields)); + OpamStd.Sys.exit_because `Bad_arguments + | Some None, _ -> + OpamConsole.error_and_exit `Bad_arguments + "Field %s is not modifiable" (OpamConsole.colorise `underline field) + | Some (Some (_, Atomic, _)), (#append_op as ar) -> + OpamConsole.error_and_exit `Bad_arguments + "Field %s can't be %s" (OpamConsole.colorise `underline field) + (match ar with `Add _ -> "appended" | `Remove _ -> "substracted") + | Some (Some (_, InModifiable (_,_), _)), (#append_op as ar) when not inner -> + OpamConsole.error_and_exit `Bad_arguments + "Field %s can't be directly %s, use `opam var` instead" + (OpamConsole.colorise `underline field) + (match ar with `Add _ -> "appended to" + | `Remove _ -> "substracted from") + | Some (Some (_, _, set_default)), `Revert -> + set_default conf.stg_config + | Some (Some (parse, fix_app, _)), + ((`Add v | `Remove v | `Overwrite v) as req_value) -> + (try + let updf v = parse v conf.stg_config in + match req_value, fix_app with + | `Add value, (Modifiable (add, _) | InModifiable (add, _)) -> + add (updf value) conf.stg_config + | `Remove value, (Modifiable (_, rem) | InModifiable (_, rem)) -> + rem (updf value) conf.stg_config + | `Overwrite value, _ -> (updf value) + | _, Atomic -> assert false + with + | (OpamPp.Bad_format (_,_) | Parsing.Parse_error) as e -> + OpamConsole.error_and_exit `Bad_arguments + "Parse error on the value of %s '%s': %s" + (OpamConsole.colorise `underline field) v + (OpamPp.string_of_bad_format e)) + in + if conf.stg_config = new_config then + OpamConsole.msg "No modification in %s\n" conf.stg_doc else - let () = match oldval, newval with - | Some old, Some _ -> - OpamConsole.note "Overriding value of \"%s\": was \"%s\"" - (OpamVariable.to_string var) - (OpamVariable.string_of_variable_contents old) - | _ -> () - in - let variables = config.OpamFile.Switch_config.variables in - let variables = - match newval with - | None -> List.remove_assoc var variables - | Some v -> OpamStd.List.update_assoc var v variables + (conf.stg_write_config new_config; + OpamConsole.msg "%s field %s in %s\n" + (match value with + | `Add value -> Printf.sprintf "Added '%s' to" value + | `Remove value -> Printf.sprintf "Removed '%s' from" value + | `Overwrite value -> Printf.sprintf "Set to '%s' the" value + | `Revert -> "Reverted") + (OpamConsole.colorise `underline field) + conf.stg_doc); + new_config + +let allwd_wrappers wdef wrappers with_wrappers = + let open OpamFile in + List.map (fun (n, set, get) -> + n, + Modifiable ( + (fun nc c -> + let w = wrappers c in + let nw = wrappers nc in + with_wrappers (set (get nw @ get w) w) c), + (fun nc c -> + let w = wrappers c in + let nw = wrappers nc in + let n_cmd = + List.filter (fun cmd -> + None = OpamStd.List.find_opt (fun cmd' -> cmd = cmd') (get nw)) + (get w) + in + with_wrappers (set n_cmd w) c) + ), + fun c -> with_wrappers (set (get wdef) (wrappers c)) c) + [ + "pre-build-commands", + Wrappers.with_pre_build, Wrappers.pre_build; + "pre-install-commands", + Wrappers.with_pre_install, Wrappers.pre_install; + "pre-remove-commands", + Wrappers.with_pre_remove, Wrappers.pre_remove; + "pre-session-commands", + Wrappers.with_pre_session, Wrappers.pre_session; + "wrap-build-commands", + Wrappers.with_wrap_build, Wrappers.wrap_build; + "wrap-install-commands", + Wrappers.with_wrap_install, Wrappers.wrap_install; + "wrap-remove-commands", + Wrappers.with_pre_remove, Wrappers.pre_remove; + "post-build-commands", + Wrappers.with_post_build, Wrappers.post_build; + "post-install-commands", + Wrappers.with_post_install, Wrappers.post_install; + "post-remove-commands", + Wrappers.with_post_remove, Wrappers.post_remove; + "post-session-commands", + Wrappers.with_post_session, Wrappers.post_session; + ] + +let switch_allowed_fields, switch_allowed_sections = + let allowed_fields = + lazy ( + OpamFile.Switch_config.( + [ + ("synopsis", Atomic, + fun t -> { t with synopsis = empty.synopsis }); + ("setenv", Modifiable ( + (fun nc c -> { c with env = nc.env @ c.env }), + (fun nc c -> + let env = + List.filter (fun (vr,op,vl,_) -> + None = OpamStd.List.find_opt (fun (vr',op',vl',_) -> + vr = vr' && op = op' && vl = vl') nc.env) c.env + in + { c with env })), + fun t -> { t with env = empty.env }); + "depext-bypass", OpamSysPkg.Set.Op.(Modifiable ( + (fun nc c -> + { c with depext_bypass = nc.depext_bypass ++ c.depext_bypass }), + (fun nc c -> + { c with depext_bypass = c.depext_bypass -- nc.depext_bypass }) + )), + (fun t -> { t with depext_bypass = empty.depext_bypass }); + ] @ allwd_wrappers empty.wrappers wrappers + (fun wrappers t -> { t with wrappers }))) + in + let allowed_sections = + let rem_elem new_elems elems = + List.filter (fun n -> not (List.mem n new_elems)) elems + in + lazy ( + OpamFile.Switch_config.([ + ("variables", InModifiable ( + (fun nc c -> { c with variables = nc.variables @ c.variables }), + (fun nc c -> + { c with variables = rem_elem nc.variables c.variables })), + (fun c -> { c with variables = empty.variables })); + ])) in - OpamFile.Switch_config.write config_f - {config with OpamFile.Switch_config.variables} + (fun () -> Lazy.force allowed_fields), + fun () -> Lazy.force allowed_sections + +let confset_switch gt switch switch_config = + let config_f = OpamPath.Switch.switch_config gt.root switch in + let write new_config = OpamFile.Switch_config.write config_f new_config in + { stg_fields = OpamFile.Switch_config.fields; + stg_allwd_fields = switch_allowed_fields (); + stg_sections = OpamFile.Switch_config.sections; + stg_allwd_sections = switch_allowed_sections (); + stg_config = switch_config; + stg_write_config = write; + stg_doc = switch_doc switch + } + +let with_switch: + 'a global_state -> 'b lock -> 'b switch_state option + -> (switch -> OpamFile.Switch_config.t -> 'c) -> 'c = + fun gt lock_kind st_opt k -> + match st_opt with + | Some st -> k st.switch st.switch_config + | None -> + let switch = OpamStateConfig.get_switch () in + let switch_config = + if lock_kind = `Lock_write then + match OpamStateConfig.Switch.read_opt ~lock_kind gt switch with + | Some c -> c + | exception (OpamPp.Bad_version _ as e) -> + OpamFormatUpgrade.hard_upgrade_from_2_1_intermediates gt.root; + raise e + | None -> OpamFile.Switch_config.empty + else + OpamStateConfig.Switch.safe_load ~lock_kind gt switch + in + let lock_file = OpamPath.Switch.lock gt.root switch in + if switch_config = OpamFile.Switch_config.empty then + OpamConsole.error "switch %s not found, display default values" + (OpamSwitch.to_string switch); + OpamFilename.with_flock lock_kind lock_file @@ fun _ -> + k switch switch_config -let set_global var value = +let set_opt_switch_t ?inner gt switch switch_config field value = + set_opt ?inner field value (confset_switch gt switch switch_config) + +let set_opt_switch gt ?st field value = + with_switch gt `Lock_write st @@ fun sw swc -> + let switch_config = set_opt_switch_t ~inner:false gt sw swc field value in + OpamStd.Option.map (fun st -> { st with switch_config }) st + +let global_allowed_fields, global_allowed_sections = + let allowed_fields = + let open OpamStd.Option.Op in + let open OpamFile in + let in_config = OpamInitDefaults.init_config () in + let wrapper_init = InitConfig.wrappers in_config in + let upd_vars get set = + (fun nc c -> set (get nc @ get c) c), + (fun nc c -> + let gv = get nc in + set (List.filter (fun (k,v,_) -> + None = OpamStd.List.find_opt (fun (k',v',_) -> k = k' && v = v') gv) + (get c)) c) + in + lazy ([ + "download-command", Atomic, + Config.with_dl_tool_opt + (InitConfig.dl_tool in_config ++ Config.dl_tool Config.empty); + "download-jobs", Atomic, + Config.with_dl_jobs + (InitConfig.dl_jobs in_config +! Config.dl_jobs Config.empty); + "jobs", Atomic, + Config.with_jobs_opt + (InitConfig.jobs in_config ++ Config.jobs Config.empty); + "best-effort-prefix-criteria", Atomic, + Config.with_best_effort_prefix_opt + (Config.best_effort_prefix Config.empty); + "solver", Atomic, + Config.with_solver_opt + (InitConfig.solver in_config ++ Config.solver Config.empty); + "global-variables", + (let add, rem = + upd_vars Config.global_variables Config.with_global_variables + in + InModifiable (add, rem)), + Config.with_global_variables (InitConfig.global_variables in_config); + "eval-variables", + (let add, rem = + upd_vars Config.eval_variables Config.with_eval_variables + in + InModifiable (add, rem)), + Config.with_eval_variables (InitConfig.eval_variables in_config); + "repository-validation-command", Atomic, + Config.with_validation_hook_opt (Config.validation_hook Config.empty); + "depext", Atomic, + Config.with_depext (Config.depext Config.empty); + "depext-run-installs", Atomic, + Config.with_depext_run_installs + (Config.depext_run_installs Config.empty); + "depext-cannot-install", Atomic, + Config.with_depext_cannot_install + (Config.depext_cannot_install Config.empty); + "depext-bypass", OpamSysPkg.Set.Op.(Modifiable ( + (fun nc c -> Config.with_depext_bypass + (Config.depext_bypass nc ++ Config.depext_bypass c) c), + (fun nc c -> Config.with_depext_bypass + (Config.depext_bypass c -- Config.depext_bypass nc) c) + )), + Config.with_depext_bypass (Config.depext_bypass Config.empty); + ] @ List.map (fun f -> + f, Atomic, Config.with_criteria + (Config.criteria Config.empty)) + [ "solver-criteria"; + "solver-upgrade-criteria"; + "solver-fixup-criteria" ] + @ allwd_wrappers wrapper_init Config.wrappers Config.with_wrappers + ) + in + (fun () -> Lazy.force allowed_fields), + fun () -> [] + +let confset_global gt = + let write new_config = OpamGlobalState.write {gt with config = new_config} in + { stg_fields = OpamFile.Config.fields; + stg_allwd_fields = global_allowed_fields (); + stg_sections = []; + stg_allwd_sections = global_allowed_sections (); + stg_config = gt.config; + stg_write_config = write; + stg_doc = global_doc; + } + +let set_opt_global_t ?inner gt field value = + let config = + set_opt ?inner field value (confset_global gt) + in + { gt with config } + +let set_opt_global = set_opt_global_t ~inner:false + +(** Variable settings *) + +(* "Configuration" of the [set_var] function. As these modification can be on + global and switch config, this record aggregates all needed inputs. *) +type ('var,'config) var_confset = + { + stv_vars: 'var list; + (* Variables list *) + stv_find: 'var -> bool; + (* Find function embedding a wanted var *) + stv_config: 'config; + (* State to use *) + stv_varstr: string -> string; + (* [stv_vars value] returns the string of the variable with the new value. + It is used to give the overall value to [set_opt] functions. *) + stv_set_opt: 'config -> update_op -> 'config; + (* The [set_opt] function call [stv_set_opt state var_value] *) + stv_remove_elem: 'var list -> 'config -> 'config; + (* As variable can't be duplicated, a function to remove it from the list *) + stv_write: 'config -> unit; + (* Write the config file *) + stv_doc: string; + (* Global or switch specification, used to print final user message *) + } + +let set_var svar value conf = + let var = OpamVariable.Full.of_string svar in + let conf = conf (OpamVariable.Full.variable var) in if not (OpamVariable.Full.is_global var) then OpamConsole.error_and_exit `Bad_arguments "Only global variables may be set using this command"; - OpamGlobalState.with_ `Lock_write @@ fun gt -> - let var = OpamVariable.Full.variable var in + let global_vars = conf.stv_vars in + let rest = List.filter (fun v -> not (conf.stv_find v)) global_vars in + let config = conf.stv_remove_elem rest conf.stv_config in + match value with + | `Overwrite value -> conf.stv_set_opt config (`Add (conf.stv_varstr value)) + | `Revert -> + (* only write, as the var is already removed *) + if config = conf.stv_config then + OpamConsole.msg "No modification in %s\n" conf.stv_doc + else + (conf.stv_write config; + OpamConsole.msg "Removed variable %s in %s\n" + (OpamConsole.colorise `underline svar) + conf.stv_doc); + config + +let set_var_global gt var value = let config = - gt.config |> - OpamFile.Config.with_global_variables - (let vars = - List.filter (fun (k,_,_) -> k <> var) - (OpamFile.Config.global_variables gt.config) - in - match value with - | Some v -> (var, S v, "Set through 'opam config set-global'") :: vars - | None -> vars) |> - OpamFile.Config.with_eval_variables - (List.filter (fun (k,_,_) -> k <> var) - (OpamFile.Config.eval_variables gt.config)) - in - OpamGlobalState.write { gt with config } - -let variable gt v = - let raw_switch_content = - match OpamStateConfig.get_switch_opt () with - | Some switch -> - let switch_config = - OpamStateConfig.Switch.safe_load ~lock_kind:`Lock_read gt switch - in - OpamPackageVar.resolve_switch_raw gt switch switch_config v - | None -> None + set_var var value @@ + fun var -> + let global_vars = OpamFile.Config.global_variables gt.config in + { stv_vars = global_vars; + stv_find = (fun (k,_,_) -> k = var); + stv_config = gt.config; + stv_varstr = (fun v -> + OpamPrinter.Normalise.value (nullify_pos @@ List (nullify_pos @@ [ + nullify_pos @@ Ident (OpamVariable.to_string var); + nullify_pos @@ String v; + nullify_pos @@ String "Set through 'opam var'" + ]))); + stv_set_opt = (fun config value -> + let gt = + set_opt_global_t ~inner:true { gt with config } + "global-variables" value + in gt.config); + stv_remove_elem = (fun rest config -> + OpamFile.Config.with_global_variables rest config + |> OpamFile.Config.with_eval_variables + (List.filter (fun (k,_,_) -> k <> var) + (OpamFile.Config.eval_variables config))); + stv_write = (fun config -> OpamGlobalState.write { gt with config }); + stv_doc = global_doc; + } in + { gt with config } + +let set_var_switch gt ?st var value = + let var_confset switch switch_config var = + let switch_vars = switch_config.OpamFile.Switch_config.variables in + { stv_vars = switch_vars; + stv_find = (fun (k,_) -> k = var); + stv_config = switch_config; + stv_varstr = (fun v -> + OpamStd.String.remove_suffix ~suffix:"\n" @@ + OpamPrinter.Normalise.items + [ nullify_pos @@ Variable + (nullify_pos @@ OpamVariable.to_string var, + nullify_pos @@ String v)]); + stv_set_opt = (fun swc value -> + set_opt_switch_t ~inner:true gt switch swc "variables" value); + stv_remove_elem = (fun rest switch_config -> + { switch_config with variables = rest }); + stv_write = (fun swc -> + OpamFile.Switch_config.write + (OpamPath.Switch.switch_config gt.root switch) swc); + stv_doc = switch_doc switch; + } in + let switch_config = + with_switch gt `Lock_write st @@ fun sw swc -> + set_var var value (var_confset sw swc) in - let switch_content = - match raw_switch_content with - | None when not (OpamVariable.Full.is_global v) -> - OpamSwitchState.with_ `Lock_none gt @@ fun st -> - OpamPackageVar.resolve st v - | rsc -> rsc - in - let content = - match switch_content with - | Some _ as some -> some - | None -> OpamPackageVar.resolve_global gt v + OpamStd.Option.map (fun st -> { st with switch_config }) st + +(** Option and var list display *) + +let print_fields fields = + let fields = + List.sort (fun (x,_) (x',_) -> compare x x') fields + |> List.map (fun (name, value) -> + let value = match value with + | None -> "{}" + | Some value -> (OpamPrinter.Normalise.value value) + in + [ + OpamConsole.colorise `bold name ; + OpamConsole.colorise `blue value + ]) in - match content with - | Some c -> OpamConsole.msg "%s\n" (OpamVariable.string_of_variable_contents c) - | None -> - OpamConsole.error_and_exit `Not_found - "Variable %s not found" - (OpamVariable.Full.to_string v) + OpamConsole.print_table stdout ~sep:" " + (OpamStd.Format.align_table fields) +let find_field field name_value = + match OpamStd.List.find_opt (fun (name, _) -> name = field) name_value with + | None -> (field, None) + | Some (name, value) -> (name, Some value) -let exec gt ~set_opamroot ~set_opamswitch ~inplace_path command = - log "config-exec command=%a" (slog (String.concat " ")) command; - OpamSwitchState.with_ `Lock_none gt @@ fun st -> - let cmd, args = - match - List.map (OpamFilter.expand_string ~default:(fun _ -> "") - (OpamPackageVar.resolve st)) command - with - | [] -> OpamSystem.internal_error "Empty command" - | h::_ as l -> h, Array.of_list l in - let env = - OpamTypesBase.env_array - (OpamEnv.get_full - ~set_opamroot ~set_opamswitch ~force_path:(not inplace_path) st) +let find_section section name_value = + let sections = + List.find_all (fun (name, _) -> + match OpamStd.String.cut_at name '.' with + | None -> false + | Some (name,_) -> name = section) + name_value in - match OpamSystem.resolve_command ~env cmd with - | Some cmd -> raise (OpamStd.Sys.Exec (cmd, args, env)) + match sections with + | [] -> [section, None] + | section -> List.map (fun (n,v) -> n, Some v) section + +let options_list_t to_list conf = + let name_value = to_list conf.stg_config in + let fields = + OpamStd.List.filter_map (fun (field, policy, _) -> + match policy with + | InModifiable _ -> None + | _ -> Some (find_field field name_value)) + conf.stg_allwd_fields + in + let sections = + OpamStd.List.filter_map (fun (field, policy, _) -> + match policy with + | InModifiable _ -> None + | _ -> Some (find_section field name_value)) + conf.stg_allwd_sections + |> List.flatten + in + print_fields (fields @ sections) + +let options_list_switch ?st gt = + with_switch gt `Lock_none st @@ fun sw swc -> + options_list_t OpamFile.Switch_config.to_list (confset_switch gt sw swc) + +let options_list_global gt = + options_list_t OpamFile.Config.to_list (confset_global gt) + +let options_list ?st gt = + OpamConsole.header_msg "Global configuration"; + options_list_global gt; + OpamConsole.header_msg "Switch configuration (%s)" + (OpamSwitch.to_string (OpamStateConfig.get_switch ())); + options_list_switch ?st gt + +let vars_list_global gt = + let (%) s col = OpamConsole.colorise col s in + let all_global_vars = + List.fold_left (fun acc (v,doc) -> + OpamVariable.Map.add (OpamVariable.of_string v) doc acc) + OpamVariable.Map.empty + OpamPackageVar.global_variable_names + in + let all_global_vars = + OpamVariable.Map.union (fun _ x -> x) + all_global_vars + (OpamVariable.Map.map snd gt.global_variables) + in + let env = OpamPackageVar.resolve_global gt in + List.map (fun (var, doc) -> + let content = + OpamFilter.ident_string env ~default:"" ([],var,None) + in + let doc = + if doc = OpamGlobalState.inferred_from_system then + match OpamStd.Option.Op.( + OpamVariable.Map.find_opt var gt.global_variables + >>| fst + >>= Lazy.force) with + | Some c + when (OpamVariable.string_of_variable_contents c) <> content -> + "Set through local opam config or env" + | _ -> doc + else doc + in + [ + OpamVariable.to_string var % `bold; + content % `blue; + "#"; doc + ]) + (List.sort (fun (x,_) (x',_) -> compare x x') + (OpamVariable.Map.bindings all_global_vars)) |> + OpamStd.Format.align_table |> + OpamConsole.print_table stdout ~sep:" " + +let vars_list_switch ?st gt = + let (%) s col = OpamConsole.colorise col s in + let switch, config = + match st with + | Some st -> st.switch, st.switch_config + | None -> + let switch = OpamStateConfig.get_switch () in + switch, + OpamStateConfig.Switch.safe_load ~lock_kind:`Lock_read gt switch + in + List.map (fun stdpath -> [ + OpamTypesBase.string_of_std_path stdpath % `bold; + OpamPath.Switch.get_stdpath gt.root switch config stdpath |> + OpamFilename.Dir.to_string |> + OpamConsole.colorise `blue + ]) + OpamTypesBase.all_std_paths @ + List.map (fun (var,value) -> [ + OpamVariable.to_string var % `bold; + OpamVariable.string_of_variable_contents value % `blue; + ]) + (List.sort (fun (x,_) (x',_) -> compare x' x) + config.OpamFile.Switch_config.variables) |> + OpamStd.Format.align_table |> + OpamConsole.print_table stdout ~sep:" " + +let vars_list ?st gt = + let (%) s col = OpamConsole.colorise col s in + OpamConsole.header_msg "Global opam variables"; + vars_list_global gt; + OpamConsole.header_msg "Configuration variables from the current switch"; + vars_list_switch ?st gt; + OpamConsole.header_msg "Package variables ('opam var --package PKG' to show)"; + List.map (fun (var, doc) -> [ + ("PKG:"^var) % `bold; + ""; + "#";doc + ]) + OpamPackageVar.package_variable_names |> + OpamStd.Format.align_table |> + OpamConsole.print_table stdout ~sep:" " + +(* Specified option/var display *) + +let option_show to_list conf field = + match OpamStd.List.assoc_opt field conf.stg_fields with + | Some pp -> + (match OpamPp.print pp conf.stg_config with + | _, Some value -> + OpamConsole.msg "%s\n" (OpamPrinter.Normalise.value value) + | _, None -> ()) | None -> - OpamConsole.error "Command not found '%s'" cmd; - raise (OpamStd.Sys.Exit 127) + if List.mem_assoc field conf.stg_sections then + let name_value = to_list conf.stg_config in + let sections = + OpamStd.List.filter_map (fun (name, v) -> + match OpamStd.String.cut_at name '.' with + | Some (name,elem) when name = field -> + Some [ elem; OpamPrinter.Normalise.value v ] + | _ -> None + ) name_value + in + OpamConsole.print_table stdout ~sep:" " + (OpamStd.Format.align_table sections) + else + OpamConsole.error_and_exit `Not_found + "Field or section %s not found" field +let option_show_switch gt ?st field = + with_switch gt `Lock_none st @@ fun sw swc -> + option_show OpamFile.Switch_config.to_list (confset_switch gt sw swc) field + +let option_show_global gt field = + option_show OpamFile.Config.to_list (confset_global gt) field + +let var_show_t resolve ?switch v = + match resolve (OpamVariable.Full.of_string v) with + | Some c -> + OpamConsole.msg "%s\n" (OpamVariable.string_of_variable_contents c) + | None -> + OpamConsole.error_and_exit `Not_found "Variable %s not found in %s" v + (match switch with + | None -> "global config" + | Some switch -> "switch " ^ (OpamSwitch.to_string switch)) + +let is_switch_defined_var switch_config v = + OpamFile.Switch_config.variable switch_config + (OpamVariable.of_string v) <> None + || (try let _path = OpamTypesBase.std_path_of_string v in true + with Failure _ -> false) + || OpamStd.String.contains_char v ':' + +let var_switch_raw gt v = + match OpamStateConfig.get_switch_opt () with + | Some switch -> + let switch_config = + OpamStateConfig.Switch.safe_load ~lock_kind:`Lock_read gt switch + in + let rsc = + if is_switch_defined_var switch_config v then + OpamPackageVar.resolve_switch_raw gt switch switch_config + (OpamVariable.Full.of_string v) + else None + in + (match rsc with + | Some c -> + OpamConsole.msg "%s\n" (OpamVariable.string_of_variable_contents c); + rsc + | None -> None) + | None -> None + +let var_show_switch gt ?st v = + if var_switch_raw gt v = None then + let resolve_switch st = + if is_switch_defined_var st.switch_config v then + var_show_t (OpamPackageVar.resolve st) ~switch:st.switch v + else + OpamConsole.error_and_exit `Not_found + "Variable %s not found in switch %s" + v (OpamSwitch.to_string st.switch) + in + match st with + | Some st -> resolve_switch st + | None -> OpamSwitchState.with_ `Lock_none gt resolve_switch + +let var_show_global gt f = var_show_t (OpamPackageVar.resolve_global gt) f + +let var_show gt v = + if var_switch_raw gt v = None then + OpamSwitchState.with_ `Lock_none gt @@ fun st -> + let switch = + if is_switch_defined_var st.switch_config v then Some st.switch else None + in + var_show_t (OpamPackageVar.resolve st) ?switch v + +(* detect scope *) +let get_scope field = + let field = + try fst (parse_update field) + with Invalid_argument _ -> field + in + let find l = OpamStd.List.find_opt (fun (f,_) -> f = field) l in + if find OpamFile.Switch_config.fields <> None + || find OpamFile.Switch_config.sections <> None then + `Switch + else if find OpamFile.Config.fields <> None then + `Global + else `None field diff -Nru opam-2.0.10/src/client/opamConfigCommand.mli opam-2.1.2/src/client/opamConfigCommand.mli --- opam-2.0.10/src/client/opamConfigCommand.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamConfigCommand.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,32 +9,33 @@ (* *) (**************************************************************************) -(** Functions handling the "opam config" subcommand *) +(** Functions handling the `opam config` subcommand and configuration actions *) open OpamTypes open OpamStateTypes -(** Display the current environment. Booleans csh, sexp and fish set an alternative - output (unspecified if more than one is true, sh-style by default). - [inplace_path] changes how the PATH variable is updated when there is already - an opam entry: either at the same rank, or pushed in front. *) +(** {2 `opam config` subcommand and their associated commands } *) + +(** Display the current environment. Booleans csh, sexp and fish set an + alternative output (unspecified if more than one is true, sh-style by + default). [inplace_path] changes how the PATH variable is updated when there + is already an opam entry: either at the same rank, or pushed in front. *) val env: 'a global_state -> switch -> ?set_opamroot:bool -> ?set_opamswitch:bool -> csh:bool -> sexp:bool -> fish:bool -> inplace_path:bool -> unit +(** Ensures that the environment file exists in the given switch, regenerating + it, if necessary. *) +val ensure_env: 'a global_state -> switch -> unit + (** Like [env] but allows one to specify the precise env to print rather than compute it from a switch state *) val print_eval_env: csh:bool -> sexp:bool -> fish:bool -> env -> unit -(** Display the content of all available variables; global summary if the list - is empty, package name "-" is understood as global configuration *) -val list: - 'a global_state -> name list -> unit - -(** Display the content of a given variable *) -val variable: 'a global_state -> full_variable -> unit +(** Display the content of all available packages variables *) +val list: 'a switch_state -> name list -> unit (** Substitute files *) val subst: 'a global_state -> basename list -> unit @@ -42,14 +43,104 @@ (** Prints expansion of variables in string *) val expand: 'a global_state -> string -> unit -(** Sets or unsets switch config variables *) -val set: full_variable -> string option -> unit - -(** Sets or unsets global config variables *) -val set_global: full_variable -> string option -> unit - (** Execute a command in a subshell, after variable expansion *) val exec: [< unlocked ] global_state -> set_opamroot:bool -> set_opamswitch:bool -> inplace_path:bool -> string list -> unit + +(** {2 Variables and options display and setting } *) +(** Functions handling `opam var` and `opam option` command *) + +(** Given an `opam option` field or field-value argument, detect the scope, + switch, global or nonexistent field + (cf. [OpamCommands.Var_Option_Common.var_option]) *) +val get_scope: string -> [> `Switch | `Global | `None of string ] + +(** {3 Setting variables and options } *) + +(** Update operation type *) +type whole_op = [ `Overwrite of string | `Revert ] +type append_op = [ `Add of string | `Remove of string ] +type update_op = [ append_op | whole_op ] + +(** Parse an update operation. String is of the form [var[(+=|-=|=)[value]]]. + If 'value' is absent, it is a revert operation. + Raise [Invalid_argument] if the string is malformed *) +val parse_update: string -> string * update_op + +(** As [parse_update] but parse only overwrites and reverts. String is of the + form [var=[value]]`. + Raise [Invalid_argument] if the string is malformed *) +val parse_whole: string -> string * whole_op + +val whole_of_update_op: update_op -> whole_op + +(** [set_opt_global gt field value] updates global config field with update + value in /config file. Modifiable fields are a subset of all + defined fields in [OpamFile.Config.t]. On revert, field is reverted to its + initial value as defined in [OpamInitDefaults.init_config], to default + value otherwise ([OpamFile.Config.empty]). + May raise [OpamStd.Sys.Exit 2]. *) +val set_opt_global: rw global_state -> string -> update_op -> rw global_state + +(** As [set_opt_global], [set_opt_switch] updates switch config file in + //.opam-switch/switch-config. If switch state is given, + uses its config and returns it with then new config. Otherwise, loads the + raw switch state and returns [None]. *) +val set_opt_switch: + 'a global_state -> ?st:rw switch_state -> string -> update_op + -> rw switch_state option + +(** [set_var_global] and [set_var_switch] update respectively `global-variables` + field in global config and `variables` field in switch config, by appending + the new variables to current set. If switch state is given, uses its + config and returns it with then new config. Otherwise, loads the raw switch + state and returns [None]. + May raise [OpamStd.Sys.Exit 2]. *) +val set_var_global: + rw global_state -> string -> whole_op -> rw global_state +val set_var_switch: + 'a global_state -> ?st:rw switch_state -> string -> whole_op + -> rw switch_state option + + +(** {3 Display list of variables and options } *) + +(** List switch and/or global fields/sections and their value. If switch state + is given, uses its config, otherwise loads the raw switch state. *) + +val options_list: + ?st:unlocked switch_state -> 'a global_state -> unit +val options_list_global: 'a global_state -> unit +val options_list_switch: + ?st:unlocked switch_state -> 'a global_state -> unit + +(** List switch and/or global variables and their value. If switch state is + given, uses its config, otherwise loads the raw switch state. *) + +val vars_list: + ?st:'a switch_state -> 'b global_state -> unit +val vars_list_global: 'a global_state -> unit +val vars_list_switch: + ?st:'a switch_state -> 'b global_state -> unit + + +(** {3 Display a variable and option } *) + +(** Display [field] name and content in the global / switch configuration. + If switch state is given, uses its config, otherwise loads the raw switch + state. *) + +val option_show_global: 'a global_state -> string -> unit +val option_show_switch: + 'a global_state -> ?st:unlocked switch_state -> string -> unit + +(** Display [var] name and content in the global / switch configuration. + Look first in the raw switch_state, if not found, uses the given switch + state or loads one. *) + +val var_show_global: 'a global_state -> string -> unit +val var_show_switch: + 'a global_state -> ?st:'b switch_state -> string -> unit +val var_show: 'a global_state -> string -> unit diff -Nru opam-2.0.10/src/client/opamGitVersion.mli opam-2.1.2/src/client/opamGitVersion.mli --- opam-2.0.10/src/client/opamGitVersion.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamGitVersion.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2014 OCamlPro *) +(* Copyright 2014-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/client/opamInitDefaults.ml opam-2.1.2/src/client/opamInitDefaults.ml --- opam-2.0.10/src/client/opamInitDefaults.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamInitDefaults.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -21,15 +21,26 @@ let default_compiler = OpamFormula.ors [ OpamFormula.Atom (OpamPackage.Name.of_string "ocaml-system", - OpamFormula.Atom - (`Geq, OpamPackage.Version.of_string "4.02.3")); + OpamFormula.Empty); OpamFormula.Atom (OpamPackage.Name.of_string "ocaml-base-compiler", OpamFormula.Empty); ] +let default_invariant = + OpamFormula.Atom + (OpamPackage.Name.of_string "ocaml", + OpamFormula.Atom + (`Geq, OpamPackage.Version.of_string "4.05.0")) + let eval_variables = [ OpamVariable.of_string "sys-ocaml-version", ["ocamlc"; "-vnum"], "OCaml version present on your system independently of opam, if any"; + OpamVariable.of_string "sys-ocaml-arch", ["sh"; "-c"; "ocamlc -config 2>/dev/null | tr -d '\\r' | grep '^architecture: ' | sed -e 's/.*: //' -e 's/i386/i686/' -e 's/amd64/x86_64/'"], + "Target architecture of the OCaml compiler present on your system"; + OpamVariable.of_string "sys-ocaml-cc", ["sh"; "-c"; "ocamlc -config 2>/dev/null | tr -d '\\r' | grep '^ccomp_type: ' | sed -e 's/.*: //'"], + "Host C Compiler type of the OCaml compiler present on your system"; + OpamVariable.of_string "sys-ocaml-libc", ["sh"; "-c"; "ocamlc -config 2>/dev/null | tr -d '\\r' | grep '^os_type: ' | sed -e 's/.*: //' -e 's/Win32/msvc/' -e '/^msvc$/!s/.*/libc/'"], + "Host C Runtime Library type of the OCaml compiler present on your system"; ] let os_filter os = @@ -39,6 +50,9 @@ let macos_filter = os_filter "macos" let openbsd_filter = os_filter "openbsd" let freebsd_filter = os_filter "freebsd" +let not_open_free_bsd_filter = + FNot (FOr (openbsd_filter, freebsd_filter)) +let win32_filter = os_filter "win32" let sandbox_filter = FOr (linux_filter, macos_filter) let gpatch_filter = FOr (openbsd_filter, freebsd_filter) @@ -47,19 +61,26 @@ let gtar_filter = openbsd_filter let tar_filter = FNot gtar_filter -let wrappers ~sandboxing () = +let getconf_filter = FNot (FOr (win32_filter, freebsd_filter)) + +let sandbox_wrappers = let cmd t = [ CString "%{hooks}%/sandbox.sh", None; CString t, None; ] in + [ `build [cmd "build", Some sandbox_filter]; + `install [cmd "install", Some sandbox_filter]; + `remove [cmd "remove", Some sandbox_filter]; + ] + +let wrappers ~sandboxing () = let w = OpamFile.Wrappers.empty in if sandboxing then - { w with - OpamFile.Wrappers. - wrap_build = [cmd "build", Some sandbox_filter]; - wrap_install = [cmd "install", Some sandbox_filter]; - wrap_remove = [cmd "remove", Some sandbox_filter]; - } + List.fold_left OpamFile.Wrappers.(fun w -> function + | `build wrap_build -> { w with wrap_build } + | `install wrap_install -> { w with wrap_install } + | `remove wrap_remove -> { w with wrap_remove } + ) OpamFile.Wrappers.empty sandbox_wrappers else w let bwrap_cmd = "bwrap" @@ -69,45 +90,54 @@ See https://opam.ocaml.org/doc/FAQ.html#Why-does-opam-require-bwrap." bwrap_cmd -let fetch_cmd_user () = +let req_dl_tools () = + let msg = + Some "A download tool is required, check env variables OPAMCURL or OPAMFETCH" + in + let default = + [ + ["curl"; "wget"], msg, Some not_open_free_bsd_filter; + ["fetch"], msg, Some freebsd_filter; + ["ftp"], msg, Some openbsd_filter + ] + in let open OpamStd.Option.Op in - match - OpamStd.Env.getopt "OPAMCURL", - OpamStd.Env.getopt "OPAMFETCH" >>| fun s -> - OpamStd.String.split s ' ' - with - | Some cmd, _ | _, Some (cmd::_) -> Some cmd - | _ -> None - -let dl_tools () = - match fetch_cmd_user () with - | None -> ["curl"; "wget"] - | Some cmd -> [cmd] + let cmd = + (OpamRepositoryConfig.E.fetch () + >>= fun s -> + match OpamStd.String.split s ' ' with + | c::_ -> Some c + | _ -> None) + >>+ fun () -> OpamRepositoryConfig.E.curl () + in + match cmd with + | Some cmd -> + [[cmd], Some "Custom download tool, check OPAMCURL or OPAMFETCH", None] + | None -> default let dl_tool () = - match fetch_cmd_user () with - | None -> None - | Some cmd -> Some [(CString cmd), None] + let open OpamStd.Option.Op in + (OpamRepositoryConfig.E.fetch () + >>+ fun () -> OpamRepositoryConfig.E.curl ()) + >>| fun cmd -> [(CString cmd), None] let recommended_tools () = let make = OpamStateConfig.(Lazy.force !r.makecmd) in [ [make], None, None; - ["m4"], None, None; ["cc"], None, None; ] let required_tools ~sandboxing () = + req_dl_tools () @ [ - dl_tools(), - Some "A download tool is required, check env variables OPAMCURL or OPAMFETCH", - None; ["diff"], None, None; ["patch"], None, Some patch_filter; ["gpatch"], None, Some gpatch_filter; ["tar"], None, Some tar_filter; ["gtar"], None, Some gtar_filter; ["unzip"], None, None; + ["getconf"], None, Some getconf_filter; ] @ if sandboxing then [ [bwrap_cmd], Some (bwrap_string()), Some bwrap_filter; @@ -128,6 +158,7 @@ I.with_repositories [OpamRepositoryName.of_string "default", (repository_url, None)] |> I.with_default_compiler default_compiler |> + I.with_default_invariant default_invariant |> I.with_eval_variables eval_variables |> I.with_wrappers @| wrappers ~sandboxing |> I.with_recommended_tools @| recommended_tools |> diff -Nru opam-2.0.10/src/client/opamInitDefaults.mli opam-2.1.2/src/client/opamInitDefaults.mli --- opam-2.0.10/src/client/opamInitDefaults.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamInitDefaults.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -21,6 +21,11 @@ val eval_variables: (OpamVariable.t * string list * string) list +val sandbox_wrappers: + [> `build of command list + | `install of command list + | `remove of command list ] list + (** Default initial configuration file for use by [opam init] if nothing is supplied. *) val init_config: ?sandboxing:bool -> unit -> OpamFile.InitConfig.t diff -Nru opam-2.0.10/src/client/opamListCommand.ml opam-2.1.2/src/client/opamListCommand.ml --- opam-2.0.10/src/client/opamListCommand.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamListCommand.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -10,6 +10,7 @@ (**************************************************************************) open OpamCompat +open OpamParserTypes.FullPos open OpamTypes open OpamStateTypes open OpamStd.Op @@ -162,32 +163,34 @@ OpamFormula.ors [acc; package_dependencies st tog nv]) pkgs OpamFormula.Empty -let get_universe st tog = +let get_universe st ?requested tog = + let requested = + match requested with + | Some r -> r + | None -> OpamPackage.names_of_packages st.packages + in OpamSwitchState.universe st ~test:tog.test ~doc:tog.doc ~force_dev_deps:tog.dev - ~requested:(OpamPackage.names_of_packages st.packages) - Query + ~requested Query let rec value_strings value = let module SS = OpamStd.String.Set in match value with | Bool _ | Int _ -> SS.empty - | Ident (_, s) -> SS.singleton s - | String (_, s) -> SS.singleton s - | Relop (_, _, v1, v2) - | Logop (_, _, v1, v2) - | Env_binding (_, v1, _, v2) -> - SS.union (value_strings v1) (value_strings v2) - | Prefix_relop (_, _, v) - | Pfxop (_, _, v) -> - value_strings v - | List (_, l) - | Group (_, l) -> - List.fold_left (fun acc v -> SS.union acc (value_strings v)) - SS.empty l - | Option (_, v, vl) -> - List.fold_left (fun acc v -> SS.union acc (value_strings v)) - (value_strings v) vl + | Ident s -> SS.singleton s + | String s -> SS.singleton s + | Relop (_, v1, v2) + | Logop (_, v1, v2) + | Env_binding (v1, _, v2) -> + SS.union (value_strings v1.pelem) (value_strings v2.pelem) + | Prefix_relop (_, v) | Pfxop (_, v) -> + value_strings v.pelem + | List l | Group l -> + List.fold_left (fun acc v -> SS.union acc (value_strings v.pelem)) + SS.empty l.pelem + | Option (v, vl) -> + List.fold_left (fun acc v -> SS.union acc (value_strings v.pelem)) + (value_strings v.pelem) vl.pelem let pattern_selector patterns = let name_patt = @@ -228,7 +231,6 @@ ~installed:false ~unavailable:true (get_universe st tog) (packages_of_atoms st atoms) - |> OpamPackage.Set.of_list | Required_by (tog, atoms) -> atom_dependencies st tog atoms |> OpamFormula.packages base @@ -245,10 +247,15 @@ | Coinstallable_with (tog, packages) -> let universe = get_universe st tog in let set = OpamPackage.Set.of_list packages in - let universe = { universe with u_base = set; u_installed = set } in - OpamSolver.installable_subset universe base + OpamSolver.coinstallable_subset universe set base | Solution (tog, atoms) -> - let universe = get_universe st tog in + let universe = + let requested = + OpamFormula.packages_of_atoms st.packages atoms + |> OpamPackage.names_of_packages + in + get_universe st tog ~requested + in let universe = { universe with u_installed = OpamPackage.Set.empty; @@ -262,7 +269,7 @@ "No solution%s for %s: %s" (if tog.depopts then " including optional dependencies" else "") (OpamFormula.string_of_atoms atoms) - (OpamCudf.string_of_conflict st.packages + (OpamCudf.string_of_conflicts st.packages (OpamSwitchState.unavailable_reason st) cs)) | Pattern (psel, pat) -> let re = @@ -275,13 +282,14 @@ let content_strings nv = let opam = get_opam st nv in if psel.fields = [] then - List.map (fun (_,v) -> value_strings v) (OpamFile.OPAM.to_list opam) + List.map (fun (_,v) -> value_strings v.pelem) + (OpamFile.OPAM.to_list opam) else try List.map (fun f -> match OpamFile.OPAM.print_field_as_syntax f opam with | None -> OpamStd.String.Set.empty - | Some v -> value_strings v) + | Some v -> value_strings v.pelem) psel.fields with Not_found -> OpamConsole.error_and_exit `Bad_arguments @@ -318,10 +326,10 @@ (try let root = st.switch_global.root in let switch = - List.find (fun sw -> - OpamFilename.remove_prefix (OpamPath.Switch.root root sw) file - <> OpamFilename.to_string file) - (OpamFile.Config.installed_switches st.switch_global.config) + List.find (fun sw -> + OpamFilename.remove_prefix (OpamPath.Switch.root root sw) file + <> OpamFilename.to_string file) + (OpamFile.Config.installed_switches st.switch_global.config) in let rel_name = OpamFilename.remove_prefix (OpamPath.Switch.root root switch) file @@ -377,6 +385,7 @@ | Synopsis_or_target | Description | Field of string + | Raw_field of string | Installed_version | Pinning_target | Source_hash @@ -397,7 +406,7 @@ | Package -> "Package" | Synopsis | Synopsis_or_target -> "Synopsis" | Description -> "Description" - | Field s -> String.capitalize_ascii s + | Field s | Raw_field s -> String.capitalize_ascii s | Installed_version -> "Installed" | Pinning_target -> "Pin" | Source_hash -> "Source hash" @@ -417,7 +426,8 @@ Synopsis, "synopsis"; Synopsis_or_target, "synopsis-or-target"; Description, "description"; - Field "", ":"; + Field "", ""; + Raw_field ":", ":"; Installed_version, "installed-version"; Pinning_target, "pin"; Source_hash, "source-hash"; @@ -431,25 +441,30 @@ Depexts, "depexts"; ] -let string_of_field = function - | Field s -> s^":" +let raw_field_names = + List.filter (function Field _, _ -> false | _ -> true) field_names + +let string_of_field ?(raw=false) = function + | Field s -> if raw then s ^":" else s + | Raw_field s -> s ^":" | f -> List.assoc f field_names -let field_of_string = +let field_of_string ~raw = let names_fields = List.map (fun (a,b) -> b, a) field_names in + let opam_fields = List.map fst OpamFile.OPAM.fields in + if raw then + fun s -> Raw_field s + else fun s -> if OpamStd.String.ends_with ~suffix:":" s then - Field (OpamStd.String.remove_suffix ~suffix:":" s) + Raw_field (OpamStd.String.remove_suffix ~suffix:":" s) else - try List.assoc s names_fields + try + List.assoc s names_fields with Not_found -> - OpamConsole.error_and_exit `Bad_arguments - "No printer for %S%s" s - (if not (OpamStd.String.ends_with ~suffix:":" s) && - List.mem_assoc s (OpamFile.OPAM.fields) - then Printf.sprintf ". Did you mean the opam field \"%s:\" \ - (with a colon)?" s - else "") + match OpamStd.List.find_opt (fun x -> s = x) opam_fields with + | Some f -> Field f + | None -> OpamConsole.error_and_exit `Bad_arguments "No printer for %S" s let version_color st nv = let installed = (* (in any switch) *) @@ -468,22 +483,28 @@ (if is_available nv then [] else [`crossed;`red]) let mini_field_printer ?(prettify=false) ?(normalise=false) = + let module OpamPrinter = OpamPrinter.FullPos in if normalise then OpamPrinter.Normalise.value else - function - | String (_, s) when prettify -> s - | List (_, l) when prettify && - List.for_all (function String _ -> true | _ -> false) l -> - OpamStd.List.concat_map ", " (function String (_, s) -> s | _ -> assert false) l - | List (_, l) -> OpamPrinter.value_list l - | f -> OpamPrinter.Normalise.value f + fun v -> match v.pelem with + | String s when prettify -> s + | List l when prettify && + List.for_all (function {pelem=String _;_} -> true | _ -> false) l.pelem -> + OpamStd.List.concat_map ", " (function {pelem=String s;_} -> s | _ -> assert false) l.pelem + | List l -> OpamPrinter.value_list l + | _ -> OpamPrinter.Normalise.value v -let detail_printer ?prettify ?normalise st nv = +let detail_printer ?prettify ?normalise ?(sort=false) st nv = let open OpamStd.Option.Op in let (%) s cols = OpamConsole.colorise' cols s in let root_sty = if OpamPackage.Set.mem nv st.installed_roots then [`underline] else [] in + let get_opam = + if not sort then get_opam else + fun st nv -> + OpamFileTools.sort_opam (get_opam st nv) + in function | Name -> OpamPackage.Name.to_string nv.name % (`bold :: root_sty) | Version -> OpamPackage.Version.to_string nv.version % version_color st nv @@ -516,7 +537,7 @@ OpamFile.OPAM.descr >>| OpamFile.Descr.body) +! "" - | Field f -> + | Raw_field f | Field f -> (try List.assoc f (OpamFile.OPAM.to_list (get_opam st nv)) |> mini_field_printer ?prettify ?normalise @@ -595,8 +616,8 @@ +! "" ) | Depexts -> - String.concat " " - (OpamStd.String.Set.elements (OpamSwitchState.depexts st nv)) + OpamStd.List.concat_map " " OpamSysPkg.to_string + (OpamSysPkg.Set.elements (OpamSwitchState.depexts st nv)) type package_listing_format = { short: bool; @@ -643,14 +664,8 @@ ~requested:(OpamPackage.names_of_packages packages) Query in - let deps_packages = - OpamSolver.dependencies - ~depopts:true ~installed:false ~unavailable:true - ~build:true ~post:false - universe packages - in - List.filter (fun nv -> OpamPackage.Set.mem nv packages) deps_packages |> - List.rev + OpamSolver.dependency_sort ~depopts:true ~build:true ~post:false + universe packages else match format.order with | `Custom o -> List.sort o (OpamPackage.Set.elements packages) | _ -> OpamPackage.Set.elements packages @@ -676,8 +691,7 @@ OpamStd.Format.align_table |> OpamConsole.print_table ?cut:format.wrap stdout ~sep:format.separator -let get_switch_state gt = - let rt = OpamRepositoryState.load `Lock_none gt in +let get_switch_state gt rt = match OpamStateConfig.get_switch_opt () with | None -> OpamSwitchState.load_virtual gt rt | Some sw -> OpamSwitchState.load `Lock_none gt rt sw @@ -690,23 +704,30 @@ if OpamPackage.Set.mem nv packages then nv else OpamPackage.Set.max_elt (OpamPackage.packages_of_name packages name) in - OpamStd.String.Set.union acc + OpamSysPkg.Set.union acc (OpamSwitchState.depexts st nv)) (OpamPackage.names_of_packages packages) - OpamStd.String.Set.empty + OpamSysPkg.Set.empty let print_depexts = - OpamStd.String.Set.iter (OpamConsole.msg "%s\n") + OpamSysPkg.Set.iter (fun d -> OpamConsole.msg "%s\n" (OpamSysPkg.to_string d)) -let info st ~fields ~raw_opam ~where ?normalise ?(show_empty=false) atoms = +let info st ~fields ~raw ~where ?normalise ?(show_empty=false) + ?(all_versions=false) ?(sort=false) atoms = let packages = - OpamFormula.packages_of_atoms (st.packages ++ st.installed) atoms + OpamFormula.packages_of_atoms ~disj:all_versions + (st.packages ++ st.installed) atoms + in + let atoms, missing_atoms = + List.partition (fun (n,_) -> OpamPackage.has_name packages n) atoms in - if OpamPackage.Set.is_empty packages then + if missing_atoms <> [] then (OpamConsole.error "No package matching %s found" - (OpamStd.List.concat_map " or " OpamFormula.short_string_of_atom atoms); - OpamStd.Sys.exit_because `Not_found); - let fields = List.map field_of_string fields in + (OpamStd.List.concat_map " or " OpamFormula.short_string_of_atom + missing_atoms); + if OpamPackage.Set.is_empty packages then + OpamStd.Sys.exit_because `Not_found); + let fields = List.map (field_of_string ~raw) fields in let all_versions_fields = [ Name; All_installed_versions; @@ -720,6 +741,7 @@ Field "url.src"; Field "url.checksum"; Field "homepage"; + Field "doc"; Field "bug-reports"; Field "dev-repo"; Field "authors"; @@ -737,45 +759,74 @@ let output_table fields nv = let tbl = List.fold_left (fun acc item -> - let contents = detail_printer ?normalise st nv item in + let contents = detail_printer ?normalise ~sort st nv item in if show_empty || contents <> "" then - [ OpamConsole.colorise `blue (string_of_field item); contents ] + [ OpamConsole.colorise `blue (string_of_field ~raw item); contents ] :: acc else acc) [] (List.rev fields) in + let cut = + match normalise with + | Some true -> Some `None + | _ -> None + in OpamStd.Format.align_table tbl |> - OpamConsole.print_table stdout ~sep:" "; + OpamConsole.print_table ?cut stdout ~sep:" "; in - OpamPackage.names_of_packages packages |> - OpamPackage.Name.Set.iter (fun name -> + let header pkg = + if fields = [] && not raw && not where then + (OpamConsole.header_msg "%s: information on all versions" + (OpamPackage.Name.to_string pkg.name); + output_table all_versions_fields pkg) + in + let output_package pkg = + let opam = get_opam st pkg in + let opam = + if not sort then opam else + OpamFileTools.sort_opam opam + in + OpamFile.OPAM.print_errors opam; + if where then + OpamConsole.msg "%s\n" + (match OpamFile.OPAM.metadata_dir opam with + | Some (None, dir) -> Filename.concat dir "opam" + | Some (Some repo, rdir) -> + let repo_dir = OpamRepositoryPath.root st.switch_global.root repo in + let tar = OpamRepositoryPath.tar st.switch_global.root repo in + if OpamFilename.exists tar && + not (OpamFilename.exists_dir repo_dir) then + Printf.sprintf "<%s>%s%s" + (OpamFilename.to_string tar) + Filename.dir_sep + rdir + else + OpamFilename.Dir.to_string OpamFilename.Op.(repo_dir / rdir) + | None -> "") + else if raw && fields = [] then + OpamFile.OPAM.write_to_channel stdout opam + else + match fields with + | [] -> + OpamConsole.header_msg "Version-specific details"; + output_table one_version_fields pkg + | [f] -> OpamConsole.msg "%s\n" (detail_printer ?normalise ~sort st pkg f) + | fields -> output_table fields pkg + in + List.iter (fun (name,_) -> (* Like OpamSwitchState.get_package, but restricted to [packages] *) let nvs = OpamPackage.packages_of_name packages name in - let choose = - try OpamPackage.Set.choose (nvs %% st.pinned) with Not_found -> - try OpamPackage.Set.choose (nvs %% st.installed) with Not_found -> - try OpamPackage.Set.max_elt (nvs %% Lazy.force st.available_packages) - with Not_found -> - OpamPackage.Set.max_elt nvs - in - let opam = get_opam st choose in - OpamFile.OPAM.print_errors opam; - if where then - OpamConsole.msg "%s\n" - (match OpamFile.OPAM.metadata_dir opam with - | Some dir -> - OpamFilename.Dir.to_string OpamFilename.Op.(dir / "opam") - | None -> "") - else if raw_opam then - OpamFile.OPAM.write_to_channel stdout opam + if all_versions then + (header (OpamPackage.Set.max_elt nvs); + OpamPackage.Set.iter output_package nvs) else - match fields with - | [] -> - OpamConsole.header_msg "%s: information on all versions" - (OpamPackage.Name.to_string choose.name); - output_table all_versions_fields choose; - OpamConsole.header_msg "Version-specific details"; - output_table one_version_fields choose - | [f] -> OpamConsole.msg "%s\n" (detail_printer ?normalise st choose f) - | fields -> output_table fields choose - ) + (let choose = + try OpamPackage.Set.choose (nvs %% st.pinned) with Not_found -> + try OpamPackage.Set.choose (nvs %% st.installed) with Not_found -> + try OpamPackage.Set.max_elt (nvs %% Lazy.force st.available_packages) + with Not_found -> + OpamPackage.Set.max_elt nvs + in + header choose; + output_package choose) + ) atoms diff -Nru opam-2.0.10/src/client/opamListCommand.mli opam-2.1.2/src/client/opamListCommand.mli --- opam-2.0.10/src/client/opamListCommand.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamListCommand.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -11,6 +11,7 @@ (** Functions handling the "opam list" subcommand *) +open OpamParserTypes.FullPos open OpamTypes open OpamStateTypes @@ -69,10 +70,10 @@ val pattern_selector: string list -> selector OpamFormula.formula (** Get the aggregated active external dependencies of the given packages *) -val get_depexts: 'a switch_state -> package_set -> OpamStd.String.Set.t +val get_depexts: 'a switch_state -> package_set -> OpamSysPkg.Set.t (** Lists the given aggregated active external dependencies of the given packages *) -val print_depexts: OpamStd.String.Set.t -> unit +val print_depexts: OpamSysPkg.Set.t -> unit (** Element of package information to be printed. Fixme: should be part of the run-time man! *) @@ -84,6 +85,7 @@ | Synopsis_or_target (** Pinning target if pinned, synopsis otherwise *) | Description (** The package description, excluding synopsis *) | Field of string (** The value of the given opam-file field *) + | Raw_field of string (** The raw value of the given opam-file field *) | Installed_version (** Installed version or "--" if none *) | Pinning_target (** Empty string if not pinned *) | Source_hash (** The VC-reported ident of current version, for dev @@ -108,14 +110,19 @@ (** Gets either the current switch state, if a switch is selected, or a virtual state corresponding to the configured repos *) -val get_switch_state: 'a global_state -> unlocked switch_state +val get_switch_state: 'a global_state -> 'a repos_state -> unlocked switch_state -(** For documentation, includes a dummy ':' for the [Field] format *) +(** For documentation, includes a dummy ':' for the [Field] format. + Used for the --columns argument. *) +val raw_field_names: (output_format * string) list + +(** For documentation, includes a dummy ':' and '' for the + [Field] format. Used for the --field argument. *) val field_names: (output_format * string) list -val string_of_field: output_format -> string +val string_of_field: ?raw:bool -> output_format -> string -val field_of_string: string -> output_format +val field_of_string: raw:bool -> string -> output_format type package_listing_format = { short: bool; @@ -139,8 +146,8 @@ (** Display a general summary of a collection of packages. *) val info: 'a switch_state -> - fields:string list -> raw_opam:bool -> where:bool -> - ?normalise:bool -> ?show_empty:bool -> + fields:string list -> raw:bool -> where:bool -> + ?normalise:bool -> ?show_empty:bool -> ?all_versions:bool -> ?sort:bool -> atom list -> unit (** Prints the value of an opam field in a shortened way (with [prettify] -- the diff -Nru opam-2.0.10/src/client/opamLockCommand.ml opam-2.1.2/src/client/opamLockCommand.ml --- opam-2.0.10/src/client/opamLockCommand.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamLockCommand.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,300 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamTypes + + +let select_packages atom_locs st = + let st, atoms = + OpamAuxCommands.simulate_autopin ~quiet:true ~for_view:true st atom_locs + in + let packages = + OpamFormula.packages_of_atoms OpamPackage.Set.Op.(st.packages ++ st.installed) atoms + in + if OpamPackage.Set.is_empty packages then + OpamConsole.error_and_exit `Not_found "No package matching %s" + (OpamFormula.string_of_atoms atoms) + else + (let names = OpamPackage.names_of_packages packages in + let missing = + OpamStd.List.filter_map (fun (n,vc) -> + if OpamPackage.Name.Set.mem n names then None + else Some (n,vc)) atoms + in + if missing <> [] then + OpamConsole.error "No package matching %s" + (OpamFormula.string_of_atoms missing); + (* we keep only one version of each package, the pinned or installed one, + the latest version otherwise ; and the one that have their dependencies \ + installed *) + let packages = + OpamPackage.Name.Set.fold (fun name acc -> + let pkgs = OpamPackage.packages_of_name packages name in + let pkg, is_pinned = + let open OpamPackage.Set.Op in + let pinned = pkgs %% st.pinned in + if OpamPackage.Set.is_empty pinned then + pkgs %% st.installed, false + else pinned, true + in + let nv = + match OpamPackage.Set.elements pkg with + | [nv] -> nv + | _ -> + (let nv = OpamPackage.Set.max_elt pkgs in + OpamConsole.note "Package %s is not installed nor pinned, generating lock \ + file for its latest version %s" + (OpamConsole.colorise `underline (OpamPackage.Name.to_string name)) + (OpamConsole.colorise `underline (OpamPackage.version_to_string nv)); + nv) + in + let opam = + if is_pinned then + let open OpamStd.Option.Op in + match + OpamSwitchState.opam st nv + |> OpamFile.OPAM.url + >>| OpamFile.URL.url + >>= OpamUrl.local_dir + >>= OpamPinned.find_opam_file_in_source name + >>| OpamFile.OPAM.read + with + | Some opam -> + (* we add the name/version because of an [OpamFile.OPAM.package] in all depends *) + let opam = + if OpamFile.OPAM.name_opt opam = None then + OpamFile.OPAM.with_name name opam + else opam + in + if OpamFile.OPAM.version_opt opam = None then + OpamFile.OPAM.with_version (OpamPackage.version nv) opam + else opam + | None -> OpamSwitchState.opam st nv + else OpamSwitchState.opam st nv + in + let atoms = + OpamFormula.atoms + (OpamPackageVar.all_depends ~depopts:false st opam) + in + let missing = + List.filter (fun (n,vc) -> + let pkgs = (OpamPackage.packages_of_name st.installed n) in + OpamPackage.Set.is_empty pkgs || + OpamPackage.Set.fold (fun nv satisf -> + satisf || not (OpamFormula.check (n,vc) nv)) + pkgs false) + atoms + in + if missing <> [] then + (OpamConsole.error + "Skipping %s, dependencies are not satisfied in this switch, \ + not installed packages are:\n%s" + (OpamPackage.to_string nv) + (OpamStd.Format.itemize OpamFormula.string_of_atom missing); + acc) + else + OpamPackage.Set.add nv acc) + names OpamPackage.Set.empty + in + st, packages) + +let get_git_url url nv dir = + let module VCS = + (val OpamRepository.find_backend_by_kind url.OpamUrl.backend) + in + let open OpamProcess.Job.Op in + OpamProcess.Job.run @@ + VCS.get_remote_url ?hash:url.OpamUrl.hash dir @@| function + | Some u -> + (if u.OpamUrl.hash = None then + OpamConsole.warning + "Referenced git branch for %s is not available in remote: %s, \ + use default branch instead." + (OpamConsole.colorise `underline (OpamPackage.to_string nv)) + (OpamUrl.to_string u); + Some u) + | _ -> + (OpamConsole.error "Can't retrieve remote informations for %s" + (OpamPackage.to_string nv); + None) + +let lock_opam ?(only_direct=false) st opam = + let nv = OpamFile.OPAM.package opam in + let univ = + OpamSwitchState.universe st + ~requested:(OpamPackage.Name.Set.singleton nv.name) + Query + in + (* Depends *) + let all_depends = + OpamSolver.dependencies + ~depopts:true ~build:true ~post:true ~installed:true + univ (OpamPackage.Set.singleton nv) |> + OpamPackage.Set.remove nv + in + let depends = + if only_direct then + let names = + OpamFilter.filter_formula ~default:true (fun _ -> None) + (OpamFile.OPAM.depends opam) |> + OpamFormula.fold_left (fun acc (n,_) -> OpamPackage.Name.Set.add n acc) + OpamPackage.Name.Set.empty + in + OpamPackage.packages_of_names all_depends names + else all_depends + in + let map_of_set x set = + OpamPackage.Map.of_list (List.map (fun nv -> nv, x) + (OpamPackage.Set.elements set)) + in + let depends_map = map_of_set `version depends in + (* others: dev, test, doc *) + let open OpamPackage.Set.Op in + let select ?(build=false) ?(test=false) ?(doc=false) ?(dev=false) + ?(default=false) ?(post=false) () = + OpamFormula.packages st.packages + (OpamFilter.filter_deps + ~build ~test ~doc ~dev ~default ~post + (OpamFile.OPAM.depends opam)) + in + let default = select () in + let select_depends typ selection = + let depends = selection -- default in + let installed = depends %% st.installed in + let uninstalled = + OpamPackage.(Name.Set.diff + (names_of_packages depends) + (names_of_packages installed)) + in + if OpamPackage.Name.Set.is_empty uninstalled then + let depends_map = map_of_set `other installed in + if only_direct then depends_map + else + (OpamSolver.dependencies + ~depopts:false ~build:true ~post:true ~installed:true + univ installed + -- all_depends) + |> map_of_set (`other_dep typ) + |> OpamPackage.Map.union (fun _v _o -> `other_dep typ) depends_map + else + (OpamConsole.msg "Not all dependencies are satisfied, won't include them\n"; + OpamPackage.Map.empty) + in + (* variables are set here as a string *) + let dev_depends_map = + select_depends "dev" (select ~dev:true ~build:true () -- select ~build:true ()) + in + let test_depends_map = select_depends "with-test" (select ~test:true ()) in + let doc_depends_map = select_depends "with-doc" (select ~doc:true ()) in + let depends = + let f a b = + match a,b with + | _, (`other_dep _ as ot) + | (`other_dep _ as ot), _ -> ot + | _, `other + | `other, _ -> `other + | `version, `version -> `version + in + OpamPackage.Map.( + depends_map + |> union f dev_depends_map + |> union f test_depends_map + |> union f doc_depends_map + ) + in + (* formulas *) + let filters = + OpamFormula.fold_left (fun acc form -> + let n, vc = form in + OpamPackage.Name.Map.add n vc acc) + OpamPackage.Name.Map.empty (OpamFile.OPAM.depends opam) + in + let depends_formula = + OpamFormula.ands + (List.rev (OpamPackage.Map.fold (fun nv cst acc -> + let filter = + let cst_v = + Atom ( + Constraint (`Eq, FString (OpamPackage.version_to_string nv))) + in + match cst with + | `version -> cst_v + | `other_dep typ -> + And (cst_v, + Atom (Filter + (FIdent ([], + OpamVariable.of_string typ, None)))) + | `other -> + let orig_deps_formula = + OpamPackage.Name.Map.find (OpamPackage.name nv) filters + in + let new_formula = + OpamFormula.map (function + | Constraint _ -> cst_v + | Filter _ as f -> Atom f + ) orig_deps_formula + in + if new_formula = orig_deps_formula then + And (cst_v, new_formula) + else new_formula + in + Atom (nv.name, filter)::acc) + depends [])) + in + (* keep installed depopts in depends and set as conflicting uninstalled ones *) + let all_depopts = + OpamFormula.packages st.packages + (OpamFilter.filter_deps + ~build:true ~test:true ~doc:true ~dev:true ~default:true ~post:false + (OpamFile.OPAM.depopts opam)) + in + let installed_depopts = OpamPackage.Set.inter all_depopts st.installed in + let uninstalled_depopts = + OpamPackage.(Name.Set.diff + (names_of_packages all_depopts) + (names_of_packages installed_depopts)) + in + let conflicts = + OpamFormula.ors + (OpamFile.OPAM.conflicts opam :: + List.map (fun n -> Atom (n, Empty)) + (OpamPackage.Name.Set.elements uninstalled_depopts)) + in + let pin_depends = + OpamPackage.Set.fold (fun nv acc -> + if not (OpamSwitchState.is_pinned st nv.name) then acc else + match OpamSwitchState.primary_url st nv with + | None -> acc + | Some u -> + match OpamUrl.local_dir u with + | Some d -> + let local_warn () = + OpamConsole.warning "Dependency %s is pinned to local target %s" + (OpamPackage.to_string nv) (OpamUrl.to_string u); + acc + in + (match u.OpamUrl.backend with + | #OpamUrl.version_control -> + (match get_git_url u nv d with + | Some resolved_u -> + OpamConsole.note "Local pin %s resolved to %s" + (OpamUrl.to_string u) (OpamUrl.to_string resolved_u); + (nv, resolved_u) :: acc + | None -> local_warn ()) + | _ -> local_warn ()) + | None -> (nv, u) :: acc) + all_depends [] + |> List.rev + in + opam |> + OpamFile.OPAM.with_depopts OpamFormula.Empty |> + OpamFile.OPAM.with_depends depends_formula |> + OpamFile.OPAM.with_conflicts conflicts |> + OpamFile.OPAM.with_pin_depends pin_depends diff -Nru opam-2.0.10/src/client/opamLockCommand.mli opam-2.1.2/src/client/opamLockCommand.mli --- opam-2.0.10/src/client/opamLockCommand.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/client/opamLockCommand.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,25 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + + +(** Functions handling the "opam lock" command *) + +open OpamTypes +open OpamStateTypes + +(** Select packages to lock. If a package have at least one of its direct + dependencies not installed in the switch, it is dropped. Returns the state + with non present packages pinned, and kept packages. *) +val select_packages: + [ `Atom of atom | `Filename of filename | `Dirname of dirname ] list -> + 'a switch_state -> 'a switch_state * package_set + +(** Returns the locked opam file, according its depends, depopts, and pins. *) +val lock_opam: ?only_direct:bool -> 'a switch_state -> OpamFile.OPAM.t -> OpamFile.OPAM.t diff -Nru opam-2.0.10/src/client/opamMain.ml opam-2.1.2/src/client/opamMain.ml --- opam-2.0.10/src/client/opamMain.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamMain.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,237 +9,4 @@ (* *) (**************************************************************************) -open Cmdliner -open OpamTypes -open OpamStateTypes -open OpamTypesBase -open OpamStd.Op - -(* Handle git-like plugins *) -let check_and_run_external_commands () = - let plugin_prefix = "opam-" in - match Array.to_list Sys.argv with - | [] | [_] -> () - | _ :: ("-y" | "--yes") :: name :: args - | _ :: name :: args -> - if - not (OpamStd.String.starts_with ~prefix:"-" name) - && List.for_all (fun (_,info) -> - not (OpamStd.String.starts_with ~prefix:name (Term.name info))) - OpamCommands.commands - then - (* No such command, check if there is a matching plugin *) - let command = plugin_prefix ^ name in - let answer = match Sys.argv.(1) with - | "-y" | "--yes" -> Some true - | _ -> OpamStd.Config.env_bool "YES" - in - OpamStd.Config.init ~answer (); - OpamFormatConfig.init (); - let root_dir = OpamStateConfig.opamroot () in - let has_init = OpamStateConfig.load_defaults root_dir <> None in - let plugins_bin = OpamPath.plugins_bin root_dir in - let env = - if has_init then - let updates = - ["PATH", PlusEq, OpamFilename.Dir.to_string plugins_bin, None] - in - OpamStateConfig.init ~root_dir (); - match OpamStateConfig.get_switch_opt () with - | None -> env_array (OpamEnv.get_pure ~updates ()) - | Some sw -> - env_array - (OpamEnv.full_with_path ~force_path:false ~updates root_dir sw) - else - Unix.environment () - in - match OpamSystem.resolve_command ~env command with - | Some command -> - let argv = Array.of_list (command :: args) in - raise (OpamStd.Sys.Exec (command, argv, env)) - | None when not has_init -> () - | None -> - (* Look for a corresponding package *) - match OpamStateConfig.get_switch_opt () with - | None -> () - | Some sw -> - OpamGlobalState.with_ `Lock_none @@ fun gt -> - OpamSwitchState.with_ `Lock_none gt ~switch:sw @@ fun st -> - let prefixed_name = plugin_prefix ^ name in - let candidates = - OpamPackage.packages_of_names - (Lazy.force st.available_packages) - (OpamPackage.Name.Set.of_list @@ - (OpamStd.List.filter_map - (fun s -> - try Some (OpamPackage.Name.of_string s) - with Failure _ -> None) - [ prefixed_name; name ])) - in - let plugins = - OpamPackage.Set.filter (fun nv -> - OpamFile.OPAM.has_flag Pkgflag_Plugin (OpamSwitchState.opam st nv)) - candidates - in - let installed = OpamPackage.Set.inter plugins st.installed in - if OpamPackage.Set.is_empty candidates then () - else if not OpamPackage.Set.(is_empty installed) then - (OpamConsole.error - "Plugin %s is already installed, but no %s command was found.\n\ - Try upgrading, and report to the package maintainer if \ - the problem persists." - (OpamPackage.to_string (OpamPackage.Set.choose installed)) - command; - exit (OpamStd.Sys.get_exit_code `Package_operation_error)) - else if OpamPackage.Set.is_empty plugins then - (OpamConsole.error - "%s is not a known command or plugin (package %s does \ - not have the 'plugin' flag set)." - name - (OpamPackage.to_string (OpamPackage.Set.max_elt candidates)); - exit (OpamStd.Sys.get_exit_code `Bad_arguments)) - else if - OpamConsole.confirm "Opam plugin \"%s\" is not installed. \ - Install it on the current switch?" - name - then - let nv = - try - OpamPackage.max_version plugins - (OpamPackage.Name.of_string prefixed_name) - with Not_found -> - OpamPackage.max_version plugins - (OpamPackage.Name.of_string name) - in - OpamRepositoryConfig.init (); - OpamSolverConfig.init (); - OpamClientConfig.init (); - OpamSwitchState.with_ `Lock_write gt (fun st -> - ignore @@ - OpamClient.install st [OpamSolution.eq_atom_of_package nv] - ); - match OpamSystem.resolve_command ~env command with - | None -> - OpamConsole.error_and_exit `Package_operation_error - "Plugin %s was installed, but no %s command was found.\n\ - This is probably an error in the plugin package." - (OpamPackage.to_string nv) - command - | Some command -> - OpamConsole.header_msg "Carrying on to \"%s\"" - (String.concat " " (Array.to_list Sys.argv)); - OpamConsole.msg "\n"; - let argv = Array.of_list (command :: args) in - raise (OpamStd.Sys.Exec (command, argv, env)) - -let rec main_catch_all f = - try f () with - | OpamStd.Sys.Exit 0 -> () - | OpamStd.Sys.Exec (cmd,args,env) -> - OpamStd.Sys.exec_at_exit (); - Unix.execvpe cmd args env - | OpamFormatUpgrade.Upgrade_done conf -> - main_catch_all @@ fun () -> - OpamConsole.header_msg "Rerunning init and update"; - OpamClient.reinit ~interactive:true ~update_config:false conf - (OpamStd.Sys.guess_shell_compat ()); - OpamConsole.msg - "Update done, please now retry your command.\n"; - exit (OpamStd.Sys.get_exit_code `Aborted) - | e -> - flush stdout; - flush stderr; - if (OpamConsole.verbose ()) then - OpamConsole.errmsg "'%s' failed.\n" - (String.concat " " (Array.to_list Sys.argv)); - let exit_code = match e with - | OpamStd.Sys.Exit i -> - if (OpamConsole.debug ()) && i <> 0 then - OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); - i - | OpamSystem.Internal_error _ -> - OpamConsole.errmsg "%s\n" (Printexc.to_string e); - OpamStd.Sys.get_exit_code `Internal_error - | OpamSystem.Process_error result -> - OpamConsole.errmsg "%s Command %S failed:\n%s\n" - (OpamConsole.colorise `red "[ERROR]") - (try List.assoc "command" result.OpamProcess.r_info with - | Not_found -> "") - (Printexc.to_string e); - OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); - OpamStd.Sys.get_exit_code `Internal_error - | Sys.Break - | OpamParallel.Errors (_, (_, Sys.Break)::_, _) -> - OpamStd.Sys.get_exit_code `User_interrupt - | Sys_error e when e = "Broken pipe" -> - (* workaround warning 52, this is a fallback (we already handle the - signal) and there is no way around at the moment *) - 141 - | Failure msg -> - OpamConsole.errmsg "Fatal error: %s\n" msg; - OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); - OpamStd.Sys.get_exit_code `Internal_error - | _ -> - OpamConsole.errmsg "Fatal error:\n%s\n" (Printexc.to_string e); - OpamConsole.errmsg "%s" (OpamStd.Exn.pretty_backtrace e); - OpamStd.Sys.get_exit_code `Internal_error - in - exit exit_code - -let run default commands = - OpamStd.Option.iter OpamVersion.set_git OpamGitVersion.version; - OpamSystem.init (); - main_catch_all @@ fun () -> - check_and_run_external_commands (); - let admin, argv1 = - if Array.length Sys.argv > 1 && Sys.argv.(1) = "admin" then - true, - Array.init (Array.length Sys.argv - 1) (function - | 0 -> Sys.argv.(0) - | i -> Sys.argv.(i+1)) - else false, Sys.argv - in - let eval () = - if admin then - Term.eval_choice ~catch:false ~argv:argv1 - OpamAdminCommand.default_subcommand OpamAdminCommand.admin_subcommands - else - Term.eval_choice ~catch:false ~argv:argv1 default commands - in - match eval () with - | `Error _ -> exit (OpamStd.Sys.get_exit_code `Bad_arguments) - | _ -> exit (OpamStd.Sys.get_exit_code `Success) - -let json_out () = - match OpamClientConfig.(!r.json_out) with - | None -> () - | Some s -> - let file_name () = - match OpamStd.String.cut_at s '%' with - | None -> OpamFilename.of_string s - | Some (pfx, sfx) -> - let rec getname i = - let f = OpamFilename.of_string (Printf.sprintf "%s%d%s" pfx i sfx) in - if OpamFilename.exists f then getname (i+1) else f - in - getname 1 - in - try - let f = OpamFilename.open_out (file_name ()) in - OpamJson.flush f; - close_out f - with e -> - OpamConsole.warning "Couldn't write json log: %s" - (Printexc.to_string e) - -let () = - OpamStd.Sys.at_exit (fun () -> - flush stderr; - flush stdout; - if OpamClientConfig.(!r.print_stats) then ( - OpamFile.Stats.print (); - OpamSystem.print_stats (); - ); - json_out () - ); - run OpamCommands.default OpamCommands.commands +let () = OpamCliMain.main () diff -Nru opam-2.0.10/src/client/opamMain.mli opam-2.1.2/src/client/opamMain.mli --- opam-2.0.10/src/client/opamMain.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamMain.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2017 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/client/opamManifest.inc.in opam-2.1.2/src/client/opamManifest.inc.in --- opam-2.0.10/src/client/opamManifest.inc.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamManifest.inc.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -(rule - (targets opamManifest.ml) - (deps @CONF_MANIFEST_O@) - (action (with-stdout-to %{targets} (echo "")))) diff -Nru opam-2.0.10/src/client/opam-mingw64.xmlf opam-2.1.2/src/client/opam-mingw64.xmlf --- opam-2.0.10/src/client/opam-mingw64.xmlf 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opam-mingw64.xmlf 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - - OCaml Package Manager - - - - - diff -Nru opam-2.0.10/src/client/opam-mingw.xmlf opam-2.1.2/src/client/opam-mingw.xmlf --- opam-2.0.10/src/client/opam-mingw.xmlf 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opam-mingw.xmlf 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - - OCaml Package Manager - - - - - diff -Nru opam-2.0.10/src/client/opamPinCommand.ml opam-2.1.2/src/client/opamPinCommand.ml --- opam-2.0.10/src/client/opamPinCommand.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamPinCommand.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -20,7 +20,10 @@ let bold = OpamConsole.colorise `bold in Printf.sprintf "pinned %s (version %s)" (OpamStd.Option.to_string ~none:(bold "locally") - (fun u -> "to " ^ (bold (OpamUrl.to_string (OpamFile.URL.url u)))) + (fun u -> Printf.sprintf "to %s%s" + (bold (OpamUrl.to_string (OpamFile.URL.url u))) + (OpamStd.Option.map_default (fun s -> bold (" ("^s^")")) + "" (OpamFile.URL.subpath u))) (OpamFile.OPAM.url opam)) (bold (OpamPackage.Version.to_string (OpamFile.OPAM.version opam))) @@ -29,9 +32,11 @@ let dir = OpamFilename.dirname (OpamFile.filename f) in (* don't add aux files for [project/opam] *) let add_files = OpamUrl.local_dir url = Some dir in - OpamStd.Option.map + let opam = (OpamFormatUpgrade.opam_file_with_aux ~quiet ~dir ~files:add_files - ~filename:f) (OpamFile.OPAM.read_opt f) + ~filename:f) (OpamFile.OPAM.safe_read f) + in + if opam = OpamFile.OPAM.empty then None else Some opam in (match opam0 with | None -> @@ -54,7 +59,7 @@ exception Fetch_Fail of string -let get_source_definition ?version st nv url = +let get_source_definition ?version ?subpath ?locked st nv url = let root = st.switch_global.root in let srcdir = OpamPath.Switch.pinned_package root st.switch nv.name in let fix opam = @@ -65,10 +70,23 @@ opam in let open OpamProcess.Job.Op in - OpamUpdate.fetch_dev_package url srcdir nv @@| function + let url = + let u = OpamFile.URL.url url in + match OpamUrl.local_dir u, u.OpamUrl.backend with + | Some dir, #OpamUrl.version_control -> + OpamFile.URL.with_url + (OpamUrl.of_string (OpamFilename.Dir.to_string dir)) + url + | _, _ -> url + in + OpamUpdate.fetch_dev_package url srcdir ?subpath nv @@| function | Not_available (_,s) -> raise (Fetch_Fail s) | Up_to_date _ | Result _ -> - match OpamPinned.find_opam_file_in_source nv.name srcdir with + let subsrcdir = + match OpamFile.URL.subpath url with + | None -> srcdir + | Some subpath -> OpamFilename.Op.(srcdir / subpath) in + match OpamPinned.find_opam_file_in_source ?locked nv.name subsrcdir with | None -> None | Some f -> match read_opam_file_for_pinning nv.name f (OpamFile.URL.url url) with @@ -83,14 +101,18 @@ let copy_files st opam = let name = OpamFile.OPAM.name opam in - let files = OpamFile.OPAM.get_extra_files opam in + let files = + OpamFile.OPAM.get_extra_files + ~repos_roots:(OpamRepositoryState.get_root st.switch_repos) + opam + in if files = [] then (match OpamFile.OPAM.extra_files opam with | Some [] | None -> () | Some files -> OpamConsole.warning - "Ignoring overlay files of %s (files/*) that were not found: %s" - (OpamPackage.Name.to_string name) + "Ignoring overlay files of %s (files%s*) that were not found: %s" + (OpamPackage.Name.to_string name) Filename.dir_sep (OpamStd.List.to_string (fun (b,_) -> OpamFilename.Base.to_string b) files)); let destdir = @@ -225,7 +247,7 @@ | Some o -> OpamFile.OPAM.with_version new_nv.version o in OpamFile.OPAM.write_with_preserved_format - ?format_from:(OpamPinned.orig_opam_file name base_opam) + ?format_from:(OpamPinned.orig_opam_file st name base_opam) temp_file base_opam); match edit_raw name temp_file with | None -> st @@ -289,7 +311,8 @@ let nv = OpamPackage.create name (OpamFile.OPAM.version opam) in let st = OpamSwitchState.update_pin nv opam st in OpamUpdate.cleanup_source st current_opam opam; - OpamSwitchAction.write_selections st; + if not OpamClientConfig.(!r.show) then + OpamSwitchAction.write_selections st; st let version_pin st name version = @@ -329,7 +352,8 @@ | None -> () end; let st = OpamSwitchState.update_pin nv repo_opam st in - OpamSwitchAction.write_selections st; + if not OpamClientConfig.(!r.show) then + OpamSwitchAction.write_selections st; OpamConsole.msg "%s is now pinned to version %s\n" (OpamPackage.Name.to_string name) (OpamPackage.Version.to_string version); @@ -342,6 +366,45 @@ try OpamPackage.version (OpamSwitchState.get_package st name) with Not_found -> OpamPackage.Version.of_string "~dev" +let fetch_all_pins st ?working_dir pins = + let root = st.switch_global.root in + let fetched = + let cache_dir = + OpamRepositoryPath.download_cache OpamStateConfig.(!r.root_dir) + in + let command (name, url, subpath) = + let srcdir = OpamPath.Switch.pinned_package root st.switch name in + let name = OpamPackage.Name.to_string name in + OpamProcess.Job.Op.( + OpamRepository.pull_tree ~cache_dir ?subpath ?working_dir + name srcdir [] [url] + @@| fun r -> (name, url, subpath, r)) + in + OpamParallel.map ~jobs:OpamStateConfig.(!r.dl_jobs) ~command pins + in + let errored, to_pin = + List.fold_left (fun (err,ok) result -> + let name, url, subpath, result = result in + match result with + | Not_available _ -> (* clean dir ? *) + (name, url, subpath)::err, ok + | _ -> err, (url, subpath)::ok) + ([],[]) fetched + in + if errored = [] + || OpamConsole.confirm + "Could not retrieve some package sources, they will not be pinned nor \ + installed:%s\n\ + Continue anyway?" + (OpamStd.Format.itemize (fun (name, url, subpath) -> + name ^ ": " ^ OpamUrl.to_string url ^ + (OpamStd.Option.to_string (fun s -> "("^s^")") subpath)) + errored) + then + to_pin + else + OpamStd.Sys.exit_because `Aborted + let rec handle_pin_depends st nv opam = let extra_pins = OpamFile.OPAM.pin_depends opam in let extra_pins = @@ -359,24 +422,43 @@ (OpamConsole.colorise `bold (OpamPackage.to_string nv)) (OpamConsole.colorise `underline (OpamUrl.to_string url))) extra_pins); - if not (OpamConsole.confirm "Continue?") then - (OpamConsole.msg "You can specify --ignore-pin-depends to bypass\n"; - OpamStd.Sys.exit_because `Aborted); - List.fold_left (fun st (nv, url) -> - source_pin st nv.name ~version:nv.version (Some url) - ~ignore_extra_pins:true) - st extra_pins) + if OpamConsole.confirm "Pin and install them?" then + (let extra_pins = + let urls_ok = + fetch_all_pins st (List.map (fun (nv, u) -> + OpamPackage.name nv, u, None) extra_pins) + in + List.filter (fun (_, url) -> List.mem (url, None) urls_ok) extra_pins + in + List.fold_left (fun st (nv, url) -> + source_pin st nv.name ~version:nv.version (Some url) + ~ignore_extra_pins:true) + st extra_pins) + else if OpamConsole.confirm + "Try to install anyway, assuming `--ignore-pin-depends'?" + then st else + OpamStd.Sys.exit_because `Aborted) and source_pin st name ?version ?edit:(need_edit=false) ?opam:opam_opt ?(quiet=false) ?(force=false) ?(ignore_extra_pins=OpamClientConfig.(!r.ignore_pin_depends)) + ?subpath ?locked target_url = - log "pin %a to %a %a" + log "pin %a to %a %a%a" (slog OpamPackage.Name.to_string) name (slog (OpamStd.Option.to_string OpamPackage.Version.to_string)) version - (slog (OpamStd.Option.to_string ~none:"none" OpamUrl.to_string)) target_url; + (slog (OpamStd.Option.to_string ~none:"none" OpamUrl.to_string)) target_url + (slog (OpamStd.Option.to_string ~none:"" (fun x -> " ("^x^")"))) subpath; + (* let installed_version = + try + Some (OpamPackage.version + (OpamSwitchState.find_installed_package_by_name st name)) + with Not_found -> None + in *) + + let open OpamStd.Option.Op in let cur_version, cur_urlf = try @@ -403,20 +485,11 @@ (* else raise Exns.Aborted *); cur_version, cur_urlf with Not_found -> - if OpamPackage.has_name st.compiler_packages name then ( - OpamConsole.warning - "Package %s is part of the base packages of this compiler." - (OpamPackage.Name.to_string name); - if not @@ OpamConsole.confirm - "Are you sure you want to override this and pin it anyway?" - then raise Aborted - ); let version = default_version st name in version, None in if not (OpamPackage.has_name st.packages name) && - opam_opt = None && not (OpamConsole.confirm "Package %s does not exist, create as a %s package?" (OpamPackage.Name.to_string name) @@ -433,11 +506,11 @@ (OpamPath.Switch.pinned_package st.switch_global.root st.switch name) | _ -> ()); - let pin_version = OpamStd.Option.Op.(version +! cur_version) in + let pin_version = version +! cur_version in let nv = OpamPackage.create name pin_version in - let urlf = OpamStd.Option.Op.(target_url >>| OpamFile.URL.create) in + let urlf = target_url >>| OpamFile.URL.create ?subpath in let temp_file = OpamPath.Switch.Overlay.tmp_opam st.switch_global.root st.switch name @@ -445,17 +518,15 @@ let opam_local = OpamFile.OPAM.read_opt temp_file - |> OpamStd.Option.map (OpamFormatUpgrade.opam_file) + >>| OpamFormatUpgrade.opam_file in OpamFilename.remove (OpamFile.filename temp_file); let opam_opt = try - OpamStd.Option.Op.( - opam_opt >>+ fun () -> - urlf >>= fun url -> - OpamProcess.Job.run @@ get_source_definition ?version st nv url - ) + opam_opt >>+ fun () -> + urlf >>= fun url -> + OpamProcess.Job.run @@ get_source_definition ?version ?subpath ?locked st nv url with Fetch_Fail err -> if force then None else (OpamConsole.error_and_exit `Sync_error @@ -463,21 +534,21 @@ (OpamStd.Option.to_string OpamUrl.to_string target_url) (OpamStd.Format.itemize (fun x -> x) [err])); in + let opam_opt = opam_opt >>| OpamFormatUpgrade.opam_file in let nv = match version with | Some _ -> nv | None -> - OpamPackage.create name OpamStd.Option.Op.( - (opam_opt >>= OpamFile.OPAM.version_opt) - +! cur_version) + OpamPackage.create name + ((opam_opt >>= OpamFile.OPAM.version_opt) + +! cur_version) in let opam_opt = - OpamStd.Option.Op.( - opam_opt >>+ fun () -> - OpamPackage.Map.find_opt nv st.installed_opams >>+ fun () -> - OpamSwitchState.opam_opt st nv) + opam_opt >>+ fun () -> + OpamPackage.Map.find_opt nv st.installed_opams >>+ fun () -> + OpamSwitchState.opam_opt st nv in let opam_opt = @@ -516,13 +587,12 @@ if need_edit then (if not (OpamFile.exists temp_file) then OpamFile.OPAM.write_with_preserved_format - ?format_from:(OpamPinned.orig_opam_file name opam_base) + ?format_from:(OpamPinned.orig_opam_file st name opam_base) temp_file opam_base; - OpamStd.Option.Op.( - edit_raw name temp_file >>| - (* Preserve metadata_dir so that copy_files below works *) - OpamFile.OPAM.(with_metadata_dir (metadata_dir opam_base)) - )) + edit_raw name temp_file >>| + (* Preserve metadata_dir so that copy_files below works *) + OpamFile.OPAM.(with_metadata_dir (metadata_dir opam_base)) + ) else Some opam_base in @@ -536,9 +606,7 @@ | Some _ -> opam | None -> OpamFile.OPAM.with_url_opt urlf opam in - let version = - OpamStd.Option.Op.(OpamFile.OPAM.version_opt opam +! nv.version) - in + let version = version +! (OpamFile.OPAM.version_opt opam +! nv.version) in let nv = OpamPackage.create nv.name version in let st = if ignore_extra_pins then st @@ -555,7 +623,7 @@ let opam = copy_files st opam in OpamFile.OPAM.write_with_preserved_format - ?format_from:(OpamPinned.orig_opam_file name opam) + ?format_from:(OpamPinned.orig_opam_file st name opam) (OpamPath.Switch.Overlay.opam st.switch_global.root st.switch nv.name) opam; @@ -563,7 +631,8 @@ let st = OpamSwitchState.update_pin nv opam st in - OpamSwitchAction.write_selections st; + if not OpamClientConfig.(!r.show) then + OpamSwitchAction.write_selections st; OpamConsole.msg "%s is now %s\n" (OpamPackage.Name.to_string name) (string_of_pinned opam); @@ -611,7 +680,8 @@ string_of_pinned (OpamSwitchState.opam_opt st nv) in let st = unpin_one st nv in - OpamSwitchAction.write_selections st; + if not OpamClientConfig.(!r.show) then + OpamSwitchAction.write_selections st; OpamConsole.msg "Ok, %s is no longer %s\n" (OpamPackage.Name.to_string name) pin_str; st @@ -630,14 +700,20 @@ let lines nv = try let opam = OpamSwitchState.opam st nv in - let url = OpamFile.OPAM.get_url opam in + let url = OpamFile.OPAM.url opam in let kind, target = if OpamSwitchState.is_version_pinned st nv.name then "version", OpamPackage.Version.to_string nv.version else match url with - | Some u -> - OpamUrl.string_of_backend u.OpamUrl.backend, OpamUrl.to_string u + | Some url -> + let u = OpamFile.URL.url url in + let subpath = + match OpamFile.URL.subpath url with + | None -> "" + | Some s -> " ("^s^")" in + OpamUrl.string_of_backend u.OpamUrl.backend, + OpamUrl.to_string u ^ subpath | None -> "local definition", "" in let state, extra = @@ -663,3 +739,115 @@ in let table = List.map lines (OpamPackage.Set.elements st.pinned) in OpamConsole.print_table stdout ~sep:" " (OpamStd.Format.align_table table) + +(* Must not be contained in a package name, version, nor url *) +let scan_sep = '^' + +let scan ~normalise ~recurse ?subpath url = + let open OpamStd.Option.Op in + let pins_of_dir dir = + OpamPinned.files_in_source ~recurse ?subpath dir + |> OpamStd.List.filter_map (fun (nf, opamf, sb) -> + let opam = OpamFile.OPAM.safe_read opamf in + match (nf ++ OpamFile.OPAM.name_opt opam) with + | Some name -> + Some (name, (OpamFile.OPAM.version_opt opam), sb) + | None -> + OpamConsole.warning "Can not retrieve a package name from %s" + (OpamFilename.to_string (OpamFile.filename opamf)); + None) + in + let pins, cleanup = + match OpamUrl.local_dir url with + | Some dir -> pins_of_dir dir, None + | None -> + let pin_cache_dir = OpamRepositoryPath.pin_cache url in + let cleanup = fun () -> + OpamFilename.rmdir @@ OpamRepositoryPath.pin_cache_dir () + in + let basename = + match OpamStd.String.split (OpamUrl.basename url) '.' with + | [] -> + OpamConsole.error_and_exit `Bad_arguments + "Can not retrieve a path from '%s'" + (OpamUrl.to_string url) + | b::_ -> b + in + try + let open OpamProcess.Job.Op in + OpamProcess.Job.run @@ + OpamRepository.pull_tree + ~cache_dir:(OpamRepositoryPath.download_cache + OpamStateConfig.(!r.root_dir)) + basename pin_cache_dir [] [url] @@| function + | Not_available (_,u) -> + OpamConsole.error_and_exit `Sync_error + "Could not retrieve %s" u + | Result _ | Up_to_date _ -> + pins_of_dir pin_cache_dir, Some cleanup + with e -> OpamStd.Exn.finalise e cleanup + in + let finalise = OpamStd.Option.default (fun () -> ()) cleanup in + OpamStd.Exn.finally finalise @@ fun () -> + if normalise then + OpamConsole.msg "%s" + (OpamStd.List.concat_map "\n" + (fun (name, version, sb) -> + Printf.sprintf "%s%s%c%s%s" + (OpamPackage.Name.to_string name) + (OpamStd.Option.to_string + (fun v -> "." ^OpamPackage.Version.to_string v) version) + scan_sep + (OpamUrl.to_string url) + (OpamStd.Option.to_string (fun sb -> + (String.make 1 scan_sep) ^ sb) sb)) + pins) + else + ["# Name"; "# Version"; "# Url" (*; "# Subpath"*)] :: + List.map (fun (name, version, _sb) -> + [ OpamPackage.Name.to_string name; + (version >>| OpamPackage.Version.to_string) +! "-"; + OpamUrl.to_string url; + (*sb +! "-"*) ]) pins + |> OpamStd.Format.align_table + |> OpamConsole.print_table stdout ~sep:" " + +let looks_like_normalised args = + List.for_all (fun s -> OpamStd.String.contains_char s scan_sep) args + +let parse_pins pins = + let separator = Re.char scan_sep in + let re = + Re.(compile @@ whole_string @@ seq [ + (* package name *) + group @@ + rep1 @@ alt [ alnum; diff punct (alt [char '.'; char scan_sep]) ]; + (* optional version *) + opt @@ seq [ char '.'; + group @@ + rep1 @@ alt [ alnum; diff punct separator ]]; + separator; + (* url *) + group @@ rep1 @@ diff any separator; + (* optional subpath *) + opt @@ seq [ separator; group @@ rep1 any ]; + ]) + in + let get s = + try + let groups = Re.exec re s in + Some ( Re.Group.( + OpamPackage.Name.of_string @@ get groups 1, + OpamStd.Option.map OpamPackage.Version.of_string + @@ OpamStd.Option.of_Not_found (get groups) 2, + OpamUrl.parse @@ get groups 3, + OpamStd.Option.of_Not_found (get groups) 4) + ) + with Not_found | Failure _ -> None + in + OpamStd.List.filter_map (fun str -> + let pin = get str in + if pin = None then + (OpamConsole.warning "Argument %S is not correct" str; + None) + else pin) pins diff -Nru opam-2.0.10/src/client/opamPinCommand.mli opam-2.1.2/src/client/opamPinCommand.mli --- opam-2.0.10/src/client/opamPinCommand.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamPinCommand.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -35,7 +35,7 @@ val source_pin: rw switch_state -> name -> ?version:version -> ?edit:bool -> ?opam:OpamFile.OPAM.t -> ?quiet:bool -> - ?force:bool -> ?ignore_extra_pins:bool -> + ?force:bool -> ?ignore_extra_pins:bool -> ?subpath: string -> ?locked:bool -> url option -> rw switch_state @@ -43,6 +43,14 @@ val handle_pin_depends: rw switch_state -> package -> OpamFile.OPAM.t -> rw switch_state +(** Fetch in parallel jobs a list of pins [name, url, subpath], and return the + successful ones. + Ask for confirmation to continue if a fetching fails. +*) +val fetch_all_pins: + 'a switch_state -> ?working_dir:bool -> (name * url * string option) list -> + (url * string option) list + (** Let the user edit a pinned package's opam file. If given, the version is put into the template in advance. Writes and returns the updated switch state. *) @@ -57,6 +65,21 @@ (** List the pinned packages to the user. *) val list: 'a switch_state -> short:bool -> unit +(** Scan pinning separator, used for printing and parsing by [scan] and + [parse_pins]. *) +val scan_sep: char + +(** Scan for available packages to pin, and display it on stdout. If + [normalise] is true, displays it's normalised format + `name.version[scan_sep]url[scan_sep]subpath`. *) +val scan: normalise:bool -> recurse:bool -> ?subpath: string -> url -> unit + +(** Detect if a string is a normalised format of [scan]. *) +val looks_like_normalised: string list -> bool + +(** Parse the normalised form of [scan], and returns pinning informations. *) +val parse_pins: string list -> (name * version option * url * string option) list + (** Lints the given opam file, prints warnings or errors accordingly (unless [quiet]), upgrades it to current format, adds references to files below the 'files/' subdir (unless the file is directly below the specified, local diff -Nru opam-2.0.10/src/client/opamRepositoryCommand.ml opam-2.1.2/src/client/opamRepositoryCommand.ml --- opam-2.0.10/src/client/opamRepositoryCommand.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamRepositoryCommand.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -64,38 +64,39 @@ -> rt | Some r -> OpamConsole.error_and_exit `Bad_arguments - "Repository %s is already set up %s. To change that, use 'opam \ - repository set-url'." + "Repository %s is already set up%s. To change that, use 'opam \ + repository set-url %s %s'." (OpamRepositoryName.to_string name) (if r.repo_url <> url then - "and points to "^OpamUrl.to_string r.repo_url + " and points to "^OpamUrl.to_string r.repo_url else match r.repo_trust with - | None -> "without trust anchors" + | None -> " without trust anchors" | Some ta -> - Printf.sprintf "with trust anchors %s and quorum %d" + Printf.sprintf " with trust anchors %s and quorum %d" (OpamStd.List.concat_map ~nil:"()" "," String.escaped ta.fingerprints) ta.quorum) + (OpamRepositoryName.to_string name) + (OpamUrl.to_string url) | None -> let repo = { repo_name = name; repo_url = url; - repo_root = OpamRepositoryPath.create root name; repo_trust = trust_anchors; } in - if OpamFilename.exists OpamFilename.(of_string (Dir.to_string repo.repo_root)) + if OpamFilename.exists_dir (OpamRepositoryPath.root root name) || + OpamFilename.exists (OpamRepositoryPath.tar root name) then OpamConsole.error_and_exit `Bad_arguments "Invalid repository name, %s exists" - (OpamFilename.Dir.to_string repo.repo_root); + (OpamFilename.Dir.to_string (OpamRepositoryPath.root root name)); if url.OpamUrl.backend = `rsync && OpamUrl.local_dir url <> None && - OpamUrl.local_dir (OpamRepositoryPath.Remote.packages_url repo.repo_url) + OpamUrl.local_dir (OpamRepositoryPath.Remote.packages_url url) = None && not (OpamConsole.confirm "%S doesn't contain a \"packages\" directory.\n\ Is it really the directory of your repo?" (OpamUrl.to_string url)) then OpamStd.Sys.exit_because `Aborted; - OpamProcess.Job.run (OpamRepository.init root name); update_repos_config rt (OpamRepositoryName.Map.add name repo rt.repositories) @@ -105,7 +106,8 @@ update_repos_config rt (OpamRepositoryName.Map.remove name rt.repositories) in OpamRepositoryState.Cache.save rt; - OpamFilename.rmdir (OpamRepositoryPath.create rt.repos_global.root name); + OpamFilename.rmdir (OpamRepositoryPath.root rt.repos_global.root name); + OpamFilename.remove (OpamRepositoryPath.tar rt.repos_global.root name); rt let set_url rt name url trust_anchors = @@ -116,8 +118,10 @@ OpamConsole.error_and_exit `Not_found "No repository %s found" (OpamRepositoryName.to_string name); in - OpamFilename.cleandir (OpamRepositoryPath.create rt.repos_global.root name); + OpamFilename.cleandir (OpamRepositoryPath.root rt.repos_global.root name); + OpamFilename.remove (OpamRepositoryPath.tar rt.repos_global.root name); let repo = { repo with repo_url = url; repo_trust = trust_anchors; } in + OpamRepositoryState.remove_from_repos_tmp rt name; update_repos_config rt (OpamRepositoryName.Map.add name repo rt.repositories) let print_selection rt ~short repos_list = @@ -171,20 +175,24 @@ rt.repositories else let repos_switches, _ = + let repos = OpamGlobalState.repos_list rt.repos_global in + let n_repos = List.length repos in List.fold_left (fun (acc,i) repo -> - OpamRepositoryName.Map.add repo [None, i] acc, + OpamRepositoryName.Map.add repo [None, (i, n_repos)] acc, i + 1) (OpamRepositoryName.Map.empty, 1) - (OpamGlobalState.repos_list rt.repos_global) + repos in let repos_switches = List.fold_left (fun acc sw -> + let repos = switch_repos rt sw in + let n_repos = List.length repos in let acc,_ = List.fold_left (fun (acc,i) repo -> OpamRepositoryName.Map.update repo - (fun s -> (Some sw, i)::s) [] acc, + (fun s -> (Some sw, (i, n_repos))::s) [] acc, i + 1) - (acc,1) (switch_repos rt sw) + (acc,1) repos in acc) repos_switches (OpamFile.Config.installed_switches rt.repos_global.config) @@ -198,10 +206,11 @@ OpamRepositoryName.to_string name |> OpamConsole.colorise `bold; OpamUrl.to_string repo.repo_url; OpamStd.List.concat_map " " - (fun (sw,i) -> + (fun (sw,(i, n)) -> OpamStd.Option.to_string ~none:"" OpamSwitch.to_string sw ^ - (Printf.sprintf "(%d)" i |> OpamConsole.colorise `yellow)) + (if n = 1 then "" else + Printf.sprintf "(%d/%d)" i n |> OpamConsole.colorise `yellow)) (List.rev (try OpamRepositoryName.Map.find name repos_switches with Not_found -> [])); ]) @@ -244,12 +253,26 @@ OpamRepositoryState.Cache.remove ()); OpamConsole.msg "Upgrading repository \"%s\"...\n" (OpamRepositoryName.to_string r.repo_name); - OpamAdminRepoUpgrade.do_upgrade r.repo_root; + let open OpamProcess.Job.Op in + let repo_root = OpamRepositoryState.get_repo_root rt r in + OpamAdminRepoUpgrade.do_upgrade repo_root; + OpamProcess.Job.run + (OpamFilename.make_tar_gz_job + (OpamRepositoryPath.tar rt.repos_global.root r.repo_name) + repo_root + @@| function + | Some e -> + Printf.ksprintf failwith + "Failed to regenerate local repository archive: %s" + (Printexc.to_string e) + | None -> ()); let def = - OpamFile.Repo.safe_read (OpamRepositoryPath.repo r.repo_root) |> + OpamFile.Repo.safe_read (OpamRepositoryPath.repo repo_root) |> OpamFile.Repo.with_root_url r.repo_url in - let opams = OpamRepositoryState.load_repo_opams r in + let opams = + OpamRepositoryState.load_opams_from_dir r.repo_name repo_root + in let rt = { rt with repos_definitions = diff -Nru opam-2.0.10/src/client/opamRepositoryCommand.mli opam-2.1.2/src/client/opamRepositoryCommand.mli --- opam-2.0.10/src/client/opamRepositoryCommand.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamRepositoryCommand.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -52,7 +52,8 @@ (** Update the given repositories, as per [OpamUpdate.repositories], checks for their version and runs the upgrade script locally if they are for an earlier - opam. Returns [true] if no update or upgrade errors were encountered. *) + opam. Returns list of repositories that failed and the new repository state. + *) val update_with_auto_upgrade: rw repos_state -> repository_name list -> repository_name list * rw repos_state diff -Nru opam-2.0.10/src/client/Opam.Runtime.amd64.manifest opam-2.1.2/src/client/Opam.Runtime.amd64.manifest --- opam-2.0.10/src/client/Opam.Runtime.amd64.manifest 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/Opam.Runtime.amd64.manifest 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - - - - - - - diff -Nru opam-2.0.10/src/client/Opam.Runtime.x86.manifest opam-2.1.2/src/client/Opam.Runtime.x86.manifest --- opam-2.0.10/src/client/Opam.Runtime.x86.manifest 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/Opam.Runtime.x86.manifest 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ - - - - - - - diff -Nru opam-2.0.10/src/client/opamSolution.ml opam-2.1.2/src/client/opamSolution.ml --- opam-2.0.10/src/client/opamSolution.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamSolution.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -19,9 +19,12 @@ module PackageAction = OpamSolver.Action module PackageActionGraph = OpamSolver.ActionGraph + +exception Fetch_fail of string + let post_message ?(failed=false) st action = match action, failed with - | `Remove _, _ | `Reinstall _, _ | `Build _, false -> () + | `Remove _, _ | `Reinstall _, _ | `Build _, false | `Fetch _, _ -> () | `Build pkg, true | `Install pkg, _ | `Change (_,_,pkg), _ -> let opam = OpamSwitchState.opam st pkg in let messages = OpamFile.OPAM.post_messages opam in @@ -56,40 +59,49 @@ ) let print_depexts_helper st actions = + if OpamFile.Config.depext st.switch_global.config then () else let depexts = List.fold_left (fun depexts -> function | `Build nv -> - OpamStd.String.Set.union depexts (OpamSwitchState.depexts st nv) + OpamSysPkg.Set.union depexts (OpamSwitchState.depexts st nv) | _ -> depexts) - OpamStd.String.Set.empty + OpamSysPkg.Set.empty actions in - if not (OpamStd.String.Set.is_empty depexts) then ( + if not (OpamSysPkg.Set.is_empty depexts) then ( OpamConsole.formatted_msg "\nThe packages you requested declare the following system dependencies. \ Please make sure they are installed before retrying:\n"; OpamConsole.formatted_msg ~indent:4 " %s\n\n" - (OpamStd.List.concat_map " " (OpamConsole.colorise `bold) - (OpamStd.String.Set.elements depexts)) + (OpamStd.List.concat_map " " (fun s -> + OpamConsole.colorise `bold (OpamSysPkg.to_string s)) + (OpamSysPkg.Set.elements depexts)) ) let check_solution ?(quiet=false) st = function - | No_solution -> + | Conflicts _ -> OpamConsole.msg "No solution found, exiting\n"; OpamStd.Sys.exit_because `No_solution - | Partial_error (success, failed, _remaining) -> - List.iter (post_message st) success; - List.iter (post_message ~failed:true st) failed; - print_depexts_helper st failed; + | Success (Partial_error { actions_successes; actions_errors; _ }) -> + List.iter (post_message st) actions_successes; + List.iter (fun (a, _) -> post_message ~failed:true st a) actions_errors; + print_depexts_helper st (List.map fst actions_errors); OpamEnv.check_and_print_env_warning st; - OpamStd.Sys.exit_because `Package_operation_error - | OK actions -> + let reason = + if List.for_all (function + _, Fetch_fail _ -> true | _ -> false) + actions_errors then + `Sync_error + else `Package_operation_error + in + OpamStd.Sys.exit_because reason + | Success (OK actions) -> List.iter (post_message st) actions; OpamEnv.check_and_print_env_warning st - | Nothing_to_do -> + | Success Nothing_to_do -> if not quiet then OpamConsole.msg "Nothing to do.\n"; OpamEnv.check_and_print_env_warning st - | Aborted -> + | Success Aborted -> if not OpamClientConfig.(!r.show) then OpamStd.Sys.exit_because `Aborted @@ -119,6 +131,32 @@ let check_availability ?permissive t set atoms = let available = OpamPackage.to_map set in + let check_depexts atom = + let pkgs = OpamFormula.packages_of_atoms t.packages [atom] in + if OpamPackage.Set.is_empty pkgs then None else + match OpamSwitchState.depexts_unavailable + t (OpamPackage.Set.max_elt pkgs) with + | Some missing -> + let missing = + List.rev_map OpamSysPkg.to_string (OpamSysPkg.Set.elements missing) + in + let msg = + match missing with + | [pkg] -> + " '" ^ pkg ^ "'" + | pkgs -> + "s " ^ (OpamStd.Format.pretty_list (List.rev_map (Printf.sprintf "'%s'") pkgs)) + in + Some + (Printf.sprintf + "Package %s depends on the unavailable system package%s. You \ + can use `--no-depexts' to attempt installation anyway.%s" + (OpamFormula.short_string_of_atom atom) + msg + (OpamStd.Option.map_default (Printf.sprintf "\n%s.") "" + (OpamSysInteract.repo_enablers ()))) + | None -> None + in let check_atom (name, cstr as atom) = let exists = try @@ -128,7 +166,8 @@ with Not_found -> false in if exists then None - else if permissive = Some true + else match check_depexts atom with Some _ as some -> some | None -> + if permissive = Some true then Some (OpamSwitchState.not_found_message t atom) else let f = name, match cstr with None -> Empty | Some c -> Atom c in @@ -190,6 +229,7 @@ | `Reinstall nv -> f "recompiling" nv | `Remove nv -> f "removing" nv | `Build nv -> f "compiling" nv + | `Fetch nv -> f "fetching sources for" nv module Json = struct let output_request request user_action = @@ -219,22 +259,15 @@ in OpamJson.append "solution" (`A (List.rev to_proceed)) | Conflicts cs -> - let causes,_,cycles = - OpamCudf.strings_of_conflict + let causes, cycles = + OpamCudf.conflict_explanations t.packages (OpamSwitchState.unavailable_reason t) cs in - let chains = OpamCudf.conflict_chains t.packages cs in - let jchains = - `A (List.map (fun c -> - `A ((List.map (fun f -> - `String (OpamFormula.to_string (Atom f))) c))) - chains) - in + let causes = List.map OpamCudf.string_of_conflict causes in let toj l = `A (List.map (fun s -> `String s) l) in OpamJson.append "conflicts" (`O ((if cycles <> [] then ["cycles", toj cycles] else []) @ - (if causes <> [] then ["causes", toj causes] else []) @ - (if chains <> [] then ["broken-deps", jchains] else []))) + (if causes <> [] then ["causes", toj causes] else []))) let exc e = let lmap f l = List.rev (List.rev_map f l) in @@ -260,7 +293,9 @@ (* Process the atomic actions in a graph in parallel, respecting graph order, and report to user. Takes a graph of atomic actions *) -let parallel_apply t _action ~requested ?add_roots ~assume_built action_graph = +let parallel_apply t + ~requested ?add_roots ~assume_built ~download_only ?(force_remove=false) + action_graph = log "parallel_apply"; let remove_action_packages = @@ -303,14 +338,91 @@ as an operation terminates *) let t_ref = ref t in + (* only needed when --update-invariant is set. Use the configured invariant, + not the current one which will be empty. *) + let original_invariant = + OpamStd.Option.default OpamFormula.Empty + t.switch_config.OpamFile.Switch_config.invariant + in + let original_invariant_packages = + OpamFormula.packages t.installed original_invariant + in + let invariant_ref = ref original_invariant in + + let bypass_ref = ref (t.switch_config.OpamFile.Switch_config.depext_bypass) in + let add_to_install nv conf = let root = OpamPackage.Name.Set.mem nv.name root_installs in let t = !t_ref in let conf_files = - let add_conf conf = OpamPackage.Map.add nv conf t.conf_files in + let add_conf conf = OpamPackage.Name.Map.add nv.name conf t.conf_files in OpamStd.Option.map_default add_conf t.conf_files conf in t_ref := OpamSwitchAction.add_to_installed {t with conf_files} ~root nv; + let missing_depexts = + (* Turns out these depexts weren't needed after all. Remember that and + make the bypass permanent. *) + try + (OpamPackage.Map.find nv (Lazy.force !t_ref.sys_packages)).s_available + with Not_found -> OpamSysPkg.Set.empty + in + let bypass = OpamSysPkg.Set.union missing_depexts !bypass_ref in + let invariant = + if OpamStateConfig.(!r.unlock_base) then + let update_cstr cstr = + if OpamFormula.check_version_formula cstr nv.version then cstr + else + OpamFormula.map (fun (relop, _ as vat) -> + if OpamFormula.check_version_formula (Atom vat) nv.version + then Atom vat + else match relop with + | `Neq -> OpamFormula.Empty + | `Gt -> Atom (`Geq, nv.version) + | `Lt -> Atom (`Leq, nv.version) + | `Eq | `Geq | `Leq -> Atom (relop, nv.version)) + cstr + in + let nvset = OpamPackage.Set.singleton nv in + let upd_packages = + OpamSwitchState.conflicts_with t nvset original_invariant_packages + in + OpamFormula.map (fun (n, cstr as at) -> + if + OpamPackage.Set.exists (OpamFormula.verifies (Atom at)) + upd_packages + then + (* a package in the previous base validated this atom but is in + conflict with what we just installed *) + Atom (nv.name, update_cstr cstr) + else if n = nv.name then + Atom (n, update_cstr cstr) + else + Atom at) + !invariant_ref + else !invariant_ref + in + if bypass <> !bypass_ref || invariant <> !invariant_ref then + (if bypass <> !bypass_ref then + (let spkgs = OpamSysPkg.Set.Op.(bypass -- !bypass_ref) in + OpamConsole.note + "Requirement for system package%s %s overridden in this switch. Use \ + `opam option depext-bypass-=%s' to revert." + (if OpamSysPkg.Set.cardinal spkgs > 1 then "s" else "") + (OpamStd.Format.pretty_list + (List.map OpamSysPkg.to_string + (OpamSysPkg.Set.elements spkgs))) + (OpamStd.List.concat_map "," OpamSysPkg.to_string + (OpamSysPkg.Set.elements spkgs))); + bypass_ref := bypass; + invariant_ref := invariant; + let switch_config = + {!t_ref.switch_config with + invariant = Some invariant; depext_bypass = bypass } + in + t_ref := {!t_ref with switch_invariant = invariant; switch_config}; + if not OpamStateConfig.(!r.dryrun) then + OpamSwitchAction.install_switch_config t.switch_global.root t.switch + switch_config) in let remove_from_install ?keep_as_root nv = @@ -331,69 +443,37 @@ else OpamPackage.Map.empty in - (* 1/ fetch needed package archives *) - - let failed_downloads = - let sources_needed = - OpamPackage.Set.Op. - (OpamAction.sources_needed t action_graph -- OpamPackage.keys inplace) - in - let sources_list = OpamPackage.Set.elements sources_needed in - if OpamPackage.Set.exists (fun nv -> - not (OpamPackage.Set.mem nv t.pinned && - OpamFilename.exists_dir (OpamSwitchState.source_dir t nv))) - sources_needed - then OpamConsole.header_msg "Gathering sources"; - let results = - OpamParallel.map - ~jobs:OpamStateConfig.(!r.dl_jobs) - ~command:(OpamAction.download_package t) - ~dry_run:OpamStateConfig.(!r.dryrun) - sources_list - in - List.fold_left2 (fun failed nv -> function - | None -> failed - | Some (s,l) -> OpamPackage.Map.add nv (s,l) failed) - OpamPackage.Map.empty sources_list results - in - - if OpamClientConfig.(!r.json_out <> None) && - not (OpamPackage.Map.is_empty failed_downloads) then - OpamJson.append "download-failures" - (`O (List.map (fun (nv,(_,err)) -> OpamPackage.to_string nv, `String err) - (OpamPackage.Map.bindings failed_downloads))); - - let fatal_dl_error = - PackageActionGraph.fold_vertex - (fun a acc -> acc || match a with - | `Remove _ -> false - | _ -> OpamPackage.Map.mem (action_contents a) failed_downloads) - action_graph false - in - if fatal_dl_error then - OpamConsole.error_and_exit `Sync_error - "The sources of the following couldn't be obtained, aborting:\n%s" - (OpamStd.Format.itemize - (fun (p, (s,l)) -> - Printf.sprintf "%s%s" (OpamPackage.to_string p) - (if OpamConsole.verbose () then ":\n" ^ l - else OpamStd.Option.map_default (fun x -> ": " ^ x) "" s)) - (OpamPackage.Map.bindings failed_downloads)) - else if not (OpamPackage.Map.is_empty failed_downloads) then - OpamConsole.warning - "The sources of the following couldn't be obtained, they may be \ - uncleanly uninstalled:\n%s" - (OpamStd.Format.itemize OpamPackage.to_string - (OpamPackage.Map.keys failed_downloads)); - + let sources_needed = + let sources_needed = OpamAction.sources_needed t action_graph in + if not OpamClientConfig.(!r.working_dir) then sources_needed else + OpamPackage.Set.Op.(sources_needed -- requested) + in - (* 2/ process the package actions (installations and removals) *) + (* 1/ process the package actions (fetch, build, installations and removals) *) - let action_graph = (* Add build actions *) + let action_graph = (* Add build and fetch actions *) let noop_remove nv = OpamAction.noop_remove_package t nv in - PackageActionGraph.explicit ~noop_remove action_graph + PackageActionGraph.explicit + ~noop_remove + ~sources_needed:(fun p -> OpamPackage.Set.mem p sources_needed) + action_graph + in + let action_graph = + if download_only then + (* remove actions other than fetches *) + let g = PackageActionGraph.copy action_graph in + PackageActionGraph.iter_vertex (fun v -> + match v with + | `Fetch _ -> () + | `Install _ | `Reinstall _ | `Change _ + | `Remove _ | `Build _ -> + PackageActionGraph.remove_vertex g v + ) action_graph; + g + else action_graph in + (match OpamSolverConfig.(!r.cudf_file) with | None -> () | Some f -> @@ -416,8 +496,19 @@ (OpamPackage.Set.empty, OpamPackage.Set.empty, PackageAction.Set.empty) pred in - if not (PackageAction.Set.is_empty failed) then - Done (`Error (`Aborted failed)) (* prerequisite failed *) + (* Check whether prerequisites failed *) + let action_is_remove = match action with `Remove _ -> true | _ -> false in + let has_failure = not (PackageAction.Set.is_empty failed) in + let has_nonfetch_failure = + List.exists (function + | (_, `Successful _) | (`Fetch _, _) -> false + | _ -> true) + pred + in + if has_failure && (not action_is_remove || has_nonfetch_failure) + then + (* fatal error *) + Done (`Error (`Aborted failed)) else let store_time = let t0 = Unix.gettimeofday () in @@ -438,15 +529,23 @@ let t = { !t_ref with installed = visible_installed; - conf_files = OpamPackage.Map.filter - (fun nv _ -> OpamPackage.Set.mem nv visible_installed) + conf_files = OpamPackage.Name.Map.filter + (fun name _ -> OpamPackage.Set.exists (fun pkg -> OpamPackage.Name.equal name pkg.name) visible_installed) !t_ref.conf_files; } in let nv = action_contents action in - let source_dir = OpamSwitchState.source_dir t nv in + let opam = OpamSwitchState.opam t nv in + let source_dir = + let raw = OpamSwitchState.source_dir t nv in + match OpamFile.OPAM.url opam with + | None -> raw + | Some url -> + match OpamFile.URL.subpath url with + | None -> raw + | Some subpath -> OpamFilename.Op.(raw / subpath) in if OpamClientConfig.(!r.fake) then match action with - | `Build _ -> Done (`Successful (installed, removed)) + | `Build _ | `Fetch _ -> Done (`Successful (installed, removed)) | `Install nv -> OpamConsole.msg "Faking installation of %s\n" (OpamPackage.to_string nv); @@ -458,6 +557,14 @@ | _ -> assert false else match action with + | `Fetch nv -> + log "Fetching sources for %s" (OpamPackage.to_string nv); + (OpamAction.download_package t nv @@+ function + | None -> + store_time (); Done (`Successful (installed, removed)) + | Some (_short_error, long_error) -> + Done (`Exception (Fetch_fail long_error))) + | `Build nv -> if assume_built && OpamPackage.Set.mem nv requested then (log "Skipping build for %s, just install%s" @@ -517,7 +624,7 @@ OpamAction.prepare_package_source t nv d else Done None) @@+ fun _ -> OpamProcess.Job.ignore_errors ~default:() - (fun () -> OpamAction.remove_package t nv) @@| fun () -> + (fun () -> OpamAction.remove_package ~force:force_remove t nv) @@| fun () -> remove_from_install ~keep_as_root:(not (OpamPackage.Set.mem nv wished_removed)) nv; @@ -529,11 +636,13 @@ let action_results = OpamConsole.header_msg "Processing actions"; try - let installs_removes = + let installs_removes, fetches = PackageActionGraph.fold_vertex - (fun a acc -> match a with `Install _ | `Remove _ as i -> i::acc - | _ -> acc) - action_graph [] + (fun a (installs_removes, fetches as acc) -> match a with + | `Install _ | `Remove _ as i -> (i::installs_removes, fetches) + | `Fetch _ as i -> (installs_removes, i::fetches) + | _ -> acc) + action_graph ([],[]) in let same_inplace_source = OpamPackage.Map.fold (fun nv dir acc -> @@ -541,8 +650,9 @@ inplace OpamFilename.Dir.Map.empty |> OpamFilename.Dir.Map.values in - let mutually_exclusive = - installs_removes :: + let pools = + (installs_removes, 1) :: + (fetches, OpamStateConfig.(!r.dl_jobs)) :: OpamStd.List.filter_map (fun excl -> match @@ -552,7 +662,7 @@ if PackageActionGraph.mem_vertex action_graph act then Some act else None) excl - with [] | [_] -> None | l -> Some l) + with [] | [_] -> None | l -> Some (l,1)) same_inplace_source in let results = @@ -560,30 +670,50 @@ ~jobs:(Lazy.force OpamStateConfig.(!r.jobs)) ~command:job ~dry_run:OpamStateConfig.(!r.dryrun) - ~mutually_exclusive + ~pools action_graph in - if OpamClientConfig.(!r.json_out <> None) then - (let j = - PackageActionGraph.Topological.fold (fun a acc -> - let r = match List.assoc a results with - | `Successful _ -> `String "OK" - | `Exception e -> Json.exc e - | `Error (`Aborted deps) -> - let deps = OpamSolver.Action.Set.elements deps in - `O ["aborted", `A (List.map OpamSolver.Action.to_json deps)] - in - let duration = - try [ "duration", `Float (Hashtbl.find timings a) ] - with Not_found -> [] - in - `O ([ "action", PackageAction.to_json a; - "result", r ] @ - duration) - :: acc - ) action_graph [] - in - OpamJson.append "results" (`A (List.rev j))); + (* For backwards-compatibility reasons, we separate the json report for + download failures from the json report for the rest *) + if OpamClientConfig.(!r.json_out <> None) then begin + (* Report download failures *) + let failed_downloads = List.fold_left (fun failed (a, err) -> + match (a, err) with + | `Fetch pkg, `Exception (Fetch_fail long_error) -> + OpamPackage.Map.add pkg long_error failed + | _ -> + failed + ) OpamPackage.Map.empty results in + if not (OpamPackage.Map.is_empty failed_downloads) then + OpamJson.append "download-failures" + (`O (List.map (fun (nv, err) -> OpamPackage.to_string nv, `String err) + (OpamPackage.Map.bindings failed_downloads))); + (* Report build/install/remove failures *) + let j = + PackageActionGraph.Topological.fold (fun a acc -> + match a with + | `Fetch _ -> acc + | _ -> + let r = match List.assoc a results with + | `Successful _ -> `String "OK" + | `Exception e -> Json.exc e + | `Error (`Aborted deps) -> + let deps = OpamSolver.Action.Set.elements deps in + `O ["aborted", `A (List.map OpamSolver.Action.to_json deps)] + in + let duration = + try [ "duration", `Float (Hashtbl.find timings a) ] + with Not_found -> [] + in + `O ([ "action", PackageAction.to_json a; + "result", r ] @ + duration) + :: acc + ) action_graph [] + in + OpamJson.append "results" (`A (List.rev j)) + end; + let success, failure, aborted = List.fold_left (fun (success, failure, aborted) -> function | a, `Successful _ -> a::success, failure, aborted @@ -591,20 +721,40 @@ | a, `Error (`Aborted _) -> success, failure, a::aborted ) ([], [], []) results in - if failure = [] && aborted = [] then `Successful () + let actions_result = { + actions_successes = success; + actions_errors = failure; + actions_aborted = aborted; + } in + if failure = [] && aborted = [] then `Successful success else ( List.iter display_error failure; - `Error (Partial_error (success, List.map fst failure, aborted)) + `Error (Partial_error actions_result) ) with | PackageActionGraph.Parallel.Errors (success, errors, remaining) -> + let actions_result = { + actions_successes = success; + actions_errors = errors; + actions_aborted = remaining; + } in List.iter display_error errors; - `Error (Partial_error (success, List.map fst errors, remaining)) + `Error (Partial_error actions_result) | e -> `Exception e in let t = !t_ref in - (* 3/ Display errors and finalize *) + (* 2/ Display errors and finalize *) + + OpamSwitchState.Installed_cache.save + (OpamPath.Switch.installed_opams_cache t.switch_global.root t.switch) + (OpamPackage.Set.fold (fun nv opams -> + let opam = + OpamSwitchState.opam t nv |> + OpamFile.OPAM.with_metadata_dir None + in + OpamPackage.Map.add nv opam opams) + t.installed OpamPackage.Map.empty); let cleanup_artefacts graph = PackageActionGraph.iter_vertex (function @@ -613,20 +763,48 @@ OpamAction.cleanup_package_artefacts t nv (* if reinstalled, only removes build dir *) | `Install nv - when not (OpamPackage.has_name t.pinned nv.name) -> + when not (OpamPackage.has_name t.pinned nv.name) + || OpamSwitchState.is_version_pinned t nv.name -> let build_dir = OpamPath.Switch.build t.switch_global.root t.switch nv in if not OpamClientConfig.(!r.keep_build_dir) then OpamFilename.rmdir build_dir - | `Remove _ | `Install _ | `Build _ -> () + | `Remove _ | `Install _ | `Build _ | `Fetch _ -> () | _ -> assert false) graph in + let t = + if OpamStateConfig.(!r.unlock_base) && + (match action_results with `Successful _ -> true | _ -> false) && + not (OpamFormula.satisfies_depends t.installed t.switch_invariant) + then + (* Fix the invariant to account for removed base packages *) + let invariant = + OpamFormula.map_formula (function + | OpamFormula.And _ as f -> f + | f when OpamFormula.satisfies_depends t.installed f -> f + | _ -> OpamFormula.Empty) + t.switch_invariant + in + let switch_config = {t.switch_config with invariant = Some invariant} in + if not OpamStateConfig.(!r.dryrun) then + OpamSwitchAction.install_switch_config t.switch_global.root t.switch + switch_config; + {t with switch_invariant = invariant; switch_config} + else t + in + if t.switch_invariant <> original_invariant then + OpamConsole.note "Switch invariant %s updated to %s\n\ + Use `opam switch set-invariant' to change it." + (if OpamStateConfig.(!r.dryrun) then "would have been" else "was") + (match t.switch_invariant with + | OpamFormula.Empty -> "" + | f -> OpamFileTools.dep_formula_to_string f); match action_results with - | `Successful () -> + | `Successful successful -> cleanup_artefacts action_graph; OpamConsole.msg "Done.\n"; - t, OK (PackageActionGraph.fold_vertex (fun a b -> a::b) action_graph []) + t, OK successful | `Exception (OpamStd.Sys.Exit _ | Sys.Break as e) -> OpamConsole.msg "Aborting.\n"; raise e @@ -647,21 +825,48 @@ | `Error err -> match err with | Aborted -> t, err - | Partial_error (successful, failed, remaining) -> + | Partial_error solution_res -> + let successful = solution_res.actions_successes in + let failed = List.map fst solution_res.actions_errors in + let remaining = solution_res.actions_aborted in (* Cleanup build/install actions when one of them failed, it's verbose and doesn't add information *) let successful = List.filter (function - | `Build p when List.mem (`Install p) failed -> false + | `Fetch p | `Build p when List.mem (`Install p) failed -> false | _ -> true) successful in let remaining = List.filter (function - | `Remove p | `Install p when List.mem (`Build p) failed -> false + | `Remove p | `Install p + when List.mem (`Build p) failed -> false + | `Remove p | `Install p | `Build p + when List.mem (`Fetch p) failed -> false | _ -> true) remaining in + let removes_missing_source = + List.filter (function + | `Remove p as rem -> + let fetch = `Fetch p in + List.mem fetch failed && + PackageActionGraph.mem_edge action_graph fetch rem + | _ -> false + ) + successful + in + let failed = + (* Filter out failed fetches that were just for removals, there is a + shorter message for them *) + List.filter (function + | `Fetch _ as a -> + let succ = PackageActionGraph.succ action_graph a in + not (List.for_all (fun a -> List.mem a removes_missing_source) + succ) + | _ -> true) + failed + in let filter_graph l = if l = [] then PackageActionGraph.create () else let g = PackageActionGraph.copy action_graph in @@ -709,6 +914,14 @@ s) | None -> () in + if removes_missing_source <> [] then + (OpamConsole.msg "\n"; + OpamConsole.warning + "The sources of the following couldn't be obtained, they may be \ + uncleanly removed:\n%s" + (OpamStd.Format.itemize + (fun rm -> OpamPackage.to_string (action_contents rm)) + removes_missing_source)); OpamConsole.msg "\n"; OpamConsole.header_msg "Error report"; if OpamConsole.debug () || OpamConsole.verbose () then @@ -721,7 +934,7 @@ (OpamConsole.colorise `red "failed")) failed; print_actions - (function `Build _ -> false | _ -> true) `cyan + (function `Build _ | `Fetch _ -> false | _ -> true) `cyan ("The following changes have been performed" ^ if remaining <> [] then " (the rest was aborted)" else "") ~empty:"No changes have been performed" @@ -738,7 +951,7 @@ OpamPackage.Set.add p installed | `Remove p -> OpamPackage.Set.remove p installed - | `Build _ -> installed + | `Build _ | `Fetch _ -> installed ) t state.installed in { state with installed } @@ -746,7 +959,7 @@ (* Ask confirmation whenever the packages to modify are not exactly the packages in the user request *) let confirmation ?ask requested solution = - OpamCoreConfig.(!r.answer = Some true) || + OpamCoreConfig.answer_is_yes () || match ask with | Some false -> true | Some true -> OpamConsole.confirm "Do you want to continue?" @@ -797,8 +1010,146 @@ | None -> Done true +let syspkgs_to_string spkgs = + OpamStd.List.concat_map " " + (fun p -> OpamConsole.colorise `bold (OpamSysPkg.to_string p)) + (OpamSysPkg.Set.elements spkgs) + +let print_depext_msg (avail, nf) = + if not (OpamSysPkg.Set.is_empty nf) then + OpamConsole.warning + "These additional system packages are required, but not available on \ + your system: %s" + (syspkgs_to_string nf); + if not (OpamSysPkg.Set.is_empty avail) then + (OpamConsole.formatted_msg + "\nThe following system packages will first need to be installed:\n"; + OpamConsole.formatted_msg ~indent:4 " %s\n" + (syspkgs_to_string avail)) + +(* Gets depexts from the state, without checking again, unless [recover] is + true. *) +let get_depexts ?(recover=false) t packages = + let sys_packages = + if recover then + OpamSwitchState.depexts_status_of_packages t packages + else + Lazy.force t.sys_packages + in + let avail, nf = + OpamPackage.Set.fold (fun pkg (avail,nf) -> + match OpamPackage.Map.find_opt pkg sys_packages with + | Some sys -> + OpamSysPkg.(Set.union avail sys.s_available), + OpamSysPkg.(Set.union nf sys.s_not_found) + | None -> avail, nf) + packages (OpamSysPkg.Set.empty, OpamSysPkg.Set.empty) + in + print_depext_msg (avail, nf); + avail + +let install_depexts ?(force_depext=false) ?(confirm=true) t packages = + let sys_packages = + if force_depext || OpamFile.Config.depext t.switch_global.config then + get_depexts ~recover:force_depext t packages + else + OpamSysPkg.Set.empty + in + if OpamSysPkg.Set.is_empty sys_packages || + OpamClientConfig.(!r.show) || + OpamClientConfig.(!r.assume_depexts) + then t else + let print () = + let commands = + OpamSysInteract.install_packages_commands sys_packages + |> List.map (fun (c,a) -> c::a) + in + OpamConsole.formatted_msg + (match commands with + | [_] -> "This command should get the requirements installed:\n" + | _ -> "These commands should get the requirements installed:\n"); + OpamConsole.msg "\n %s\n\n" + (OpamStd.List.concat_map "\n " (String.concat " ") commands) + in + let map_sysmap f t = + let sys_packages = + OpamPackage.Set.fold (fun nv sys_map -> + match OpamPackage.Map.find_opt nv sys_map with + | Some status -> + OpamPackage.Map.add + nv { status with OpamSysPkg.s_available = + f status.OpamSysPkg.s_available } + sys_map + | None -> sys_map) + packages + (Lazy.force t.sys_packages) + in + { t with sys_packages = lazy sys_packages } + in + let recheck t sys_packages = + let needed, _notfound = OpamSysInteract.packages_status sys_packages in + let installed = OpamSysPkg.Set.diff sys_packages needed in + map_sysmap (fun sysp -> OpamSysPkg.Set.diff sysp installed) t, needed + in + let rec wait msg sys_packages = + let give_up () = + OpamConsole.formatted_msg + "You can retry with '--assume-depexts' to skip this check, or run \ + 'opam option depext=false' to permanently disable handling of \ + system packages altogether.\n"; + OpamStd.Sys.exit_because `Aborted + in + if not (OpamStd.Sys.tty_in && OpamCoreConfig.answer_is `ask) then + give_up () + else if OpamConsole.confirm + "%s\nWhen you are done: check again and continue?" + msg + then + let t, to_install = recheck t sys_packages in + if OpamSysPkg.Set.is_empty to_install then t else + let msg = + Printf.sprintf + "\nThe following remain to be installed: %s" + (syspkgs_to_string to_install) + in + wait msg to_install + else if + OpamConsole.confirm ~default:false + "Do you want to attempt to proceed anyway?" + then t + else give_up () + in + OpamConsole.header_msg "Handling external dependencies"; + if not (OpamFile.Config.depext_run_installs t.switch_global.config) then + (print (); + wait "You may now install the packages on your system." + sys_packages) + else if OpamClientConfig.(!r.fake) then (print (); t) + else if + not confirm + || OpamConsole.confirm ~require_unsafe_yes:true + "Let opam run your package manager to install the required system \ + packages?\n(answer 'n' for other options)" + then + try + OpamSysInteract.install sys_packages; (* handles dry_run *) + map_sysmap (fun _ -> OpamSysPkg.Set.empty) t + with Failure msg -> + OpamConsole.error "%s" msg; + if not confirm then (print (); t) else + wait "You can now try to get them installed manually." + sys_packages + else + (OpamConsole.note "Use 'opam option depext-run-installs=false' \ + if you don't want to be prompted again."; + print (); + wait + "You may now install the packages manually on your system." + sys_packages) + (* Apply a solution *) -let apply ?ask t action ~requested ?add_roots ?(assume_built=false) solution = +let apply ?ask t ~requested ?add_roots ?(assume_built=false) + ?(download_only=false) ?force_remove solution = log "apply"; if OpamSolver.solution_is_empty solution then (* The current state satisfies the request contraints *) @@ -838,16 +1189,19 @@ else "" in OpamSolver.print_solution ~messages ~append - ~requested ~reinstall:t.reinstall + ~requested ~reinstall:(Lazy.force t.reinstall) solution; let total_actions = sum stats in if total_actions >= 2 then OpamConsole.msg "===== %s =====\n" (OpamSolver.string_of_stats stats); ); - if not OpamClientConfig.(!r.show) && - confirmation ?ask requested action_graph + (download_only || confirmation ?ask requested action_graph) then ( + let t = + install_depexts t @@ OpamPackage.Set.inter + new_state.installed (OpamSolver.all_packages solution) + in let requested = OpamPackage.packages_of_names new_state.installed requested in @@ -859,15 +1213,19 @@ let var_def name l = OpamVariable.Full.of_string name, L l in - let var_def_set name set = + let var_def_pset name set = var_def name (List.map OpamPackage.to_string (OpamPackage.Set.elements set)) in + let var_def_spset name set = + var_def name + (List.map OpamSysPkg.to_string (OpamSysPkg.Set.elements set)) + in let depexts = OpamPackage.Set.fold (fun nv depexts -> - OpamStd.String.Set.union depexts + OpamSysPkg.Set.union depexts (OpamSwitchState.depexts t nv)) - new_state.installed OpamStd.String.Set.empty + new_state.installed OpamSysPkg.Set.empty in let wrappers = OpamFile.Wrappers.add @@ -877,10 +1235,10 @@ let pre_session = let open OpamPackage.Set.Op in let local = [ - var_def_set "installed" new_state.installed; - var_def_set "new" (new_state.installed -- t.installed); - var_def_set "removed" (t.installed -- new_state.installed); - var_def "depexts" (OpamStd.String.Set.elements depexts); + var_def_pset "installed" new_state.installed; + var_def_pset "new" (new_state.installed -- t.installed); + var_def_pset "removed" (t.installed -- new_state.installed); + var_def_spset "depexts" depexts; ] in run_job @@ run_hook_job t "pre-session" ~local ~allow_stdout:true @@ -890,15 +1248,17 @@ OpamStd.Sys.exit_because `Configuration_error; let t0 = t in let t, r = - parallel_apply t action ~requested ?add_roots ~assume_built action_graph + parallel_apply t + ~requested ?add_roots ~assume_built ~download_only ?force_remove + action_graph in let success = match r with | OK _ -> true | _ -> false in let post_session = let open OpamPackage.Set.Op in let local = [ - var_def_set "installed" t.installed; - var_def_set "new" (t.installed -- t0.installed); - var_def_set "removed" (t0.installed -- t.installed); + var_def_pset "installed" t.installed; + var_def_pset "new" (t.installed -- t0.installed); + var_def_pset "removed" (t0.installed -- t.installed); OpamVariable.Full.of_string "success", B (success); OpamVariable.Full.of_string "failure", B (not success); ] in @@ -919,23 +1279,27 @@ (`A (List.map (fun s -> `String s) (Array.to_list Sys.argv))); OpamJson.append "switch" (OpamSwitch.to_json t.switch) ); - Json.output_request request action; - let r = - OpamSolver.resolve - (OpamSwitchState.universe t ~requested ?reinstall action) - ~orphans - request + let universe = + OpamSwitchState.universe t ~requested ?reinstall action in + Json.output_request request action; + let r = OpamSolver.resolve universe ~orphans request in Json.output_solution t r; r let resolve_and_apply ?ask t action ~orphans ?reinstall ~requested ?add_roots - ?(assume_built=false) request = + ?(assume_built=false) ?download_only ?force_remove request = match resolve t action ~orphans ?reinstall ~requested request with | Conflicts cs -> log "conflict!"; OpamConsole.msg "%s" - (OpamCudf.string_of_conflict t.packages + (OpamCudf.string_of_conflicts t.packages (OpamSwitchState.unavailable_reason t) cs); - t, No_solution - | Success solution -> apply ?ask t action ~requested ?add_roots ~assume_built solution + t, Conflicts cs + | Success solution -> + let t, res = + apply ?ask t + ~requested ?add_roots ~assume_built ?download_only ?force_remove + solution + in + t, Success res diff -Nru opam-2.0.10/src/client/opamSolution.mli opam-2.1.2/src/client/opamSolution.mli --- opam-2.0.10/src/client/opamSolution.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamSolution.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -27,21 +27,23 @@ (** Apply a solution returned by the solver. If [ask] is not specified, prompts the user whenever the solution isn't obvious from the request. [add_roots] defaults to the set of newly installed packages that are part of - [requested]. *) + [requested]. If [force_remove] is true, modified files are not kept.*) val apply: ?ask:bool -> rw switch_state -> - user_action -> requested:OpamPackage.Name.Set.t -> ?add_roots:OpamPackage.Name.Set.t -> ?assume_built:bool -> + ?download_only:bool -> + ?force_remove:bool -> OpamSolver.solution -> - rw switch_state * solver_result + rw switch_state * solution_result (** Call the solver to get a solution and then call [apply]. If [ask] is not specified, prompts the user whenever the solution isn't obvious from the request. [add_roots] defaults to the set of newly installed packages that - are part of [requested]. *) + are part of [requested]. If [force_remove] is true, modified files are + not kept. *) val resolve_and_apply: ?ask:bool -> rw switch_state -> @@ -51,20 +53,32 @@ requested:OpamPackage.Name.Set.t -> ?add_roots:OpamPackage.Name.Set.t -> ?assume_built:bool -> + ?download_only:bool -> + ?force_remove:bool -> atom request -> - rw switch_state * solver_result + rw switch_state * (solution_result, OpamCudf.conflict) result (** Raise an error if no solution is found or in case of error. Unless [quiet] is set, print a message indicating that nothing was done on an empty solution. *) -val check_solution: ?quiet:bool -> 'a switch_state -> solver_result -> unit +val check_solution: + ?quiet:bool -> 'a switch_state -> + (solution_result, 'conflict) result -> + unit + +(* Install external dependencies of the given package set, according the depext + configuration. If [confirm] is false, install commands are directly + launched, without asking user (used by the `--depext-only` option). If + [force_depext] is true, it overrides [OpamFile.Config.depext] value. *) +val install_depexts: + ?force_depext:bool -> ?confirm:bool -> rw switch_state -> package_set -> rw switch_state (** {2 Atoms} *) (** Return an atom with a strict version constraint *) val eq_atom: name -> version -> atom -(** Return a simple atom, with no version constrain, from a package*) +(** Return a simple atom, with no version constraint, from a package*) val atom_of_package: package -> atom (** Returns an atom with a strict version constraint from a package *) diff -Nru opam-2.0.10/src/client/opamSwitchCommand.ml opam-2.1.2/src/client/opamSwitchCommand.ml --- opam-2.0.10/src/client/opamSwitchCommand.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamSwitchCommand.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -145,7 +145,7 @@ (OpamEnv.eval_string gt (Some switch))) | _ -> () -let clear_switch ?(keep_debug=false) gt switch = +let clear_switch ?(keep_debug=false) (gt: rw global_state) switch = let module C = OpamFile.Config in let config = gt.config in let config = @@ -184,62 +184,91 @@ clear_switch gt switch else gt -let install_compiler_packages t atoms = - (* install the compiler packages *) - if atoms = [] then t else - let roots = OpamPackage.Name.Set.of_list (List.map fst atoms) in - let not_found = - OpamPackage.Name.Set.diff roots @@ - OpamPackage.names_of_packages @@ - OpamPackage.packages_of_names t.packages roots - in - if not (OpamPackage.Name.Set.is_empty not_found) then - OpamConsole.error_and_exit `Not_found - "No packages %s found." - (OpamPackage.Name.Set.to_string not_found); +let set_invariant_raw st invariant = + let switch_config = {st.switch_config with invariant = Some invariant} in + let st = {st with switch_invariant = invariant; switch_config } in + if not (OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show)) then + OpamSwitchAction.install_switch_config st.switch_global.root st.switch + switch_config; + st + +let install_compiler + ?(additional_installs=[]) ?(deps_only=false) ?(ask=false) t = + let invariant = t.switch_invariant in + if invariant = OpamFormula.Empty && additional_installs = [] then begin + (if not OpamClientConfig.(!r.show) && + not OpamStateConfig.(!r.dryrun) then + OpamFile.Environment.write + (OpamPath.Switch.environment t.switch_global.root t.switch) + (OpamEnv.compute_updates t); + OpamEnv.check_and_print_env_warning t); + t + end else + let () = OpamRepositoryState.check_last_update () in + let atoms = OpamFormula.atoms invariant in + let names_of_atoms at = OpamPackage.Name.Set.of_list (List.map fst at) in + let comp_roots = names_of_atoms atoms in + let add_names = names_of_atoms additional_installs in + let roots = + OpamPackage.Name.Set.union comp_roots add_names + in + OpamConsole.header_msg "Installing new switch packages"; + OpamConsole.msg "Switch invariant: %s\n" + (OpamFileTools.dep_formula_to_string invariant); let solution = OpamSolution.resolve t Switch ~orphans:OpamPackage.Set.empty ~requested:roots - { wish_install = []; - wish_remove = []; - wish_upgrade = atoms; - criteria = `Default; - extra_attributes = []; } in + (OpamSolver.request ~install:additional_installs ()) + in let solution = match solution with | Success s -> s | Conflicts cs -> - OpamConsole.error_and_exit `No_solution - "Could not resolve set of base packages:\n%s" - (OpamCudf.string_of_conflict t.packages + OpamConsole.error + "Could not determine which packages to install for this switch:"; + OpamConsole.errmsg "%s\n" + (OpamCudf.string_of_conflicts t.packages (OpamSwitchState.unavailable_reason t) cs); + OpamStd.Sys.exit_because `No_solution in let () = match OpamSolver.stats solution with | { s_install = _; s_reinstall = 0; s_upgrade = 0; s_downgrade=0; s_remove = 0 } -> () | stats -> OpamConsole.error_and_exit `No_solution - "Inconsistent resolution of base package installs:\n%s" + "Inconsistent resolution of packages:\n%s" (OpamSolver.string_of_stats stats) in let to_install_pkgs = OpamSolver.new_packages solution in - let base_comp = OpamPackage.packages_of_names to_install_pkgs roots in - let non_comp = + let base_comp = OpamPackage.packages_of_names to_install_pkgs comp_roots in + let has_comp_flag = + let is_comp nv = + try OpamFile.OPAM.has_flag Pkgflag_Compiler (OpamSwitchState.opam t nv) + with Not_found -> false + in + (* Packages with the Compiler flag, or with a direct dependency with that + flag (just for the warning) *) OpamPackage.Set.filter (fun nv -> - not (OpamFile.OPAM.has_flag Pkgflag_Compiler - (OpamSwitchState.opam t nv))) + is_comp nv || + OpamPackage.Set.exists is_comp + (OpamFormula.packages t.packages + (OpamPackageVar.all_depends ~filter_default:true t + (OpamSwitchState.opam t nv)))) base_comp in - if not (OpamPackage.Set.is_empty non_comp) && - not (OpamConsole.confirm ~default:false - "Packages %s don't have the 'compiler' flag set. Are you sure \ - you want to set them as the compiler base for this switch?" - (OpamPackage.Set.to_string non_comp)) - then - OpamConsole.error_and_exit `Aborted - "Aborted installation of non-compiler packages \ - as switch base."; + if invariant = OpamFormula.Empty then + OpamConsole.note + "No invariant was set, you may want to use `opam switch set-invariant' \ + to keep a stable compiler version on upgrades." + else if OpamPackage.Set.is_empty has_comp_flag then + OpamConsole.note + "Packages %s don't have the 'compiler' flag set (nor any of their \ + direct dependencies).\n\ + You may want to use `opam switch set-invariant' to keep a stable \ + compiler version on upgrades." + (OpamStd.List.concat_map ", " OpamPackage.to_string + (OpamPackage.Set.elements base_comp)); let t = if t.switch_config.OpamFile.Switch_config.synopsis = "" then let synopsis = @@ -260,18 +289,29 @@ { t with switch_config } else t in - let t = { t with compiler_packages = to_install_pkgs } in + let t = { t with compiler_packages = base_comp } in + let solution = + if deps_only then + OpamSolver.filter_solution (fun nv -> + not (OpamPackage.Name.Set.mem nv.name add_names)) + solution + else solution + in let t, result = - OpamSolution.apply ~ask:OpamClientConfig.(!r.show) t Switch + OpamSolution.apply t + ~ask:(OpamClientConfig.(!r.show) || ask) ~requested:roots + ~add_roots:roots solution in - OpamSolution.check_solution ~quiet:OpamClientConfig.(not !r.show) t result; + OpamSolution.check_solution ~quiet:OpamClientConfig.(not !r.show) t + (Success result); t -let install gt ?rt ?synopsis ?repos ~update_config ~packages ?(local_compiler=false) switch = +let create + gt ~rt ?synopsis ?repos ~update_config ~invariant switch post = let update_config = update_config && not (OpamSwitch.is_external switch) in - let old_switch_opt = OpamFile.Config.switch gt.config in let comp_dir = OpamPath.Switch.root gt.root switch in + let simulate = OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show) in if OpamGlobalState.switch_exists gt switch then OpamConsole.error_and_exit `Bad_arguments "There already is an installed switch named %s" @@ -281,83 +321,68 @@ "Directory %S already exists, please choose a different name" (OpamFilename.Dir.to_string comp_dir); let gt, st = - if not (OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show)) then + if not simulate then let gt = - OpamSwitchAction.create_empty_switch gt ?synopsis ?repos switch + OpamSwitchAction.create_empty_switch gt ?synopsis ?repos ~invariant + switch in - if update_config then - gt, OpamSwitchAction.set_current_switch `Lock_write gt ?rt switch - else - let rt = match rt with - | None -> OpamRepositoryState.load `Lock_none gt - | Some rt -> - ({ rt with repos_global = (gt :> unlocked global_state) } - :> unlocked repos_state) + let rt = + ({ rt with repos_global = (gt :> unlocked global_state) } + :> unlocked repos_state) in gt, OpamSwitchState.load `Lock_write gt rt switch else - gt, - let rt = match rt with - | None -> OpamRepositoryState.load `Lock_none gt - | Some rt -> (rt :> unlocked repos_state) - in + let rt = (rt :> unlocked repos_state) in let st = OpamSwitchState.load_virtual ?repos_list:repos gt rt in + let switch_config = + OpamSwitchAction.gen_switch_config gt.root ?repos switch ~invariant + in + let st = { st with switch_invariant = invariant } in let available_packages = - lazy (OpamSwitchState.compute_available_packages gt switch - (OpamSwitchAction.gen_switch_config gt.root ?repos switch) + lazy (OpamSwitchState.compute_available_packages gt switch switch_config ~pinned:OpamPackage.Set.empty ~opams:st.opams) in - { st with switch; available_packages } + gt, { st with switch; switch_config; available_packages } in - let st = - if OpamSwitch.is_external switch && local_compiler then - OpamAuxCommands.autopin st ~quiet:true - [`Dirname (OpamFilename.Dir.of_string (OpamSwitch.to_string switch))] - |> fst - else st - in - let packages = - try OpamSolution.sanitize_atom_list st packages - with e -> - OpamStd.Exn.finalise e @@ fun () -> - if update_config then - (OpamEnv.clear_dynamic_init_scripts gt; - OpamStd.Option.iter - (ignore @* OpamSwitchAction.set_current_switch `Lock_write gt) - old_switch_opt); - ignore (OpamSwitchState.unlock st); - ignore (clear_switch gt switch) - in - let gt = OpamGlobalState.unlock gt in - try - gt, install_compiler_packages st packages - with e -> - if not (OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show)) then - ((try OpamStd.Exn.fatal e with e -> - OpamConsole.warning "Switch %s left partially installed" - (OpamSwitch.to_string switch); - raise e); - if OpamConsole.confirm "Switch initialisation failed: clean up? \ - ('n' will leave the switch partially installed)" - then begin - ignore (OpamSwitchState.unlock st); - ignore (clear_switch gt switch) - end); - raise e + match post st with + | ret, st -> + let st = + if update_config && not simulate + then OpamSwitchAction.set_current_switch gt st + else st + in + OpamGlobalState.drop gt; + ret, st + | exception e when not simulate -> + let () = + try OpamStd.Exn.fatal e with e -> + OpamStd.Exn.finalise e @@ fun () -> + OpamConsole.warning "Switch %s left partially installed" + (OpamSwitch.to_string st.switch) + in + OpamStd.Exn.finalise e @@ fun () -> + let gt, st = + if OpamConsole.confirm "Switch initialisation failed: clean up? \ + ('n' will leave the switch partially installed)" + then clear_switch gt st.switch, st + else if update_config && not simulate + then gt, OpamSwitchAction.set_current_switch gt st + else gt, st + in + OpamSwitchState.drop st; + OpamGlobalState.drop gt let switch lock gt switch = log "switch switch=%a" (slog OpamSwitch.to_string) switch; if OpamGlobalState.switch_exists gt switch then + OpamRepositoryState.with_ `Lock_none gt @@ fun rt -> + let st = OpamSwitchState.load lock gt rt switch in let st = - if not (OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show)) then - OpamSwitchAction.set_current_switch lock gt switch - else - let rt = OpamRepositoryState.load `Lock_none gt in - OpamSwitchState.load lock gt rt switch + if OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show) then st + else OpamSwitchAction.set_current_switch gt st in - OpamEnv.check_and_print_env_warning st; - st + OpamEnv.check_and_print_env_warning st else let installed_switches = OpamFile.Config.installed_switches gt.config in OpamConsole.error_and_exit `Not_found @@ -370,6 +395,22 @@ let import_t ?ask importfile t = log "import switch"; + let extra_files = importfile.OpamFile.SwitchExport.extra_files in + let xfiles_dir = + OpamPath.Switch.extra_files_dir t.switch_global.root t.switch + in + OpamHash.Map.iter (fun hash content -> + let value = Base64.decode_string content in + let my = OpamHash.compute_from_string ~kind:(OpamHash.kind hash) value in + if OpamHash.contents my = OpamHash.contents hash then + let dst = + let base = OpamFilename.Base.of_string (OpamHash.contents hash) in + OpamFilename.create xfiles_dir base + in + OpamFilename.write dst value + else + failwith "Bad hash for inline extra-files") extra_files; + let import_sel = importfile.OpamFile.SwitchExport.selections in let import_opams = importfile.OpamFile.SwitchExport.overlays in @@ -452,6 +493,7 @@ extra_attributes = []; } in OpamSolution.check_solution t solution; + OpamFilename.rmdir xfiles_dir; if not (OpamStateConfig.(!r.dryrun) || OpamClientConfig.(!r.show)) then begin (* Put imported overlays in place *) @@ -475,47 +517,110 @@ end; t -let read_overlays (read: package -> OpamFile.OPAM.t option) packages = - OpamPackage.Set.fold (fun nv acc -> - match read nv with - | Some opam -> - if OpamFile.OPAM.extra_files opam <> None then - (OpamConsole.warning - "Metadata of package %s uses a files/ subdirectory, it may not be \ - re-imported correctly (skipping definition)" - (OpamPackage.to_string nv); - acc) - else OpamPackage.Name.Map.add nv.name opam acc - | None -> acc) - packages - OpamPackage.Name.Map.empty +let freeze_opam src_dir nv opam = + match OpamFile.OPAM.url opam with + | None -> opam + | Some url -> + let url_t = OpamFile.URL.url url in + match url_t.backend with + | #OpamUrl.version_control -> + (match OpamProcess.Job.run + (OpamRepository.revision (src_dir nv) url_t) with + | None -> + OpamConsole.error_and_exit `Not_found + "Unable to retrieve %s url revision: %s, \ + it can't be exported with --freeze." + (OpamPackage.to_string nv) + (OpamUrl.to_string url_t) + | Some hash -> + OpamFile.OPAM.with_url + (OpamFile.URL.with_url + { url_t with hash = Some (OpamPackage.Version.to_string hash) } + url) + opam) + | `http -> + (match OpamFile.URL.checksum url with + | [] -> + OpamConsole.error_and_exit `Not_found + "%s url doesn't have an associated checksum, \ + it can't be exported with --freeze." + (OpamPackage.Name.to_string nv.name) + | _ -> opam) + | `rsync -> + OpamConsole.error_and_exit `Not_found + "%s is path pinned, it can't be exported with --freeze." + (OpamPackage.Name.to_string nv.name) -let export ?(full=false) filename = - let switch = OpamStateConfig.get_switch () in +let export rt ?(freeze=false) ?(full=false) + ?(switch=OpamStateConfig.get_switch ()) filename = let root = OpamStateConfig.(!r.root_dir) in let export = OpamFilename.with_flock `Lock_none (OpamPath.Switch.lock root switch) @@ fun _ -> let selections = - OpamStateConfig.Switch.safe_read_selections_t - ~lock_kind:`Lock_none root switch + OpamStateConfig.Switch.safe_read_selections + ~lock_kind:`Lock_none rt.repos_global switch in - let overlays = - read_overlays (fun nv -> - OpamFileTools.read_opam - (OpamPath.Switch.Overlay.package root switch nv.name)) - selections.sel_pinned + let opams = + let read_opams read pkgs = + let src_dir nv = + if OpamPackage.Set.mem nv selections.sel_pinned then + OpamPath.Switch.pinned_package root switch nv.name + else + OpamPath.Switch.sources root switch nv + in + OpamPackage.Set.fold (fun nv map -> + match read nv with + | Some opam -> + let opam = + if not freeze then opam else + freeze_opam src_dir nv opam + in + OpamPackage.Map.add nv opam map + | None -> map) pkgs OpamPackage.Map.empty + in + let overlays = + read_opams (fun nv -> + OpamFileTools.read_opam + (OpamPath.Switch.Overlay.package root switch nv.name)) + selections.sel_pinned + in + if not full then overlays else + OpamPackage.Map.union (fun a _ -> a) overlays + @@ read_opams (fun nv -> OpamFile.OPAM.read_opt + (OpamPath.Switch.installed_opam root switch nv)) + (selections.sel_installed -- selections.sel_pinned) in let overlays = - if full then - OpamPackage.Name.Map.union (fun a _ -> a) overlays @@ - read_overlays (fun nv -> - OpamFile.OPAM.read_opt - (OpamPath.Switch.installed_opam root switch nv)) - (selections.sel_installed -- selections.sel_pinned) - else overlays + OpamPackage.Map.fold (fun nv opam nmap -> + OpamPackage.Name.Map.add nv.name opam nmap) + opams OpamPackage.Name.Map.empty + in + let extra_files = + let repos_roots = OpamRepositoryState.get_root rt in + OpamPackage.Map.fold (fun nv opam hmap -> + match OpamFile.OPAM.get_extra_files ~repos_roots opam with + | [] -> hmap + | files -> + let hmap, err = + List.fold_left (fun (hmap,err) (file, base, hash) -> + if OpamFilename.exists file && + OpamHash.check_file (OpamFilename.to_string file) hash then + let value = Base64.encode_string (OpamFilename.read file) in + OpamHash.Map.add hash value hmap, err + else hmap, base::err) + (hmap,[]) files + in + if err <> [] then + OpamConsole.warning "Invalid hash%s, ignoring package %s extra-file%s: %s" + (match err with | [_] -> "" | _ -> "es") + (OpamPackage.to_string nv) + (match err with | [_] -> "" | _ -> "s") + (OpamStd.Format.pretty_list (List.map OpamFilename.Base.to_string err)); + hmap) + opams OpamHash.Map.empty in - { OpamFile.SwitchExport.selections; overlays } + { OpamFile.SwitchExport.selections; extra_files; overlays } in match filename with | None -> OpamFile.SwitchExport.write_to_channel stdout export @@ -543,10 +648,11 @@ { init_st with installed = OpamPackage.Set.empty; installed_roots = OpamPackage.Set.empty; - reinstall = OpamPackage.Set.empty; } + reinstall = lazy OpamPackage.Set.empty; } in import_t { OpamFile.SwitchExport. selections = OpamSwitchState.selections init_st; + extra_files = OpamHash.Map.empty; overlays = OpamPackage.Name.Map.empty; } st @@ -562,39 +668,45 @@ try let selections = OpamFile.LegacyState.read_from_string import_str in { OpamFile.SwitchExport.selections; + extra_files = OpamHash.Map.empty; overlays = OpamPackage.Name.Map.empty } with e1 -> OpamStd.Exn.fatal e1; raise e in import_t importfile st -let set_compiler st namesv = +let set_invariant ?(force=false) st invariant = + let satisfied = OpamFormula.satisfies_depends st.installed invariant in + let names = + OpamPackage.Name.Set.of_list (List.map fst (OpamFormula.atoms invariant)) + in let name_unknown = - List.filter (fun (name,_) -> not (OpamPackage.has_name st.packages name)) - namesv + OpamPackage.Name.Set.filter + (fun n -> not (OpamPackage.has_name st.packages n)) + names + in + if not (OpamPackage.Name.Set.is_empty name_unknown) then + (if satisfied || force then OpamConsole.warning + else OpamConsole.error_and_exit `Not_found) + "No packages by these names found: %s" + (OpamStd.List.concat_map ", " + OpamPackage.Name.to_string + (OpamPackage.Name.Set.elements name_unknown)); + let packages = OpamFormula.packages st.installed invariant in + let not_comp = + OpamPackage.Set.filter (fun nv -> + match OpamSwitchState.opam_opt st nv with + | Some opam -> not (OpamFile.OPAM.has_flag Pkgflag_Compiler opam) + | None -> false) + packages in - if name_unknown <> [] then - OpamConsole.error_and_exit `Not_found "No packages by these names found: %s" - (OpamStd.List.concat_map ", " (OpamPackage.Name.to_string @* fst) - name_unknown); - let packages = - List.map (function - | name, Some v -> OpamPackage.create name v - | name, None -> OpamSwitchState.get_package st name) - namesv - in - let uninstalled = - List.filter (fun nv -> not (OpamPackage.Set.mem nv st.installed)) packages - in - if uninstalled <> [] then - (OpamConsole.warning - "These packages are not installed:\n%s" - (OpamStd.List.concat_map ", " OpamPackage.to_string uninstalled); - if not (OpamConsole.confirm - "Set them as compilers at the proposed versions regardless?") - then OpamStd.Sys.exit_because `Aborted); - let st = { st with compiler_packages = OpamPackage.Set.of_list packages } in - OpamSwitchAction.write_selections st; - st + if not (OpamPackage.Set.is_empty not_comp) then + OpamConsole.warning + "Packages %s don't have the 'compiler' flag set." + (OpamStd.Format.pretty_list + (List.map OpamPackage.Name.to_string + (OpamPackage.Name.Set.elements + (OpamPackage.names_of_packages not_comp)))); + set_invariant_raw st invariant let get_compiler_packages ?repos rt = let repos = match repos with @@ -611,96 +723,57 @@ package_index |> OpamPackage.keys -let advise_compiler_dependencies rt opams compilers name atoms = - let packages = OpamFormula.packages_of_atoms (OpamPackage.keys opams) atoms in - let deps = - List.map (fun nv -> - let opam = OpamPackage.Map.find nv opams in - OpamPackageVar.filter_depends_formula - ~default:false - ~env:(OpamPackageVar.resolve_switch_raw ~package:nv rt.repos_global - (OpamSwitch.of_string name) - (OpamFile.Switch_config.empty)) - (OpamFile.OPAM.depends opam)) - (OpamPackage.Set.elements packages) - in - let comp_deps = - List.fold_left (fun acc f -> - OpamPackage.Set.union acc (OpamFormula.packages compilers f)) - OpamPackage.Set.empty deps - in - if not (OpamPackage.Set.is_empty comp_deps) then - OpamConsole.formatted_msg - "Package%s %s do%sn't have the 'compiler' flag set, and may not be \ - suitable to set as switch base. You probably meant to choose among \ - the following compiler implementations, which they depend \ - upon:\n%s" - (match atoms with [_] -> "" | _ -> "s") - (OpamStd.List.concat_map ", " OpamFormula.short_string_of_atom atoms) - (match atoms with [_] -> "es" | _ -> "") - (OpamStd.Format.itemize OpamPackage.Name.to_string - (OpamPackage.Name.Set.elements - (OpamPackage.names_of_packages comp_deps))) - -let guess_compiler_package ?repos rt name = +let guess_compiler_invariant ?repos rt strings = let repos = match repos with | None -> OpamGlobalState.repos_list rt.repos_global | Some r -> r in - let opams = - OpamRepositoryState.build_index rt repos |> - OpamPackage.Map.filter - (fun _ opam -> - OpamFilter.eval_to_bool ~default:false - (OpamPackageVar.resolve_global rt.repos_global) - (OpamFile.OPAM.available opam)) - in + let opams = OpamRepositoryState.build_index rt repos in + let packages = OpamPackage.keys opams in let compiler_packages = OpamPackage.Map.filter (fun _ -> OpamFile.OPAM.has_flag Pkgflag_Compiler) opams |> OpamPackage.keys in - let no_compiler_error () = - OpamConsole.error_and_exit `Not_found - "No compiler matching '%s' found, use 'opam switch list-available' \ - to see what is available, or use '--packages' to select packages \ - explicitly." - name - in - match OpamPackage.of_string_opt name with - | Some nv when OpamPackage.Set.mem nv compiler_packages -> - [OpamSolution.eq_atom_of_package nv] - | Some nv when OpamRepositoryState.find_package_opt rt repos nv <> None -> - advise_compiler_dependencies rt opams compiler_packages name - [OpamSolution.eq_atom_of_package nv]; - no_compiler_error () - | _ -> - let pkgname = - try Some (OpamPackage.Name.of_string name) - with Failure _ -> None - in - match pkgname with - | Some pkgname when OpamPackage.has_name compiler_packages pkgname -> - [pkgname, None] - | Some pkgname when - OpamPackage.Map.exists (fun nv _ -> OpamPackage.name nv = pkgname) opams - -> - advise_compiler_dependencies rt opams compiler_packages name - [pkgname, None]; - no_compiler_error () - | _ -> - let version = OpamPackage.Version.of_string name in - let has_version = - OpamPackage.Set.filter (fun nv -> nv.version = version) - compiler_packages - in - try - [OpamSolution.eq_atom_of_package - (OpamPackage.Set.choose_one has_version)] - with - | Not_found -> no_compiler_error () - | Failure _ -> - OpamConsole.error_and_exit `Bad_arguments - "Compiler selection '%s' is ambiguous. matching packages: %s" - name (OpamPackage.Set.to_string has_version) + let invariant = + List.fold_left (fun acc str -> + try + let (name, _) as atom = OpamFormula.atom_of_string str in + if OpamPackage.Set.exists (OpamFormula.check atom) + (OpamPackage.packages_of_name packages name) + then OpamFormula.ands [acc; Atom atom] + else raise Not_found + with Failure _ | Not_found -> + try + let v = OpamPackage.Version.of_string str in + let candidates = + OpamPackage.Set.filter (fun nv -> nv.version = v) + compiler_packages + in + if OpamPackage.Set.is_empty candidates then + raise Not_found + else + let disj = + OpamPackage.Set.fold + (fun nv acc -> + OpamFormula.ors + [acc; Atom (OpamSolution.eq_atom_of_package nv)]) + candidates OpamFormula.Empty + in + OpamFormula.ands [acc; disj] + with + | Failure _ -> + OpamConsole.error_and_exit `Bad_arguments + "Invalid package specification or version %S" + str + | Not_found -> + OpamConsole.error_and_exit `Not_found + "No compiler matching `%s' found, use `opam switch list-available' \ + to see what is available, or use `--packages' to select packages \ + explicitly." + str) + OpamFormula.Empty + strings + in + OpamFormula.of_atom_formula invariant diff -Nru opam-2.0.10/src/client/opamSwitchCommand.mli opam-2.1.2/src/client/opamSwitchCommand.mli --- opam-2.0.10/src/client/opamSwitchCommand.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/client/opamSwitchCommand.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -14,24 +14,30 @@ open OpamTypes open OpamStateTypes -(** Install a new switch, with the given packages set as compiler. The given - [global_state] is unlocked as soon as possible, i.e. after registering the - existence of the new switch. [update_config] sets the switch as current - globally, unless it is external *) -val install: +(** Creates and configures a new switch. The given [global_state] is unlocked + once done. [update_config] sets the switch as current globally, unless it is + external. + + [post] can be used to run guarded operations after the switch creation + (cleanup will be proposed to the user if they fail). You probably want to + call [install_compiler] there. *) +val create: rw global_state -> - ?rt:'a repos_state -> + rt:'a repos_state -> ?synopsis:string -> ?repos:repository_name list -> update_config:bool -> - packages:atom conjunction -> - ?local_compiler:bool -> + invariant:formula -> switch -> - unlocked global_state * rw switch_state + (rw switch_state -> 'ret * rw switch_state) -> + 'ret * rw switch_state -(** Install a compiler's base packages *) -val install_compiler_packages: - rw switch_state -> atom conjunction -> rw switch_state +(** Used to initially install a compiler's base packages, according to its + invariant. [ask] triggers prompting the user as for normal installs; + defaults to [false]. *) +val install_compiler: + ?additional_installs:atom list -> ?deps_only:bool -> ?ask:bool -> + rw switch_state -> rw switch_state (** Import a file which contains the packages to install. *) val import: @@ -39,24 +45,40 @@ OpamFile.SwitchExport.t OpamFile.t option -> rw switch_state -(** Export a file which contains the installed packages. If full is specified +(** Export a file which contains the installed packages. If [full] is specified and true, export metadata of all installed packages (excluding overlay - files) as part of the export. [None] means export to stdout. *) -val export: ?full:bool -> OpamFile.SwitchExport.t OpamFile.t option -> unit + files) as part of the export. The export will be extended with a map of all + extra-files. If [freeze] is specified and true, VCS urls will be frozen to + the specific commit ID. If [None] is provided as file argument, the export + is done to stdout. *) +val export: + 'a repos_state -> + ?freeze:bool -> + ?full:bool -> + ?switch:switch -> + OpamFile.SwitchExport.t OpamFile.t option -> + unit (** Remove the given compiler switch, and returns the updated state (unchanged in case [confirm] is [true] and the user didn't confirm) *) val remove: rw global_state -> ?confirm:bool -> switch -> rw global_state (** Changes the currently active switch *) -val switch: 'a lock -> rw global_state -> switch -> 'a switch_state +val switch: 'a lock -> rw global_state -> switch -> unit (** Reinstall the given compiler switch. *) val reinstall: rw switch_state -> rw switch_state -(** Sets the packages configured as the current switch compiler base *) -val set_compiler: - rw switch_state -> (name * version option) list -> rw switch_state +(** Updates the switch invariant and the associated config files, and writes the + config file unless [show] or [dry_run] are activated globally. Low-level + function, see [set_invariant] for the user-facing function. *) +val set_invariant_raw: + rw switch_state -> formula -> rw switch_state + +(** Sets the packages configured as the current switch compiler base, after some + checks and messages. *) +val set_invariant: + ?force:bool -> rw switch_state -> formula -> rw switch_state (** Display the current compiler switch. *) val show: unit -> unit @@ -68,8 +90,9 @@ val get_compiler_packages: ?repos:repository_name list -> 'a repos_state -> package_set -(** Guess the compiler from the switch name: within compiler packages, - match [name] against "pkg.version", "pkg", and, as a last resort, - "version" (for compat with older opams, eg. 'opam switch 4.02.3') *) -val guess_compiler_package: - ?repos:repository_name list -> 'a repos_state -> string -> atom list +(** Guess a real compiler spec from a list of strings, which may refer to + packages with optional version constraints, or just versions. + This uses some heuristics. *) +val guess_compiler_invariant: + ?repos:repository_name list -> 'a repos_state -> string list -> OpamFormula.t + diff -Nru opam-2.0.10/src/core/dune opam-2.1.2/src/core/dune --- opam-2.0.10/src/core/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/dune 2021-12-07 16:09:27.000000000 +0000 @@ -3,22 +3,28 @@ (public_name opam-core) (synopsis "OCaml Package Manager core internal stdlib") (libraries re ocamlgraph unix bigarray + (select opamACL.ml from + (opam-core.libacl -> opamACL.ml.libacl) + ( -> opamACL.ml.dummy)) (select opamStubs.ml from (opam-core.stubs -> opamStubs.ml.win32) ( -> opamStubs.ml.dummy))) (flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp))) (preprocess (per_module - ((action (run %{bin:cppo} -V OCAML:%{ocaml_version} %{input-file})) opamCompat))) + ((action (run %{bin:cppo} -V OCAML:%{ocaml_version} %{input-file})) opamCompat) + ((action (run %{bin:cppo} %{read-lines:developer} %{input-file})) opamCoreConfig) + ((action (run %{bin:cppo} -D "VERSION %{read-lines:version}" %{input-file})) opamVersion))) (wrapped false)) (rule - (targets opamVersion.ml) - (deps (:input opamVersion.ml.in) (:script ../../shell/subst_var.ml) ../../config.status) - (action (with-stdout-to %{targets} (run ocaml %{script} PACKAGE_VERSION "" %{input})))) + (targets version) + (deps ../../shell/get_version.ml ../../configure.ac) + (action (with-stdout-to %{targets} (run ocaml ../../shell/get_version.ml ../../configure.ac)))) (rule - (targets opamCoreConfig.ml) - (deps (:input opamCoreConfig.ml.in) (:script ../../shell/subst_var.ml) ../../config.status) - (action (with-stdout-to %{targets} (run ocaml %{script} DEVELOPER false %{input})))) + (targets developer) + (mode fallback) + (action (with-stdout-to %{targets} (echo "")))) diff -Nru opam-2.0.10/src/core/opamACL.ml.dummy opam-2.1.2/src/core/opamACL.ml.dummy --- opam-2.0.10/src/core/opamACL.ml.dummy 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/core/opamACL.ml.dummy 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,11 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 David Allsopp Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +let get_acl_executable_info _ _ = None diff -Nru opam-2.0.10/src/core/opamACL.mli opam-2.1.2/src/core/opamACL.mli --- opam-2.0.10/src/core/opamACL.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/core/opamACL.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,22 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 David Allsopp Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** C auxiliary function used for POSIX 1003.1e DRAFT 17 permission checking. *) + +val get_acl_executable_info : string -> int -> int list option + (** If compiled without libacl support, this function always returns None + When opam is built with libacl support, + [get_acl_executable_info file owner] takes a filename and the uid of the + owner of that file (this is passed since the caller will have already + called {!Unix.stat}). The function returns [Some []] if the process can + execute [file] or [Some gids] if the process can execute [file] if one + of its groups matches [gids]. If the process cannot under any + circumstances execute [file] (or if an unexpected error occurred), then + [None] is returned. *) diff -Nru opam-2.0.10/src/core/opamACL.ml.libacl opam-2.1.2/src/core/opamACL.ml.libacl --- opam-2.0.10/src/core/opamACL.ml.libacl 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/core/opamACL.ml.libacl 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,11 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 David Allsopp Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +include OpamlibACL diff -Nru opam-2.0.10/src/core/opamCached.ml opam-2.1.2/src/core/opamCached.ml --- opam-2.0.10/src/core/opamCached.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/core/opamCached.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,105 @@ +(**************************************************************************) +(* *) +(* Copyright 2012-2020 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamStd.Op + +module type ARG = sig + type t + val name: string +end + +module Make (X: ARG): sig + + type t = X.t + + val save: OpamFilename.t -> t -> unit + + val load: OpamFilename.t -> t option + + val remove: OpamFilename.t -> unit + +end = struct + + let log fmt = OpamConsole.log (Printf.sprintf "CACHE(%s)" X.name) fmt + let slog = OpamConsole.slog + + type t = X.t + + let check_marshaled_file fd = + try + let ic = Unix.in_channel_of_descr fd in + let this_magic = OpamVersion.magic () in + let magic_len = String.length this_magic in + let file_magic = + let b = Bytes.create magic_len in + really_input ic b 0 magic_len; + Bytes.to_string b in + if not OpamCoreConfig.developer && + file_magic <> this_magic then ( + log "Bad %s cache: incompatible magic string %S (expected %S)." + X.name file_magic this_magic; + None + ) else + Some ic + with e -> + OpamStd.Exn.fatal e; + log "Bad %s cache: %s" X.name (Printexc.to_string e); + None + + let marshal_from_file file fd = + let chrono = OpamConsole.timer () in + let f ic = + try + let (cache: t) = Marshal.from_channel ic in + log "Loaded %a in %.3fs" (slog OpamFilename.to_string) file (chrono ()); + Some cache + with End_of_file | Failure _ -> + log "Bad %s cache: likely a truncated file, ignoring." X.name; + None + in + OpamStd.Option.Op.(check_marshaled_file fd >>= f) + + let load cache_file = + match OpamFilename.opt_file cache_file with + | Some file -> + let r = + OpamFilename.with_flock `Lock_read file @@ fun fd -> + marshal_from_file file fd + in + if r = None then begin + log "Invalid %s cache, removing" X.name; + OpamFilename.remove file + end; + r + | None -> None + + let save cache_file t = + if OpamCoreConfig.(!r.safe_mode) then + log "Running in safe mode, not upgrading the %s cache" X.name + else + try + let chrono = OpamConsole.timer () in + OpamFilename.with_flock `Lock_write cache_file @@ fun fd -> + log "Writing the %s cache to %s ..." + X.name (OpamFilename.prettify cache_file); + let oc = Unix.out_channel_of_descr fd in + output_string oc (OpamVersion.magic ()); + Marshal.to_channel oc t []; + flush oc; + log "%a written in %.3fs" (slog OpamFilename.prettify) cache_file (chrono ()) + with Unix.Unix_error _ -> + log "Could not acquire lock for writing %s, skipping %s cache update" + (OpamFilename.prettify cache_file) X.name + + let remove cache_file = + OpamFilename.remove cache_file + +end diff -Nru opam-2.0.10/src/core/opamCached.mli opam-2.1.2/src/core/opamCached.mli --- opam-2.0.10/src/core/opamCached.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/core/opamCached.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,33 @@ +(**************************************************************************) +(* *) +(* Copyright 2012-2020 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** Argument type for the [Make] functor *) +module type ARG = sig + type t + val name: string +end + +(** Module handling a simple marshalled cache for a given data structure. A + magic number is added to validate the version of the cache. *) +module Make (X: ARG): sig + + type t = X.t + + (** Marshal and write the cache to disk *) + val save: OpamFilename.t -> t -> unit + + (** Load the cache if it exists and is valid and compatible with the current + binary. Clear it otherwise. *) + val load: OpamFilename.t -> t option + + val remove: OpamFilename.t -> unit + +end diff -Nru opam-2.0.10/src/core/opamCompat.ml opam-2.1.2/src/core/opamCompat.ml --- opam-2.0.10/src/core/opamCompat.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamCompat.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2018 OCamlPro *) +(* Copyright 2018-2020 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -29,6 +29,7 @@ include Char let lowercase_ascii = lowercase + let uppercase_ascii = uppercase end #endif @@ -121,6 +122,19 @@ end #endif +module Result = +#if OCAML_VERSION >= (4, 8, 0) + Result +#else +struct + type ('a, 'e) t +#if OCAML_VERSION >= (4, 3, 0) + = ('a, 'e) result +#endif + = Ok of 'a | Error of 'e +end +#endif + #if OCAML_VERSION < (4, 7, 0) module Stdlib = Pervasives #endif diff -Nru opam-2.0.10/src/core/opamCompat.mli opam-2.1.2/src/core/opamCompat.mli --- opam-2.0.10/src/core/opamCompat.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamCompat.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2018 OCamlPro *) +(* Copyright 2018-2020 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -29,6 +29,7 @@ include module type of struct include Char end val lowercase_ascii: char -> char + val uppercase_ascii: char -> char end #endif @@ -101,6 +102,19 @@ end #endif +module Result +#if OCAML_VERSION >= (4, 8, 0) += Result +#else +: sig + type ('a, 'e) t +#if OCAML_VERSION >= (4, 3, 0) + = ('a, 'e) result +#endif + = Ok of 'a | Error of 'e +end +#endif + #if OCAML_VERSION < (4, 7, 0) module Stdlib = Pervasives #endif diff -Nru opam-2.0.10/src/core/opamConsole.ml opam-2.1.2/src/core/opamConsole.ml --- opam-2.0.10/src/core/opamConsole.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamConsole.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -13,7 +13,7 @@ (* Global configuration *) -let debug () = OpamCoreConfig.(!r.debug_level) > 0 +let debug () = abs OpamCoreConfig.(!r.debug_level) > 0 let verbose () = OpamCoreConfig.(!r.verbose_level) > 0 @@ -97,6 +97,9 @@ let upwards_arrow = Uchar.of_int 0x2191 let downwards_arrow = Uchar.of_int 0x2193 let up_down_arrow = Uchar.of_int 0x2195 + let downwards_double_arrow = Uchar.of_int 0x21d3 + let black_down_pointing_triangle = Uchar.of_int 0x25bc + let downwards_black_arrow = Uchar.of_int 0x2b07 end type win32_glyph_checker = { @@ -171,7 +174,7 @@ s let timer () = - if debug () then + if OpamCoreConfig.(!r.debug_level) > 0 then let t = Unix.gettimeofday () in fun () -> Unix.gettimeofday () -. t else @@ -227,40 +230,51 @@ else String.make (w-String.length str) ' ' let acolor c () = colorise c -let acolor_w width c oc s = - output_string oc (acolor_with_width (Some width) c () s) +let acolor_w width c f s = + Format.pp_print_string f (acolor_with_width (Some width) c () s) -type win32_color_mode = Shim | VT100 +type win32_color_mode = Shim | VT100 of (unit -> unit) type _ shim_return = - | Handle : (OpamStubs.handle * win32_color_mode) shim_return + | Handle : (OpamStubs.stdhandle * win32_color_mode) shim_return | Mode : win32_color_mode shim_return - | Peek : (win32_color_mode -> bool) shim_return + | Peek : bool shim_return + +let force_win32_vt100 handle () = + try + let hConsoleOutput = OpamStubs.getStdHandle handle in + let mode = OpamStubs.getConsoleMode hConsoleOutput in + (* ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 *) + let vt100_on = 0x4 in + if mode land vt100_on = 0 then + OpamStubs.setConsoleMode hConsoleOutput (mode lor vt100_on) |> ignore + with Not_found -> () let enable_win32_vt100 ch = - let hConsoleOutput = - OpamStubs.getStdHandle ch - in try + let hConsoleOutput = OpamStubs.getStdHandle ch in let mode = OpamStubs.getConsoleMode hConsoleOutput in (* ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x4 *) let vt100_on = 0x4 in if mode land vt100_on <> 0 then - (hConsoleOutput, VT100) + (ch, VT100(force_win32_vt100 ch)) else if OpamStubs.setConsoleMode hConsoleOutput (mode lor vt100_on) then begin let restore_console () = - let mode = - OpamStubs.getConsoleMode hConsoleOutput land (lnot vt100_on) - in - OpamStubs.setConsoleMode hConsoleOutput mode |> ignore + try + let hConsoleOutput = OpamStubs.getStdHandle ch in + let mode = + OpamStubs.getConsoleMode hConsoleOutput land (lnot vt100_on) + in + OpamStubs.setConsoleMode hConsoleOutput mode |> ignore + with Not_found -> () in at_exit restore_console; - (hConsoleOutput, VT100) + (ch, VT100(force_win32_vt100 ch)) end else - (hConsoleOutput, Shim) + (ch, Shim) with Not_found -> - (hConsoleOutput, VT100) + (ch, VT100 ignore) let stdout_state = lazy (enable_win32_vt100 OpamStubs.STD_OUTPUT_HANDLE) let stderr_state = lazy (enable_win32_vt100 OpamStubs.STD_ERROR_HANDLE) @@ -274,11 +288,12 @@ | Mode -> Lazy.force ch |> snd | Peek -> - fun mode -> - if Lazy.is_val ch then - snd (Lazy.force ch) = mode - else - false + if Lazy.is_val ch then + match Lazy.force ch with + | (_, Shim) -> false + | (_, VT100 force) -> force (); true + else + false (* * Layout of attributes (wincon.h) @@ -310,14 +325,17 @@ | `stdout -> stdout | `stderr -> stderr in - if get_win32_console_shim ch Peek VT100 then + if get_win32_console_shim ch Peek then Printf.fprintf ocaml_ch "%s%!" msg else - let (hConsoleOutput, mode) = get_win32_console_shim ch Handle in - if mode = VT100 then begin + let (ch, mode) = get_win32_console_shim ch Handle in + match mode with + | VT100 force -> + force (); output_string ocaml_ch msg; flush ocaml_ch - end else + | Shim -> + let hConsoleOutput = OpamStubs.getStdHandle ch in let {OpamStubs.attributes; _} = OpamStubs.getConsoleScreenBufferInfo hConsoleOutput in @@ -415,15 +433,16 @@ print_string "\r\027[K" let carriage_delete_windows () = - let (hConsoleOutput, mode) = get_win32_console_shim `stdout Handle in - match mode with - | Shim -> + match get_win32_console_shim `stdout Handle with + | (ch, Shim) -> + let hConsoleOutput = OpamStubs.getStdHandle ch in let {OpamStubs.size = (w, _); cursorPosition = (_, row); _} = OpamStubs.getConsoleScreenBufferInfo hConsoleOutput in Printf.printf "\r%!"; OpamStubs.fillConsoleOutputCharacter hConsoleOutput '\000' w (0, row) |> ignore - | VT100 -> + | (_, VT100 force) -> + force (); carriage_delete_unix () let carriage_delete = @@ -432,8 +451,10 @@ match get_win32_console_shim `stdout Mode with | Shim -> carriage_delete_windows - | VT100 -> - carriage_delete_unix) + | VT100 force -> + fun () -> + force (); + carriage_delete_unix ()) in fun () -> Lazy.force carriage_delete () else @@ -455,8 +476,10 @@ fun () -> carriage_delete_windows (); displaying_status := false - | VT100 -> - clear_status_unix) + | VT100 force -> + fun () -> + force (); + clear_status_unix ()) in fun () -> Lazy.force clear_status () @@ -492,27 +515,62 @@ tm.Unix.tm_sec (int_of_float (1000.0 *. msec)) +let log_formatter, finalise_output = + if Sys.win32 then + let b = Buffer.create 128 in + let f _ = + win32_print_message `stderr (Buffer.contents b); + Buffer.clear b + in + Format.formatter_of_buffer b, f + else + Format.formatter_of_out_channel stderr, ignore + +let () = + Format.pp_set_margin log_formatter 0 + +let pending = Queue.create () + +let clear_pending debug_level = + let f (level, timestamp, msg) = + if level <= abs debug_level then + let timestamp = if debug_level < 0 then "" else timestamp in + Format.kfprintf finalise_output log_formatter "%s%s%!" timestamp msg + in + Queue.iter f pending; + Queue.clear pending + let log section ?(level=1) fmt = - if level <= OpamCoreConfig.(!r.debug_level) then - let () = clear_status () in - if Sys.win32 then begin - (* - * In order not to break [slog], split the output into two. A side-effect - * of this is that logging lines may not use colour. - *) - win32_print_message `stderr (Printf.sprintf "%s %a " - (timestamp ()) (acolor_with_width (Some 30) `yellow) section); - Printf.fprintf stderr (fmt ^^ "\n%!") end + let debug_level = + let debug_level = OpamCoreConfig.(!r.debug_level) in + let sections = OpamCoreConfig.(!r.debug_sections) in + if OpamStd.String.Map.is_empty sections then + debug_level else - Printf.fprintf stderr ("%s %a " ^^ fmt ^^ "\n%!") - (timestamp ()) (acolor_w 30 `yellow) section + match OpamStd.String.Map.find section sections with + | Some level -> level + | None -> debug_level + | exception Not_found -> 0 + in + if not OpamCoreConfig.(!r.set) then + let b = Buffer.create 128 in + let timestamp = timestamp () ^ " " in + let k _ = Queue.push (level, timestamp, Buffer.contents b) pending in + Format.kfprintf k (Format.formatter_of_buffer b) ("%a " ^^ fmt ^^ "\n%!") + (acolor_w 30 `yellow) section + else if level <= abs debug_level then + let () = clear_status () in + let () = clear_pending debug_level in + let timestamp = if debug_level < 0 then "" else timestamp () ^ " " in + Format.kfprintf finalise_output log_formatter ("%s%a " ^^ fmt ^^ "\n%!") + timestamp (acolor_w 30 `yellow) section else - Printf.ifprintf stderr fmt + Format.ifprintf Format.err_formatter fmt (* Helper to pass stringifiers to log (use [log "%a" (slog to_string) x] rather than [log "%s" (to_string x)] to avoid costly unneeded stringifications *) -let slog to_string channel x = output_string channel (to_string x) +let slog to_string f x = Format.pp_print_string f (to_string x) let error fmt = Printf.ksprintf (fun str -> @@ -549,7 +607,7 @@ let last_status = ref "" -let write_status_unix fmt = +let write_status_unix print_string fmt = let print_string s = print_string s; flush stdout; @@ -569,18 +627,18 @@ let win32_print_functions = lazy ( match get_win32_console_shim `stdout Mode with | Shim -> - (true, (fun s -> win32_print_message `stdout (s ^ "\n"))) - | VT100 -> - (false, print_endline)) + (true, (fun s -> win32_print_message `stdout (s ^ "\n")), print_string) + | VT100 force -> + (false, (fun s -> force (); print_endline s), (fun s -> force (); print_string s))) let status_line fmt = let batch = debug () || not (disp_status_line ()) in - let (use_shim, print_msg) = + let (use_shim, print_msg, print_string) = if Sys.win32 then Lazy.force win32_print_functions else - (false, print_endline) + (false, print_endline, print_string) in if batch then Printf.ksprintf @@ -590,7 +648,7 @@ if use_shim then write_status_windows fmt else - write_status_unix fmt + write_status_unix print_string fmt let header_width () = min 80 (OpamStd.Sys.terminal_columns ()) @@ -642,16 +700,17 @@ ) fmt -let confirm ?(default=true) fmt = +let confirm ?(require_unsafe_yes=false) ?(default=true) fmt = Printf.ksprintf (fun s -> try if OpamCoreConfig.(!r.safe_mode) then false else let prompt () = formatted_msg "%s [%s] " s (if default then "Y/n" else "y/N") in - if OpamCoreConfig.(!r.answer) = Some true then + if (require_unsafe_yes && OpamCoreConfig.answer_is `unsafe_yes) + || (not require_unsafe_yes && OpamCoreConfig.answer_is_yes ()) then (prompt (); msg "y\n"; true) - else if OpamCoreConfig.(!r.answer) = Some false || + else if OpamCoreConfig.answer_is `all_no || OpamStd.Sys.(not tty_in) then (prompt (); msg "n\n"; false) @@ -705,7 +764,7 @@ let read fmt = Printf.ksprintf (fun s -> formatted_msg "%s " s; - if OpamCoreConfig.(!r.answer = None && not !r.safe_mode) then ( + if OpamCoreConfig.(answer_is `ask && not !r.safe_mode) then ( try match read_line () with | "" -> None | s -> Some s diff -Nru opam-2.0.10/src/core/opamConsole.mli opam-2.1.2/src/core/opamConsole.mli --- opam-2.0.10/src/core/opamConsole.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamConsole.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -43,7 +43,7 @@ val colorise : text_style -> string -> string val colorise' : text_style list -> string -> string val acolor : text_style -> unit -> string -> string -val acolor_w : int -> text_style -> out_channel -> string -> unit +val acolor_w : int -> text_style -> Format.formatter -> string -> unit module Symbols : sig val rightwards_arrow : OpamCompat.Uchar.t @@ -63,6 +63,9 @@ val upwards_arrow : OpamCompat.Uchar.t val downwards_arrow : OpamCompat.Uchar.t val up_down_arrow : OpamCompat.Uchar.t + val downwards_double_arrow : OpamCompat.Uchar.t + val downwards_black_arrow : OpamCompat.Uchar.t + val black_down_pointing_triangle : OpamCompat.Uchar.t end val utf8_symbol: @@ -76,12 +79,12 @@ (** [log section ~level fmt args]. Used for debug messages, default level is 1 *) -val log : string -> ?level:int -> ('a, out_channel, unit) format -> 'a +val log : string -> ?level:int -> ('a, Format.formatter, unit) format -> 'a (** Helper to pass stringifiers to log (use [log "%a" (slog to_string) x] rather than [log "%s" (to_string x)] to avoid costly unneeded stringifications *) -val slog : ('a -> string) -> out_channel -> 'a -> unit +val slog : ('a -> string) -> Format.formatter -> 'a -> unit val error : ('a, unit, string, unit) format4 -> 'a val warning : ('a, unit, string, unit) format4 -> 'a @@ -111,8 +114,14 @@ val clear_status : unit -> unit (** Ask the user to press Y/y/N/n to continue (returns a boolean). - Defaults to true (yes) if unspecified *) -val confirm: ?default:bool -> ('a, unit, string, bool) format4 -> 'a + Defaults to true (yes) if unspecified. + If [require_unsafe_yes] is true, it automatically answer yes to the + question if automatic answering is set to [`unsafe_yes] ; otherwise it will + prompt and wait user input if it is set [`all_yes] (interactive). Its + default is false. *) +val confirm: + ?require_unsafe_yes:bool -> ?default:bool -> + ('a, unit, string, bool) format4 -> 'a (** Read some input from the user (returns a string option) *) val read: ('a, unit, string, string option) format4 -> 'a diff -Nru opam-2.0.10/src/core/opamCoreConfig.ml opam-2.1.2/src/core/opamCoreConfig.ml --- opam-2.0.10/src/core/opamCoreConfig.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/core/opamCoreConfig.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,213 @@ +(**************************************************************************) +(* *) +(* Copyright 2015-2018 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamCompat + +module E = struct + + type OpamStd.Config.E.t += + | COLOR of OpamStd.Config.when_ option + | CONFIRMLEVEL of OpamStd.Config.answer option + | DEBUG of int option + | DEBUGSECTIONS of OpamStd.Config.sections option + | ERRLOGLEN of int option + | KEEPLOGS of bool option + | LOGS of string option + | MERGEOUT of bool option + | NO of bool option + | PRECISETRACKING of bool option + | SAFE of bool option + | STATUSLINE of OpamStd.Config.when_ option + | USEOPENSSL of bool option + | UTF8 of OpamStd.Config.when_ext option + | UTF8MSGS of bool option + | VERBOSE of OpamStd.Config.level option + | YES of bool option + + open OpamStd.Config.E + let color = value (function COLOR c -> c | _ -> None) + let confirmlevel = value (function CONFIRMLEVEL c -> c | _ -> None) + let debug = value (function DEBUG i -> i | _ -> None) + let debugsections = value (function DEBUGSECTIONS s -> s | _ -> None) + let errloglen = value (function ERRLOGLEN i -> i | _ -> None) + let keeplogs = value (function KEEPLOGS b -> b | _ -> None) + let logs = value (function LOGS s -> s | _ -> None) + let mergeout = value (function MERGEOUT b -> b | _ -> None) + let no = value (function NO b -> b | _ -> None) + let precisetracking = value (function PRECISETRACKING b -> b | _ -> None) + let safe = value (function SAFE b -> b | _ -> None) + let statusline = value (function STATUSLINE c -> c | _ -> None) + let useopenssl = value (function USEOPENSSL b -> b | _ -> None) + let utf8 = value (function UTF8 c -> c | _ -> None) + let utf8msgs = value (function UTF8MSGS b -> b | _ -> None) + let verbose = value (function VERBOSE l -> l | _ -> None) + let yes = value (function YES b -> b | _ -> None) + +end + +type t = { + debug_level: int; + debug_sections: OpamStd.Config.sections; + verbose_level: OpamStd.Config.level; + color: OpamStd.Config.when_; + utf8: OpamStd.Config.when_ext; + disp_status_line: OpamStd.Config.when_; + confirm_level: [ OpamStd.Config.answer | `undefined ]; + yes: bool option; + safe_mode: bool; + log_dir: string; + keep_log_dir: bool; + errlog_length: int; + merged_output: bool; + use_openssl: bool; + precise_tracking: bool; + set: bool; +} + +type 'a options_fun = + ?debug_level:int -> + ?debug_sections:OpamStd.Config.sections -> + ?verbose_level:OpamStd.Config.level -> + ?color:OpamStd.Config.when_ -> + ?utf8:OpamStd.Config.when_ext -> + ?disp_status_line:OpamStd.Config.when_ -> + ?confirm_level:OpamStd.Config.answer -> + ?yes:bool option -> + ?safe_mode:bool -> + ?log_dir:string -> + ?keep_log_dir:bool -> + ?errlog_length:int -> + ?merged_output:bool -> + ?use_openssl:bool -> + ?precise_tracking:bool -> + 'a + +let default = { + debug_level = 0; + debug_sections = OpamStd.String.Map.empty; + verbose_level = 0; + color = `Auto; + utf8 = `Auto; + disp_status_line = `Auto; + confirm_level = `undefined; + yes = None; + safe_mode = false; + log_dir = + (let user = try Unix.getlogin() with Unix.Unix_error _ -> "xxx" in + let base = Printf.sprintf "opam-%s-%d" user (OpamStubs.getpid()) in + Filename.(concat (get_temp_dir_name ()) base)); + keep_log_dir = false; + errlog_length = 12; + merged_output = true; + use_openssl = true; + precise_tracking = false; + set = false; +} + +let setk k t + ?debug_level + ?debug_sections + ?verbose_level + ?color + ?utf8 + ?disp_status_line + ?confirm_level + ?yes + ?safe_mode + ?log_dir + ?keep_log_dir + ?errlog_length + ?merged_output + ?use_openssl + ?precise_tracking + = + let (+) x opt = match opt with Some x -> x | None -> x in + k { + debug_level = t.debug_level + debug_level; + debug_sections = t.debug_sections + debug_sections; + verbose_level = t.verbose_level + verbose_level; + color = t.color + color; + utf8 = t.utf8 + utf8; + disp_status_line = t.disp_status_line + disp_status_line; + confirm_level = + (match confirm_level with + | Some (`all_yes|`all_no|`ask|`unsafe_yes as c) -> c + | None -> t.confirm_level); + yes = t.yes + yes; + safe_mode = t.safe_mode + safe_mode; + log_dir = t.log_dir + log_dir; + keep_log_dir = t.keep_log_dir + keep_log_dir; + errlog_length = t.errlog_length + errlog_length; + merged_output = t.merged_output + merged_output; + use_openssl = t.use_openssl + use_openssl; + precise_tracking = t.precise_tracking + precise_tracking; + set = true; + } + +let set t = setk (fun x () -> x) t + +(* Global configuration reference *) + +let r = ref default + +let update ?noop:_ = setk (fun cfg () -> r := cfg) !r + +let initk k = + let open OpamStd in + let utf8 = Option.Op.( + E.utf8 () ++ + (E.utf8msgs () >>= function + | true -> Some `Extended + | false -> None) + ) in + let yes = + match E.yes (), E.no () with + | Some true, _ -> Some (Some true) + | _, Some true -> Some (Some false) + | _, _ -> None + in + (setk (setk (fun c -> r := c; k)) !r) + ?debug_level:(E.debug ()) + ?debug_sections:(E.debugsections ()) + ?verbose_level:(E.verbose ()) + ?color:(E.color ()) + ?utf8 + ?disp_status_line:(E.statusline ()) + ?confirm_level:(E.confirmlevel ()) + ?yes + ?safe_mode:(E.safe ()) + ?log_dir:(E.logs ()) + ?keep_log_dir:(E.keeplogs ()) + ?errlog_length:(E.errloglen ()) + ?merged_output:(E.mergeout ()) + ?use_openssl:(E.useopenssl ()) + ?precise_tracking:(E.precisetracking ()) + +let init ?noop:_ = initk (fun () -> ()) + +let answer () = + match !r.confirm_level, !r.yes with + | #OpamStd.Config.answer as c, _ -> c + | _, Some true -> `all_yes + | _, Some false -> `all_no + | _ -> `ask + +let answer_is = + let answer = lazy (answer ()) in + fun a -> Lazy.force answer = a + +let answer_is_yes () = + answer_is `all_yes || answer_is `unsafe_yes + +#ifdef DEVELOPER +let developer = true +#else +let developer = false +#endif diff -Nru opam-2.0.10/src/core/opamCoreConfig.mli opam-2.1.2/src/core/opamCoreConfig.mli --- opam-2.0.10/src/core/opamCoreConfig.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamCoreConfig.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -11,23 +11,53 @@ (** Configuration options for the core lib (record, global reference and setter) *) +module E : sig + type OpamStd.Config.E.t += + | COLOR of OpamStd.Config.when_ option + | CONFIRMLEVEL of OpamStd.Config.answer option + | DEBUG of int option + | DEBUGSECTIONS of OpamStd.Config.sections option + | ERRLOGLEN of int option + | KEEPLOGS of bool option + | LOGS of string option + | MERGEOUT of bool option + | NO of bool option + | PRECISETRACKING of bool option + | SAFE of bool option + | STATUSLINE of OpamStd.Config.when_ option + | USEOPENSSL of bool option + | UTF8 of OpamStd.Config.when_ext option + | UTF8MSGS of bool option + | VERBOSE of OpamStd.Config.level option + | YES of bool option + + val confirmlevel: unit -> OpamStd.Config.answer option + val debug: unit -> int option + val logs: unit -> string option + val yes: unit -> bool option +end + type t = private { debug_level : int; (** Controls debug messages, 0 to disable *) - verbose_level : int; + debug_sections : OpamStd.Config.sections; + (** Controls which sections display debugging messages. If empty, all messages + are displayed. *) + verbose_level : OpamStd.Config.level; (** Controls printing of external commands and output, 0 to disable, more means print more low-level commands *) - color : [ `Always | `Never | `Auto ]; + color : OpamStd.Config.when_; (** Console ANSI color control *) - utf8 : [ `Extended | `Always | `Never | `Auto ]; + utf8 : OpamStd.Config.when_ext; (** Controls usage of UTF8 in OPAM-generated messages. Extended adds camel emojis *) - disp_status_line: [ `Always | `Never | `Auto ]; + disp_status_line: OpamStd.Config.when_; (** Controls on-line display of parallel commands being run, using ANSI escapes *) - answer : bool option; - (** Affects interactive questions in OpamConsole: auto-answer with the given - bool if Some *) + confirm_level : [ OpamStd.Config.answer | `undefined ]; + yes: bool option; + (** Affects interactive questions in OpamConsole: used to compute the + automatic ansering level *) safe_mode : bool; (** Fail on writes or delays, don't ask questions (for quick queries, e.g. for shell completion) *) @@ -45,15 +75,19 @@ precise_tracking : bool; (** If set, will take full md5 of all files when checking diffs (to track installations), rather than rely on just file size and mtime *) + set : bool; + (** Options have not yet been initialised (i.e. defaults are active) *) } type 'a options_fun = ?debug_level:int -> - ?verbose_level:int -> - ?color:[ `Always | `Never | `Auto ] -> - ?utf8:[ `Extended | `Always | `Never | `Auto ] -> - ?disp_status_line:[ `Always | `Never | `Auto ] -> - ?answer:bool option -> + ?debug_sections:OpamStd.Config.sections -> + ?verbose_level:OpamStd.Config.level -> + ?color:OpamStd.Config.when_ -> + ?utf8:OpamStd.Config.when_ext -> + ?disp_status_line:OpamStd.Config.when_ -> + ?confirm_level:OpamStd.Config.answer -> + ?yes:bool option -> ?safe_mode:bool -> ?log_dir:string -> ?keep_log_dir:bool -> @@ -73,5 +107,30 @@ val update : ?noop:_ -> (unit -> unit) options_fun +(** Sets the OpamCoreConfig options, reading the environment to get default + values when unspecified *) +val init: ?noop:_ -> (unit -> unit) options_fun + +(** Like [init], but returns the given value. For optional argument + stacking *) +val initk: 'a -> 'a options_fun + +(** Automatic answering levels + * [`ask]: prompt and ask user + * [`no]: answer no to all opam questions + * [`yes]: answer yes to all opam questions + * [`unsafe_yes]: answer yes to all opam question and launch system package + command wit non interactive options + If confirm-level is set (from cli or environment variable), its value is + returned. Otherwise, is takes last yes/no cli flag. For environment + variables, if [OPAMYES] is set to true, it has priority over [OPAMNO]. As + other environment variables, cli flags content is taken if given. + [answer_is] and [answer_is_yes] computes the answer lazily, use [answer] in + case of config update. +*) +val answer_is: OpamStd.Config.answer -> bool +val answer_is_yes : unit -> bool +val answer: unit -> OpamStd.Config.answer + (** [true] if OPAM was compiled in developer mode *) val developer : bool diff -Nru opam-2.0.10/src/core/opamCoreConfig.ml.in opam-2.1.2/src/core/opamCoreConfig.ml.in --- opam-2.0.10/src/core/opamCoreConfig.ml.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamCoreConfig.ml.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,104 +0,0 @@ -(**************************************************************************) -(* *) -(* Copyright 2015 OCamlPro *) -(* *) -(* All rights reserved. This file is distributed under the terms of the *) -(* GNU Lesser General Public License version 2.1, with the special *) -(* exception on linking described in the file LICENSE. *) -(* *) -(**************************************************************************) - -open OpamCompat - -type t = { - debug_level: int; - verbose_level: int; - color: [ `Always | `Never | `Auto ]; - utf8: [ `Extended | `Always | `Never | `Auto ]; - disp_status_line: [ `Always | `Never | `Auto ]; - answer: bool option; - safe_mode: bool; - log_dir: string; - keep_log_dir: bool; - errlog_length: int; - merged_output: bool; - use_openssl: bool; - precise_tracking: bool; -} - -type 'a options_fun = - ?debug_level:int -> - ?verbose_level:int -> - ?color:[ `Always | `Never | `Auto ] -> - ?utf8:[ `Extended | `Always | `Never | `Auto ] -> - ?disp_status_line:[ `Always | `Never | `Auto ] -> - ?answer:bool option -> - ?safe_mode:bool -> - ?log_dir:string -> - ?keep_log_dir:bool -> - ?errlog_length:int -> - ?merged_output:bool -> - ?use_openssl:bool -> - ?precise_tracking:bool -> - 'a - -let default = { - debug_level = 0; - verbose_level = 0; - color = `Auto; - utf8 = `Auto; - disp_status_line = `Auto; - answer = None; - safe_mode = false; - log_dir = - (let user = try Unix.getlogin() with Unix.Unix_error _ -> "xxx" in - let base = Printf.sprintf "opam-%s-%d" user (OpamStubs.getpid()) in - Filename.(concat (get_temp_dir_name ()) base)); - keep_log_dir = false; - errlog_length = 12; - merged_output = true; - use_openssl = true; - precise_tracking = false; -} - -let setk k t - ?debug_level - ?verbose_level - ?color - ?utf8 - ?disp_status_line - ?answer - ?safe_mode - ?log_dir - ?keep_log_dir - ?errlog_length - ?merged_output - ?use_openssl - ?precise_tracking - = - let (+) x opt = match opt with Some x -> x | None -> x in - k { - debug_level = t.debug_level + debug_level; - verbose_level = t.verbose_level + verbose_level; - color = t.color + color; - utf8 = t.utf8 + utf8; - disp_status_line = t.disp_status_line + disp_status_line; - answer = t.answer + answer; - safe_mode = t.safe_mode + safe_mode; - log_dir = t.log_dir + log_dir; - keep_log_dir = t.keep_log_dir + keep_log_dir; - errlog_length = t.errlog_length + errlog_length; - merged_output = t.merged_output + merged_output; - use_openssl = t.use_openssl + use_openssl; - precise_tracking = t.precise_tracking + precise_tracking; - } - -let set t = setk (fun x () -> x) t - -(* Global configuration reference *) - -let r = ref default - -let update ?noop:_ = setk (fun cfg () -> r := cfg) !r - -let developer = @DEVELOPER@ diff -Nru opam-2.0.10/src/core/opamDirTrack.ml opam-2.1.2/src/core/opamDirTrack.ml --- opam-2.0.10/src/core/opamDirTrack.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamDirTrack.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2020 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -31,12 +31,17 @@ type t = change SM.t -let string_of_change = function - | Added _ -> "addition" +let string_of_change ?(full=false) = + let str s d = + if not full then s else + Printf.sprintf "%s %s" s (string_of_digest d) + in + function + | Added d -> str "addition" d | Removed -> "removal" - | Contents_changed _ -> "modifications" - | Perm_changed _ -> "permission change" - | Kind_changed _ -> "kind change" + | Contents_changed d -> str "modifications" d + | Perm_changed d -> str "permission change" d + | Kind_changed d -> str "kind change" d let to_string t = OpamStd.Format.itemize (fun (f, change) -> @@ -85,6 +90,10 @@ | Unix.S_CHR | Unix.S_BLK | Unix.S_FIFO | Unix.S_SOCK -> Special Unix.(stats.st_dev, stats.st_rdev) +let item_of_filename_opt ?precise f = + try Some (item_of_filename ?precise f) + with Unix.Unix_error _ -> None + let item_digest = function | _perms, File d -> "F:" ^ d | _perms, Dir -> "D" @@ -94,9 +103,9 @@ let is_precise_digest d = not (OpamStd.String.starts_with ~prefix:"F:S" d) -let track dir ?(except=OpamFilename.Base.Set.empty) job_f = +let track_t to_track ?(except=OpamFilename.Base.Set.empty) job_f = let module SM = OpamStd.String.Map in - let rec make_index acc prefix dir = + let rec make_index_topdir acc prefix dir = let files = try Sys.readdir (Filename.concat prefix dir) with Sys_error _ as e -> @@ -113,21 +122,35 @@ let item = item_of_filename f in let acc = SM.add rel item acc in match item with - | _, Dir -> make_index acc prefix rel + | _, Dir -> make_index_topdir acc prefix rel | _ -> acc with Unix.Unix_error _ as e -> log "Error at %s: %a" f (slog Printexc.to_string) e; acc) acc files in - let str_dir = OpamFilename.Dir.to_string dir in + let make_index = + match to_track with + | `Top dir -> + fun () -> make_index_topdir SM.empty (OpamFilename.Dir.to_string dir) "" + | `Paths (prefix, files) -> + fun () -> + List.fold_left (fun acc f -> + let prefix = OpamFilename.Dir.to_string prefix in + let rel = Filename.concat prefix f in + let item = item_of_filename_opt rel in + match item with + | None -> acc + | Some item -> SM.add f item acc) + SM.empty files + in let scan_timer = OpamConsole.timer () in - let before = make_index SM.empty str_dir "" in + let before = make_index () in log ~level:2 "before install: %a elements scanned in %.3fs" (slog @@ string_of_int @* SM.cardinal) before (scan_timer ()); job_f () @@| fun result -> let scan_timer = OpamConsole.timer () in - let after = make_index SM.empty str_dir "" in + let after = make_index () in let diff = SM.merge (fun _ before after -> match before, after with @@ -153,6 +176,12 @@ diff (scan_timer ()); result, diff +let track_files ~prefix files ?except job_f = + track_t (`Paths (prefix, files)) ?except job_f + +let track dir ?except job_f = + track_t (`Top dir) ?except job_f + let check_digest file digest = let precise = is_precise_digest digest in let it = item_of_filename ~precise file in @@ -224,14 +253,72 @@ if already <> [] then log ~level:2 "%sfiles %s were already removed" title (String.concat ", " (List.rev already)); - if modified <> [] && verbose then - OpamConsole.warning "%snot removing files that changed since:\n%s" title - (OpamStd.Format.itemize (fun s -> s) (List.rev modified)); + if modified <> [] then + if OpamConsole.confirm ~default:false + "%sthese files have been modified since installation:\n%s\ + Remove them anyway?" title + (OpamStd.Format.itemize (fun s -> s) (List.rev modified)) then + List.iter (fun f -> OpamFilename.remove (OpamFilename.Op.(prefix // f))) + modified; if nonempty <> [] && verbose then OpamConsole.note "%snot removing non-empty directories:\n%s" title (OpamStd.Format.itemize (fun s -> s) (List.rev nonempty)); if cannot <> [] && verbose then - OpamConsole.warning "%scannot revert:\n%s" title - (OpamStd.Format.itemize - (fun (op,f) -> string_of_change op ^" of "^ f) - (List.rev cannot)) + let cannot = + let rem, modf, perm = + List.fold_left (fun (rem, modf, perm as acc) (op,f) -> + match op with + | Removed -> (None, f)::rem, modf, perm + | Contents_changed dg -> + let precise = Some (is_precise_digest dg) in + rem, (precise, f)::modf, perm + | Perm_changed dg -> + let precise = Some (is_precise_digest dg) in + rem, modf, (precise, f)::perm + | _ -> acc) + ([],[],[]) cannot + in + (if rem = [] then [] else [Removed, rem]) + @ (if modf = [] then [] else [Contents_changed "_", modf]) + @ (if perm = [] then [] else [Perm_changed "_", perm]) + in + (OpamConsole.warning "%scannot revert:" title; + OpamConsole.errmsg "%s" + (OpamStd.Format.itemize + (fun (op,lf) -> + Printf.sprintf "%s of:\n%s" + (string_of_change op) + (OpamStd.Format.itemize (fun (pre,x) -> + (OpamStd.Option.to_string (fun pr -> + if pr then "[hash] " else "[tms] ") pre) ^ x) lf)) + cannot)) + +let update prefix t = + let removed = ref [] in + let prefix = OpamFilename.Dir.to_string prefix in + let update_digest file digest = + match + let filename = Filename.concat prefix file in + let precise = is_precise_digest digest in + item_digest ( item_of_filename ~precise filename ) + with + | exception Unix.Unix_error ( ENOENT, _, _) -> + removed := file :: !removed; + digest + | exception _exn -> digest + | digest -> digest + in + let t = + SM.mapi (fun file change -> + match change with + | Added digest -> Added (update_digest file digest) + | Removed -> Removed + | Contents_changed digest -> + Contents_changed (update_digest file digest) + | Perm_changed digest -> Perm_changed (update_digest file digest) + | Kind_changed digest -> Kind_changed (update_digest file digest) + ) t + in + List.fold_left (fun t file -> + SM.remove file t + ) t !removed diff -Nru opam-2.0.10/src/core/opamDirTrack.mli opam-2.1.2/src/core/opamDirTrack.mli --- opam-2.0.10/src/core/opamDirTrack.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamDirTrack.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -31,6 +31,9 @@ val digest_of_string: string -> digest val string_of_digest: digest -> string +(** Return the [change] action, with digest if [full] is set to true *) +val string_of_change: ?full:bool -> change -> string + (** Wraps a job to track the changes that happened under [dirname] during its execution (changes done by the application of the job function to [()] are tracked too, for consistency with jobs without commands) *) @@ -38,6 +41,13 @@ OpamFilename.Dir.t -> ?except:OpamFilename.Base.Set.t -> (unit -> 'a OpamProcess.job) -> ('a * t) OpamProcess.job +(** [track_files prefix paths ?except job] as [track] wraps a job to track + changes for a predefined list of [paths] (files and directories). + [paths] are relative to [prefix]. *) +val track_files: + prefix:OpamFilename.Dir.t -> string list -> ?except:OpamFilename.Base.Set.t -> + (unit -> 'a OpamProcess.job) -> ('a * t) OpamProcess.job + (** Removes the added and kind-changed items unless their contents changed and [force] isn't set, and prints warnings for other changes unless [verbose] is set to [false]. Ignores non-existing files. @@ -51,3 +61,7 @@ val check: OpamFilename.Dir.t -> t -> (OpamFilename.t * [`Unchanged | `Removed | `Changed]) list + +(** Reload all the digests from the directory [prefix]. Remove a file + from the map if it has been removed from the file-system. *) +val update : OpamFilename.Dir.t -> t -> t diff -Nru opam-2.0.10/src/core/opamFilename.ml opam-2.1.2/src/core/opamFilename.ml --- opam-2.0.10/src/core/opamFilename.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamFilename.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -36,7 +36,7 @@ (OpamStd.String.remove_prefix ~prefix:("~"^Filename.dir_sep) dirname) else dirname in - OpamSystem.real_path dirname + OpamSystem.real_path (OpamSystem.forward_to_back dirname) let to_string dirname = dirname @@ -44,6 +44,9 @@ let raw_dir s = s +let mk_tmp_dir () = + Dir.of_string @@ OpamSystem.mk_temp_dir () + let with_tmp_dir fn = OpamSystem.with_tmp_dir (fun dir -> fn (Dir.of_string dir)) @@ -139,8 +142,9 @@ } let create dirname basename = - let b1 = Filename.dirname (Base.to_string basename) in + let b1 = OpamSystem.forward_to_back (Filename.dirname (Base.to_string basename)) in let b2 = Base.of_string (Filename.basename (Base.to_string basename)) in + let dirname = OpamSystem.forward_to_back dirname in if basename = b2 then { dirname; basename } else @@ -164,6 +168,12 @@ let chmod t p = Unix.chmod (to_string t) p +let written_since file = + let last_update = + (Unix.stat (to_string file)).Unix.st_mtime + in + (Unix.time () -. last_update) + let of_string s = let dirname = Filename.dirname s in let basename = Filename.basename s in @@ -183,10 +193,18 @@ try open_in (to_string filename) with Sys_error _ -> raise (OpamSystem.File_not_found (to_string filename)) +let open_in_bin filename = + try open_in_bin (to_string filename) + with Sys_error _ -> raise (OpamSystem.File_not_found (to_string filename)) + let open_out filename = try open_out (to_string filename) with Sys_error _ -> raise (OpamSystem.File_not_found (to_string filename)) +let open_out_bin filename = + try open_out_bin (to_string filename) + with Sys_error _ -> raise (OpamSystem.File_not_found (to_string filename)) + let write filename raw = OpamSystem.write (to_string filename) raw @@ -220,14 +238,18 @@ let fs = OpamSystem.files (Dir.to_string d) in List.rev_map of_string fs +let files_and_links d = + let fs = OpamSystem.files_all_not_dir (Dir.to_string d) in + List.rev_map of_string fs + let copy ~src ~dst = if src <> dst then OpamSystem.copy_file (to_string src) (to_string dst) let copy_dir ~src ~dst = if src <> dst then OpamSystem.copy_dir (Dir.to_string src) (Dir.to_string dst) -let install ?exec ~src ~dst () = - if src <> dst then OpamSystem.install ?exec (to_string src) (to_string dst) +let install ?warning ?exec ~src ~dst () = + if src <> dst then OpamSystem.install ?warning ?exec (to_string src) (to_string dst) let move ~src ~dst = if src <> dst then @@ -310,6 +332,9 @@ let extract_in_job filename dirname = OpamSystem.extract_in_job (to_string filename) ~dir:(Dir.to_string dirname) +let make_tar_gz_job filename dirname = + OpamSystem.make_tar_gz_job (to_string filename) ~dir:(Dir.to_string dirname) + type generic_file = | D of Dir.t | F of t @@ -365,6 +390,7 @@ Filename.concat back forward in OpamSystem.link target (to_string link) +[@@ocaml.warning "-16"] let patch ?preprocess filename dirname = OpamSystem.patch ?preprocess ~dir:(Dir.to_string dirname) (to_string filename) @@ -444,6 +470,9 @@ prettify_path (to_string s) let to_json x = `String (to_string x) +let of_json = function + | `String x -> (try Some (of_string x) with _ -> None) + | _ -> None module O = struct type tmp = t @@ -451,6 +480,7 @@ let compare = compare let to_string = to_string let to_json = to_json + let of_json = of_json end module Map = OpamStd.Map.Make(O) @@ -514,12 +544,31 @@ | None -> [] | Some p -> ["perm", `String (string_of_int p)]) + let of_json = function + | `O dict -> + begin try + let open OpamStd.Option.Op in + Base.of_json (List.assoc "base" dict) >>= fun base -> + OpamHash.of_json (List.assoc "md5" dict) >>= fun md5 -> + let perm = + if not (List.mem_assoc "perm" dict) then None + else match List.assoc "perm" dict with + | `String hash -> + (try Some (int_of_string hash) with _ -> raise Not_found) + | _ -> raise Not_found + in + Some { base; md5; perm } + with Not_found -> None + end + | _ -> None + module O = struct type tmp = t type t = tmp let to_string = to_string let compare = compare let to_json = to_json + let of_json = of_json end module Set = OpamStd.Set.Make(O) diff -Nru opam-2.0.10/src/core/opamFilename.mli opam-2.1.2/src/core/opamFilename.mli --- opam-2.0.10/src/core/opamFilename.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamFilename.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -35,7 +35,9 @@ (** Cleans the contents of a directory, but keeps the directory in place. *) val cleandir: Dir.t -> unit -(** Removes an empty directory, as well as any empty leading path components *) +(** Removes an empty directory, as well as any empty leading path components. + Must be called only on a directory that is known to not have empty parents, + only internal opam directory (and not tmp dir). *) val rmdir_cleanup: Dir.t -> unit (** Create a directory *) @@ -93,6 +95,9 @@ (** Provide an automatically cleaned up temp directory to a job *) val with_tmp_dir_job: (Dir.t -> 'a OpamProcess.job) -> 'a OpamProcess.job +(** Raw function to create a temporary directory. No automatic cleanup *) +val mk_tmp_dir: unit -> Dir.t + (** Create a new Dir.t and resolve symlinks *) val concat_and_resolve: Dir.t -> string -> Dir.t @@ -133,7 +138,9 @@ (** Open a channel from a given file. *) val open_in: t -> in_channel +val open_in_bin: t -> in_channel val open_out: t -> out_channel +val open_out_bin: t -> out_channel (** Removes everything in [filename] if existed. *) val remove: t -> unit @@ -163,6 +170,10 @@ (** List all the filename. Do not recurse. *) val files: Dir.t -> t list +(** Returns alll the files, including dangling symlinks (which means that + not all entries will satisfy {!exists}). *) +val files_and_links: Dir.t -> t list + (** Apply a function on the contents of a file *) val with_contents: (string -> 'a) -> t -> 'a @@ -188,9 +199,9 @@ (** Copy a file *) val copy: src:t -> dst:t -> unit -(** Installs a file to a destination. Optionnally set if the destination should +(** Installs a file to a destination. Optionally set if the destination should be set executable *) -val install: ?exec:bool -> src:t -> dst:t -> unit -> unit +val install: ?warning:OpamSystem.install_warning_fn -> ?exec:bool -> src:t -> dst:t -> unit -> unit (** Symlink a file. If symlink is not possible on the system, use copy instead. With [relative], creates a relative link through the closest common ancestor @@ -212,6 +223,8 @@ val extract_in_job: t -> Dir.t -> exn option OpamProcess.job +val make_tar_gz_job: t -> Dir.t -> exn option OpamProcess.job + (** Extract a generic file *) val extract_generic_file: generic_file -> Dir.t -> unit @@ -245,6 +258,9 @@ (** Change file permissions *) val chmod: t -> int -> unit +(** Returns delay since last file update, based on mtime *) +val written_since: t -> float + (** Returns the closest parent of a directory (including itself) for which the predicate holds, if any *) val find_in_parents: (Dir.t -> bool) -> Dir.t -> Dir.t option @@ -283,7 +299,7 @@ end -(** Simple structure to hanle file attributes *) +(** Simple structure to handle file attributes *) module Attribute: sig include OpamStd.ABSTRACT diff -Nru opam-2.0.10/src/core/opamHash.ml opam-2.1.2/src/core/opamHash.ml --- opam-2.0.10/src/core/opamHash.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamHash.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -80,6 +80,9 @@ String.concat pfx_sep_str [string_of_kind kind; s] let to_json s = `String (to_string s) +let of_json = function +| `String s -> of_string_opt s +| _ -> None let to_path (kind,s) = [string_of_kind kind; String.sub s 0 2; s] @@ -101,7 +104,7 @@ | _ -> log "openssl error, use internal sha library"; OpamSHA.hash kind file - with OpamSystem.Command_not_found _ | OpamSystem.Process_error _ -> + with OpamSystem.Command_not_found _ | OpamSystem.Process_error _ | OpamSystem.Permission_denied _ -> log "openssl not found, use internal sha library"; OpamSHA.hash kind file in @@ -123,6 +126,7 @@ type t = _t let to_string = to_string let to_json = to_json + let of_json = of_json let compare = compare end diff -Nru opam-2.0.10/src/core/opamHash.mli opam-2.1.2/src/core/opamHash.mli --- opam-2.0.10/src/core/opamHash.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamHash.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2017 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) diff -Nru opam-2.0.10/src/core/opamJson.ml opam-2.1.2/src/core/opamJson.ml --- opam-2.0.10/src/core/opamJson.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamJson.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -12,6 +12,9 @@ [ `Null | `Bool of bool | `Float of float| `String of string | `A of t list | `O of (string * t) list ] +type 'a encoder = 'a -> t +type 'a decoder = t -> 'a option + let addc b c = Buffer.add_char b c let adds b s = Buffer.add_string b s let adds_esc b s = diff -Nru opam-2.0.10/src/core/opamJson.mli opam-2.1.2/src/core/opamJson.mli --- opam-2.0.10/src/core/opamJson.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamJson.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -17,6 +17,9 @@ [ `Null | `Bool of bool | `Float of float| `String of string | `A of t list | `O of (string * t) list ] +type 'a encoder = 'a -> t +type 'a decoder = t -> 'a option + val to_string : t -> string val append: string -> t -> unit diff -Nru opam-2.0.10/src/core/opamParallel.ml opam-2.1.2/src/core/opamParallel.ml --- opam-2.0.10/src/core/opamParallel.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamParallel.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -22,8 +22,10 @@ include Graph.Sig.COMPARABLE with type t := t end +type dependency_label = unit + module type G = sig - include Graph.Sig.I + include Graph.Sig.I with type E.label = dependency_label module Vertex: VERTEX with type t = V.t module Topological: sig val fold: (V.t -> 'a -> 'a) -> t -> 'a -> 'a @@ -40,7 +42,7 @@ jobs:int -> command:(pred:(G.V.t * 'a) list -> G.V.t -> 'a OpamProcess.job) -> ?dry_run:bool -> - ?mutually_exclusive:(G.V.t list list) -> + ?pools:((G.V.t list * int) list) -> G.t -> unit @@ -48,7 +50,7 @@ jobs:int -> command:(pred:(G.V.t * 'a) list -> G.V.t -> 'a OpamProcess.job) -> ?dry_run:bool -> - ?mutually_exclusive:(G.V.t list list) -> + ?pools:((G.V.t list * int) list) -> G.t -> (G.V.t * 'a) list @@ -64,19 +66,31 @@ module M = OpamStd.Map.Make (V) module S = OpamStd.Set.Make (V) - let map_keys m = M.fold (fun k _ s -> S.add k s) m S.empty - exception Errors of G.V.t list * (G.V.t * exn) list * G.V.t list exception Cyclic of V.t list list open S.Op (* Returns a map (node -> return value) *) - let aux_map ~jobs ~command ?(dry_run=false) ?(mutually_exclusive=[]) g = + let aux_map ~jobs ~command ?(dry_run=false) ?(pools=[]) g = log "Iterate over %a task(s) with %d process(es)" (slog @@ G.nb_vertex @> string_of_int) g jobs; - let mutually_exclusive = List.map S.of_list mutually_exclusive in + let njobs = G.nb_vertex g in + + let all_jobs = G.fold_vertex S.add g S.empty in + + let pools = + let defined = + List.map (fun (elts, jobs) -> S.of_list elts, jobs) + pools + in + let default = + List.fold_left (fun acc (pool, _) -> acc -- pool) + all_jobs defined, jobs + in + default :: defined + in if G.has_cycle g then ( let sccs = G.scc_list g in @@ -84,8 +98,6 @@ raise (Cyclic sccs) ); - let njobs = G.nb_vertex g in - let print_status (finished: int) (running: (OpamProcess.t * 'a * string option) M.t) = @@ -119,14 +131,25 @@ (* nslots is the number of free slots *) let rec loop - (nslots: int) (* number of free slots *) + (nslots: (S.t * int) list) (* number of free slots *) (results: 'b M.t) (running: (OpamProcess.t * 'a * string option) M.t) (ready: S.t) = - let mutual_exclusion_set n = - List.fold_left (fun acc s -> if S.mem n s then acc ++ s else acc) - S.empty mutually_exclusive + let get_slots nslots n = + List.filter (fun (pool, _) -> S.mem n pool) nslots + in + let take_slot nslots n = + List.map (fun (pool, slots) -> + if S.mem n pool then (assert (slots > 0); pool, slots - 1) + else pool, slots) + nslots + in + let release_slot nslots n = + List.map (fun (pool, slots) -> + if S.mem n pool then (pool, slots + 1) + else pool, slots) + nslots in let run_seq_command nslots ready n = function | Done r -> @@ -135,15 +158,21 @@ let running = M.remove n running in if not (M.is_empty running) then print_status (M.cardinal results) running; + let nslots = release_slot nslots n in let new_ready = S.filter (fun n -> - List.for_all (fun n -> M.mem n results) (G.pred g n) && + not (M.mem n running) && not (M.mem n results) && - S.is_empty (mutual_exclusion_set n %% map_keys running)) - (S.of_list (G.succ g n) ++ mutual_exclusion_set n) + List.for_all (fun n -> M.mem n results) (G.pred g n) && + List.for_all (fun (_, slots) -> slots > 0) + (get_slots nslots n)) + (List.fold_left (fun acc (pool, slots) -> + if slots = 1 then acc ++ pool else acc) + (S.of_list (G.succ g n)) + (get_slots nslots n)) in - loop (nslots + 1) results running (ready ++ new_ready) + loop nslots results running (ready ++ new_ready) | Run (cmd, cont) -> log "Next task in job %a: %a" (slog (string_of_int @* V.hash)) n (slog OpamProcess.string_of_command) cmd; @@ -198,23 +227,47 @@ if M.is_empty running && S.is_empty ready then results - else if nslots > 0 && not (S.is_empty ready) then + else if + not (S.is_empty ready) && + List.exists (fun (_, slots) -> slots > 0) nslots + then (* Start a new process *) let n = S.choose ready in - log "Starting job %a (worker %d/%d): %a" - (slog (string_of_int @* V.hash)) n (jobs - nslots + 1) jobs + log "Starting job %a (worker %a): %a" + (slog (string_of_int @* V.hash)) n + (slog + (fun pools -> + let slots = get_slots nslots n in + OpamStd.List.concat_map " " (fun (pool, jobs) -> + let nslots = + OpamStd.Option.of_Not_found (List.assoc pool) slots + in + Printf.sprintf "%s/%d" + (match nslots with + | None -> "-" + | Some n -> string_of_int (jobs - n + 1)) + jobs) + pools)) + pools (slog V.to_string) n; let pred = G.pred g n in let pred = List.map (fun n -> n, M.find n results) pred in let cmd = try command ~pred n with e -> fail n e in - let ready = S.remove n ready -- mutual_exclusion_set n in - run_seq_command (nslots - 1) ready n cmd + let nslots = take_slot nslots n in + let ready = + List.fold_left + (fun acc (pool, slots) -> + if slots = 0 then acc -- pool else acc) + (S.remove n ready) + (get_slots nslots n) + in + run_seq_command nslots ready n cmd else (* Wait for a process to end *) let processes = M.fold (fun n (p,x,_) acc -> (p,(n,x)) :: acc) running [] in - let process,result = + let process, result = if dry_run then OpamProcess.dry_wait_one (List.map fst processes) else try match processes with @@ -237,15 +290,15 @@ (fun n roots -> if G.in_degree g n = 0 then S.add n roots else roots) g S.empty in - let r = loop jobs M.empty M.empty roots in + let r = loop pools M.empty M.empty roots in OpamConsole.clear_status (); r - let iter ~jobs ~command ?dry_run ?mutually_exclusive g = - ignore (aux_map ~jobs ~command ?dry_run ?mutually_exclusive g) + let iter ~jobs ~command ?dry_run ?pools g = + ignore (aux_map ~jobs ~command ?dry_run ?pools g) - let map ~jobs ~command ?dry_run ?mutually_exclusive g = - M.bindings (aux_map ~jobs ~command ?dry_run ?mutually_exclusive g) + let map ~jobs ~command ?dry_run ?pools g = + M.bindings (aux_map ~jobs ~command ?dry_run ?pools g) (* Only print the originally raised exception, which should come first. Ignore Aborted exceptions due to other commands termination, and simultaneous @@ -259,7 +312,7 @@ end module type GRAPH = sig - include Graph.Sig.I + include Graph.Sig.I with type E.label = dependency_label include Graph.Oper.S with type g = t module Topological : sig val fold : (V.t -> 'a -> 'a) -> t -> 'a -> 'a @@ -269,6 +322,10 @@ and type G.V.t = vertex module Dot : sig val output_graph : out_channel -> t -> unit end val transitive_closure: ?reflexive:bool -> t -> unit + val build: V.t list -> E.t list -> t + val compare : t -> t -> int + val to_json : t OpamJson.encoder + val of_json : t OpamJson.decoder end module MakeGraph (X: VERTEX) = struct @@ -296,8 +353,90 @@ end) include PG include Graph.Oper.I (PG) + let transitive_closure ?reflexive g = ignore (add_transitive_closure ?reflexive g) + + let build vertices edges = + let graph = create ~size:(List.length vertices) () in + List.iter (add_vertex graph) vertices; + List.iter (add_edge_e graph) edges; + graph + + let compare g1 g2 = + let module Vertices = Set.Make(Vertex) in + let module Edges = Set.Make(E) in + let vertices g = fold_vertex Vertices.add g Vertices.empty in + let edges g = fold_edges_e Edges.add g Edges.empty in + match Vertices.compare (vertices g1) (vertices g2) with + | 0 -> Edges.compare (edges g1) (edges g2) + | n -> n + + let to_json (graph : t) : OpamJson.t = + let vertex_map = + (* we ensure that the map indexing respects the vertex ordering *) + let module Vertices = Set.Make(Vertex) in + let vertices = fold_vertex Vertices.add graph Vertices.empty in + List.mapi (fun i v -> (i, v)) (Vertices.elements vertices) + in + let vertices = + let vertex_to_json (i, v) = (string_of_int i, X.to_json v) in + `O (List.map vertex_to_json vertex_map) in + let edges = + let vertex_inv_map = List.map (fun (i, v) -> (v, i)) vertex_map in + let index v = List.assoc v vertex_inv_map in + let index_to_json v = `String (string_of_int (index v)) in + let edge_to_json edge = + let () = E.label edge in + (* labels carry no information; if this changes, + we should add a "label" field in the JSON output *) + `O [ + ("src", index_to_json (E.src edge)); + ("dst", index_to_json (E.dst edge)); + ] in + `A (fold_edges_e (fun edge li -> edge_to_json edge :: li) graph []) + in + `O [ + ("vertices", vertices); + ("edges", edges); + ] + + let of_json : t OpamJson.decoder = function + | `O dict -> + begin try + let vertices_json = match List.assoc "vertices" dict with + | `O vertices -> vertices + | _ -> raise Not_found in + let edges_json = match List.assoc "edges" dict with + | `A edges -> edges + | _ -> raise Not_found in + let vertex_map = + let vertex_of_json (ij, vj) = + let i = try int_of_string ij with _ -> raise Not_found in + let v = match X.of_json vj with + | None -> raise Not_found + | Some v -> v in + (i, v) in + List.map vertex_of_json vertices_json + in + let edges = + let int_of_jsonstring = function + | `String s -> (try int_of_string s with _ -> raise Not_found) + | _ -> raise Not_found in + let find kj = List.assoc (int_of_jsonstring kj) vertex_map in + let edge_of_json = function + | `O dict -> + let src = find (List.assoc "src" dict) in + let label = () in + let dst = find (List.assoc "dst" dict) in + E.create src label dst + | _ -> raise Not_found + in List.map edge_of_json edges_json + in + Some (build (List.map snd vertex_map) edges) + with Not_found -> None + end + | _ -> None end (* Simple polymorphic implem on lists when we don't need full graphs. @@ -309,6 +448,9 @@ let equal x y = x = y let to_string = string_of_int let to_json x = `Float (float_of_int x) + let of_json = function + | `Float x -> (try Some (int_of_float x) with _ -> None) + | _ -> None end) let flat_graph_of_array a = diff -Nru opam-2.0.10/src/core/opamParallel.mli opam-2.1.2/src/core/opamParallel.mli --- opam-2.0.10/src/core/opamParallel.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamParallel.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -16,8 +16,10 @@ include Graph.Sig.COMPARABLE with type t := t end +type dependency_label = unit + module type G = sig - include Graph.Sig.I + include Graph.Sig.I with type E.label = dependency_label module Vertex: VERTEX with type t = V.t module Topological: sig val fold: (V.t -> 'a -> 'a) -> t -> 'a -> 'a @@ -33,7 +35,8 @@ (** Simply parallel execution of tasks *) (** In the simple iter, map and reduce cases, ints are the indexes of the jobs - in the list *) + in the list. First list is return code of sucessfull commands, second those + which raised expcetions, and third one those which were canceled. *) exception Errors of int list * (int * exn) list * int list val iter: jobs:int -> command:('a -> unit OpamProcess.job) -> ?dry_run:bool -> @@ -53,13 +56,17 @@ module G : G (** Runs the job [command ~pred v] for every node [v] in a graph, in - topological order, using [jobs] concurrent processes. [pred] is the - associative list of job results on direct predecessors of [v]. *) + topological order using [jobs] concurrent processes. Separate (possibly + intersecting) node pools can be specified, with a separate number of + allowed processes. The [jobs] maximum applies to the remaining nodes. + + The [pred] argument provided to the [command] function is the associative + list of job results on direct predecessors of [v]. *) val iter: jobs:int -> command:(pred:(G.V.t * 'a) list -> G.V.t -> 'a OpamProcess.job) -> ?dry_run:bool -> - ?mutually_exclusive:(G.V.t list list) -> + ?pools:((G.V.t list * int) list) -> G.t -> unit @@ -69,7 +76,7 @@ jobs:int -> command:(pred:(G.V.t * 'a) list -> G.V.t -> 'a OpamProcess.job) -> ?dry_run:bool -> - ?mutually_exclusive:(G.V.t list list) -> + ?pools:((G.V.t list * int) list) -> G.t -> (G.V.t * 'a) list @@ -86,7 +93,7 @@ and type G.V.t = G.V.t module type GRAPH = sig - include Graph.Sig.I + include Graph.Sig.I with type E.label = dependency_label include Graph.Oper.S with type g = t module Topological : sig val fold : (V.t -> 'a -> 'a) -> t -> 'a -> 'a @@ -96,6 +103,10 @@ and type G.V.t = vertex module Dot : sig val output_graph : out_channel -> t -> unit end val transitive_closure: ?reflexive:bool -> t -> unit + val build: V.t list -> E.t list -> t + val compare : t -> t -> int + val to_json : t OpamJson.encoder + val of_json : t OpamJson.decoder end module MakeGraph (V: VERTEX) : GRAPH with type V.t = V.t diff -Nru opam-2.0.10/src/core/opamProcess.ml opam-2.1.2/src/core/opamProcess.ml --- opam-2.0.10/src/core/opamProcess.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamProcess.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,9 +9,191 @@ (* *) (**************************************************************************) +open OpamCompat + let log ?level fmt = OpamConsole.log "PROC" ?level fmt +let cygwin_create_process_env prog args env fd1 fd2 fd3 = + (* + * Unix.create_process_env correctly converts arguments to a command line for + * native Windows execution, but it does not correctly handle Cygwin's quoting + * requirements. + * + * The process followed here is based on an analysis of the sources for the + * Cygwin DLL (git://sourceware.org/git/newlib-cygwin.git, + * winsup/cygwin/dcrt0.cc) and a lack of refutation on the Cygwin mailing list + * in May 2016! + * + * In case this seems terminally stupid, it's worth noting that Cygwin's + * implementation of the exec system calls do not pass argv using the Windows + * command line, these weird and wonderful rules exist for corner cases and, + * as here, for invocations from native Windows processes. + * + * There are two forms of escaping which can apply, controlled by the CYGWIN + * environment variable option noglob. + * + * If none of the strings in argv contains the double-quote character, then + * the process should be invoked with the noglob option (to ensure that no + * characters are unexpectedly expanded). In this mode of escaping, it is + * necessary to protect any whitespace characters (\r, \n, \t, and space + * itself) by surrounding sequences of them with double-quotes). Additionally, + * if any string in argv begins with the @ sign, this should be double-quoted. + * + * If any one of the strings in argv does contain a double-quote character, + * then the process should be invoked with the glob option (this is the + * default). Every string in argv should have double-quotes put around it. Any + * double-quote characters within the string should be translated to "'"'". + * + * The reason for supporting both mechanisms is that the noglob method has + * shorter command lines and Windows has an upper limit of 32767 characters + * for the command line. + * + * COMBAK If the command line does exceed 32767 characters, then Cygwin allows + * a parameter beginning @ to refer to a file from which to read the + * rest of the command line. This support is not implemented at this + * time in OPAM. + * + * [This stray " is here to terminate a previous part of the comment!] + *) + let make_args argv = + let b = Buffer.create 128 in + let gen_quote ~quote ~pre ?(post = pre) s = + log ~level:3 "gen_quote: %S" s; + Buffer.clear b; + let l = String.length s in + let rec f i = + let j = + try + OpamStd.String.find_from (fun c -> try String.index quote c >= 0 with Not_found -> false) s (succ i) + with Not_found -> + l in + Buffer.add_string b (String.sub s i (j - i)); + if j < l then begin + Buffer.add_string b pre; + let i = j in + let j = + try + OpamStd.String.find_from (fun c -> try String.index quote c < 0 with Not_found -> true) s (succ i) + with Not_found -> + l in + Buffer.add_string b (String.sub s i (j - i)); + Buffer.add_string b post; + if j < l then + f j + else + Buffer.contents b + end else + Buffer.contents b in + let r = + if s = "" then + "\"\"" + else + f 0 + in + log ~level:3 "result: %S" r; r in + (* Setting noglob is causing some problems for ocamlbuild invoking Cygwin's + find. The reason for using it is to try to keep command line lengths + below the maximum, but for now disable the use of noglob. *) + if true || List.exists (fun s -> try String.index s '"' >= 0 with Not_found -> false) argv then + ("\"" ^ String.concat "\" \"" (List.map (gen_quote ~quote:"\"" ~pre:"\"'" ~post:"'\"") argv) ^ "\"", false) + else + (String.concat " " (List.map (gen_quote ~quote:"\b\r\n " ~pre:"\"") argv), true) in + let (command_line, no_glob) = make_args (Array.to_list args) in + log "cygvoke(%sglob): %s" (if no_glob then "no" else "") command_line; + let env = Array.to_list env in + let set = ref false in + let f item = + let (key, value) = + match OpamStd.String.cut_at item '=' with + Some pair -> pair + | None -> (item, "") in + match String.lowercase_ascii key with + | "cygwin" -> + let () = + if key = "CYGWIN" then + set := true in + let settings = OpamStd.String.split value ' ' in + let set = ref false in + let f setting = + let setting = String.trim setting in + let setting = + match OpamStd.String.cut_at setting ':' with + Some (setting, _) -> setting + | None -> setting in + match setting with + "glob" -> + if no_glob then begin + log ~level:2 "Removing glob from %s" key; + false + end else begin + log ~level:2 "Leaving glob in %s" key; + set := true; + true + end + | "noglob" -> + if no_glob then begin + log ~level:2 "Leaving noglob in %s" key; + set := true; + true + end else begin + log ~level:2 "Removing noglob from %s" key; + false + end + | _ -> + true in + let settings = List.filter f settings in + let settings = + if not !set && no_glob then begin + log ~level:2 "Setting noglob in %s" key; + "noglob"::settings + end else + settings in + if settings = [] then begin + log ~level:2 "Removing %s completely" key; + None + end else + Some (key ^ "=" ^ String.concat " " settings) + | "path" -> + let path_dirs = OpamStd.Sys.split_path_variable item in + let winsys = Filename.concat (OpamStd.Sys.system ()) "." |> String.lowercase_ascii in + let rec f prefix suffix = function + | dir::dirs -> + let contains_cygpath = Sys.file_exists (Filename.concat dir "cygpath.exe") in + if suffix = [] then + if String.lowercase_ascii (Filename.concat dir ".") = winsys then + f prefix [dir] dirs + else + if contains_cygpath then + path_dirs + else + f (dir::prefix) [] dirs + else + if contains_cygpath then begin + log ~level:2 "Moving %s to after %s in PATH" dir (List.hd prefix); + List.rev_append prefix (dir::(List.rev_append suffix dirs)) + end else + f prefix (dir::suffix) dirs + | [] -> + assert false + in + Some (String.concat ";" (f [] [] path_dirs)) + | _ -> + Some item in + let env = OpamStd.List.filter_map f env in + let env = + if !set then + env + else + if no_glob then begin + log ~level:2 "Adding CYGWIN=noglob"; + "CYGWIN=noglob"::env + end else + env in + OpamStubs.win_create_process prog command_line + (Some(String.concat "\000" env ^ "\000")) + fd1 fd2 fd3 + (** Shell commands *) type command = { cmd: string; @@ -80,14 +262,6 @@ output_string oc "\n"; flush oc -let option_map fn = function - | None -> None - | Some o -> Some (fn o) - -let option_default d = function - | None -> d - | Some v -> v - let make_info ?code ?signal ~cmd ~args ~cwd ~env_file ~stdout_file ~stderr_file ~metadata () = let b = ref [] in @@ -107,8 +281,8 @@ List.iter (fun (k,v) -> print k v) metadata; print "path" cwd; print "command" (String.concat " " (cmd :: args)); - print_opt "exit-code" (option_map string_of_int code); - print_opt "signalled" (option_map string_of_int signal); + print_opt "exit-code" (OpamStd.Option.map string_of_int code); + print_opt "signalled" (OpamStd.Option.map string_of_int signal); print_opt "env-file" env_file; if stderr_file = stdout_file then print_opt "output-file" stdout_file @@ -127,6 +301,26 @@ (OpamConsole.colorise color k) v) info; Buffer.contents b +let resolve_command_fn = ref (fun ?env:_ ?dir:_ _ -> None) +let set_resolve_command = + let called = ref false in + fun resolve_command -> + if !called then invalid_arg "Just what do you think you're doing, Dave?"; + called := true; + resolve_command_fn := resolve_command +let resolve_command cmd = !resolve_command_fn cmd + +let create_process_env = + if Sys.win32 then + fun cmd -> + let resolved_cmd = resolve_command cmd in + if OpamStd.(Option.map_default Sys.is_cygwin_variant `Native resolved_cmd) = `Cygwin then + cygwin_create_process_env cmd + else + Unix.create_process_env cmd + else + Unix.create_process_env + (** [create cmd args] create a new process to execute the command [cmd] with arguments [args]. If [stdout_file] or [stderr_file] are set, the channels are redirected to the corresponding files. The @@ -191,8 +385,66 @@ close_out chan in let pid = + let cmd, args = + if Sys.win32 then + try + let actual_command = + if Sys.file_exists cmd then + cmd + else if Sys.file_exists (cmd ^ ".exe") then + cmd ^ ".exe" + else + raise Exit in + let actual_image, args = + let c = open_in actual_command in + set_binary_mode_in c true; + try + if really_input_string c 2 = "#!" then begin + (* The input_line will only fail for a 2-byte file consisting of exactly #! (with no \n), which is acceptable! *) + let l = String.trim (input_line c) in + let cmd, arg = + try + let i = String.index l ' ' in + let cmd = Filename.basename (String.trim (String.sub l 0 i)) in + let arg = String.trim (String.sub l i (String.length l - i)) in + if cmd = "env" then + arg, None + else + cmd, Some arg + with Not_found -> + Filename.basename l, None in + close_in c; + try + let cmd = OpamStd.Option.default cmd (resolve_command cmd) in + (*Printf.eprintf "Deduced %s => %s to be executed via %s\n%!" cmd actual_command cmd;*) + let args = actual_command::args in + cmd, OpamStd.Option.map_default (fun arg -> arg::args) args arg + with Not_found -> + (* Script interpreter isn't available - fall back *) + raise Exit + end else begin + close_in c; + actual_command, args + end + with End_of_file -> + close_in c; + (* A two-byte image can't be executable! *) + raise Exit in + (*Printf.eprintf "Final deduction: %s -> %s\n%!" cmd actual_image;*) + actual_image, args + with Exit -> + (* Fall back to default behaviour if anything went wrong - almost certainly means a broken package *) + cmd, args + else + cmd, args in + let create_process, cmd, args = + if Sys.win32 && OpamStd.Sys.is_cygwin_variant cmd = `Cygwin then + cygwin_create_process_env, cmd, args + else + Unix.create_process_env, cmd, args + in try - Unix.create_process_env + create_process cmd (Array.of_list (cmd :: args)) env @@ -231,6 +483,16 @@ r_cleanup : string list; } +let empty_result = { + r_code = 0; + r_signal = None; + r_duration = 0.; + r_info = []; + r_stdout = []; + r_stderr = []; + r_cleanup = []; +} + (* XXX: the function might block for ever for some channels kinds *) let read_lines f = try @@ -319,9 +581,9 @@ else Printf.sprintf " (CWD=%s)" p.p_cwd) let verbose_print_out = - let pfx = OpamConsole.colorise `yellow "- " in + let pfx = lazy (OpamConsole.colorise `yellow "- ") in fun s -> - OpamConsole.msg "%s%s\n" pfx s + OpamConsole.msg "%s%s\n" (Lazy.force pfx) s (** Semi-synchronous printing of the output of a command *) let set_verbose_f, print_verbose_f, isset_verbose_f, stop_verbose_f = @@ -369,8 +631,8 @@ let exit_status p return = let duration = Unix.gettimeofday () -. p.p_time in - let stdout = option_default [] (option_map read_lines p.p_stdout) in - let stderr = option_default [] (option_map read_lines p.p_stderr) in + let stdout = OpamStd.Option.default [] (OpamStd.Option.map read_lines p.p_stdout) in + let stderr = OpamStd.Option.default [] (OpamStd.Option.map read_lines p.p_stderr) in let cleanup = p.p_tmp_files in let code,signal = match return with | Unix.WEXITED r -> Some r, None @@ -379,8 +641,7 @@ if isset_verbose_f () then stop_verbose_f () else if p.p_verbose then - (let open OpamCompat in - verbose_print_cmd p; + (verbose_print_cmd p; List.iter verbose_print_out stdout; List.iter verbose_print_out stderr; flush Stdlib.stdout); @@ -474,14 +735,7 @@ let dry_wait_one = function | {p_pid = -1; _} as p :: _ -> if p.p_verbose then (verbose_print_cmd p; flush stdout); - p, - { r_code = 0; - r_signal = None; - r_duration = 0.; - r_info = []; - r_stdout = []; - r_stderr = []; - r_cleanup = []; } + p, empty_result | _ -> raise (Invalid_argument "dry_wait_one") let run command = @@ -621,7 +875,9 @@ OpamStd.Option.iter (if OpamConsole.disp_status_line () then OpamConsole.status_line "Processing: %s" - else OpamConsole.msg "%s\n") + else if OpamConsole.verbose () then + OpamConsole.msg "%s\n" + else fun _ -> ()) (text_of_command cmd); let r = run cmd in let k = @@ -640,14 +896,7 @@ let rec dry_run = function | Done x -> x | Run (_command,cont) -> - let result = { r_code = 0; - r_signal = None; - r_duration = 0.; - r_info = []; - r_stdout = []; - r_stderr = []; - r_cleanup = []; } - in dry_run (cont result) + dry_run (cont empty_result) let rec catch handler fjob = try match fjob () with diff -Nru opam-2.0.10/src/core/opamProcess.mli opam-2.1.2/src/core/opamProcess.mli --- opam-2.0.10/src/core/opamProcess.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamProcess.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -210,3 +210,14 @@ end type 'a job = 'a Job.Op.job + +(**/**) +val set_resolve_command : + (?env:string array -> ?dir:string -> string -> string option) -> unit + +(** Like Unix.create_process_env, but with correct escaping of arguments when + invoking a cygwin executable from a native Windows executable. *) +val create_process_env : + string -> string array -> string array -> + Unix.file_descr -> Unix.file_descr -> Unix.file_descr -> + int diff -Nru opam-2.0.10/src/core/opamSHA.ml opam-2.1.2/src/core/opamSHA.ml --- opam-2.0.10/src/core/opamSHA.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamSHA.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) diff -Nru opam-2.0.10/src/core/opamSHA.mli opam-2.1.2/src/core/opamSHA.mli --- opam-2.0.10/src/core/opamSHA.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamSHA.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2016 OCamlPro *) +(* Copyright 2016-2017 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) diff -Nru opam-2.0.10/src/core/opamStd.ml opam-2.1.2/src/core/opamStd.ml --- opam-2.0.10/src/core/opamStd.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamStd.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -16,12 +16,16 @@ val map: (elt -> elt) -> t -> t val is_singleton: t -> bool val choose_one : t -> elt + val choose_opt : t -> elt option val of_list: elt list -> t val to_string: t -> string val to_json: t -> OpamJson.t + val of_json: OpamJson.t -> t option val find: (elt -> bool) -> t -> elt val find_opt: (elt -> bool) -> t -> elt option val safe_add: elt -> t -> t + val fixpoint: (elt -> t) -> t -> t + val map_reduce: ?default:'a -> (elt -> 'a) -> ('a -> 'a -> 'a) -> t -> 'a module Op : sig val (++): t -> t -> t @@ -33,20 +37,25 @@ include Map.S val to_string: ('a -> string) -> 'a t -> string val to_json: ('a -> OpamJson.t) -> 'a t -> OpamJson.t + val of_json: (OpamJson.t -> 'a option) -> OpamJson.t -> 'a t option val keys: 'a t -> key list val values: 'a t -> 'a list val find_opt: key -> 'a t -> 'a option + val choose_opt: 'a t -> (key * 'a) option val union: ('a -> 'a -> 'a) -> 'a t -> 'a t -> 'a t val is_singleton: 'a t -> bool val of_list: (key * 'a) list -> 'a t val safe_add: key -> 'a -> 'a t -> 'a t val update: key -> ('a -> 'a) -> 'a -> 'a t -> 'a t + val map_reduce: + ?default:'b -> (key -> 'a -> 'b) -> ('b -> 'b -> 'b) -> 'a t -> 'b end module type ABSTRACT = sig type t val of_string: string -> t val to_string: t -> string val to_json: t -> OpamJson.t + val of_json: OpamJson.t -> t option module Set: SET with type elt = t module Map: MAP with type key = t end @@ -55,6 +64,7 @@ include Set.OrderedType val to_string: t -> string val to_json: t -> OpamJson.t + val of_json: OpamJson.t -> t option end let max_print = 100 @@ -102,12 +112,15 @@ let to_string f = concat_map ~left:"{ " ~right:" }" ~nil:"{}" ", " f - let rec remove_duplicates = function - | a::(b::_ as r) when a = b -> remove_duplicates r - | a::r -> a::remove_duplicates r + let rec remove_duplicates_eq eq = function + | a::(b::_ as r) when eq a b -> remove_duplicates_eq eq r + | a::r -> a::remove_duplicates_eq eq r | [] -> [] - let sort_nodup cmp l = remove_duplicates (List.sort cmp l) + let remove_duplicates l = remove_duplicates_eq ( = ) l + + let sort_nodup cmp l = + remove_duplicates_eq (fun a b -> cmp a b = 0) (List.sort cmp l) let filter_map f l = let rec loop accu = function @@ -138,6 +151,10 @@ | l when index <= 0 -> value :: l | x::l -> x :: insert_at (index - 1) value l + let rec assoc_opt x = function + [] -> None + | (a,b)::l -> if compare a x = 0 then Some b else assoc_opt x l + let pick_assoc x l = let rec aux acc = function | [] -> None, l @@ -183,6 +200,9 @@ else if is_singleton s then choose s else failwith "choose_one" + let choose_opt s = + try Some (choose s) with Not_found -> None + let of_list l = List.fold_left (fun set e -> add e set) empty l @@ -212,6 +232,18 @@ let jsons = List.map O.to_json elements in `A jsons + let of_json = function + | `A jsons -> + begin try + let get = function + | None -> raise Not_found + | Some v -> v in + let elems = List.map get (List.map O.of_json jsons) in + Some (S.of_list elems) + with Not_found -> None + end + | _ -> None + module Op = struct let (++) = union let (--) = diff @@ -222,6 +254,26 @@ if mem elt t then failwith (Printf.sprintf "duplicate entry %s" (O.to_string elt)) else add elt t + + let fixpoint f = + let open Op in + let rec aux fullset curset = + if is_empty curset then fullset else + let newset = fold (fun nv set -> set ++ f nv) curset empty in + let fullset = fullset ++ curset in + aux fullset (newset -- fullset) + in + aux empty + + let map_reduce ?default f op t = + match choose_opt t with + | Some x -> + fold (fun x acc -> op acc (f x)) (remove x t) (f x) + | None -> + match default with + | Some d -> d + | None -> invalid_arg "Set.map_reduce" + end end @@ -287,8 +339,30 @@ ) bindings in `A jsons + let of_json value_of_json = function + | `A jsons -> + begin try + let get_pair = function + | `O binding -> + begin match + O.of_json (List.assoc "key" binding), + value_of_json (List.assoc "value" binding) + with + | Some key, Some value -> (key, value) + | _ -> raise Not_found + end + | _ -> raise Not_found in + let pairs = List.map get_pair jsons in + Some (of_list pairs) + with Not_found -> None + end + | _ -> None + let find_opt k map = try Some (find k map) with Not_found -> None + let choose_opt m = + try Some (choose m) with Not_found -> None + let safe_add k v map = if mem k map then failwith (Printf.sprintf "duplicate entry %s" (O.to_string k)) @@ -298,6 +372,14 @@ let v = try find k map with Not_found -> zero in add k (f v) map + let map_reduce ?default f op t = + match choose_opt t with + | Some (k, v) -> + fold (fun k v acc -> op acc (f k v)) (remove k t) (f k v) + | None -> + match default with + | Some d -> d + | None -> invalid_arg "Map.map_reduce" end end @@ -307,11 +389,15 @@ let of_string x = x let to_string x = x let to_json x = `String x + let of_json = function + | `String x -> Some x + | _ -> None module O = struct type t = string let to_string = to_string let compare = compare let to_json = to_json + let of_json = of_json end module Set = Set.Make(O) module Map = Map.Make(O) @@ -323,6 +409,9 @@ let compare = compare let to_string = string_of_int let to_json i = `String (string_of_int i) + let of_json = function + | `String s -> (try Some (int_of_string s) with _ -> None) + | _ -> None end module IntMap = Map.Make(OInt) @@ -365,6 +454,10 @@ | Some x -> f x | None -> none + let to_list = function + | None -> [] + | Some x -> [x] + let some x = Some x let none _ = None @@ -395,6 +488,9 @@ let compare = compare let to_string x = x let to_json x = `String x + let of_json = function + | `String s -> Some s + | _ -> None end module StringSet = Set.Make(OString) @@ -409,14 +505,16 @@ let starts_with ~prefix s = let x = String.length prefix in let n = String.length s in - n >= x - && String.sub s 0 x = prefix + n >= x && + let rec chk i = i >= x || prefix.[i] = s.[i] && chk (i+1) in + chk 0 let ends_with ~suffix s = let x = String.length suffix in let n = String.length s in - n >= x - && String.sub s (n - x) x = suffix + n >= x && + let rec chk i = i >= x || suffix.[i] = s.[i+n-x] && chk (i+1) in + chk 0 let contains_char s c = try let _ = String.index s c in true @@ -428,13 +526,28 @@ let exact_match re s = try let subs = Re.exec re s in - let subs = Array.to_list (Re.get_all_ofs subs) in + let subs = Array.to_list (Re.Group.all_offset subs) in let n = String.length s in let subs = List.filter (fun (s,e) -> s=0 && e=n) subs in List.length subs > 0 with Not_found -> false + let find_from f s i = + let l = String.length s in + if i < 0 || i > l then + invalid_arg "find_from" + else + let rec g i = + if i < l then + if f s.[i] then + i + else + g (succ i) + else + raise Not_found in + g i + let map f s = let len = String.length s in let b = Bytes.create len in @@ -526,13 +639,42 @@ for i = 0 to String.length s - 1 do acc := f !acc s.[i] done; !acc + let compare_case s1 s2 = + let l1 = String.length s1 and l2 = String.length s2 in + let len = min l1 l2 in + let rec aux i = + if i < len then + let c1 = s1.[i] and c2 = s2.[i] in + match Char.compare (Char.lowercase_ascii c1) (Char.lowercase_ascii c2) + with + | 0 -> + (match Char.compare c1 c2 with + | 0 -> aux (i+1) + | c -> c) + | c -> c + else + if l1 < l2 then -1 + else if l1 > l2 then 1 + else 0 + in + aux 0 + + let is_prefix_of ~from ~full s = + let length_s = String.length s in + let length_full = String.length full in + if from < 0 || from > length_full then + invalid_arg "is_prefix_of" + else + length_s <= length_full + && length_s > from + && String.sub full 0 length_s = s + end type warning_printer = {mutable warning : 'a . ('a, unit, string, unit) format4 -> 'a} let console = ref {warning = fun fmt -> Printf.ksprintf prerr_string fmt} - module Env = struct (* Remove from a c-separated list of string the one with the given prefix *) @@ -597,9 +739,9 @@ if Sys.win32 then fun path -> let length = String.length path in let rec f acc index current last normal = - if index = length - then let current = current ^ String.sub path last (index - last) in - if current <> "" then current::acc else acc + if index = length then + let current = current ^ String.sub path last (index - last) in + List.rev (if current <> "" then current::acc else acc) else let c = path.[index] and next = succ index in if c = ';' && normal || c = '"' then @@ -634,7 +776,7 @@ let tty_in = Unix.isatty Unix.stdin - let default_columns = + let default_columns = lazy ( let default = 16_000_000 in let cols = try int_of_string (Env.get "COLUMNS") with @@ -642,6 +784,7 @@ | Failure _ -> default in if cols > 0 then cols else default + ) let get_terminal_columns () = let fallback = 80 in @@ -663,12 +806,15 @@ in if cols > 0 then cols else fallback - let win32_get_console_width () = - let hConsoleOutput = OpamStubs.(getStdHandle STD_OUTPUT_HANDLE) in - let {OpamStubs.size = (width, _); _} = - OpamStubs.getConsoleScreenBufferInfo hConsoleOutput - in - width + let win32_get_console_width default_columns = + try + let hConsoleOutput = OpamStubs.(getStdHandle STD_OUTPUT_HANDLE) in + let {OpamStubs.size = (width, _); _} = + OpamStubs.getConsoleScreenBufferInfo hConsoleOutput + in + width + with Not_found -> + Lazy.force default_columns let terminal_columns = let v = ref (lazy (get_terminal_columns ())) in @@ -680,17 +826,19 @@ in if Sys.win32 then fun () -> - if tty_out - then win32_get_console_width () - else default_columns + win32_get_console_width default_columns else fun () -> if tty_out then Lazy.force !v - else default_columns + else Lazy.force default_columns let home = - let home = lazy (try Env.get "HOME" with Not_found -> Sys.getcwd ()) in + (* Note: we ask Unix.getenv instead of Env.get to avoid + forcing the environment in this function that is used + before the .init() functions are called -- see + OpamStateConfig.default. *) + let home = lazy (try Unix.getenv "HOME" with Not_found -> Sys.getcwd ()) in fun () -> Lazy.force home let etc () = "/etc" @@ -708,6 +856,10 @@ Hashtbl.add memo arg r; r + let system () = + (* CSIDL_SYSTEM = 0x25 *) + OpamStubs.(shGetFolderPath 0x25 SHGFP_TYPE_CURRENT) + type os = | Darwin | Linux @@ -845,6 +997,45 @@ (fun f -> try f () with _ -> ()) !registered_at_exit + let is_cygwin_variant = + if Sys.win32 then + let results = Hashtbl.create 17 in + let requires_cygwin name = + let cmd = Printf.sprintf "cygcheck \"%s\"" name in + let ((c, _, _) as process) = Unix.open_process_full cmd (Unix.environment ()) in + let rec f a = + match input_line c with + | x -> + if OpamString.ends_with ~suffix:"cygwin1.dll" (String.trim x) then + if OpamString.starts_with ~prefix:" " x then + f `Cygwin + else if a <> `Cygwin then + f `CygLinked + else + f a + else + f a + | exception e -> + Unix.close_process_full process |> ignore; + fatal e; + a + in + f `Native + in + fun name -> + if Filename.is_relative name then + requires_cygwin name + else + try + Hashtbl.find results name + with Not_found -> + let result = requires_cygwin name + in + Hashtbl.add results name result; + result + else + fun _ -> `Native + exception Exit of int exception Exec of string * string array * string array @@ -1128,7 +1319,24 @@ | [] -> "" | [a] -> a | [a;b] -> Printf.sprintf "%s %s %s" a last b - | h::t -> Printf.sprintf "%s, %s" h (pretty_list t) + | h::t -> Printf.sprintf "%s, %s" h (pretty_list ~last t) + + let as_aligned_table ?(width=OpamSys.terminal_columns ()) l = + let itlen = + List.fold_left (fun acc s -> max acc (visual_length s)) + 0 l + in + let by_line = (width + 1) / (itlen + 1) in + if by_line <= 1 then + List.map (fun x -> [x]) l + else + let rec aux rline n = function + | [] -> [List.rev rline] + | x::r as line -> + if n = 0 then List.rev rline :: aux [] by_line line + else aux (x :: rline) (n-1) r + in + align_table (aux [] by_line l) end @@ -1202,6 +1410,10 @@ type env_var = string + type when_ = [ `Always | `Never | `Auto ] + type when_ext = [ `Extended | when_ ] + type answer = [ `unsafe_yes | `all_yes | `all_no | `ask ] + let env conv var = try Option.map conv (Env.getopt ("OPAM"^var)) with Failure _ -> @@ -1210,22 +1422,48 @@ "Invalid value for environment variable OPAM%s, ignored." var; None - let env_bool var = - env (fun s -> match String.lowercase_ascii s with - | "" | "0" | "no" | "false" -> false - | "1" | "yes" | "true" -> true - | _ -> failwith "env_bool") - var + let bool_of_string s = + match String.lowercase_ascii s with + | "" | "0" | "no" | "false" -> Some false + | "1" | "yes" | "true" -> Some true + | _ -> None + + let bool s = + match bool_of_string s with + | Some s -> s + | None -> failwith "env_bool" + + let env_bool var = env bool var let env_int var = env int_of_string var + type level = int let env_level var = - env (fun s -> match String.lowercase_ascii s with - | "" | "no" | "false" -> 0 - | "yes" | "true" -> 1 - | s -> int_of_string s) + env (function s -> + if s = "" then 0 else + match bool_of_string s with + | Some true -> 0 + | Some false -> 1 + | None -> int_of_string s) var + type sections = int option OpamString.Map.t + let env_sections var = + env (fun s -> + let f map elt = + let parse_value (section, value) = + try + (section, Some (int_of_string value)) + with Failure _ -> + (section, None) + in + let (section, level) = + Option.map_default parse_value (elt, None) (OpamString.cut_at elt ':') + in + OpamString.Map.add section level map + in + List.fold_left f OpamString.Map.empty (OpamString.split s ' ')) var + let env_string var = env (fun s -> s) var @@ -1253,35 +1491,33 @@ | `Never -> false | `Auto -> Lazy.force auto - let initk k = - let utf8 = Option.Op.( - env_when_ext "UTF8" ++ - (env_bool "UTF8MSGS" >>= function - | true -> Some `Extended - | false -> None) - ) in - let answer = match env_bool "YES", env_bool "NO" with - | Some true, _ -> Some (Some true) - | _, Some true -> Some (Some false) - | None, None -> None - | _ -> Some None - in - OpamCoreConfig.(setk (setk (fun c -> r := c; k)) !r) - ?debug_level:(env_level "DEBUG") - ?verbose_level:(env_level "VERBOSE") - ?color:(env_when "COLOR") - ?utf8 - ?disp_status_line:(env_when "STATUSLINE") - ?answer - ?safe_mode:(env_bool "SAFE") - ?log_dir:(env_string "LOGS") - ?keep_log_dir:(env_bool "KEEPLOGS") - ?errlog_length:(env_int "ERRLOGLEN") - ?merged_output:(env_bool "MERGEOUT") - ?use_openssl:(env_bool "USEOPENSSL") - ?precise_tracking:(env_bool "PRECISETRACKING") + let answer s = + match String.lowercase_ascii s with + | "ask" -> `ask + | "yes" -> `all_yes + | "no" -> `all_no + | "unsafe-yes" -> `unsafe_yes + | _ -> failwith "env_answer" + + let env_answer = + env (fun s -> + try if bool s then `all_yes else `all_no + with Failure _ -> answer s) + + + module E = struct + type t = .. + let (r : t list ref) = ref [] + + let update v = r := v :: !r + let updates l = r := l @ !r + + let find var = OpamList.find_map var !r + let value var = + let l = lazy (try Some (find var); with Not_found -> None) in + fun () -> Lazy.force l + end - let init ?noop:_ = initk (fun () -> ()) end module List = OpamList diff -Nru opam-2.0.10/src/core/opamStd.mli opam-2.1.2/src/core/opamStd.mli --- opam-2.0.10/src/core/opamStd.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamStd.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -26,15 +26,27 @@ on an empty set, [Failure] on a non-singleton. *) val choose_one : t -> elt + val choose_opt: t -> elt option + val of_list: elt list -> t val to_string: t -> string val to_json: t -> OpamJson.t + val of_json: OpamJson.t -> t option val find: (elt -> bool) -> t -> elt val find_opt: (elt -> bool) -> t -> elt option (** Raises Failure in case the element is already present *) val safe_add: elt -> t -> t + (** Accumulates the resulting sets of a function of elements until a fixpoint + is reached *) + val fixpoint: (elt -> t) -> t -> t + + (** [map_reduce f op t] applies [f] to every element of [t] and combines the + results using associative operator [op]. Raises [Invalid_argument] on an + empty set, or returns [default] if it is defined. *) + val map_reduce: ?default:'a -> (elt -> 'a) -> ('a -> 'a -> 'a) -> t -> 'a + module Op : sig val (++): t -> t -> t (** Infix set union *) @@ -52,9 +64,11 @@ val to_string: ('a -> string) -> 'a t -> string val to_json: ('a -> OpamJson.t) -> 'a t -> OpamJson.t + val of_json: (OpamJson.t -> 'a option) -> OpamJson.t -> 'a t option val keys: 'a t -> key list val values: 'a t -> 'a list val find_opt: key -> 'a t -> 'a option + val choose_opt: 'a t -> (key * 'a) option (** A key will be in the union of [m1] and [m2] if it is appears either [m1] or [m2], with the corresponding value. If a key @@ -72,6 +86,13 @@ (** [update k f zero map] updates the binding of [k] in [map] using function [f], applied to the current value bound to [k] or [zero] if none *) val update: key -> ('a -> 'a) -> 'a -> 'a t -> 'a t + + (** [map_reduce f op t] applies [f] to every binding of [t] and combines the + results using associative operator [op]. Raises [Invalid_argument] on an + empty map, or returns [default] if it is defined. *) + val map_reduce: + ?default:'b -> (key -> 'a -> 'b) -> ('b -> 'b -> 'b) -> 'a t -> 'b + end (** A signature for handling abstract keys and collections thereof *) @@ -82,6 +103,7 @@ val of_string: string -> t val to_string: t -> string val to_json: t -> OpamJson.t + val of_json: OpamJson.t -> t option module Set: SET with type elt = t module Map: MAP with type key = t @@ -96,6 +118,7 @@ include Set.OrderedType val to_string: t -> string val to_json: t -> OpamJson.t + val of_json: OpamJson.t -> t option end module Set: sig @@ -135,6 +158,8 @@ val to_string: ?none:string -> ('a -> string) -> 'a option -> string + val to_list: 'a option -> 'a list + val some: 'a -> 'a option val none: 'a -> 'b option @@ -190,6 +215,9 @@ end if index < 0 or > length respectively). Not tail-recursive *) val insert_at: int -> 'a -> 'a list -> 'a list + (** Like [List.find], but returning option instead of raising *) + val assoc_opt: 'a -> ('a * 'b) list -> 'b option + (** Like [List.assoc], but as an option, and also returns the list with the binding removed, e.g. equivalent to [(List.assoc_opt x l, List.remove_assoc x l)] @@ -224,6 +252,11 @@ val contains_char: string -> char -> bool val contains: sub:string -> string -> bool val exact_match: Re.re -> string -> bool + val find_from: (char -> bool) -> string -> int -> int + + (** Like [String.compare], but with lowercase/uppercase variants ordered next + to each other (still considered not equal though) *) + val compare_case: string -> string -> int (** {3 Manipulation} *) @@ -234,6 +267,10 @@ val remove_prefix: prefix:string -> string -> string val remove_suffix: suffix:string -> string -> string + (** [is_prefix_of from full str] returns true if [str] if a prefix of [full], + with at least [from] first characters *) + val is_prefix_of: from:int -> full:string -> string -> bool + (** {4 Transformations} *) (** Cut a string at the first occurrence of the given char *) @@ -288,6 +325,10 @@ (** Display a pretty list: ["x";"y";"z"] -> "x, y and z". "and" can be changed by specifying [last] *) val pretty_list: ?last:string -> string list -> string + + (** Splits a list of strings so that it can be printed as a table that should + fit on screen *) + val as_aligned_table: ?width:int -> string list -> string list list end module Exn : sig @@ -368,6 +409,9 @@ (** The /etc directory *) val etc: unit -> string + (** The system directory (Windows only) *) + val system: unit -> string + type os = Darwin | Linux | FreeBSD @@ -407,6 +451,15 @@ Optional argument [clean] permits to keep those empty strings. *) val split_path_variable: ?clean:bool -> string -> string list + (** For native Windows builds, returns [`Cygwin] if the command is a Cygwin- + compiled executable, [`CygLinked] if the command links to a library which is + itself Cygwin-compiled or [`Native] otherwise. + + Note that this returns [`Native] on a Cygwin-build of opam! + + Both cygcheck and an unqualified command will be resolved using the current PATH. *) + val is_cygwin_variant: string -> [ `Native | `Cygwin | `CygLinked ] + (** {3 Exit handling} *) (** Like Stdlib.at_exit but with the possibility to call manually @@ -494,30 +547,37 @@ type env_var = string + type when_ = [ `Always | `Never | `Auto ] + type when_ext = [ `Extended | when_ ] + type answer = [ `unsafe_yes | `all_yes | `all_no | `ask ] + + (* Parse a envrionement variable boolean value *) + val bool_of_string: string -> bool option + + val env: (string -> 'a) -> string -> 'a option + val env_bool: env_var -> bool option val env_int: env_var -> int option + type level = int (* Like [env_int], but accept boolean values for 0 and 1 *) - val env_level: env_var -> int option + val env_level: env_var -> level option + + type sections = int option String.Map.t + val env_sections: env_var -> sections option val env_string: env_var -> string option val env_float: env_var -> float option - val env_when: env_var -> [ `Always | `Never | `Auto ] option - - val env_when_ext: env_var -> [ `Extended | `Always | `Never | `Auto ] option + val env_when: env_var -> when_ option - val resolve_when: auto:(bool Lazy.t) -> [ `Always | `Never | `Auto ] -> bool + val env_when_ext: env_var -> when_ext option - (** Sets the OpamCoreConfig options, reading the environment to get default - values when unspecified *) - val init: ?noop:_ -> (unit -> unit) OpamCoreConfig.options_fun + val resolve_when: auto:(bool Lazy.t) -> when_ -> bool - (** Like [init], but returns the given value. For optional argument - stacking *) - val initk: 'a -> 'a OpamCoreConfig.options_fun + val env_answer: env_var -> answer option module type Sig = sig @@ -557,4 +617,13 @@ end + (* Opam environment variables handling *) + module E : sig + type t = .. + val find: (t -> 'a option) -> 'a + val value: (t -> 'a option) -> (unit ->'a option) + val update: t -> unit + val updates: t list -> unit + end + end diff -Nru opam-2.0.10/src/core/opamStubs.ml.dummy opam-2.1.2/src/core/opamStubs.ml.dummy --- opam-2.0.10/src/core/opamStubs.ml.dummy 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamStubs.ml.dummy 2021-12-07 16:09:27.000000000 +0000 @@ -35,3 +35,4 @@ let sendMessageTimeout _ _ _ _ _ = that's_a_no_no let getParentProcessID = that's_a_no_no let getConsoleAlias _ = that's_a_no_no +let win_create_process _ _ _ _ _ = that's_a_no_no diff -Nru opam-2.0.10/src/core/opamStubs.mli opam-2.1.2/src/core/opamStubs.mli --- opam-2.0.10/src/core/opamStubs.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamStubs.mli 2021-12-07 16:09:27.000000000 +0000 @@ -128,3 +128,7 @@ (** Windows only. [getConsoleAlias alias exeName] retrieves the value for a given executable or [""] if the alias is not defined. See https://docs.microsoft.com/en-us/windows/console/getconsolealias *) + +val win_create_process : string -> string -> string option -> Unix.file_descr -> + Unix.file_descr -> Unix.file_descr -> int +(** Windows only. Provided by OCaml's win32unix library. *) diff -Nru opam-2.0.10/src/core/opamStubs.ml.win32 opam-2.1.2/src/core/opamStubs.ml.win32 --- opam-2.0.10/src/core/opamStubs.ml.win32 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamStubs.ml.win32 2021-12-07 16:09:27.000000000 +0000 @@ -11,3 +11,7 @@ include OpamStubsTypes include OpamWin32Stubs let getpid () = Int32.to_int (getCurrentProcessID ()) + +external win_create_process : string -> string -> string option -> + Unix.file_descr -> Unix.file_descr -> Unix.file_descr -> int + = "win_create_process" "win_create_process_native" diff -Nru opam-2.0.10/src/core/opamSystem.ml opam-2.1.2/src/core/opamSystem.ml --- opam-2.0.10/src/core/opamSystem.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamSystem.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -11,10 +11,15 @@ open OpamCompat +type install_warning = + [ `Add_exe | `Install_dll | `Install_script | `Install_unknown | `Cygwin | `Cygwin_libraries ] +type install_warning_fn = string -> install_warning -> unit + exception Process_error of OpamProcess.result exception Internal_error of string exception Command_not_found of string exception File_not_found of string +exception Permission_denied of string let log ?level fmt = OpamConsole.log "SYSTEM" ?level fmt let slog = OpamConsole.slog @@ -35,6 +40,9 @@ let command_not_found cmd = raise (Command_not_found cmd) +let permission_denied cmd = + raise (Permission_denied cmd) + module Sys2 = struct (* same as [Sys.is_directory] except for symlinks, which returns always [false]. *) let is_directory file = @@ -167,6 +175,56 @@ output_string oc contents; close_out oc +let setup_copy ?(chmod = fun x -> x) ~src ~dst () = + let ic = open_in_bin src in + try + let perm = + (Unix.fstat (Unix.descr_of_in_channel ic)).st_perm |> chmod + in + let () = + try if Unix.((lstat dst).st_kind <> S_REG) then + remove_file dst + with Unix.Unix_error(ENOENT, _, _) -> () + in + let oc = + open_out_gen + [ Open_wronly; Open_creat; Open_trunc; Open_binary ] + perm dst + in + let fd = Unix.descr_of_out_channel oc in + try + if Unix.((fstat fd).st_perm) <> perm then + Unix.fchmod fd perm; + (ic, oc) + with exn -> + OpamStd.Exn.finalise exn (fun () -> close_out oc) + with exn -> + OpamStd.Exn.finalise exn (fun () -> close_in ic) + +let copy_channels = + let buf_len = 4096 in + let buf = Bytes.create buf_len in + let rec loop ic oc = + match input ic buf 0 buf_len with + | 0 -> () + | n -> + output oc buf 0 n; + loop ic oc + in + loop + +let copy_file_aux ?chmod ~src ~dst () = + let close_channels ic oc = + OpamStd.Exn.finally (fun () -> close_in ic) (fun () -> close_out oc) in + try + let ic, oc = setup_copy ?chmod ~src ~dst () in + OpamStd.Exn.finally (fun () -> close_channels ic oc) + (fun () -> copy_channels ic oc); + with Unix.Unix_error _ as e -> + (* Remove the partial destination file, if any. *) + (try Unix.unlink dst with Unix.Unix_error _ -> ()); + internal_error "Cannot copy %s to %s (%s)." src dst (Printexc.to_string e) + let chdir dir = try Unix.chdir dir with Unix.Unix_error _ -> raise (File_not_found dir) @@ -246,6 +304,10 @@ OpamStd.Exn.finalise e @@ fun () -> remove_dir dir +let in_tmp_dir fn = + with_tmp_dir @@ fun dir -> + in_dir dir fn + let with_tmp_dir_job fjob = let dir = mk_temp_dir () in mkdir dir; @@ -296,8 +358,8 @@ type command = string list -let default_env = - Unix.environment () +let default_env () = + OpamStd.Env.list () |> List.map (fun (var, v) -> var^"="^v) |> Array.of_list let env_var env var = let len = Array.length env in @@ -327,7 +389,7 @@ (* OCaml 4.05.0 no longer follows the updated PATH to resolve commands. This makes unqualified commands absolute as a workaround. *) -let resolve_command = +let t_resolve_command = let is_external_cmd name = let name = forward_to_back name in OpamStd.String.contains_char name Filename.dir_sep.[0] @@ -339,51 +401,96 @@ else fun f -> try let open Unix in - let uid = getuid() and groups = Array.to_list(getgroups()) in + let uid = geteuid () in + let groups = OpamStd.IntSet.of_list (getegid () :: Array.to_list (getgroups ())) in let {st_uid; st_gid; st_perm; _} = stat f in - let mask = 0o001 - lor (if uid = st_uid then 0o100 else 0) - lor (if List.mem st_gid groups then 0o010 else 0) in - (st_perm land mask) <> 0 + let mask = + if uid = st_uid then + 0o100 + else if OpamStd.IntSet.mem st_gid groups then + 0o010 + else + 0o001 + in + if (st_perm land mask) <> 0 then + true + else + match OpamACL.get_acl_executable_info f st_uid with + | None -> false + | Some [] -> true + | Some gids -> OpamStd.IntSet.(not (is_empty (inter (of_list gids) groups))) with e -> OpamStd.Exn.fatal e; false in let resolve ?dir env name = - if not (Filename.is_relative name) then (* absolute path *) - if check_perms name then Some name else None - else if is_external_cmd name then (* relative *) + if not (Filename.is_relative name) then begin + (* absolute path *) + if not (Sys.file_exists name) then `Not_found + else if not (check_perms name) then `Denied + else `Cmd name + end else if is_external_cmd name then begin + (* relative path *) let cmd = match dir with | None -> name | Some d -> Filename.concat d name in - if check_perms cmd then Some cmd else None - else (* bare command, lookup in PATH *) - if Sys.win32 then - let path = OpamStd.Sys.split_path_variable (env_var env "PATH") in - let name = - if Filename.check_suffix name ".exe" then name else name ^ ".exe" - in - OpamStd.(List.find_opt (fun path -> - check_perms (Filename.concat path name)) - path |> Option.map (fun path -> Filename.concat path name)) - else - let cmd, args = "/bin/sh", ["-c"; Printf.sprintf "command -v %s" name] in - let r = - OpamProcess.run - (OpamProcess.command ~env ?dir - ~name:(temp_file ("command-"^(Filename.basename name))) - ~verbose:false cmd args) + if not (Sys.file_exists cmd) then `Not_found + else if not (check_perms cmd) then `Denied + else `Cmd cmd + end else + (* bare command, lookup in PATH *) + (* Following the shell sematics for looking up PATH, programs with the + expected name but not the right permissions are skipped silently. + Therefore, only two outcomes are possible in that case, [`Cmd ..] or + [`Not_found]. *) + let path = OpamStd.Sys.split_path_variable (env_var env "PATH") in + let name = + if Sys.win32 && not (Filename.check_suffix name ".exe") then + name ^ ".exe" + else name in - if OpamProcess.check_success_and_cleanup r then - match r.OpamProcess.r_stdout with - | cmdname::_ when cmdname = name || check_perms cmdname -> - (* "command -v echo" returns just echo, hence the first when check *) - Some cmdname - | _ -> None - else None + let possibles = OpamStd.List.filter_map (fun path -> + let candidate = Filename.concat path name in + if Sys.file_exists candidate then Some candidate else None) path + in + match List.find check_perms possibles with + | cmdname -> `Cmd cmdname + | exception Not_found -> + if possibles = [] then + `Not_found + else + `Denied in - fun ?(env=default_env) ?dir name -> + fun ?env ?dir name -> + let env = match env with None -> default_env () | Some e -> e in resolve env ?dir name +let resolve_command ?env ?dir name = + match t_resolve_command ?env ?dir name with + | `Cmd cmd -> Some cmd + | `Denied | `Not_found -> None + +let apply_cygpath name = + let r = + OpamProcess.run + (OpamProcess.command ~name:(temp_file "command") ~verbose:false "cygpath" ["--"; name]) + in + OpamProcess.cleanup ~force:true r; + if OpamProcess.is_success r then + List.hd r.OpamProcess.r_stdout + else + OpamConsole.error_and_exit `Internal_error "Could not apply cygpath to %s" name + +let get_cygpath_function = + if Sys.win32 then + fun ~command -> + lazy (if OpamStd.(Option.map_default Sys.is_cygwin_variant `Native (resolve_command command)) = `Cygwin then + apply_cygpath + else + fun x -> x) + else + let f = Lazy.from_val (fun x -> x) in + fun ~command:_ -> f + let runs = ref [] let print_stats () = match !runs with @@ -395,9 +502,10 @@ let log_file ?dir name = temp_file ?dir (OpamStd.Option.default "log" name) let make_command - ?verbose ?(env=default_env) ?name ?text ?metadata ?allow_stdin ?stdout + ?verbose ?env ?name ?text ?metadata ?allow_stdin ?stdout ?dir ?(resolve_path=true) cmd args = + let env = match env with None -> default_env () | Some e -> e in let name = log_file name in let verbose = OpamStd.Option.default OpamCoreConfig.(!r.verbose_level >= 2) verbose @@ -406,19 +514,20 @@ if None <> try Some (String.index cmd ' ') with Not_found -> None then OpamConsole.warning "Command %S contains space characters" cmd; let full_cmd = - if resolve_path then resolve_command ~env ?dir cmd - else Some cmd + if resolve_path then t_resolve_command ~env ?dir cmd + else `Cmd cmd in match full_cmd with - | Some cmd -> + | `Cmd cmd -> OpamProcess.command ~env ~name ?text ~verbose ?metadata ?allow_stdin ?stdout ?dir cmd args - | None -> - command_not_found cmd + | `Not_found -> command_not_found cmd + | `Denied -> permission_denied cmd let run_process - ?verbose ?(env=default_env) ~name ?metadata ?stdout ?allow_stdin command = + ?verbose ?env ~name ?metadata ?stdout ?allow_stdin command = + let env = match env with None -> default_env () | Some e -> e in let chrono = OpamConsole.timer () in runs := command :: !runs; match command with @@ -428,8 +537,8 @@ if OpamStd.String.contains_char cmd ' ' then OpamConsole.warning "Command %S contains space characters" cmd; - match resolve_command ~env cmd with - | Some full_cmd -> + match t_resolve_command ~env cmd with + | `Cmd full_cmd -> let verbose = match verbose with | None -> OpamCoreConfig.(!r.verbose_level) >= 2 | Some b -> b in @@ -441,12 +550,12 @@ full_cmd args) in let str = String.concat " " (cmd :: args) in - log "[%a] (in %.3fs) %s" + log ~level:2 "[%a] (in %.3fs) %s" (OpamConsole.slog Filename.basename) name (chrono ()) str; r - | None -> - command_not_found cmd + | `Not_found -> command_not_found cmd + | `Denied -> permission_denied cmd let command ?verbose ?env ?name ?metadata ?allow_stdin cmd = let name = log_file name in @@ -488,6 +597,12 @@ let verbose_for_base_commands () = OpamCoreConfig.(!r.verbose_level) >= 3 +let cygify f = + if Sys.win32 then + List.map (Lazy.force f) + else + fun x -> x + let copy_file src dst = if (try Sys.is_directory src with Sys_error _ -> raise (File_not_found src)) @@ -497,7 +612,8 @@ if file_or_symlink_exists dst then remove_file dst; mkdir (Filename.dirname dst); - command ~verbose:(verbose_for_base_commands ()) ["cp"; src; dst ] + log "copy %s -> %s" src dst; + copy_file_aux ~src ~dst () let copy_dir src dst = if Sys.file_exists dst then @@ -514,10 +630,12 @@ command ~verbose:(verbose_for_base_commands ()) [ "cp"; "-PRp"; src; dst ]) -let mv src dst = +let mv_aux f src dst = if file_or_symlink_exists dst then remove_file dst; mkdir (Filename.dirname dst); - command ~verbose:(verbose_for_base_commands ()) ["mv"; src; dst ] + command ~verbose:(verbose_for_base_commands ()) ("mv"::(cygify f [src; dst])) + +let mv = mv_aux (get_cygpath_function ~command:"mv") let is_exec file = let stat = Unix.stat file in @@ -526,7 +644,90 @@ let file_is_empty f = Unix.((stat f).st_size = 0) -let install ?exec src dst = +let classify_executable file = + let c = open_in file in + (* On a 32-bit system, this could fail for a PE image with a 2GB+ DOS header =-o *) + let input_int_little c = + let b1 = input_byte c in + let b2 = input_byte c in + let b3 = input_byte c in + let b4 = input_byte c in + b1 lor (b2 lsl 8) lor (b3 lsl 16) lor (b4 lsl 24) in + let input_short_little c = + let b1 = input_byte c in + let b2 = input_byte c in + b1 lor (b2 lsl 8) in + set_binary_mode_in c true; + try + match really_input_string c 2 with + "#!" -> + close_in c; + `Script + | "MZ" -> + let is_pe = + try + (* Offset to PE header at 0x3c (but we've already read two bytes) *) + ignore (really_input_string c 0x3a); + ignore (really_input_string c (input_int_little c - 0x40)); + let magic = really_input_string c 4 in + magic = "PE\000\000" + with End_of_file -> + close_in c; + false in + if is_pe then + try + let arch = + (* NB It's not necessary to determine PE/PE+ headers for x64/x86 determination *) + match input_short_little c with + 0x8664 -> + `x86_64 + | 0x14c -> + `x86 + | _ -> + raise End_of_file + in + ignore (really_input_string c 14); + let size_of_opt_header = input_short_little c in + let characteristics = input_short_little c in + (* Executable images must have a PE "optional" header and be marked executable *) + (* Could also validate IMAGE_FILE_32BIT_MACHINE (0x100) for x86 and IMAGE_FILE_LARGE_ADDRESS_AWARE (0x20) for x64 *) + if size_of_opt_header <= 0 || characteristics land 0x2 = 0 then + raise End_of_file; + close_in c; + if characteristics land 0x2000 <> 0 then + `Dll arch + else + `Exe arch + with End_of_file -> + close_in c; + `Unknown + else + `Exe `i386 + | _ -> + close_in c; + `Unknown + with End_of_file -> + close_in c; + `Unknown + +let default_install_warning dst = function +| `Add_exe -> + OpamConsole.warning "Automatically adding .exe to %s" dst +| `Install_dll -> + (* TODO Installation of .dll to bin is unfortunate, but not sure if it should be a warning *) + () +| `Install_script -> + (* TODO Generate a .cmd wrapper (and warn about it - they're not perfect) *) + OpamConsole.warning "%s is a script; the command won't be available" dst; +| `Install_unknown -> + (* TODO Installation of a non-executable file is unexpected, but not sure if it should be a warning/error *) + () +| `Cygwin -> + OpamConsole.warning "%s is a Cygwin-linked executable" dst +| `Cygwin_libraries -> + OpamConsole.warning "%s links with a Cygwin-compiled DLL (almost certainly a packaging or environment error)" dst + +let install ?(warning=default_install_warning) ?exec src dst = if Sys.is_directory src then internal_error "Cannot install %s: it is a directory." src; if (try Sys.is_directory dst with Sys_error _ -> false) then @@ -535,8 +736,41 @@ let exec = match exec with | Some e -> e | None -> is_exec src in - command ("install" :: "-m" :: (if exec then "0755" else "0644") :: - [ src; dst ]) + let perm = if exec then 0o755 else 0o644 in + log "install %s -> %s (%o)" src dst perm; + if Sys.win32 then + if exec then begin + let (dst, cygcheck) = + match classify_executable src with + `Exe _ -> + if not (Filename.check_suffix dst ".exe") && not (Filename.check_suffix dst ".dll") then begin + warning dst `Add_exe; + (dst ^ ".exe", true) + end else + (dst, true) + | `Dll _ -> + warning dst `Install_dll; + (dst, true) + | `Script -> + warning dst `Install_script; + (dst, false) + | `Unknown -> + warning dst `Install_unknown; + (dst, false) + in + copy_file_aux ~src ~dst (); + if cygcheck then + match OpamStd.Sys.is_cygwin_variant dst with + `Native -> + () + | `Cygwin -> + warning dst `Cygwin + | `CygLinked -> + warning dst `Cygwin_libraries + end else + copy_file_aux ~src ~dst () + else + copy_file_aux ~chmod:(fun _ -> perm) ~src ~dst () let cpu_count () = try @@ -628,18 +862,37 @@ Some (Printf.sprintf "Tar needs %s to extract the archive" cmd) else None) - let extract_command file = - OpamStd.Option.Op.( - get_type file >>| fun typ -> - let tar_cmd = - match OpamStd.Sys.os () with - | OpamStd.Sys.OpenBSD -> "gtar" - | _ -> "tar" - in - let command c dir = - make_command tar_cmd [ Printf.sprintf "xf%c" c ; file; "-C" ; dir ] - in - command (extract_option typ)) + let tar_cmd = lazy ( + match OpamStd.Sys.os () with + | OpamStd.Sys.OpenBSD -> "gtar" + | _ -> "tar" + ) + + let cygpath_tar = lazy ( + Lazy.force (get_cygpath_function ~command:(Lazy.force tar_cmd)) + ) + + let extract_command = + fun file -> + OpamStd.Option.Op.( + get_type file >>| fun typ -> + let f = Lazy.force cygpath_tar in + let tar_cmd = Lazy.force tar_cmd in + let command c dir = + make_command tar_cmd [ Printf.sprintf "xf%c" c ; f file; "-C" ; f dir ] + in + command (extract_option typ)) + + let compress_command = + fun file dir -> + let f = Lazy.force cygpath_tar in + let tar_cmd = Lazy.force tar_cmd in + make_command tar_cmd [ + "cfz"; f file; + "-C" ; f (Filename.dirname dir); + f (Filename.basename dir) + ] + end module Zip = struct @@ -667,6 +920,16 @@ if Zip.is_archive file then Zip.extract_command file else Tar.extract_command file +let make_tar_gz_job ~dir file = + let tmpfile = file ^ ".tmp" in + remove_file tmpfile; + Tar.compress_command tmpfile dir @@> fun r -> + OpamProcess.cleanup r; + if OpamProcess.is_success r then + (mv tmpfile file; Done None) + else + (remove_file tmpfile; Done (Some (Process_error r))) + let extract_job ~dir file = if not (Sys.file_exists file) then Done (Some (File_not_found file)) @@ -1260,11 +1523,12 @@ | Process_error r -> Some (OpamProcess.result_summary r) | Internal_error m -> Some m | Command_not_found c -> Some (Printf.sprintf "%S: command not found." c) + | Permission_denied c -> Some (Printf.sprintf "%S: permission denied." c) | Sys.Break -> Some "User interruption" | Unix.Unix_error (e, fn, msg) -> let msg = if msg = "" then "" else " on " ^ msg in let error = Printf.sprintf "%s: %S failed%s: %s" - Sys.argv.(0) fn msg (Unix.error_message e) in + Sys.executable_name fn msg (Unix.error_message e) in Some error | _ -> None ) @@ -1274,3 +1538,6 @@ Sys.catch_break true; try Sys.set_signal Sys.sigpipe (Sys.Signal_handle (fun _ -> ())) with Invalid_argument _ -> () + +let () = + OpamProcess.set_resolve_command resolve_command diff -Nru opam-2.0.10/src/core/opamSystem.mli opam-2.1.2/src/core/opamSystem.mli --- opam-2.0.10/src/core/opamSystem.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamSystem.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -16,6 +16,8 @@ exception Command_not_found of string +exception Permission_denied of string + (** raise [Process_error] *) val process_error: OpamProcess.result -> 'a @@ -29,9 +31,13 @@ (** Raise [Internal_error] *) val internal_error: ('a, unit, string, 'b) format4 -> 'a -(** [with_tmp_dir fn] executes [fn] in a tempory directory *) +(** [with_tmp_dir fn] executes [fn] creates a temporary directory and + passes its name to [fn]. The directory is alwasy removed on completion. *) val with_tmp_dir: (string -> 'a) -> 'a +(** [in_tmp_dir fn] executes [fn] in a temporary directory. *) +val in_tmp_dir: (unit -> 'a) -> 'a + (** Runs a job with a temp dir that is cleaned up afterwards *) val with_tmp_dir_job: (string -> 'a OpamProcess.job) -> 'a OpamProcess.job @@ -39,6 +45,8 @@ is reached *) val verbose_for_base_commands: unit -> bool +(** {2 Filesystem management} *) + (** Returns a directory name, in the temporary directory, composed by {i opam} (if [prefix] is not set), pid, and random number. *) val mk_temp_dir: ?prefix:string -> unit -> string @@ -54,10 +62,24 @@ val mv: string -> string -> unit +type install_warning = [ `Add_exe (* [.exe] had to be added *) + | `Install_dll (* Installation of [.dll] to bin/libexec *) + | `Install_script (* Installation of script on Windows *) + | `Install_unknown (* Installation of unknown file to bin/libexec *) + | `Cygwin (* Installation of a Cygwin-linked executable *) + | `Cygwin_libraries (* Installation of a binary linked to a Cygwin library *) + ] +(** Warnings which come from {!install} *) + +type install_warning_fn = string -> install_warning -> unit + +val default_install_warning : install_warning_fn +(** The default warning function - displays a message on OpamConsole *) + (** [install ?exec src dst] copies file [src] as file [dst] using [install]. If [exec], make the resulting file executable (otherwise, look at the permissions of the original file to decide). *) -val install: ?exec:bool -> string -> string -> unit +val install: ?warning:install_warning_fn -> ?exec:bool -> string -> string -> unit (** Checks if a file is an executable (regular file with execution permission) *) @@ -122,6 +144,10 @@ (** Return the list of files in the current directory. *) val files: string -> string list +(** Return the list of files in the current directory, inclduing any + dangling symlinks. *) +val files_all_not_dir: string -> string list + (** [rec_dirs dir] return the list list of all directories recursively (going through symbolink links). *) val rec_dirs: string -> string list @@ -157,6 +183,11 @@ if found in PATH) *) val resolve_command: ?env:string array -> ?dir:string -> string -> string option +(** Returns a function which should be applied to arguments for a given command + by determining if the command is the Cygwin variant of the command. Returns + the identity function otherwise. *) +val get_cygpath_function: command:string -> (string -> string) lazy_t + (** [command cmd] executes the command [cmd] in the correct OPAM environment. *) val command: ?verbose:bool -> ?env:string array -> ?name:string -> @@ -197,6 +228,8 @@ (** [extract_in_job] is similar to [extract_in], but as a job *) val extract_in_job: dir:string -> string -> exn option OpamProcess.job +val make_tar_gz_job: dir:string -> string -> exn option OpamProcess.job + (** Create a directory. Do not fail if the directory already exist. *) val mkdir: string -> unit @@ -257,6 +290,21 @@ apply. *) val patch: ?preprocess:bool -> dir:string -> string -> exn option OpamProcess.job +(** Returns the end-of-line encoding style for the given file. [None] means that + either the encoding of line endings is mixed, or the file contains no line + endings at all (an empty file, or a file with one line and no EOL at EOF). + Otherwise it returns [Some true] if all endings are encoded CRLF. *) +val get_eol_encoding : string -> bool option + +(** [translate_patch ~dir input_patch output_patch] writes a copy of + [input_patch] to [output_patch] as though [input_patch] had been applied in + [dir]. The patch is rewritten such that if text files have different line + endings then the patch is transformed to patch using the encoding on disk. + In particular, this means that patches generated against Unix checkouts of + Git sources will correctly apply to Windows checkouts of the same sources. +*) +val translate_patch: dir:string -> string -> string -> unit + (** Create a temporary file in {i ~/.opam/logs/XXX}, if [dir] is not set. ?auto_clean controls whether the file is automatically deleted when opam terminates (default: [true]). *) @@ -279,3 +327,12 @@ (** On Unix, a no-op. On Windows, convert \ to / *) val back_to_forward : string -> string + +(** Identifies kinds of executable files. At present, only useful on Windows. + Executable or DLLs are recognised based on their content, not on their + filename. Any file beginning "#!" is assumed to be a shell script and all + files are classified [`Unknown]. *) +val classify_executable : string -> [ `Exe of [ `i386 | `x86 | `x86_64 ] + | `Dll of [ `x86 | `x86_64 ] + | `Script + | `Unknown ] diff -Nru opam-2.0.10/src/core/opamUrl.ml opam-2.1.2/src/core/opamUrl.ml --- opam-2.0.10/src/core/opamUrl.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamUrl.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -29,6 +29,9 @@ hash = None; } +exception Parse_error of string +let parse_error s = raise (Parse_error s) + let split_url = let re = Re.(compile @@ whole_string @@ seq [ @@ -53,7 +56,7 @@ ]) in fun u -> - match Re.get_all (Re.exec re u) with + match Re.Group.all (Re.exec re u) with | [| _; vc; transport; path; suffix; hash |] -> let opt = function "" -> None | s -> Some s in opt vc, opt transport, path, opt suffix, opt hash @@ -63,7 +66,9 @@ | "git" -> `git | "hg" -> `hg | "darcs" -> `darcs - | x -> failwith (Printf.sprintf "Unsupported version control system %S" x) + | vc -> + parse_error (Printf.sprintf "unsupported version control system %s" + (OpamConsole.colorise `underline vc)) let string_of_vc = function | `git -> "git" @@ -82,7 +87,9 @@ | "darcs" -> `darcs | "hg" -> `hg | "path" | "local" | "rsync" | "ssh" | "scp" | "sftp" -> `rsync - | p -> failwith (Printf.sprintf "Unsupported protocol %S" p) + | p -> + parse_error (Printf.sprintf "unsupported protocol %s" + (OpamConsole.colorise `underline p)) let looks_like_ssh_path = @@ -106,10 +113,11 @@ fun path -> try let sub = Re.exec re path in - Some (Re.get sub 1 ^ try "/" ^ Re.get sub 2 with Not_found -> "") + Some (Re.Group.get sub 1 ^ + try "/" ^ Re.Group.get sub 2 with Not_found -> "") with Not_found -> None -let parse ?backend ?(handle_suffix=true) s = +let parse ?backend ?(handle_suffix=true) ?(from_file=true) s = let vc, transport, path, suffix, hash = split_url s in let backend = match backend with @@ -121,17 +129,17 @@ let of_suffix ~default = if not handle_suffix then default else match suffix with - | Some sf -> (try vc_of_string sf with Failure _ -> default) + | Some sf -> (try vc_of_string sf with Parse_error _ -> default) | None -> match OpamStd.String.cut_at path '@' with | Some (user, _) -> - (try vc_of_string user with Failure _ -> default) + (try vc_of_string user with Parse_error _ -> default) | None -> default in match transport with | None -> of_suffix ~default:`rsync | Some tr -> - try vc_of_string tr with Failure _ -> + try vc_of_string tr with Parse_error _ -> of_suffix ~default:(backend_of_string tr) in let transport, path = @@ -142,6 +150,8 @@ "ssh", path | _, (None | Some ("hg"|"darcs")), None -> "file", OpamSystem.real_path path |> OpamSystem.back_to_forward + | `rsync, Some "file", _ when not from_file -> + "file", OpamSystem.real_path path |> OpamSystem.back_to_forward | _, Some tr, _ -> tr, path in @@ -152,6 +162,15 @@ backend; } +let parse_opt ?(quiet=false) ?backend ?handle_suffix ?from_file s = + try + Some (parse ?backend ?handle_suffix ?from_file s) + with Parse_error pe -> + if not quiet then + OpamConsole.warning "URL parsing error on %s: %s" + (OpamConsole.colorise `underline s) pe; + None + let of_string url = parse ~handle_suffix:false url let to_string url = @@ -216,7 +235,7 @@ in fun t -> try - Re.get (Re.exec re t.path) 1 + Re.Group.get (Re.exec re t.path) 1 with Not_found -> "" let root = @@ -235,13 +254,23 @@ OpamStd.String.ends_with ~suffix:"/" url.path let to_json url = `String (to_string url) +let of_json = function +| `String s -> (try Some (of_string s) with _ -> None) +| _ -> None type url = t +let map_file_url f url = + if url.transport = "file" then + {url with path = f url.path} + else + url + module O = struct type t = url let to_string = to_string let to_json = to_json + let of_json = of_json let compare = compare end diff -Nru opam-2.0.10/src/core/opamUrl.mli opam-2.1.2/src/core/opamUrl.mli --- opam-2.0.10/src/core/opamUrl.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamUrl.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -17,8 +17,10 @@ val string_of_backend: backend -> string +exception Parse_error of string + (** Tolerates lots of backward compatibility names; - @raise Failure on unknown protocol *) + @raise Parse_error on unknown protocol *) val backend_of_string: string -> [> backend] type t = { @@ -34,8 +36,17 @@ otherwise guess version control from the suffix by default (for e.g. https://foo/bar.git). (this should be disabled when parsing from files). Note that [handle_suffix] also handles user-name in ssh addresses (e.g. - "ssh://git@github.com/...") *) -val parse: ?backend:backend -> ?handle_suffix:bool -> string -> t + "ssh://git@github.com/..."). If [from_file] is set to false, it resolves + rsync/file relative path. + @raise Parse_error *) +val parse: + ?backend:backend -> ?handle_suffix:bool -> ?from_file:bool -> string -> t + +(** Same as [parse], but catch [Parse_error]. In this case, display a warning + if [quiet] is not set to true. *) +val parse_opt: + ?quiet:bool -> ?backend:backend -> ?handle_suffix:bool -> ?from_file:bool + -> string -> t option include OpamStd.ABSTRACT with type t := t @@ -66,6 +77,10 @@ to an existing local path, check for version-control clues at that path *) val guess_version_control: string -> [> version_control ] option +(** [map_file_url f url] applies [f] to the [path] portion of [url] if + [transport] is ["file"]. *) +val map_file_url : (string -> string) -> t -> t + module Op: sig (** Appends at the end of an URL path with '/' separator. Gets back to the diff -Nru opam-2.0.10/src/core/opamVersionCompare.ml opam-2.1.2/src/core/opamVersionCompare.ml --- opam-2.0.10/src/core/opamVersionCompare.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamVersionCompare.ml 2021-12-07 16:09:27.000000000 +0000 @@ -18,7 +18,7 @@ ;; (* [skip_while_from i f w m] yields the index of the leftmost character - * in the string [s], starting from [i], end ending at [m], that does + * in the string [s], starting from [i], and ending at [m], that does * not satisfy the predicate [f], or [length w] if no such index exists. *) let skip_while_from i f w m = let rec loop i = diff -Nru opam-2.0.10/src/core/opamVersion.ml opam-2.1.2/src/core/opamVersion.ml --- opam-2.0.10/src/core/opamVersion.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/core/opamVersion.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,95 @@ +(**************************************************************************) +(* *) +(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +type t = string + +let to_string x = x + +let of_string x = x + +let to_json x = `String x + +let of_json = function +| `String x -> Some x +| _ -> None + +let compare v w = OpamVersionCompare.compare v w + +module O = struct + type t = string + let to_string = to_string + let to_json = to_json + let of_json = of_json + let compare = compare +end + +module Set = OpamStd.Set.Make(O) + +module Map = OpamStd.Map.Make(O) + +let current_raw = STRINGIFY(VERSION) + +let current = of_string current_raw + +let major v = + try + let i = String.index v '.' in + of_string (String.sub v 0 i) + with Not_found -> v + +let nopatch v = + try + let i = String.index v '.' in + let i = String.index_from v (i+1) '.' in + (String.sub v 0 i) + with Not_found -> + let rec f i = + if i >= String.length v then v + else match String.get v i with + | '0'..'9' | '.' -> f (i+1) + | _ -> String.sub v 0 i + in + f 0 + +let current_nopatch = nopatch current_raw + +let message () = + OpamConsole.msg "\n\ + %s version %s\n\ + \n\ + Copyright (C) 2012 OCamlPro - INRIA, 2013-2015 OCamlPro\n\ + \n\ + This is free software; see the source for copying conditions. There is NO\n\ + warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" + Sys.executable_name current_raw; + exit 0 + +let gitversion = ref None + +let set_git s = gitversion := Some s + +let git () = + match !gitversion with + | None -> None + | Some v -> Some (of_string v) + +let is_dev_version () = + (!gitversion <> None) + +let full () = + let git_version = match git () with + | None -> "" + | Some v -> Printf.sprintf " (%s)" (to_string v) in + Printf.sprintf "%s%s" (to_string current) git_version + +let magic () = + let hash = Hashtbl.hash (full ()) in + String.sub (Printf.sprintf "%08X" hash) 0 8 diff -Nru opam-2.0.10/src/core/opamVersion.mli opam-2.1.2/src/core/opamVersion.mli --- opam-2.0.10/src/core/opamVersion.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamVersion.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -31,6 +31,9 @@ (** Side-effect to set the git version later in the build *) val set_git: string -> unit +(** [true] if this is a development version of opam *) +val is_dev_version : unit -> bool + (** The full version (current + git) *) val full: unit -> t diff -Nru opam-2.0.10/src/core/opamVersion.ml.in opam-2.1.2/src/core/opamVersion.ml.in --- opam-2.0.10/src/core/opamVersion.ml.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/core/opamVersion.ml.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,87 +0,0 @@ -(**************************************************************************) -(* *) -(* Copyright 2012-2015 OCamlPro *) -(* Copyright 2012 INRIA *) -(* *) -(* All rights reserved. This file is distributed under the terms of the *) -(* GNU Lesser General Public License version 2.1, with the special *) -(* exception on linking described in the file LICENSE. *) -(* *) -(**************************************************************************) - -type t = string - -let to_string x = x - -let of_string x = x - -let to_json x = `String x - -let compare v w = OpamVersionCompare.compare v w - -module O = struct - type t = string - let to_string = to_string - let to_json = to_json - let compare = compare -end - -module Set = OpamStd.Set.Make(O) - -module Map = OpamStd.Map.Make(O) - -let current_raw = "@PACKAGE_VERSION@" - -let current = of_string current_raw - -let major v = - try - let i = String.index v '.' in - of_string (String.sub v 0 i) - with Not_found -> v - -let nopatch v = - try - let i = String.index v '.' in - let i = String.index_from v (i+1) '.' in - (String.sub v 0 i) - with Not_found -> - let rec f i = - if i >= String.length v then v - else match String.get v i with - | '0'..'9' | '.' -> f (i+1) - | _ -> String.sub v 0 i - in - f 0 - -let current_nopatch = nopatch current_raw - -let message () = - OpamConsole.msg "\n\ - %s version %s\n\ - \n\ - Copyright (C) 2012 OCamlPro - INRIA, 2013-2015 OCamlPro\n\ - \n\ - This is free software; see the source for copying conditions. There is NO\n\ - warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n" - Sys.argv.(0) current_raw; - exit 0 - -let gitversion = ref None - -let set_git s = gitversion := Some s - -let git () = - match !gitversion with - | None -> None - | Some v -> Some (of_string v) - -let full () = - let git_version = match git () with - | None -> "" - | Some v -> Printf.sprintf " (%s)" (to_string v) in - Printf.sprintf "%s%s" (to_string current) git_version - -let magic () = - let hash = Hashtbl.hash (full ()) in - String.sub (Printf.sprintf "%08X" hash) 0 8 diff -Nru opam-2.0.10/src/crowbar/dune opam-2.1.2/src/crowbar/dune --- opam-2.0.10/src/crowbar/dune 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/dune 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,18 @@ +(executable + (name test) + (modules opamCrowbar + opamFilename_crowbar + opamHash_crowbar + opamUrl_crowbar + opamVersion_crowbar + opamCudf_crowbar + opamPackage_crowbar + opamVariable_crowbar + opamActionGraph_crowbar + test) + (libraries crowbar opam-core opam-format opam-solver) + (flags (:standard + (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) + (:include ../ocaml-context-flags.sexp))) +) diff -Nru opam-2.0.10/src/crowbar/dune-project opam-2.1.2/src/crowbar/dune-project --- opam-2.0.10/src/crowbar/dune-project 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/dune-project 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,2 @@ +(lang dune 1.2) +(name opam-crowbar) diff -Nru opam-2.0.10/src/crowbar/opamActionGraph_crowbar.ml opam-2.1.2/src/crowbar/opamActionGraph_crowbar.ml --- opam-2.0.10/src/crowbar/opamActionGraph_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamActionGraph_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,63 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open! Crowbar +open OpamCrowbar + +let atomic_action gen = choose [ + (map [gen] @@ fun v -> `Remove v); + (map [gen] @@ fun v -> `Install v); +] +let highlevel_action gen = choose [ + atomic_action gen; + (map + [choose [const `Up; const `Down]; gen; gen] + @@ fun dir a b -> `Change (dir, a, b)); + (map [gen] @@ fun v -> `Reinstall v); +] +let concrete_action gen = choose [ + atomic_action gen; + (map [gen] @@ fun v -> `Build v); +] + +let action gen = choose [ + atomic_action gen; + highlevel_action gen; + concrete_action gen; +] + +module Action = OpamCudf.Action + +let cudf_action = action OpamCudf_crowbar.package + +module ActionGraph = OpamCudf.ActionGraph + +let cudf_graph = + map [list cudf_action; list (pair int int)] @@ fun vertices edge_codes -> + if vertices = [] then ActionGraph.build [] [] + else begin + let get_vertex = + let array = Array.of_list vertices in + fun i -> array.((abs i) mod Array.length array) in + let get_edge (i, j) = + let src = get_vertex i in + let dst = get_vertex j in + ActionGraph.E.create src () dst + in + let edges = List.map get_edge edge_codes in + ActionGraph.build vertices edges + end + +let check () = + check_json_roundtrip ~name:"OpamActionGraph.Make(OpamCudf).t" + cudf_action (eq_of_comp Action.compare) Action.to_json Action.of_json; + check_json_roundtrip ~name:"OpamActionGraph.Make(OpamCudf).g" + cudf_graph (eq_of_comp ActionGraph.compare) + ActionGraph.to_json ActionGraph.of_json; diff -Nru opam-2.0.10/src/crowbar/opamCrowbar.ml opam-2.1.2/src/crowbar/opamCrowbar.ml --- opam-2.0.10/src/crowbar/opamCrowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamCrowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,38 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open! Crowbar + +(* this 'pair' combinator exists in crowbar master but not 0.1 *) +let pair gena genb = + map [gena; genb] (fun a b -> (a,b)) + +let nice_int = int8 +let nice_uint = uint8 +let nice_string = + let letter = map [range 25] (fun n -> char_of_int (int_of_char 'a' + n)) in + with_printer Format.pp_print_string @@ + map [letter; letter; letter; letter; letter] @@ + fun a b c d e -> String.of_seq (List.to_seq [a;b;c;d;e]) + +let eq_of_comp comp v1 v2 = (comp v1 v2 = 0) + +let check_json_roundtrip ~name gen equal to_json of_json = + let pp ppf = function + | None -> assert false + | Some x -> Format.fprintf ppf "%s\n%!" (OpamJson.to_string (to_json x)) + in + let equal x y = match x, y with + | None, None -> true + | Some _, None | None, Some _ -> false + | Some x, Some y -> equal x y in + Crowbar.add_test ~name [gen] @@ (fun x -> + Crowbar.check_eq ~pp ~eq:equal (Some x) (of_json (to_json x)) + ) diff -Nru opam-2.0.10/src/crowbar/opamCrowbar.mli opam-2.1.2/src/crowbar/opamCrowbar.mli --- opam-2.0.10/src/crowbar/opamCrowbar.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamCrowbar.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,21 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +val nice_int : int Crowbar.gen +val nice_uint : int Crowbar.gen +val nice_string : string Crowbar.gen + +val eq_of_comp : ('a -> 'a -> int) -> ('a -> 'a -> bool) + +val pair : 'a Crowbar.gen -> 'b Crowbar.gen -> ('a * 'b) Crowbar.gen + +val check_json_roundtrip : + name:string -> 'a Crowbar.gen -> + ('a -> 'a -> bool) -> 'a OpamJson.encoder -> 'a OpamJson.decoder -> unit diff -Nru opam-2.0.10/src/crowbar/opam-crowbar.opam opam-2.1.2/src/crowbar/opam-crowbar.opam --- opam-2.0.10/src/crowbar/opam-crowbar.opam 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opam-crowbar.opam 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,20 @@ +opam-version: "2.0" +version: "2.0.0" +maintainer: "opam-devel@lists.ocaml.org" +authors: [ + "Gabriel Scherer " +] +homepage: "https://opam.ocaml.org" +bug-reports: "https://github.com/ocaml/opam/issues" +dev-repo: "git+https://github.com/ocaml/opam.git" +depends: [ + "ocaml" {>= "4.02.3"} + "crowbar" + "afl" +] + +messages: """ + This file is for easy use of (opam install --deps-only), + use the Makefile targets 'crowbar' and 'crowbar-afl' at the root + for actual usage. (crowbar-afl requires being in a +afl switch. +""" diff -Nru opam-2.0.10/src/crowbar/opamCudf_crowbar.ml opam-2.1.2/src/crowbar/opamCudf_crowbar.ml --- opam-2.0.10/src/crowbar/opamCudf_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamCudf_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,146 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open Cudf_types +open Cudf +open! Crowbar + +open OpamCrowbar + +let version = nice_int +let relop : relop gen = choose [ + const `Eq; + const `Neq; + const `Geq; + const `Gt; + const `Leq; + const `Lt; +] +let constr = option (pair relop version) + +let pkgname = nice_string +let vpkg = pair pkgname constr +let vpkglist = list vpkg + +let enum_keep : enum_keep gen = choose [ + const `Keep_version; + const `Keep_package; + const `Keep_feature; + const `Keep_none; +] + +let vpkgformula = list (list vpkg) +let veqpkg = pair pkgname (option (pair (const `Eq) version)) +let veqpkglist = list veqpkg + +let rec typedecl1 : typedecl1 gen Lazy.t = lazy (choose [ + (map [option nice_int] @@ fun v -> `Int v); + (map [option nice_uint] @@ fun v -> `Posint v); + (map [option nice_uint] @@ fun v -> `Nat v); + (map [option bool] @@ fun v -> `Bool v); + (map [option nice_string] @@ fun v -> `String v); + (map [option nice_string] @@ fun v -> `Pkgname v); + (map [option nice_string] @@ fun v -> `Ident v); + (map [list nice_string; option nice_string; list nice_string] + @@ fun left s right -> + `Enum (left @ (match s with None -> [] | Some s -> [s]) @ right, s)); + (map [option vpkg] @@ fun v -> `Vpkg v); + (map [option vpkgformula] @@ fun v -> `Vpkgformula v); + (map [option vpkglist] @@ fun v -> `Vpkglist v); + (map [option veqpkg] @@ fun v -> `Veqpkg v); + (map [option veqpkglist] @@ fun v -> `Veqpkglist v); + (map [option (Crowbar.unlazy typedecl)] @@ fun v -> `Typedecl v); + ]) +and typedecl = lazy (list (pair nice_string (unlazy typedecl1))) +let (lazy typedecl1) = typedecl1 +and (lazy typedecl) = typedecl + +let typed_value : typed_value gen = choose [ + (map [nice_int] @@ fun v -> `Int v); + (map [nice_uint] @@ fun v -> `Posint v); + (map [nice_uint] @@ fun v -> `Nat v); + (map [bool] @@ fun v -> `Bool v); + (map [nice_string] @@ fun v -> `String v); + (map [nice_string] @@ fun v -> `Pkgname v); + (map [nice_string] @@ fun v -> `Ident v); + (map [nice_string] @@ fun v -> `Pkgname v); + (map [list nice_string; nice_string; list nice_string] + @@ fun left s right -> + `Enum (left @ [s] @ right, s)); + (map [vpkg] @@ fun v -> `Vpkg v); + (map [vpkgformula] @@ fun v -> `Vpkgformula v); + (map [vpkglist] @@ fun v -> `Vpkglist v); + (map [veqpkg] @@ fun v -> `Veqpkg v); + (map [veqpkglist] @@ fun v -> `Veqpkglist v); + (map [typedecl] @@ fun v -> `Typedecl v); +] + +let stanza gen = list (pair nice_string gen) + +let package : package gen = + map [ + pkgname; + version; + vpkgformula; + vpkglist; + veqpkglist; + bool; + bool; + enum_keep; + stanza typed_value; + ] (fun + package + version + depends + conflicts + provides + installed + was_installed + keep + pkg_extra + -> + { + package; + version; + depends; + conflicts; + provides; + installed; + was_installed; + keep; + pkg_extra; + }) + +let check () = + let open OpamCudf.Json in + check_json_roundtrip ~name:"Cudf_types.relop" + relop (=) + relop_to_json relop_of_json; + check_json_roundtrip ~name:"Cudf_types.constr" + constr (=) + constr_to_json constr_of_json; + check_json_roundtrip ~name:"Cudf_types.vpkg" + vpkg (=) + vpkg_to_json vpkg_of_json; + check_json_roundtrip ~name:"Cudf_types.enum_keep" + enum_keep (=) + enum_keep_to_json enum_keep_of_json; + check_json_roundtrip ~name:"Cudf_types.veqpkg" + veqpkg (=) + veqpkg_to_json veqpkg_of_json; + check_json_roundtrip ~name:"Cudf_types.typedecl1" + typedecl1 (=) + typedecl1_to_json typedecl1_of_json; + check_json_roundtrip ~name:"Cudf_types.typed_value" + typed_value (=) + typed_value_to_json typed_value_of_json; + check_json_roundtrip ~name:"Cudf.package" + package OpamCudf.Package.equal + package_to_json package_of_json; diff -Nru opam-2.0.10/src/crowbar/opamFilename_crowbar.ml opam-2.1.2/src/crowbar/opamFilename_crowbar.ml --- opam-2.0.10/src/crowbar/opamFilename_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamFilename_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,39 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamFilename +open! Crowbar +open OpamCrowbar + +let base : Base.t gen = + let dirname = map [nice_string] @@ Base.of_string in + choose [ + dirname; + map [dirname; nice_string] Base.add_extension; + ] + +let dir : Dir.t gen = + let dir_s = choose [ + nice_string; + (map [nice_string] @@ fun s -> "~" ^ Filename.dir_sep ^ s); + const "~"; + ] in + map [dir_s] @@ OpamFilename.Dir.of_string + +let filename : t gen = + map [dir; base] @@ create + +let check () = + check_json_roundtrip ~name:"OpamFilename.Base.t" + base (=) Base.to_json Base.of_json; + check_json_roundtrip ~name:"OpamFilename.Dir.t" + dir (=) Dir.to_json Dir.of_json; + check_json_roundtrip ~name:"OpamFilename.t" + filename (=) to_json of_json; diff -Nru opam-2.0.10/src/crowbar/opamHash_crowbar.ml opam-2.1.2/src/crowbar/opamHash_crowbar.ml --- opam-2.0.10/src/crowbar/opamHash_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamHash_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,26 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamHash +open! Crowbar +open OpamCrowbar + +let kind : kind gen = choose [ + const `MD5; + const `SHA256; + const `SHA512; +] + +let hash = map [kind; bytes] @@ fun kind string -> + OpamHash.compute_from_string ~kind string + +let check () = + check_json_roundtrip ~name:"OpamHash.t" + hash (=) OpamHash.to_json OpamHash.of_json; diff -Nru opam-2.0.10/src/crowbar/opamPackage_crowbar.ml opam-2.1.2/src/crowbar/opamPackage_crowbar.ml --- opam-2.0.10/src/crowbar/opamPackage_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamPackage_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,45 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamPackage +open! Crowbar +open OpamCrowbar + +let version = + let main_part = + map [list1 nice_uint] @@ fun ns -> + List.map string_of_int ns |> String.concat "." + in + let weird_stuff = choose [ + map [nice_uint] (fun n -> "~" ^ string_of_int n); + map [nice_uint] (fun n -> "+" ^ string_of_int n); + map [nice_string] (fun s -> "+" ^ s); + ] in + let version_string = choose [ + main_part; + map [main_part; weird_stuff] @@ (^); + map + [main_part; weird_stuff; weird_stuff; weird_stuff] + @@ Printf.sprintf "%s%s%s%s" + ] in + map [version_string] Version.of_string + +let name = + let name_string = choose [ + nice_string; + map [nice_string; nice_uint] (Printf.sprintf "%s%d"); + ] in + map [name_string] Name.of_string + +let package = map [name; version] create + +let check () = + check_json_roundtrip ~name:"OpamPackage.t" + package (eq_of_comp OpamPackage.compare) to_json of_json; diff -Nru opam-2.0.10/src/crowbar/opamUrl_crowbar.ml opam-2.1.2/src/crowbar/opamUrl_crowbar.ml --- opam-2.0.10/src/crowbar/opamUrl_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamUrl_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,52 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamUrl +open! Crowbar +open OpamCrowbar + +let version_control = choose [ + const `git; + const `darcs; + const `hg; +] +let backend = + let print ppf b = Format.pp_print_string ppf (string_of_backend b) in + with_printer print @@ choose [ + const `http; + const `rsync; + version_control; +] + +let transport = choose [ + const "http"; + const "ssh"; + const "file"; + const "git"; + const "hg"; + const "darcs"; +] + +let url : OpamUrl.t gen = map [ + transport; + nice_string; + option nice_string; + ] @@ fun + transport + path + hash + -> + String.concat "" + [transport; "://"; path; (match hash with None -> "" | Some h -> h)] + |> OpamUrl.parse + +let check () = + check_json_roundtrip ~name:"OpamUrl.t" + url (=) OpamUrl.to_json OpamUrl.of_json; diff -Nru opam-2.0.10/src/crowbar/opamVariable_crowbar.ml opam-2.1.2/src/crowbar/opamVariable_crowbar.ml --- opam-2.0.10/src/crowbar/opamVariable_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamVariable_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,26 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamVariable +open! Crowbar +open OpamCrowbar + +let variable = map [nice_string] @@ of_string + +let full = choose [ + map [variable] @@ Full.global; + map [variable] @@ Full.self; + map [OpamPackage_crowbar.name; variable] @@ Full.create; +] + +let check () = + let equal v1 v2 = Full.to_string v1 = Full.to_string v2 in + check_json_roundtrip ~name:"OpamVariable.t" + full equal Full.to_json Full.of_json; diff -Nru opam-2.0.10/src/crowbar/opamVersion_crowbar.ml opam-2.1.2/src/crowbar/opamVersion_crowbar.ml --- opam-2.0.10/src/crowbar/opamVersion_crowbar.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/opamVersion_crowbar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,24 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamVersion +open! Crowbar +open OpamCrowbar + +let version = choose [ + const current; + const (major current); + const current_nopatch; + const (full ()); +] + +let check () = + check_json_roundtrip ~name:"OpamVersion.t" + version (eq_of_comp OpamVersion.compare) to_json of_json; diff -Nru opam-2.0.10/src/crowbar/test.ml opam-2.1.2/src/crowbar/test.ml --- opam-2.0.10/src/crowbar/test.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/crowbar/test.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,19 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +let () = + OpamCudf_crowbar.check (); + OpamFilename_crowbar.check (); + OpamHash_crowbar.check (); + OpamUrl_crowbar.check (); + OpamVersion_crowbar.check (); + OpamPackage_crowbar.check (); + OpamVariable_crowbar.check (); + OpamActionGraph_crowbar.check (); diff -Nru opam-2.0.10/src/dune opam-2.1.2/src/dune --- opam-2.0.10/src/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/dune 2021-12-07 16:09:27.000000000 +0000 @@ -1,7 +1,7 @@ (rule - (targets ocaml-flags-standard.sexp) - (deps (:input ocaml-flags-standard.sexp.in) (:script ../shell/subst_var.ml) ../config.status) - (action (with-stdout-to %{targets} (run ocaml %{script} CONF_OCAMLFLAGS "" %{input})))) + (targets ocaml-flags-configure.sexp) + (mode fallback) + (action (with-stdout-to %{targets} (echo "()")))) (rule (with-stdout-to ocaml-context-flags.sexp (run ocaml %{dep:../shell/context_flags.ml} flags))) diff -Nru opam-2.0.10/src/format/dune opam-2.1.2/src/format/dune --- opam-2.0.10/src/format/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/dune 2021-12-07 16:09:27.000000000 +0000 @@ -6,7 +6,8 @@ (modules_without_implementation OpamTypes) (flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp))) (wrapped false)) -(ocamllex opamLineLexer) +(ocamllex opamLineLexer opamInterpLexer) diff -Nru opam-2.0.10/src/format/opamFile.ml opam-2.1.2/src/format/opamFile.ml --- opam-2.0.10/src/format/opamFile.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFile.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -19,10 +19,14 @@ - files using the "opam syntax" and lexer, parsed using OpamFormat.Pp.V *) +open OpamParserTypes.FullPos open OpamTypes open OpamTypesBase open OpamStd.Op +module OpamParser = OpamParser.FullPos +module OpamPrinter = OpamPrinter.FullPos + module Pp = struct include OpamPp module V = OpamFormat.V @@ -58,6 +62,7 @@ module type IO_FILE = sig type t + val format_version: OpamVersion.t val empty: t val write: 'a typed_file -> t -> unit val read : 'a typed_file -> t @@ -182,17 +187,19 @@ let write_to_string ?(filename=dummy_file) t = F.to_string filename t + end (** I - Raw text files (no parsing) *) -(** Compiler and package description files - (/packages/.../descr, /compilers/.../.descr): - one-line title and content *) +(** Compiler and package description opam file fields: one-line title and + content. Formerly, (/packages/.../descr, + /compilers/.../.descr) *) module DescrIO = struct let internal = "descr" + let format_version = OpamVersion.of_string "0" type t = string * string @@ -278,6 +285,8 @@ (* Lines of space separated words *) type t = string list list + let format_version = OpamVersion.of_string "0" + let empty = [] let internal = "lines" @@ -380,6 +389,8 @@ module IO = struct include X + let format_version = OpamVersion.of_string "0" + let to_channel _ oc t = Pp.print (Lines.pp_channel stdin oc -| pp) t let to_string _ t = Pp.print (Lines.pp_string -| pp) t @@ -389,7 +400,7 @@ let of_string filename str = Pp.parse (Lines.pp_string -| pp) - ~pos:(OpamFilename.to_string filename,0,0) + ~pos:{ pos_null with filename = OpamFilename.to_string filename } str end @@ -542,7 +553,7 @@ (OpamFormat.lines_set ~empty:[] ~add:OpamStd.List.cons ~fold:List.fold_right @@ Pp.identity ^+ Pp.of_pair "env_update_op" - (OpamLexer.env_update_op, OpamPrinter.env_update_op) ^+ + (OpamLexer.FullPos.env_update_op, OpamPrinter.env_update_op_kind) ^+ Pp.identity ^+ Pp.opt Pp.singleton) -| Pp.pp (fun ~pos:_ -> List.rev) List.rev @@ -711,9 +722,14 @@ let curr = lexbuf.Lexing.lex_curr_p in let start = lexbuf.Lexing.lex_start_p in let pos = - curr.Lexing.pos_fname, - start.Lexing.pos_lnum, - start.Lexing.pos_cnum - start.Lexing.pos_bol + { filename = curr.Lexing.pos_fname; + start = + start.Lexing.pos_lnum, + start.Lexing.pos_cnum - start.Lexing.pos_bol; + stop = (* XXX here we take current position, where error occurs as end position *) + curr.Lexing.pos_lnum, + curr.Lexing.pos_cnum - curr.Lexing.pos_bol; + } in raise (OpamPp.Bad_format (Some pos, msg)) in @@ -760,111 +776,241 @@ match current_str_opt with | None -> to_string filename (Pp.print pp (filename, t)) | Some str -> - let syn_file = of_string filename str in - let syn_t = Pp.print pp (filename, t) in - let it_ident = function - | Variable (_, f, _) -> `Var f - | Section (_, {section_kind = k; section_name = n; _}) -> `Sec (k,n) - in - let it_pos = function - | Section (pos,_) | Variable (pos,_,_) -> pos - in - let lines_index = - let rec aux acc s = - let until = - try Some (String.index_from s (List.hd acc) '\n') - with Not_found -> None - in - match until with - | Some until -> aux (until+1 :: acc) s - | None -> Array.of_list (List.rev acc) + let syn_file = of_string filename str in + let syn_t = Pp.print pp (filename, t) in + let it_ident it = match it.pelem with + | Variable (f, _) -> `Var f.pelem + | Section ({section_kind = k; section_name = n; _}) -> + `Sec (k.pelem, OpamStd.Option.map (fun x -> x.pelem) n) in - aux [0] str - in - let pos_index (_file, li, col) = lines_index.(li - 1) + col in - let field_str ident = - let rec aux = function - | it1 :: r when it_ident it1 = ident -> - let start = pos_index (it_pos it1) in - let stop = match r with - | it2 :: _ -> pos_index (it_pos it2) - 1 - | [] -> - let len = ref (String.length str) in - while str.[!len - 1] = '\n' do decr len done; - !len + let lines_index = + let rec aux acc s = + let until = + try Some (String.index_from s (List.hd acc) '\n') + with Not_found -> None in - String.sub str start (stop - start) - | _ :: r -> aux r - | [] -> raise Not_found + match until with + | Some until -> aux (until+1 :: acc) s + | None -> Array.of_list (List.rev acc) + in + aux [0] str in - aux syn_file.file_contents - in - let rem, strs = - List.fold_left (fun (rem, strs) item -> - List.filter (fun i -> it_ident i <> it_ident item) rem, - match item with - | Variable (pos, name, v) -> - (try - let ppa = List.assoc name fields in - match snd (Pp.print ppa t) with - | None | Some (List (_, [])) | Some (List (_,[List(_,[])])) -> - strs - | field_syn_t when - field_syn_t = - snd (Pp.print ppa (Pp.parse ppa ~pos (empty, Some v))) - -> - (* unchanged *) - field_str (`Var name) :: strs - | _ -> - try - let f = - List.find (fun i -> it_ident i = `Var name) syn_t.file_contents + let pos_index (li,col) = lines_index.(li - 1) + col in + let extract start stop = String.sub str start (stop - start) in + let value_list_str lastpos vlst vlst_raw = + let extract_pos start stop = extract (pos_index start) (pos_index stop) in + let def_blank blank = OpamStd.Option.default "\n " blank in + let find_split f = + let rec aux p = function + | x::r when f x -> Some (p, x, r) + | p::r -> aux (Some p) r + | [] -> None + in + aux None + in + let full_vlst_raw = vlst_raw in + let rec aux lastpos blank acc vlst vlst_raw = + match vlst, vlst_raw with + | v::r, vraw :: rraw when OpamPrinter.value_equals v vraw -> + let blank = extract lastpos (pos_index vraw.pos.start) in + let str = extract_pos vraw.pos.start vraw.pos.stop in + let new_v = blank ^ str in + let blank = Some blank in + let lastpos = pos_index vraw.pos.stop in + aux lastpos blank (new_v :: acc) r rraw + | v::r , _ -> + (match find_split (OpamPrinter.value_equals v) full_vlst_raw with + | Some (pvraw, vraw, rraw) -> + let str = extract_pos vraw.pos.start vraw.pos.stop in + let blank, lastpos = + if pos_index vraw.pos.start - lastpos <= 0 then + def_blank blank, lastpos + else + (let start = match pvraw with + | Some pvraw -> pos_index pvraw.pos.stop + | None -> lastpos + in + let stop = pos_index vraw.pos.start in + extract start stop), + pos_index vraw.pos.stop + in + let new_v = blank ^ str in + let blank = Some blank in + aux lastpos blank (new_v :: acc) r rraw + | None -> + let blank, rraw, lastpos = + match vlst_raw with + | vraw :: rraw -> + let blank = extract lastpos (pos_index vraw.pos.start) in + let rraw, lastpos = + if OpamStd.List.find_opt + (OpamPrinter.value_equals vraw) vlst <> None then + vlst_raw, lastpos + else + rraw, pos_index vraw.pos.stop in - OpamPrinter.items [f] :: strs - with Not_found -> strs - with Not_found | OpamPp.Bad_format _ -> - if OpamStd.String.starts_with ~prefix:"x-" name then - field_str (`Var name) :: strs - else strs) - | Section (pos, {section_kind; section_name; section_items}) -> - (try - let ppa = List.assoc section_kind sections in - let print_sec ppa t = - match snd (Pp.print ppa t) with - | None -> None - | Some v -> - try Some (List.assoc section_name v) with Not_found -> None + blank, rraw, lastpos + | [] -> def_blank blank, vlst_raw, lastpos in - let sec_field_t = print_sec ppa t in - if sec_field_t <> None && - sec_field_t = - print_sec ppa - (Pp.parse ppa ~pos - (empty, Some [section_name, section_items])) - then - (* unchanged *) - field_str (`Sec (section_kind, section_name)) :: strs - else - try + let new_v = blank ^ (OpamPrinter.value v) in + let blank = Some blank in + aux lastpos blank (new_v :: acc) r rraw) + | [], _ -> acc + in + aux lastpos None [] vlst vlst_raw + in + let item_var_str name field = + let field_raw = + List.find (fun i -> it_ident i = `Var name) syn_file.file_contents + in + match field.pelem with + | Variable (n, { pelem = List { pelem = full_vlst;_}; _}) + when n.pelem = name -> + let full_vlst_raw, full_vlst_raw_pos = + match field_raw.pelem with + | Variable (_, {pelem = List vlst_raw; pos}) -> vlst_raw.pelem, pos + | _ -> raise Not_found + in + (* if empty, rewrite full field *) + if full_vlst_raw = [] then OpamPrinter.items [field] else + (* aux *) + let item_var_str = + let lastpos = pos_index full_vlst_raw_pos.start +1 in + let final_list = value_list_str lastpos full_vlst full_vlst_raw in + String.concat "" (List.rev final_list) + in + let beginning = + let start = pos_index field_raw.pos.start in + let stop = pos_index full_vlst_raw_pos.start +1 in + extract start stop + in + let ending = + let start = pos_index (List.hd (List.rev full_vlst_raw)).pos.stop in + let stop = pos_index full_vlst_raw_pos.stop in + extract start stop + in + beginning ^ item_var_str ^ ending + | _ -> OpamPrinter.items [field] + in + (* Fields *) + let get_padding item lastpos = + let start = pos_index item.pos.start in + let stop = pos_index item.pos.stop in + let padding = extract lastpos start in + padding, stop + in + let field_str item lastpos strs = + let start = pos_index item.pos.start in + let padding, stop = get_padding item lastpos in + let field = extract start stop in + field :: padding :: strs, stop + in + let rem, (strs, lastpos) = + List.fold_left (fun (rem, (strs, lastpos)) item -> + List.filter (fun i -> it_ident i <> it_ident item) rem, + let pos = item.pos in + match item.pelem with + | Variable (name, v) -> + let name = name.pelem in + (try + let ppa = List.assoc name fields in + match snd (Pp.print ppa t) with + | None + | Some { pelem = List { pelem = []; _}; _} + | Some { pelem = List + { pelem = [ { pelem = List + { pelem = []; _}; _}]; _}; _} -> + strs, pos_index item.pos.stop + | field_syn_t when + field_syn_t = + snd (Pp.print ppa (Pp.parse ppa ~pos (empty, Some v))) + -> + (* unchanged *) + field_str item lastpos strs + | _ -> + try + let field = + List.find (fun i -> it_ident i = `Var name) syn_t.file_contents + in + let f = item_var_str name field in + let padding, stop = get_padding item lastpos in + f :: padding :: strs, stop + with Not_found -> strs, pos_index item.pos.stop + with Not_found | OpamPp.Bad_format _ -> + if OpamStd.String.starts_with ~prefix:"x-" name && + OpamStd.List.find_opt (fun i -> it_ident i = `Var name) + syn_t.file_contents <> None then + field_str item lastpos strs + else strs, pos_index item.pos.stop) + | Section {section_kind; section_name; section_items} -> + let section_kind = section_kind.pelem in + let section_items = section_items.pelem in + let section_name = OpamStd.Option.map (fun x -> x.pelem) section_name in + (try + let ppa = List.assoc section_kind sections in + let print_sec ppa t = + match snd (Pp.print ppa t) with + | None -> None + | Some v -> + try Some (List.assoc section_name v) with Not_found -> None + in + let sec_field_t = print_sec ppa t in + if sec_field_t <> None && + sec_field_t = + print_sec ppa + (Pp.parse ppa ~pos + (empty, Some [section_name, section_items])) + then + (* unchanged *) + field_str item lastpos strs + else let f = List.filter (fun i -> it_ident i = `Sec (section_kind, section_name)) syn_t.file_contents in - OpamPrinter.items f :: strs - with Not_found -> strs - with Not_found | OpamPp.Bad_format _ -> strs) - ) - (syn_t.file_contents, []) syn_file.file_contents + let padding, stop = get_padding item lastpos in + (OpamPrinter.items f :: padding :: strs), stop + with Not_found | OpamPp.Bad_format _ -> + strs, pos_index item.pos.stop)) + (syn_t.file_contents, ([], 0)) syn_file.file_contents + in + let str = String.concat "" (List.rev strs) in + let str = + if rem = [] then str else + str ^ "\n" ^ (OpamPrinter.items rem) + in + let str = + let last = lines_index.(Array.length lines_index -1) in + if last <= lastpos then str else str ^ extract lastpos last + in + str + + let contents pp ?(filename=dummy_file) t = + Pp.print pp (filename, t) + + let to_list pp ?(filename=dummy_file) t = + let rec aux acc pfx = function + | {pelem=Section ({section_kind; section_name=None; section_items});_} :: r -> + aux (aux acc (section_kind.pelem :: pfx) section_items.pelem) pfx r + | {pelem=Section ({section_kind; section_name=Some n; section_items});_} :: r -> + aux + (aux acc (Printf.sprintf "%s(%s)" section_kind.pelem n.pelem :: pfx) + section_items.pelem) + pfx r + | {pelem=Variable (name, value);_} :: r -> + aux (((name.pelem :: pfx), value) :: acc) pfx r + | [] -> acc in - String.concat "\n" - (List.rev_append strs - (if rem = [] then [""] else [OpamPrinter.items rem;""])) + List.rev_map + (fun (pfx, value) -> String.concat "." (List.rev pfx), value) + (aux [] [] (contents pp ~filename t).file_contents) end module type SyntaxFileArg = sig val internal: string + val format_version: OpamVersion.t type t val empty: t val pp: (opamfile, filename * t) Pp.t @@ -876,13 +1022,11 @@ let to_opamfile filename t = Pp.print X.pp (filename, t) let catch_future_syntax_error = function - | {file_contents = [ Variable (_, "opam-version", - String (_, ver)); - Section (pos, {section_kind = "#"; _})]; _} - when OpamVersion.(compare (nopatch (of_string ver)) - (OpamVersion.of_string "2.0")) <= 0 -> - raise (OpamPp.Bad_format (Some pos, "Parse error")) - | opamfile -> opamfile + | {file_contents = [{pelem = Variable({pelem = "opam-version"; _}, {pelem = String ver; _}); _ }; + {pelem = Section {section_kind = {pelem = "#"; _}; _}; pos}]; _} + when OpamVersion.(compare (nopatch (of_string ver)) (nopatch X.format_version)) <= 0 -> + raise (OpamPp.Bad_version (Some pos, "Parse error")) + | opamfile -> opamfile let of_channel filename (ic:in_channel) = let opamfile = Syntax.of_channel filename ic |> catch_future_syntax_error in @@ -907,16 +1051,26 @@ include X include IO end) + end (* Error less reading for forward compatibility of opam roots *) module type BestEffortArg = sig include SyntaxFileArg + (* Version of file format, as understood by [opam-file-format] *) + (* This attribute can be deleted when 4.02 is ditched *) + [@@@ocaml.warning "-32"] + val file_format_version: OpamVersion.t [@@ocaml.warning "-32"] + (* Construct the syntax pp, under some conditions. If [condition] is given, it is passed to [OpamFormat.show_erros] call, for error display - conditions, default is to display it as a warning. *) - val pp_cond: ?condition:(t -> bool) -> unit -> (opamfile, filename * t) Pp.t + conditions, default is to display it as a warning. If [f] is given, it is + passed to [OpamFormat.check_opam_version] call, it is the check function, + default is to check regarding [file_format_version]. *) + val pp_cond: + ?f:(OpamVersion.t -> bool) -> ?condition:(t -> bool) -> unit -> + (opamfile, filename * t) Pp.t end module type BestEffortRead = sig @@ -932,7 +1086,13 @@ with type t := S.t = struct module ES = struct include S - let pp = pp_cond ~condition:(fun _ -> false) () + let pp = + pp_cond + (* to read newer oapm root with newer `opam-version` field, we need to + be less strict on the check_opam_version + ~f:(fun _ -> true) + *) + ~condition:(fun _ -> false) () end include ES include SyntaxFile(ES) @@ -1068,19 +1228,19 @@ module ConfigSyntax = struct let internal = "config" + let format_version = OpamVersion.of_string "2.1" + let file_format_version = OpamVersion.of_string "2.0" + let root_version = OpamVersion.of_string "2.1" - (* Unused, present as a hint *) - let _format_version = OpamVersion.of_string "2.0" - let _file_format_version = OpamVersion.of_string "2.0" - let root_version = OpamVersion.of_string "2.0" + let default_old_root_version = OpamVersion.of_string "2.1~~previous" type t = { opam_version : opam_version; - opam_root_version: opam_version option; + opam_root_version: opam_version; repositories : repository_name list; installed_switches : switch list; switch : switch option; - jobs : int; + jobs : int option; dl_tool : arg list option; dl_jobs : int; dl_cache : url list option; @@ -1092,10 +1252,18 @@ eval_variables : (variable * string list * string) list; validation_hook : arg list option; default_compiler : formula; + default_invariant : formula; + depext: bool; + depext_run_installs : bool; + depext_cannot_install : bool; + depext_bypass: OpamSysPkg.Set.t; } let opam_version t = t.opam_version let opam_root_version t = t.opam_root_version + let opam_root_version_opt t = + if OpamVersion.compare t.opam_root_version default_old_root_version = 0 then + None else Some t.opam_root_version let repositories t = t.repositories let installed_switches t = t.installed_switches let switch t = t.switch @@ -1116,16 +1284,23 @@ let validation_hook t = t.validation_hook let default_compiler t = t.default_compiler + let default_invariant t = t.default_invariant + + let depext t = t.depext + let depext_run_installs t = t.depext_run_installs + let depext_cannot_install t = t.depext_cannot_install + let depext_bypass t = t.depext_bypass + let with_opam_version opam_version t = { t with opam_version } - let with_opam_root_version opam_root_version t = - { t with opam_root_version = Some opam_root_version } + let with_opam_root_version opam_root_version t = { t with opam_root_version } let with_repositories repositories t = { t with repositories } let with_installed_switches installed_switches t = { t with installed_switches } let with_switch_opt switch t = { t with switch } let with_switch switch t = { t with switch = Some switch } - let with_jobs jobs t = { t with jobs } + let with_jobs jobs t = { t with jobs = Some jobs} + let with_jobs_opt jobs t = { t with jobs } let with_dl_tool dl_tool t = { t with dl_tool = Some dl_tool } let with_dl_tool_opt dl_tool t = { t with dl_tool } let with_dl_jobs dl_jobs t = { t with dl_jobs } @@ -1135,8 +1310,9 @@ { t with solver_criteria = (kind,criterion)::List.remove_assoc kind t.solver_criteria } let with_best_effort_prefix s t = { t with best_effort_prefix = Some s } + let with_best_effort_prefix_opt s t = { t with best_effort_prefix = s } let with_solver solver t = { t with solver = Some solver } - let with_solver_opt solver t = { t with solver = solver } + let with_solver_opt solver t = { t with solver } let with_wrappers wrappers t = { t with wrappers } let with_global_variables global_variables t = { t with global_variables } let with_eval_variables eval_variables t = { t with eval_variables } @@ -1144,14 +1320,21 @@ { t with validation_hook = Some validation_hook} let with_validation_hook_opt validation_hook t = { t with validation_hook } let with_default_compiler default_compiler t = { t with default_compiler } + let with_default_invariant default_invariant t = { t with default_invariant } + let with_depext depext t = { t with depext } + let with_depext_run_installs depext_run_installs t = + { t with depext_run_installs } + let with_depext_cannot_install depext_cannot_install t = + { t with depext_cannot_install } + let with_depext_bypass depext_bypass t = { t with depext_bypass } let empty = { - opam_version = OpamVersion.current_nopatch; - opam_root_version = None; + opam_version = file_format_version; + opam_root_version = default_old_root_version; repositories = []; installed_switches = []; switch = None; - jobs = 1; + jobs = None; dl_tool = None; dl_jobs = 1; dl_cache = None; @@ -1163,8 +1346,18 @@ eval_variables = []; validation_hook = None; default_compiler = OpamFormula.Empty; + default_invariant = OpamFormula.Empty; + depext = true; + depext_run_installs = true; + depext_cannot_install = false; + depext_bypass = OpamSysPkg.Set.empty; } + (* When adding a field, make sure to add it in + [OpamConfigCommand.global_allowed_fields] if it is a user modifiable field. + When creating sections, make sure to update + [OpamConfigCommand.global_allowed_sections] and + [OpamConfigCommand.get_scope]. *) let fields = let with_switch sw t = if t.switch = None then with_switch sw t @@ -1174,7 +1367,7 @@ "opam-version", Pp.ppacc with_opam_version opam_version (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); - "opam-root-version", Pp.ppacc_opt + "opam-root-version", Pp.ppacc with_opam_root_version opam_root_version (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); "repositories", Pp.ppacc @@ -1190,7 +1383,7 @@ "switch", Pp.ppacc_opt with_switch switch (Pp.V.string -| Pp.of_module "switch" (module OpamSwitch)); - "jobs", Pp.ppacc + "jobs", Pp.ppacc_opt with_jobs jobs Pp.V.pos_int; "download-command", Pp.ppacc_opt @@ -1237,6 +1430,23 @@ "default-compiler", Pp.ppacc with_default_compiler default_compiler (Pp.V.package_formula `Disj Pp.V.(constraints Pp.V.version)); + "default-invariant", Pp.ppacc + with_default_invariant default_invariant + (Pp.V.package_formula `Conj Pp.V.(constraints Pp.V.version)); + "depext", Pp.ppacc + with_depext depext + Pp.V.bool; + "depext-run-installs", Pp.ppacc + with_depext_run_installs depext_run_installs + Pp.V.bool; + "depext-cannot-install", Pp.ppacc + with_depext_cannot_install depext_cannot_install + Pp.V.bool; + "depext-bypass", Pp.ppacc + with_depext_bypass depext_bypass + (Pp.V.map_list + (Pp.V.string -| Pp.of_module "sys-package" (module OpamSysPkg)) -| + Pp.of_pair "System package set" OpamSysPkg.Set.(of_list, elements)); (* deprecated fields *) "alias", Pp.ppacc_opt @@ -1248,22 +1458,24 @@ "cores", Pp.ppacc_opt with_jobs OpamStd.Option.none Pp.V.pos_int; - "system_ocaml-version", Pp.ppacc_ignore; "system-ocaml-version", Pp.ppacc_ignore; ] @ List.map (fun (fld, ppacc) -> fld, Pp.embed with_wrappers wrappers ppacc) Wrappers.fields - let pp_cond ?condition () = + let pp_cond ?f ?condition () = let name = internal in + let format_version = file_format_version in Pp.I.map_file @@ - Pp.I.check_opam_version () -| + Pp.I.check_opam_version ?f ~format_version () -| + Pp.I.opam_version ~format_version () -| Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name ?condition () let pp = pp_cond () + let to_list = Syntax.to_list pp end module Config = struct @@ -1275,28 +1487,25 @@ let raw_root_version f = try let opamfile = OpamParser.file (OpamFilename.to_string (filename f)) in - let get_version field = - try - Some (OpamStd.List.find_map (function - | Variable (_, field', String (_, version)) - when field' = field -> - Some (OpamVersion.of_string version) - | _ -> None) - opamfile.file_contents) - with Not_found -> None - in - get_version "opam-root-version", get_version "opam-version" + Some (OpamStd.List.find_map (function + | { pelem = Variable ({ pelem = "opam-root-version"; _}, + {pelem = String version; _}); _} -> + Some (OpamVersion.of_string version) + | _ -> None) + opamfile.file_contents) with - | Sys_error _ -> None, None + | Sys_error _ | Not_found -> None end module InitConfigSyntax = struct let internal = "init-config" + let format_version = OpamVersion.of_string "2.0" type t = { opam_version : opam_version; repositories : (repository_name * (url * trust_anchors option)) list; default_compiler : formula; + default_invariant : formula; jobs : int option; dl_tool : arg list option; dl_jobs : int option; @@ -1314,6 +1523,7 @@ let opam_version t = t.opam_version let repositories t = t.repositories let default_compiler t = t.default_compiler + let default_invariant t = t.default_invariant let jobs t = t.jobs let dl_tool t = t.dl_tool let dl_jobs t = t.dl_jobs @@ -1330,6 +1540,7 @@ let with_opam_version opam_version t = {t with opam_version} let with_repositories repositories t = {t with repositories} let with_default_compiler default_compiler t = {t with default_compiler} + let with_default_invariant default_invariant t = {t with default_invariant} let with_jobs jobs t = {t with jobs} let with_dl_tool dl_tool t = {t with dl_tool} let with_dl_jobs dl_jobs t = {t with dl_jobs} @@ -1352,9 +1563,10 @@ (kind,criterion)::List.remove_assoc kind t.solver_criteria } let empty = { - opam_version = OpamVersion.current_nopatch; + opam_version = format_version; repositories = []; default_compiler = OpamFormula.Empty; + default_invariant = OpamFormula.Empty; jobs = None; dl_tool = None; dl_jobs = None; @@ -1403,6 +1615,9 @@ "default-compiler", Pp.ppacc with_default_compiler default_compiler (Pp.V.package_formula `Disj Pp.V.(constraints Pp.V.version)); + "default-invariant", Pp.ppacc + with_default_invariant default_invariant + (Pp.V.package_formula `Disj Pp.V.(constraints Pp.V.version)); "jobs", Pp.ppacc_opt (with_jobs @* OpamStd.Option.some) jobs Pp.V.pos_int; @@ -1472,6 +1687,7 @@ let pp = let name = internal in Pp.I.map_file @@ + Pp.I.check_opam_version ~optional:true ~format_version () -| Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name ~strict:true () @@ -1484,6 +1700,9 @@ default_compiler = if t2.default_compiler <> Empty then t2.default_compiler else t1.default_compiler; + default_invariant = + if t2.default_invariant <> Empty + then t2.default_invariant else t1.default_invariant; jobs = opt t2.jobs t1.jobs; dl_tool = opt t2.dl_tool t1.dl_tool; dl_jobs = opt t2.dl_jobs t1.dl_jobs; @@ -1512,15 +1731,14 @@ module Repos_configSyntax = struct let internal = "repos-config" + let format_version = OpamVersion.of_string "2.0" + let file_format_version = OpamVersion.of_string "2.0" type t = ((url * trust_anchors option) option) OpamRepositoryName.Map.t let empty = OpamRepositoryName.Map.empty let fields = [ - (* Since 2.1, opam-version is printed in almost all files *) - "opam-version", Pp.ppacc_opt (fun _ acc -> acc) (fun _ -> None) - (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); "repositories", Pp.ppacc (fun x _ -> x) (fun x -> x) ((Pp.V.map_list ~depth:1 @@ @@ -1536,10 +1754,12 @@ OpamRepositoryName.Map.(of_list, bindings)); ] - let pp_cond ?condition () = + let pp_cond ?f ?condition () = let name = internal in + let format_version = file_format_version in Pp.I.map_file @@ - Pp.I.check_opam_version ~optional:true () -| + Pp.I.check_opam_version ~optional:true ?f ~format_version () -| + Pp.I.opam_version ~format_version ~undefined:true () -| Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name ?condition () @@ -1555,6 +1775,9 @@ module Switch_configSyntax = struct let internal = "switch-config" + let format_version = OpamVersion.of_string "2.1" + let file_format_version = OpamVersion.of_string "2.0" + let oldest_compatible_format_version = OpamVersion.of_string "2.0" type t = { opam_version: OpamVersion.t; @@ -1565,10 +1788,12 @@ opam_root: dirname option; wrappers: Wrappers.t; env: env_update list; + invariant: OpamFormula.t option; + depext_bypass: OpamSysPkg.Set.t; } let empty = { - opam_version = OpamVersion.current_nopatch; + opam_version = file_format_version; synopsis = ""; repos = None; paths = []; @@ -1576,8 +1801,14 @@ opam_root = None; wrappers = Wrappers.empty; env = []; + invariant = None; + depext_bypass = OpamSysPkg.Set.empty; } + (* When adding a field or section, make sure to add it in + [OpamConfigCommand.switch_allowed_fields] and + [OpamConfigCommand.switch_allowed_sections] if it is a user modifiable + field *) let sections = [ "paths", Pp.ppacc (fun paths t -> {t with paths}) (fun t -> t.paths) @@ -1612,15 +1843,27 @@ "setenv", Pp.ppacc (fun env t -> {t with env}) (fun t -> t.env) (Pp.V.map_list ~depth:2 Pp.V.env_binding); + "invariant", Pp.ppacc_opt + (fun inv t -> {t with invariant = Some inv }) (fun t -> t.invariant) + (Pp.V.package_formula `Conj Pp.V.(constraints version)); + "depext-bypass", Pp.ppacc + (fun depext_bypass t -> { t with depext_bypass}) + (fun t -> t.depext_bypass) + (Pp.V.map_list + (Pp.V.string -| Pp.of_module "sys-package" (module OpamSysPkg)) -| + Pp.of_pair "System package set" OpamSysPkg.Set.(of_list, elements)); ] @ List.map (fun (fld, ppacc) -> fld, Pp.embed (fun wrappers t -> {t with wrappers}) (fun t -> t.wrappers) ppacc) Wrappers.fields - let pp_cond ?condition () = + let pp_cond ?f ?condition () = let name = internal in + let format_version = file_format_version in Pp.I.map_file @@ + Pp.I.check_opam_version ?f ~format_version () -| + Pp.I.opam_version ~format_version () -| Pp.I.fields ~name ~empty ~sections fields -| Pp.I.show_errors ~name ?condition () @@ -1636,29 +1879,20 @@ let wrappers t = t.wrappers + let to_list = Syntax.to_list pp end module Switch_config = struct include Switch_configSyntax include SyntaxFile(Switch_configSyntax) module BestEffort = MakeBestEffort(Switch_configSyntax) - - let raw_opam_version f = - try - let opamfile = OpamParser.file (OpamFilename.to_string (filename f)) in - Some (OpamStd.List.find_map (function - | Variable (_, "opam-version", String (_, version)) -> - Some (OpamVersion.of_string version) - | _ -> None) - opamfile.file_contents) - with - | Sys_error _ | Not_found -> None - end module SwitchSelectionsSyntax = struct let internal = "switch-state" + let format_version = OpamVersion.of_string "2.0" + let file_format_version = OpamVersion.of_string "2.0" type t = switch_selections @@ -1678,7 +1912,7 @@ let fields = [ "opam-version", Pp.ppacc - (fun _ t -> t) (fun _ -> OpamVersion.current_nopatch) + (fun _ t -> t) (fun _ -> file_format_version) (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); "compiler", Pp.ppacc (fun sel_compiler t -> {t with sel_compiler}) (fun t -> t.sel_compiler) @@ -1703,10 +1937,12 @@ Pp.of_pair "Package set" OpamPackage.Set.(of_list, elements)) ] - let pp_cond ?condition () = + let pp_cond ?f ?condition () = let name = "switch-state" in + let format_version = file_format_version in Pp.I.map_file @@ - Pp.I.check_opam_version () -| + Pp.I.check_opam_version ~optional:true ?f ~format_version () -| + Pp.I.opam_version ~format_version () -| Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name ?condition () @@ -1725,6 +1961,7 @@ module Repo_config_legacySyntax = struct let internal = "repo-file" + let format_version = OpamVersion.of_string "1.2" type t = { repo_name : repository_name; @@ -1788,6 +2025,7 @@ module Dot_configSyntax = struct let internal = ".config" + let format_version = OpamVersion.of_string "2.0" type t = { vars: (variable * variable_contents) list; @@ -1821,7 +2059,7 @@ (Pp.I.anonymous_section pp_variables) ] [ - "opam-version", Pp.ppacc (fun _ t -> t) (fun _ -> OpamVersion.current) + "opam-version", Pp.ppacc (fun _ t -> t) (fun _ -> format_version) (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); "file-depends", Pp.ppacc with_file_depends file_depends (Pp.V.map_list ~depth:2 @@ Pp.V.map_pair @@ -1834,6 +2072,8 @@ backwards-compat, when opam-version is unset or too old *) let pp = Pp.I.map_file @@ + Pp.I.check_opam_version ~format_version ~optional:true () -| + Pp.I.opam_version ~format_version () -| Pp.I.field "opam-version" (Pp.parse (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion))) @@ -1877,6 +2117,7 @@ module RepoSyntax = struct let internal = "repo" + let format_version = OpamVersion.of_string "2.0" type t = { opam_version : OpamVersion.t option; @@ -1942,10 +2183,13 @@ let pp = let name = internal in Pp.I.map_file @@ + Pp.I.check_opam_version ~format_version ~optional:true () -| + Pp.I.opam_version ~format_version () -| Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name ~condition:(function - | {opam_version = Some v; _} -> OpamVersion.(compare current v) >= 0 + | {opam_version = Some v; _} -> + OpamVersion.(compare format_version v) >= 0 | _ -> true) () @@ -1956,22 +2200,25 @@ end -(** Package url files (/packages/.../url) *) +(** Package url field in opam file. Formerly, file + (/packages/.../url) *) module URLSyntax = struct let internal = "url-file" + let format_version = OpamVersion.of_string "1.2" type t = { url : url; mirrors : url list; checksum: OpamHash.t list; errors : (string * Pp.bad_format) list; + subpath : string option; } - let create ?(mirrors=[]) ?(checksum=[]) url = + let create ?(mirrors=[]) ?(checksum=[]) ?subpath url = { - url; mirrors; checksum; errors = []; + url; mirrors; checksum; errors = []; subpath; } let empty = { @@ -1979,15 +2226,19 @@ mirrors = []; checksum= []; errors = []; + subpath = None; } let url t = t.url let mirrors t = t.mirrors let checksum t = t.checksum + let subpath t = t.subpath let with_url url t = { t with url } let with_mirrors mirrors t = { t with mirrors } let with_checksum checksum t = { t with checksum = checksum } + let with_subpath subpath t = { t with subpath = Some subpath } + let with_subpath_opt subpath t = { t with subpath = subpath } let fields = let with_url url t = @@ -2014,6 +2265,9 @@ (Pp.V.string -| Pp.of_module "checksum" (module OpamHash))); "mirrors", Pp.ppacc with_mirrors mirrors (Pp.V.map_list ~depth:1 Pp.V.url); + "subpath", Pp.ppacc_opt + with_subpath subpath + Pp.V.string; ] let pp_contents = @@ -2041,6 +2295,7 @@ module OPAMSyntax = struct let internal = "opam" + let format_version = OpamVersion.of_string "2.0" type t = { opam_version: opam_version; @@ -2074,7 +2329,7 @@ (* User-facing data used by opam *) messages : (string * filter option) list; post_messages: (string * filter option) list; - depexts : (string list * filter) list; + depexts : (OpamSysPkg.Set.t * filter) list; libraries : (string * filter option) list; syntax : (string * filter option) list; dev_repo : url option; @@ -2090,7 +2345,7 @@ bug_reports: string list; (* Extension fields (x-foo: "bar") *) - extensions : (pos * value) OpamStd.String.Map.t; + extensions : value OpamStd.String.Map.t; (* Extra sections *) url : URL.t option; @@ -2100,7 +2355,7 @@ (* Related metadata directory (not an actual field of the file) This can be used to locate e.g. the files/ overlays *) - metadata_dir: dirname option; + metadata_dir: (repository_name option * string) option; (* Names and hashes of the files below files/ *) extra_files: (OpamFilename.Base.t * OpamHash.t) list option; @@ -2117,7 +2372,7 @@ } let empty = { - opam_version = OpamVersion.current_nopatch; + opam_version = format_version; name = None; version = None; @@ -2180,8 +2435,13 @@ let check t name = function | None -> let pos = - OpamStd.Option.Op.(OpamFilename.Op.( - t.metadata_dir >>| fun d -> pos_file (d // "opam"))) + OpamStd.Option.Op.(>>|) t.metadata_dir @@ function + | Some r, rel -> + { pos_null with + filename = + Printf.sprintf "<%s>/%s/opam" (OpamRepositoryName.to_string r) rel } + | None, d -> + pos_file OpamFilename.Op.(OpamFilename.Dir.of_string d // "opam") in Pp.bad_format ?pos "Field '%s:' is required" name | Some n -> n @@ -2243,13 +2503,13 @@ let doc t = t.doc let bug_reports t = t.bug_reports - let extensions t = OpamStd.String.Map.map snd t.extensions + let extensions t = t.extensions let extended t fld parse = if not (is_ext_field fld) then invalid_arg "OpamFile.OPAM.extended"; try - let pos, s = OpamStd.String.Map.find fld t.extensions in + let s = OpamStd.String.Map.find fld t.extensions in (try Some (parse s) with - | Pp.Bad_format _ as e -> raise (Pp.add_pos pos e)) + | Pp.Bad_format _ as e -> raise (Pp.add_pos s.pos e)) with Not_found -> None let url t = t.url @@ -2323,11 +2583,14 @@ if not (OpamStd.String.Map.for_all (fun k _ -> is_ext_field k) extensions) then invalid_arg "OpamFile.OPAM.with_extensions"; {t with - extensions = OpamStd.String.Map.map (fun s -> pos_null, s) extensions } + extensions = extensions } let add_extension t fld syn = if not (is_ext_field fld) then invalid_arg "OpamFile.OPAM.add_extension"; {t with - extensions = OpamStd.String.Map.add fld (pos_null,syn) t.extensions } + extensions = OpamStd.String.Map.add fld syn t.extensions } + let remove_extension t fld = + if not (is_ext_field fld) then invalid_arg "OpamFile.OPAM.remove_extension"; + {t with extensions = OpamStd.String.Map.remove fld t.extensions } let with_url url t = let format_errors = @@ -2362,6 +2625,64 @@ { t with ocaml_version = Some ocaml_version } let with_os os t = { t with os } + (* Adds an opam constraint as an 'available' constraint, without restricting + the file format compatibility *) + let pp_minimal_opam_version min_version = + let opam_version_var = OpamVariable.of_string "opam-version" in + let add_avail_constr t = + if OpamVersion.compare t.opam_version min_version >= 0 then t else + let available = + let opam_restricted = + OpamFilter.fold_down_left (fun acc filter -> + acc || + match filter with + | FOp (FIdent ([], var, None), (`Eq|`Geq), FString version) + | FOp (FString version, (`Eq|`Leq), FIdent ([], var, None)) -> + var = opam_version_var && + OpamVersion.(compare (of_string version) min_version) >= 0 + | _ -> false) + false t.available + in + if opam_restricted then t.available else + let opam_restriction = + FOp (FIdent ([], opam_version_var, None), `Geq, + FString (OpamVersion.to_string min_version)) + in + match t.available with + | FBool true -> opam_restriction + | available -> FAnd (available, opam_restriction) + in + { t with available } + in + let parse ~pos:_ t = + add_avail_constr t + (* This is not strictly needed since we know the constraint will be + verified for the running opam version, but avoids a discrepency if + re-parsing a printed file. *) + in + let print t = + (* remove constraints that are already implied by the file format + version *) + let available = + OpamFilter.map_up (function + | FOp (FIdent ([], var, None), (`Eq|`Geq), FString version) + | FOp (FString version, (`Eq|`Leq), FIdent ([], var, None)) + when var = opam_version_var && + OpamVersion.compare (OpamVersion.of_string version) + t.opam_version <= 0 + -> FBool true + | FAnd (FBool true, f) | FAnd (f, FBool true) -> f + | FOr (FBool true, _) | FOr (_, FBool true) -> FBool true + | f -> f + ) + t.available + in + add_avail_constr { t with available } + (* The constraint needs to be added here as well, in case the file was + just generated and has a subpath without the constraint already *) + in + Pp.pp parse print + (* Post-processing functions used for some fields (optional, because we don't want them when linting). It's better to do them in the same pass as parsing, because it allows one to get file positions, which we lose @@ -2374,24 +2695,6 @@ Some (pkg_flag_of_string (OpamStd.String.remove_prefix ~prefix tag)) else None - let cleanup_name _opam_version ~pos:(file,_,_ as pos) name = - match OpamPackage.of_filename (OpamFilename.of_string file) with - | Some nv when nv.OpamPackage.name <> name -> - Pp.warn ~pos "This file is for package '%s' but its 'name:' field \ - advertises '%s'." - (OpamPackage.name_to_string nv) (OpamPackage.Name.to_string name); - nv.OpamPackage.name - | _ -> name - - let cleanup_version _opam_version ~pos:(file,_,_ as pos) version = - match OpamPackage.of_filename (OpamFilename.of_string file) with - | Some nv when nv.OpamPackage.version <> version -> - Pp.warn ~pos "This file is for version '%s' but its 'version:' field \ - advertises '%s'." - (OpamPackage.version_to_string nv) (OpamPackage.Version.to_string version); - nv.OpamPackage.version - | _ -> version - let cleanup_depopts opam_version ~pos depopts = if OpamFormatConfig.(!r.skip_version_checks) || OpamVersion.compare opam_version (OpamVersion.of_string "1.2") < 0 @@ -2479,10 +2782,9 @@ [ "opam-version", no_cleanup Pp.ppacc with_opam_version opam_version (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); - "name", with_cleanup cleanup_name Pp.ppacc_opt with_name name_opt + "name", no_cleanup Pp.ppacc_opt with_name name_opt Pp.V.pkgname; - "version", with_cleanup cleanup_version - Pp.ppacc_opt with_version version_opt + "version", no_cleanup Pp.ppacc_opt with_version version_opt (Pp.V.string_tr -| Pp.of_module "version" (module OpamPackage.Version)); "synopsis", no_cleanup Pp.ppacc_opt with_synopsis synopsis @@ -2546,7 +2848,7 @@ "build-env", no_cleanup Pp.ppacc with_build_env build_env (Pp.V.map_list ~depth:2 Pp.V.env_binding); "features", no_cleanup Pp.ppacc with_features features - (Pp.V.map_list ~depth:2 @@ + (Pp.V.map_list ~depth:1 @@ Pp.V.map_options_2 (Pp.V.ident -| Pp.of_module "variable" (module OpamVariable)) (Pp.V.package_formula_items `Conj Pp.V.(filtered_constraints ext_version)) @@ -2559,20 +2861,25 @@ (Pp.V.map_list ~depth:1 @@ Pp.V.map_option Pp.V.string_tr (Pp.opt Pp.V.filter)); "depexts", no_cleanup Pp.ppacc with_depexts depexts - (Pp.fallback + (let map_syspkg = + (Pp.V.map_list + (Pp.V.string -| Pp.of_module "sys-package" (module OpamSysPkg)) + -| Pp.pp (fun ~pos:_ -> OpamSysPkg.Set.of_list) OpamSysPkg.Set.elements) + in + Pp.fallback (Pp.V.map_list ~depth:2 @@ - Pp.V.map_option (Pp.V.map_list Pp.V.string) (Pp.V.filter)) + Pp.V.map_option map_syspkg (Pp.V.filter)) (Pp.V.map_list ~depth:3 (let rec filter_of_taglist = function - | [] -> FBool true - | [v] -> FString v - | v :: r -> FAnd (FString v, filter_of_taglist r) + | [] -> FBool true + | [v] -> FString v + | v :: r -> FAnd (FString v, filter_of_taglist r) in Pp.V.map_pair (Pp.V.map_list Pp.V.string -| Pp.of_pair "tag-list" (filter_of_taglist, fun _ -> assert false)) - (Pp.V.map_list Pp.V.string) -| + map_syspkg -| Pp.pp (fun ~pos:_ (a,b) -> b,a) (fun (b,a) -> a,b)))); "libraries", no_cleanup Pp.ppacc with_libraries libraries (Pp.V.map_list ~depth:1 @@ @@ -2583,12 +2890,13 @@ "dev-repo", with_cleanup cleanup_dev_repo Pp.ppacc_opt with_dev_repo dev_repo (Pp.V.string -| Pp.of_pair "vc-url" - OpamUrl.(parse ?backend:None ~handle_suffix:false, to_string)); + OpamUrl.(parse ?backend:None ~handle_suffix:false ~from_file:true, + to_string)); "pin-depends", no_cleanup Pp.ppacc with_pin_depends pin_depends (OpamFormat.V.map_list ~depth:2 (OpamFormat.V.map_pair (OpamFormat.V.string -| - OpamPp.of_module "package" (module OpamPackage)) + OpamPp.of_module "versioned package" (module OpamPackage)) (OpamFormat.V.string -| OpamPp.of_module "URL" (module OpamUrl)))); @@ -2731,19 +3039,59 @@ in Pp.pp parse (fun x -> x) + let handle_subpath_2_0 = + let subpath_xfield = "x-subpath" in + let pp_constraint = + pp_minimal_opam_version (OpamVersion.of_string "2.1") + in + let parse ~pos t = + if OpamVersion.(compare t.opam_version (of_string "2.0") > 0) then t + else + match OpamStd.String.Map.find_opt subpath_xfield t.extensions with + | Some {pelem = String subpath;_} -> + let url = match t.url with + | Some u -> Some (URL.with_subpath subpath u) + | None -> None + in + { t with url } + |> Pp.parse ~pos pp_constraint + | Some {pos;_} -> + Pp.bad_format ~pos "Field %s must be a string" + (OpamConsole.colorise `underline subpath_xfield) + | None -> t + in + let print t = + match t.url with + | Some ({ URL.subpath = Some sb ; _ } as url) -> + if OpamVersion.(compare t.opam_version (of_string "2.0") > 0) then t + else + add_extension t subpath_xfield (nullify_pos @@ String sb) + |> with_url (URL.with_subpath_opt None url) + |> Pp.print pp_constraint + | _ -> t + in + Pp.pp parse print + (* Doesn't handle package name encoded in directory name *) let pp_raw_fields = - Pp.I.check_opam_version () -| - Pp.I.partition_fields is_ext_field -| Pp.map_pair - (Pp.I.items -| - OpamStd.String.Map.(Pp.pp (fun ~pos:_ -> of_list) bindings)) + Pp.I.check_opam_version ~format_version () -| + Pp.I.opam_version ~format_version () -| + Pp.I.partition_fields ~section:true (is_ext_field @> not) -| Pp.map_pair (Pp.I.fields ~name:"opam-file" ~empty ~sections fields -| Pp.I.on_errors (fun t e -> {t with format_errors=e::t.format_errors}) -| handle_flags_in_tags -| - handle_deprecated_available) -| + handle_deprecated_available) + (Pp.I.items -| + OpamStd.String.Map.(Pp.pp (fun ~pos:_ -> of_list) bindings)) -| Pp.pp - (fun ~pos:_ (extensions, t) -> with_extensions extensions t) - (fun t -> extensions t, t) + (fun ~pos:_ (t, extensions) -> with_extensions extensions t) + (fun t -> t, extensions t) -| + Pp.check (fun t -> + OpamVersion.(compare t.opam_version (of_string "2.0") > 0) || + OpamStd.Option.Op.(t.url >>= URL.subpath) = None) + ~errmsg:"The url.subpath field is not allowed in files with \ + `opam-version` <= 2.0" -| + handle_subpath_2_0 let pp_raw = Pp.I.map_file @@ pp_raw_fields @@ -2753,12 +3101,26 @@ (fun ~pos:_ (filename, t) -> filename, let metadata_dir = - if filename <> dummy_file then Some (OpamFilename.dirname filename) + if filename <> dummy_file + then Some (None, OpamFilename.(Dir.to_string (dirname filename))) else None in let t = { t with metadata_dir } in match OpamPackage.of_filename filename with - | Some nv -> with_nv nv t + | Some nv -> + if t.name <> None && t.name <> Some nv.name || + t.version <> None && t.version <> Some nv.version + then + Pp.warn + "This file is for package '%s' but has mismatching fields%s%s." + (OpamPackage.to_string nv) + (OpamStd.Option.to_string + (fun n -> " 'name:"^OpamPackage.Name.to_string n) + t.name) + (OpamStd.Option.to_string + (fun v -> " 'version:"^OpamPackage.Version.to_string v) + t.version); + with_nv nv t | None -> t) (fun (filename, t) -> filename, @@ -2782,7 +3144,8 @@ (OpamStd.Option.default (nv.OpamPackage.name) t.name) (OpamStd.Option.default (nv.OpamPackage.version) t.version)) (OpamFilename.prettify filename); - {t with name = None; version = None}) + {t with name = None; version = None} + ) let to_string_with_preserved_format ?format_from ?format_from_string filename t = @@ -2792,28 +3155,15 @@ let write_with_preserved_format ?format_from ?format_from_string filename t = - let s = to_string_with_preserved_format ?format_from ?format_from_string filename t in + let s = + to_string_with_preserved_format ?format_from ?format_from_string + filename t + in OpamFilename.write filename s - let contents ?(filename=dummy_file) t = - Pp.print pp (filename, t) + let contents = Syntax.contents pp - let to_list ?filename t = - let rec aux acc pfx = function - | Section (_, {section_kind; section_name=None; section_items}) :: r -> - aux (aux acc (section_kind :: pfx) section_items) pfx r - | Section (_, {section_kind; section_name=Some n; section_items}) :: r -> - aux - (aux acc (Printf.sprintf "%s(%s)" section_kind n :: pfx) - section_items) - pfx r - | Variable (_, name, value) :: r -> - aux (((name :: pfx), value) :: acc) pfx r - | [] -> acc - in - List.rev_map - (fun (pfx, value) -> String.concat "." (List.rev pfx), value) - (aux [] [] (contents ?filename t).file_contents) + let to_list = Syntax.to_list pp let print_field_as_syntax field t = let field = try List.assoc field alias_fields with Not_found -> field in @@ -2821,17 +3171,15 @@ match OpamStd.String.cut_at field '.' with | None -> if is_ext_field field - then - OpamStd.Option.map snd - (OpamStd.String.Map.find_opt field t.extensions) + then OpamStd.String.Map.find_opt field t.extensions else snd (Pp.print (List.assoc field fields) t) | Some (sec, field) -> match snd (Pp.print (List.assoc sec sections) t) with | None -> None | Some items -> (* /!\ returns only the first result for multiple named sections *) - Some (OpamStd.List.find_map (function - | Variable (_, f, contents) when f = field -> Some contents + Some (OpamStd.List.find_map (fun i -> match i.pelem with + | Variable (f, contents) when f.pelem = field -> Some contents | _ -> None) (List.flatten (List.map snd items))) @@ -2854,7 +3202,17 @@ conflicts = t.conflicts; conflict_class = t.conflict_class; available = t.available; - flags = t.flags; + flags = + (List.filter (function + | Pkgflag_LightUninstall + | Pkgflag_Verbose + | Pkgflag_Plugin + | Pkgflag_Compiler + | Pkgflag_Conf + | Pkgflag_AvoidVersion + | Pkgflag_Unknown _ + -> false) + t.flags); env = t.env; build = t.build; @@ -2913,9 +3271,17 @@ let equal o1 o2 = with_metadata_dir None o1 = with_metadata_dir None o2 - let get_extra_files o = + let get_metadata_dir ~repos_roots o = + match metadata_dir o with + | None -> None + | Some (None, abs) -> + Some (OpamFilename.Dir.of_string abs) + | Some (Some r, rel) -> + Some OpamFilename.Op.(repos_roots r / rel) + + let get_extra_files ~repos_roots o = OpamStd.Option.Op.( - (metadata_dir o >>= fun mdir -> + (get_metadata_dir ~repos_roots o >>= fun mdir -> let files_dir = OpamFilename.Op.(mdir / "files") in extra_files o >>| List.map @@ fun (basename, hash) -> OpamFilename.create files_dir basename, @@ -2933,8 +3299,12 @@ (OpamPackage.to_string (OpamPackage.create n v)) | _, _, Some f, _ -> Printf.sprintf " at %s" (to_string f) - | _, _, _, Some dir -> - Printf.sprintf " in %s" (OpamFilename.Dir.to_string dir) + | _, _, _, Some (None, dir) -> + Printf.sprintf " in %s" dir + | _, _, _, Some (Some repo, dir) -> + Printf.sprintf " %s from repository %s" + (Filename.concat dir "opam") + (OpamRepositoryName.to_string repo) | _ -> "") (OpamStd.Format.itemize (fun (_, bf) -> Pp.string_of_bad_format (OpamPp.Bad_format bf)) @@ -2952,6 +3322,7 @@ module Dot_installSyntax = struct let internal = ".install" + let format_version = OpamVersion.of_string "2.0" type t = { bin : (basename optional * basename option) list; @@ -3079,9 +3450,6 @@ OpamFilename.to_string) in [ - (* Since 2.1, opam-version is printed in almost all files *) - "opam-version", Pp.ppacc_opt (fun _ acc -> acc) (fun _ -> None) - (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); "lib", Pp.ppacc with_lib lib pp_field; "bin", Pp.ppacc with_bin bin pp_field; "sbin", Pp.ppacc with_sbin sbin pp_field; @@ -3101,7 +3469,8 @@ let pp = let name = internal in Pp.I.map_file @@ - Pp.I.check_opam_version ~optional:true () -| + Pp.I.check_opam_version ~optional:true ~format_version () -| + Pp.I.opam_version ~format_version ~undefined:true () -| Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name () -| Pp.check ~errmsg:"man file without destination or recognised suffix" @@ -3120,6 +3489,7 @@ module ChangesSyntax = struct let internal = "changes" + let format_version = OpamVersion.of_string "2.0" open OpamDirTrack @@ -3145,9 +3515,6 @@ Pp.of_pair "digest" (digest_of_string, string_of_digest)))) let fields = [ - (* Since 2.1, opam-version is printed in almost all files *) - "opam-version", Pp.ppacc_opt (fun _ acc -> acc) (fun _ -> None) - (Pp.V.string -| Pp.of_module "opam-version" (module OpamVersion)); "added", field (function Some dg -> Added dg | None -> Pp.bad_format "Missing digest") @@ -3171,7 +3538,8 @@ ] let pp_contents = - Pp.I.check_opam_version ~optional:true () -| + Pp.I.check_opam_version ~format_version ~optional:true () -| + Pp.I.opam_version ~format_version ~undefined:true () -| Pp.I.fields ~name:internal ~empty fields -| Pp.I.show_errors ~name:internal () @@ -3186,30 +3554,47 @@ module SwitchExportSyntax = struct let internal = "switch-export" + let format_version = OpamVersion.of_string "2.1" type t = { selections: switch_selections; + extra_files: string OpamHash.Map.t; overlays: OPAM.t OpamPackage.Name.Map.t; } let empty = { selections = SwitchSelectionsSyntax.empty; + extra_files = OpamHash.Map.empty; overlays = OpamPackage.Name.Map.empty; } - let fields = SwitchSelectionsSyntax.fields + + let fields = + [ "extra-files", Pp.ppacc (fun extra_files t -> { t with extra_files }) + (fun t -> t.extra_files) + ((Pp.V.map_list ~depth:2 @@ + (Pp.V.map_pair + (Pp.V.string -| Pp.of_module "checksum" (module OpamHash)) + Pp.V.string)) -| + Pp.of_pair "HashMap" OpamHash.Map.(of_list, bindings)) + ] @ + List.map + (fun (fld, ppacc) -> + fld, Pp.embed (fun selections t -> { t with selections }) + (fun t -> t.selections) ppacc) + SwitchSelectionsSyntax.fields let pp = let name = "export-file" in Pp.I.map_file @@ - Pp.I.check_opam_version ~optional:true () -| - Pp.I.partition (function - | Section (_, { section_kind="package"; section_name=Some _; _ }) -> + Pp.I.check_opam_version ~format_version () -| + Pp.I.opam_version ~format_version ~undefined:true () -| + Pp.I.partition (fun i -> match i.pelem with + | Section ({ section_kind={pelem="package";_}; section_name=Some _; _ }) -> false | _ -> true) -| Pp.map_pair - (Pp.I.fields ~name - ~empty:SwitchSelectionsSyntax.empty fields -| + (Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name ()) (Pp.map_list (Pp.I.section "package" -| @@ -3227,14 +3612,15 @@ Pp.of_pair "package-metadata-map" OpamPackage.Name.Map.(of_list,bindings)) -| Pp.pp - (fun ~pos:_ (selections, overlays) -> {selections; overlays}) - (fun {selections; overlays} -> (selections, overlays)) + (fun ~pos:_ (t, overlays) -> {t with overlays}) + (fun t -> t, t.overlays) end module SwitchExport = struct type t = SwitchExportSyntax.t = { selections: switch_selections; + extra_files: string OpamHash.Map.t; overlays: OPAM.t OpamPackage.Name.Map.t; } @@ -3245,6 +3631,7 @@ module CompSyntax = struct let internal = "comp" + let format_version = OpamVersion.of_string "1.2" type compiler = string type compiler_version = string @@ -3265,7 +3652,7 @@ } let empty = { - opam_version = OpamVersion.current; + opam_version = format_version; name = ""; version = ""; src = None; @@ -3380,7 +3767,8 @@ let pp_raw = let name = internal in Pp.I.map_file @@ - Pp.I.check_opam_version () -| + Pp.I.check_opam_version ~format_version () -| + Pp.I.opam_version ~format_version () -| Pp.I.fields ~name ~empty fields -| Pp.I.show_errors ~name () -| Pp.check ~errmsg:"fields 'build:' and 'configure:'+'make:' are mutually \ diff -Nru opam-2.0.10/src/format/opamFile.mli opam-2.1.2/src/format/opamFile.mli --- opam-2.0.10/src/format/opamFile.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFile.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -12,6 +12,7 @@ (** Handles all OPAM file formats as record types and submodules, conversion to and from syntax *) +open OpamParserTypes.FullPos open OpamTypes (** Functions to read and write OPAM configuration files in a typed way *) @@ -32,6 +33,8 @@ (** File contents *) type t + val format_version: OpamVersion.t + (** Empty file *) val empty: t @@ -98,6 +101,18 @@ val pre_session: t -> command list val post_session: t -> command list + val with_pre_build: command list -> t -> t + val with_wrap_build: command list -> t -> t + val with_post_build: command list -> t -> t + val with_pre_install: command list -> t -> t + val with_wrap_install: command list -> t -> t + val with_post_install: command list -> t -> t + val with_pre_remove: command list -> t -> t + val with_wrap_remove: command list -> t -> t + val with_post_remove: command list -> t -> t + val with_pre_session: command list -> t -> t + val with_post_session: command list -> t -> t + val empty : t val add: outer:t -> inner:t -> t @@ -126,11 +141,13 @@ val with_criteria: (solver_criteria * string) list -> t -> t val with_best_effort_prefix: string -> t -> t + val with_best_effort_prefix_opt: string option-> t -> t val with_solver: arg list -> t -> t val with_solver_opt: arg list option -> t -> t val with_jobs: int -> t -> t + val with_jobs_opt: int option -> t -> t val with_dl_tool: arg list -> t -> t val with_dl_tool_opt: arg list option -> t -> t val with_dl_jobs: int -> t -> t @@ -145,12 +162,22 @@ arg list option -> t -> t val with_default_compiler: formula -> t -> t + val with_default_invariant: + formula -> t -> t + val with_depext: bool -> t -> t + val with_depext_run_installs: bool -> t -> t + val with_depext_cannot_install: bool -> t -> t + val with_depext_bypass: OpamSysPkg.Set.t -> t -> t (** Return the opam version *) val opam_version: t -> opam_version - (** Return the opam root version *) - val opam_root_version: t -> opam_version option + (** Return the opam root version, if not defined returns the default version + value [2.1~~previous] *) + val opam_root_version: t -> opam_version + + (** Return the opam root version if defined *) + val opam_root_version_opt: t -> opam_version option (** Return the list of repository *) val repositories: t -> repository_name list @@ -160,8 +187,8 @@ val installed_switches: t -> switch list - (** Return the number of jobs *) - val jobs: t -> int + (** Return the number of jobs defined *) + val jobs: t -> int option val dl_tool: t -> arg list option @@ -187,13 +214,23 @@ val validation_hook: t -> arg list option val default_compiler: t -> formula + val default_invariant: t -> formula + + val depext: t -> bool + val depext_run_installs: t -> bool + val depext_cannot_install: t -> bool + val depext_bypass: t -> OpamSysPkg.Set.t - (** Raw read the config file to extract [opam-root-version] and - [opam-version] fields value. *) - val raw_root_version: - 'a typed_file -> OpamVersion.t option * OpamVersion.t option + val fields: (string * (t, value) OpamPp.field_parser) list + + (** All file fields as print-AST, Fields within sections are + accessed through dot-separated paths *) + val to_list: ?filename:'a typed_file -> t -> (string * value) list module BestEffort: BestEffortRead with type t := t + + (** Raw read the config file to extract [opam-root-version] field value. *) + val raw_root_version: 'a typed_file -> OpamVersion.t option end (** Init config file [/etc/opamrc] *) @@ -203,6 +240,7 @@ val opam_version: t -> opam_version val repositories: t -> (repository_name * (url * trust_anchors option)) list val default_compiler: t -> formula + val default_invariant: t -> formula val jobs: t -> int option val dl_tool: t -> arg list option val dl_jobs: t -> int option @@ -220,6 +258,7 @@ val with_repositories: (repository_name * (url * trust_anchors option)) list -> t -> t val with_default_compiler: formula -> t -> t + val with_default_invariant: formula -> t -> t val with_jobs: int option -> t -> t val with_dl_tool: arg list option -> t -> t val with_dl_jobs: int option -> t -> t @@ -264,7 +303,9 @@ include IO_FILE - val create: ?mirrors:url list -> ?checksum:OpamHash.t list -> url -> t + val create: + ?mirrors:url list -> ?checksum:OpamHash.t list -> ?subpath:string -> + url -> t (** URL address *) val url: t -> url @@ -275,7 +316,12 @@ val checksum: t -> OpamHash.t list (** Constructor *) + val with_url: url -> t -> t val with_checksum: OpamHash.t list -> t -> t + val with_subpath: string -> t -> t + val with_subpath_opt: string option -> t -> t + + val subpath: t -> string option end @@ -314,7 +360,7 @@ (* User-facing data used by opam *) messages : (string * filter option) list; post_messages: (string * filter option) list; - depexts : (string list * filter) list; + depexts : (OpamSysPkg.Set.t * filter) list; libraries : (string * filter option) list; syntax : (string * filter option) list; dev_repo : url option; @@ -330,15 +376,17 @@ bug_reports: string list; (* Extension fields (x-foo: "bar") *) - extensions : (pos * value) OpamStd.String.Map.t; + extensions : value OpamStd.String.Map.t; (* Extra sections *) url : URL.t option; descr : Descr.t option; (* Related metadata directory (not an actual field of the file) - This can be used to locate e.g. the files/ overlays *) - metadata_dir: dirname option; + This can be used to locate e.g. the files/ overlays. + If the repository is specified, the string is a relative path from its + root. It should otherwise be an absolute path. *) + metadata_dir: (repository_name option * string) option; (* Names and hashes of the files below files/ *) extra_files: (OpamFilename.Base.t * OpamHash.t) list option; @@ -417,7 +465,7 @@ val depopts: t -> filtered_formula (** External dependencies *) - val depexts: t -> (string list * filter) list + val depexts: t -> (OpamSysPkg.Set.t * filter) list val extra_sources: t -> (basename * URL.t) list @@ -500,17 +548,25 @@ val get_url: t -> url option - (** Related metadata directory (not an actual field of the file, linked to the - file location). + (** Related metadata directory (either repository name + relative path, or + absolute path; not an actual field of the file, linked to the file + location). This can be used to locate e.g. the files/ overlays *) - val metadata_dir: t -> dirname option + val metadata_dir: t -> (repository_name option * string) option + + (** Gets the resolved metadata dir, given a mapping of repository names to + their roots *) + val get_metadata_dir: + repos_roots:(repository_name -> dirname) -> t -> dirname option (** Names and hashes of the files below files/ *) val extra_files: t -> (OpamFilename.Base.t * OpamHash.t) list option (** Looks up the extra files, and returns their full paths, relative path to the package source, and hash. Doesn't check the hashes. *) - val get_extra_files: t -> (filename * basename * OpamHash.t) list + val get_extra_files: + repos_roots:(repository_name -> dirname) -> + t -> (filename * basename * OpamHash.t) list (** Returns the errors that were found when parsing the file, associated to their fields (that were consequently ignored) *) @@ -583,7 +639,7 @@ val with_bug_reports: string list -> t -> t (** Construct using [depexts] *) - val with_depexts: (string list * filter) list -> t -> t + val with_depexts: (OpamSysPkg.Set.t * filter) list -> t -> t val with_flags: package_flag list -> t -> t @@ -605,6 +661,8 @@ val add_extension: t -> string -> value -> t + val remove_extension: t -> string -> t + val with_deprecated_build_doc: command list -> t -> t val with_deprecated_build_test: command list -> t -> t @@ -620,7 +678,7 @@ val with_url: URL.t -> t -> t val with_url_opt: URL.t option -> t -> t - val with_metadata_dir: dirname option -> t -> t + val with_metadata_dir: (repository_name option * string) option -> t -> t val with_extra_files: (OpamFilename.Base.t * OpamHash.t) list -> t -> t val with_extra_files_opt: (OpamFilename.Base.t * OpamHash.t) list option -> t -> t @@ -693,6 +751,7 @@ module SwitchExport: sig type t = { selections: switch_selections; + extra_files: string OpamHash.Map.t; overlays: OPAM.t OpamPackage.Name.Map.t; } include IO_FILE with type t := t @@ -896,16 +955,20 @@ opam_root: dirname option; wrappers: Wrappers.t; env: env_update list; + invariant: OpamFormula.t option; + depext_bypass: OpamSysPkg.Set.t; } + val file_format_version: OpamVersion.t val variable: t -> variable -> variable_contents option val path: t -> std_path -> string option val wrappers: t -> Wrappers.t + val sections: (string * (t, (string option * opamfile_item list) list) OpamPp.field_parser) list + val fields: (string * (t, value) OpamPp.field_parser) list + val to_list: ?filename:'a typed_file -> t -> (string * value) list include IO_FILE with type t := t + val oldest_compatible_format_version: OpamVersion.t module BestEffort: BestEffortRead with type t := t - - (** Raw read the switch config file to extract [opam-version] field value. *) - val raw_opam_version: 'a typed_file -> OpamVersion.t option end (** Pinned package files (only used for migration from 1.2, the inclusive State @@ -1008,6 +1071,7 @@ module type SyntaxFileArg = sig val internal: string + val format_version: OpamVersion.t type t val empty: t val pp: (opamfile, filename * t) OpamPp.t diff -Nru opam-2.0.10/src/format/opamFilter.ml opam-2.1.2/src/format/opamFilter.ml --- opam-2.0.10/src/format/opamFilter.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFilter.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -13,6 +13,9 @@ open OpamTypesBase open OpamStd.Op +module OpamParser = OpamParser.FullPos +module OpamPrinter = OpamPrinter.FullPos + let log ?level fmt = OpamConsole.log "FILTER" ?level fmt let slog = OpamConsole.slog @@ -41,7 +44,7 @@ | FOp(e,s,f) -> paren ~cond:(context <> `Or && context <> `And) (Printf.sprintf "%s %s %s" - (aux ~context:`Relop e) (OpamPrinter.relop s) (aux ~context:`Relop f)) + (aux ~context:`Relop e) (OpamPrinter.relop_kind s) (aux ~context:`Relop f)) | FAnd (e,f) -> paren ~cond:(context <> `Or && context <> `And) (Printf.sprintf "%s & %s" (aux ~context:`And e) (aux ~context:`And f)) @@ -86,6 +89,10 @@ seq [str "%{"; group (greedy notclose); opt (group (str "}%"))]; ]) +let escape_value = + let rex = Re.(compile @@ set "\\\"") in + Re.Pcre.substitute ~rex ~subst:(fun s -> "\\"^s) + let escape_expansions = Re.replace_string Re.(compile @@ char '%') ~by:"%%" @@ -106,9 +113,9 @@ let rec aux acc pos = try let ss = Re.exec ~pos string_interp_regex s in - if Re.test ss 2 then - aux (Re.get ss 1 :: acc) - (fst (Re.get_ofs ss 0) + String.length (Re.get ss 0)) + if Re.Group.test ss 2 then + aux (Re.Group.get ss 1 :: acc) + (fst (Re.Group.offset ss 0) + String.length (Re.Group.get ss 0)) else aux acc (pos+1) with Not_found -> acc @@ -214,7 +221,7 @@ | None -> FUndef (FIdent fident) (* Resolves ["%{x}%"] string interpolations *) -let expand_string ?(partial=false) ?default env text = +let expand_string_aux ?(partial=false) ?(escape_value=fun x -> x) ?default env text = let default fident = match default, partial with | None, false -> None | Some df, false -> Some (df fident) @@ -237,10 +244,12 @@ else let fident = String.sub str 2 (String.length str - 4) in resolve_ident ~no_undef_expand:partial env (filter_ident_of_string fident) - |> value_string ?default:(default fident) + |> value_string ?default:(default fident) |> escape_value in Re.replace string_interp_regex ~f text +let expand_string = expand_string_aux ?escape_value:None + let unclosed_expansions text = let re = Re.( @@ -400,17 +409,37 @@ let expand_interpolations_in_file env file = let f = OpamFilename.of_basename file in let src = OpamFilename.add_extension f "in" in - let ic = OpamFilename.open_in src in - let oc = OpamFilename.open_out f in - let rec aux () = - match try Some (input_line ic) with End_of_file -> None with - | Some s -> - output_string oc (expand_string ~default:(fun _ -> "") env s); - output_char oc '\n'; - aux () - | None -> () + let ic = OpamFilename.open_in_bin src in + let oc = OpamFilename.open_out_bin f in + (* Determine if the input file parses in opam-file-format *) + let is_opam_format = + try + let _ = + OpamParser.channel ic (OpamFilename.to_string src) + in + true + with e -> + OpamStd.Exn.fatal e; + false + in + (* Reset the input for processing *) + seek_in ic 0; + let default _ = "" in + let write = output_string oc in + let unquoted s = write @@ expand_string ~default env s in + let quoted s = write @@ expand_string_aux ~escape_value ~default env s in + let process = + if is_opam_format then + fun () -> OpamInterpLexer.main unquoted quoted (Lexing.from_channel ic) + else + let rec aux () = + match input_line ic with + | s -> unquoted s; output_char oc '\n'; aux () + | exception End_of_file -> () + in + aux in - aux (); + process (); close_in ic; close_out oc @@ -521,11 +550,11 @@ let string_of_constraint = OpamFormula.string_of_formula (function | Constraint (op, FString s) -> - Printf.sprintf "%s \"%s\"" (OpamPrinter.relop op) s + Printf.sprintf "%s \"%s\"" (OpamPrinter.relop_kind op) s | Constraint (op, (FIdent _ as v)) -> - Printf.sprintf "%s %s" (OpamPrinter.relop op) (to_string v) + Printf.sprintf "%s %s" (OpamPrinter.relop_kind op) (to_string v) | Constraint (op, v) -> - Printf.sprintf "%s (%s)" (OpamPrinter.relop op) (to_string v) + Printf.sprintf "%s (%s)" (OpamPrinter.relop_kind op) (to_string v) | Filter f -> to_string f) in OpamFormula.string_of_formula (function @@ -633,3 +662,16 @@ | Or (a, b) -> Or (aux filters a, aux filters b) in aux (FBool true) cs) + +let sort_filtered_formula compare ff = + let f = OpamFormula.sort compare ff in + let rec vc_sort = function + | Empty -> Empty + | Atom (n,vf) -> + Atom (n, (OpamStd.Option.default vf + (simplify_extended_version_formula vf))) + | Block f -> Block (vc_sort f) + | And (f,f') -> And (vc_sort f, vc_sort f') + | Or (f,f') -> Or (vc_sort f, vc_sort f') + in + vc_sort f diff -Nru opam-2.0.10/src/format/opamFilter.mli opam-2.1.2/src/format/opamFilter.mli --- opam-2.0.10/src/format/opamFilter.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFilter.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -123,7 +123,11 @@ (** Like [ident_value], but casts the result to a bool *) val ident_bool: ?default:bool -> env -> fident -> bool -(** Rewrites [basename].in to [basename], expanding interpolations *) +(** Rewrites [basename].in to [basename], expanding interpolations. + If the first line begins ["opam-version:"], assumes that expansion of + variables within strings should be properly escaped. In particular, this + means that Windows paths should expand correctly when generating .config + files. *) val expand_interpolations_in_file: env -> basename -> unit @@ -195,3 +199,10 @@ filtered_formula -> (OpamPackage.Name.t * (filter * (relop * filter) option)) OpamFormula.formula + +(* Uses [OpamFormula.sort] to sort on names, and sort version formulas with + [simplify_extended_version_formula]. *) +val sort_filtered_formula: + ((name * filter filter_or_constraint OpamFormula.formula) + -> (name * filter filter_or_constraint OpamFormula.formula) -> int) + -> filtered_formula -> filtered_formula diff -Nru opam-2.0.10/src/format/opamFormatConfig.ml opam-2.1.2/src/format/opamFormatConfig.ml --- opam-2.0.10/src/format/opamFormatConfig.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFormatConfig.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2016 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -8,6 +8,20 @@ (* *) (**************************************************************************) +module E = struct + + type OpamStd.Config.E.t += + | ALLPARENS of bool option + | SKIPVERSIONCHECKS of bool option + | STRICT of bool option + + open OpamStd.Config.E + let allparens = value (function ALLPARENS b -> b | _ -> None) + let skipversionchecks = value (function SKIPVERSIONCHECKS b -> b | _ -> None) + let strict = value (function STRICT b -> b | _ -> None) + +end + type t = { strict: bool; skip_version_checks: bool; @@ -47,10 +61,9 @@ let update ?noop:_ = setk (fun cfg () -> r := cfg) !r let initk k = - let open OpamStd.Config in setk (setk (fun c -> r := c; k)) !r - ?strict:(env_bool "STRICT") - ?skip_version_checks:(env_bool "SKIPVERSIONCHECKS") - ?all_parens:(env_bool "ALLPARENS") + ?strict:(E.strict ()) + ?skip_version_checks:(E.skipversionchecks ()) + ?all_parens:(E.allparens ()) let init ?noop:_ = initk (fun () -> ()) diff -Nru opam-2.0.10/src/format/opamFormatConfig.mli opam-2.1.2/src/format/opamFormatConfig.mli --- opam-2.0.10/src/format/opamFormatConfig.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFormatConfig.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2016 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -8,6 +8,13 @@ (* *) (**************************************************************************) +module E : sig + type OpamStd.Config.E.t += + | ALLPARENS of bool option + | SKIPVERSIONCHECKS of bool option + | STRICT of bool option +end + (** Configuration options for the format lib (record, global reference and setter) *) diff -Nru opam-2.0.10/src/format/opamFormat.ml opam-2.1.2/src/format/opamFormat.ml --- opam-2.0.10/src/format/opamFormat.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFormat.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,27 +9,24 @@ (* *) (**************************************************************************) +open OpamParserTypes.FullPos open OpamTypes open OpamTypesBase open OpamStd.Op open OpamPp open OpamPp.Op -let item_pos = function - | Section (pos,_) | Variable (pos,_,_) -> pos +let item_pos (i: opamfile_item) = i.pos -let value_pos = function - | Bool (pos, _) | Int (pos, _) | String (pos, _) - | Logop (pos, _, _, _) | Pfxop (pos, _, _) - | Relop (pos, _, _, _) | Prefix_relop (pos, _, _) - | Ident (pos, _) | List (pos, _) | Group (pos, _) | Option (pos, _, _) - | Env_binding (pos, _, _, _) - -> pos +let value_pos (v: value) = v.pos let values_pos = function | [] -> None | x::_ -> Some (value_pos x) +let optelem = OpamStd.Option.map (fun x -> x.pelem) +let nullposelem = OpamStd.Option.map nullify_pos + (** low-level Pps for the Lines parser ([string list list]) *) type lines = string list list @@ -37,10 +34,14 @@ let lines_set ~empty ~add ~fold pp1 = pp ~name:(Printf.sprintf "(%s) lines" pp1.ppname) - (fun ~pos:(file,_,_) lines -> + (fun ~pos lines -> List.fold_left (fun (i,acc) -> function | [] -> i + 1, acc - | line -> i + 1, add (parse pp1 ~pos:(file,i,0) line) acc) + | line -> + (* XXX POSCHECK *) + let pos = { pos with start = i,0 ; stop = i,0 } in + i + 1, + add (parse pp1 ~pos line) acc) (1, empty) lines |> snd) (fun x -> @@ -49,18 +50,19 @@ let lines_map ~empty ~add ~fold pp1 = pp ~name:(Printf.sprintf "(%s) lines" pp1.ppname) - (fun ~pos:(file,_,_) lines -> + (fun ~pos lines -> List.fold_left (fun (i,acc) -> function | [] -> i + 1, acc | line -> - let k,v = parse pp1 ~pos:(file,i,0) line in + let pos = { pos with start = i,0 ; stop = i,0 } in + (* XXX POSCHECK *) + let k,v = parse pp1 ~pos line in i + 1, add k v acc) (1, empty) lines |> snd) (fun x -> List.rev (fold (fun k v acc -> print pp1 (k,v)::acc) x [])) - (* let list2 pp1 pp2 = pp ~name:(Printf.sprintf "%s %s" pp1.ppname pp2.ppname) @@ -76,98 +78,109 @@ let bool = pp ~name:"bool" - (fun ~pos:_ -> function Bool (_,b) -> b | _ -> unexpected ()) - (fun b -> Bool (pos_null,b)) + (fun ~pos:_ v -> match v.pelem with Bool b -> b | _ -> unexpected ()) + (fun b -> nullify_pos (Bool b)) let int = pp ~name:"int" - (fun ~pos:_ -> function Int (_,i) -> i | _ -> unexpected ()) - (fun i -> Int (pos_null,i)) + (fun ~pos:_ v -> match v.pelem with Int i -> i | _ -> unexpected ()) + (fun i -> nullify_pos (Int i)) let pos_int = int -| check ~name:"positive-int" (fun i -> i >= 0) let ident = pp ~name:"ident" - (fun ~pos:_ -> function Ident (_,i) -> i | _ -> unexpected ()) - (fun str -> Ident (pos_null,str)) + (fun ~pos:_ v -> match v.pelem with Ident i -> i | _ -> unexpected ()) + (fun str -> nullify_pos (Ident str)) let string = pp ~name:"string" - (fun ~pos:_ -> function String (_,s) -> s | _ -> unexpected ()) - (fun str -> String (pos_null,str)) + (fun ~pos:_ v -> match v.pelem with String s -> s | _ -> unexpected ()) + (fun str -> nullify_pos (String str)) let string_tr = string -| pp (fun ~pos:_ -> OpamStd.String.strip) (fun x -> x) let simple_arg = pp ~name:"ident-or-string" - (fun ~pos:_ -> function - | Ident (_,i) -> CIdent i - | String (_,s) -> CString s + (fun ~pos:_ v -> + match v.pelem with + | Ident i -> CIdent i + | String s -> CString s | _ -> unexpected ()) (function - | CIdent i -> Ident (pos_null, i) - | CString s -> String (pos_null, s)) + | CIdent i -> nullify_pos (Ident i) + | CString s -> nullify_pos (String s)) let variable_contents = pp ~name:"string-or-stringlist-or-bool" - (fun ~pos:_ -> function - | String (_,s) -> S s - | Bool (_,b) -> B b - | List (_,l) -> - L (List.map (function String (_, s) -> s | _ -> unexpected ()) l) + (fun ~pos:_ v -> + match v.pelem with + | String s -> S s + | Bool b -> B b + | List l -> + L (List.map (fun v -> + match v.pelem with String s -> s | _ -> unexpected ()) + l.pelem) | _ -> unexpected ()) (function - | S s -> String (pos_null, s) - | B b -> Bool (pos_null, b) - | L l -> List (pos_null, List.map (fun s -> String (pos_null, s)) l)) + | S s -> nullify_pos (String s) + | B b -> nullify_pos (Bool b) + | L l -> + nullify_pos (List (nullify_pos (List.map (fun s -> + nullify_pos (String s)) l)))) let list = pp ~name:"list" ~name_constr:(Printf.sprintf "[%s]") - (fun ~pos:_ -> function - | List (_,l) -> l - | x -> [x]) - (fun l -> List (pos_null, l)) + (fun ~pos:_ v -> + match v.pelem with + | List l -> l.pelem + | _ -> [v]) + (fun l -> nullify_pos (List (nullify_pos l))) let group = pp ~name:"group" ~name_constr:(Printf.sprintf "(%s)") - (fun ~pos:_ -> function - | Group (_,l) -> l - | x -> [x]) - (fun l -> Group (pos_null, l)) + (fun ~pos:_ v -> + match v.pelem with + | Group l -> l.pelem + | _ -> [v]) + (fun l -> nullify_pos (Group (nullify_pos l))) let option = pp ~name:"option" - (fun ~pos:_ -> function - | Option (_,k,l) -> k, l - | k -> k, []) + (fun ~pos:_ v -> + match v.pelem with + | Option (k,l) -> k, l.pelem + | _ -> v, []) (function | (v, []) -> v - | (v, l) -> Option (pos_null, v, l)) + | (v, l) -> nullify_pos (Option (v, nullify_pos l))) let option_strict = pp ~name:"option" - (fun ~pos -> function - | Option (_,k,l) -> k, l + (fun ~pos v -> + match v.pelem with + | Option (k,l) -> k, l.pelem | _ -> bad_format ~pos "Expected an option") - (function (v, l) -> Option (pos_null, v, l)) + (function (v, l) -> nullify_pos (Option (v, nullify_pos l))) let map_group pp1 = group -| map_list ~posf:value_pos pp1 let list_depth expected_depth = - let rec depth = function - | List (_,[]) -> 1 - | List (_,(v::_)) -> 1 + depth v - | Option (_,v,_) -> depth v + let rec depth v = + match v.pelem with + | List { pelem = []; _} -> 1 + | List { pelem = v::_; _} -> 1 + depth v + | Option (v,_) -> depth v | _ -> 0 in let rec wrap n v = - if n <= 0 then v else wrap (n-1) (List (pos_null, [v])) + if n <= 0 then v else wrap (n-1) (nullify_pos (List (nullify_pos [v]))) in let rec lift n v = if n <= 0 then v else - match v with - | List (_, [v]) -> lift (n-1) v - | v -> v + match v.pelem with + | List { pelem = [v]; _} -> lift (n-1) v + | _ -> v in pp (fun ~pos:_ v -> wrap (expected_depth - depth v) v) @@ -175,17 +188,18 @@ let option_depth expected_depth = let rec depth = function - | Option (_,v,_) -> 1 + depth v + | { pelem = Option (v,_); _} -> 1 + depth v | _ -> 0 in let rec wrap n v = - if n <= 0 then v else wrap (n-1) (Option (pos_null, v, [])) + if n <= 0 then v else + wrap (n-1) (nullify_pos (Option (v, nullify_pos []))) in let rec lift n v = if n <= 0 then v else - match v with - | Option (_, v, []) -> lift (n-1) v - | v -> v + match v.pelem with + | Option (v, {pelem = []; _}) -> lift (n-1) v + | _ -> v in pp (fun ~pos:_ v -> wrap (expected_depth - depth v) v) @@ -195,13 +209,14 @@ list_depth depth -| pp ~name:(Printf.sprintf "[%s]" pp1.ppname) (fun ~pos:_ v -> - match v with - | List (_, l) -> + match v.pelem with + | List l -> List.rev @@ - List.rev_map (fun v -> parse pp1 ~pos:(value_pos v) v) l + List.rev_map (fun v -> parse pp1 ~pos:(value_pos v) v) l.pelem | _ -> unexpected ()) - (function - | l -> List (pos_null, List.rev @@ List.rev_map (print pp1) l)) + (fun l -> + nullify_pos (List (nullify_pos @@ + List.rev @@ List.rev_map (print pp1) l))) let map_option_contents pp1 pp2 = map_pair ~name:(Printf.sprintf "%s ?{%s}" pp1.ppname pp2.ppname) @@ -231,22 +246,24 @@ let map_pair pp1 pp2 = pp ~name:(Printf.sprintf "[%s %s]" pp1.ppname pp2.ppname) - (fun ~pos:_ -> function - | List (_,[a; b]) -> + (fun ~pos:_ v -> match v.pelem with + | List { pelem = [a; b]; _} -> parse pp1 ~pos:(value_pos a) a, parse pp2 ~pos:(value_pos b) b | _ -> unexpected ()) - (fun (a, b) -> List (pos_null, [pp1.print a; pp2.print b])) + (fun (a, b) -> + nullify_pos @@ List (nullify_pos @@ [pp1.print a; pp2.print b])) let map_triple pp1 pp2 pp3 = pp ~name:(Printf.sprintf "[%s %s %s]" pp1.ppname pp2.ppname pp3.ppname) - (fun ~pos:_ -> function - | List (_,[a; b; c]) -> + (fun ~pos:_ v -> match v.pelem with + | List { pelem = [a; b; c]; _} -> parse pp1 ~pos:(value_pos a) a, parse pp2 ~pos:(value_pos b) b, parse pp3 ~pos:(value_pos c) c | _ -> unexpected ()) (fun (a, b, c) -> - List (pos_null, [pp1.print a; pp2.print b; pp3.print c])) + nullify_pos @@ List (nullify_pos @@ + [pp1.print a; pp2.print b; pp3.print c])) (** Pps for the [value] type to higher level types *) @@ -255,16 +272,17 @@ let url_with_backend backend = string -| pp ~name:"url" - (fun ~pos:_ -> OpamUrl.parse ~backend ~handle_suffix:false) + (fun ~pos:_ s -> OpamUrl.parse ~backend ~handle_suffix:false s) (fun url -> OpamUrl.to_string url) (* a hack to allow "system" compiler as ident rather than string. For backwards-compat. Deprecated, for migration only *) let compiler_version = let system_compiler = "system" in - let parse ~pos:_ = function - | Ident (_, v) when v = system_compiler -> v - | String (_, v) -> v + let parse ~pos:_ = fun v -> + match v.pelem with + | Ident v when v = system_compiler -> v + | String v -> v | _ -> unexpected () in let print v = @@ -281,51 +299,53 @@ let filter = let rec parse_filter ~pos l = - let rec aux = function - | Bool (_,b) -> FBool b - | String (_,s) -> FString s - | Int (_,i) -> FString (string_of_int i) - | Ident (pos,_) as id -> FIdent (parse ~pos filter_ident id) - | Group (pos,g) -> parse_filter ~pos g - | Relop (_,op,e,f) -> FOp (aux e, op, aux f) - | Pfxop (_,`Not,e) -> FNot (aux e) - | Pfxop (_,`Defined,e) -> FDefined (aux e) - | Logop(_,`And,e,f)-> FAnd (aux e, aux f) - | Logop(_,`Or, e,f)-> FOr (aux e, aux f) + let rec aux = fun v -> + match v.pelem with + | Bool b -> FBool b + | String s -> FString s + | Int i -> FString (string_of_int i) + | Ident _ -> FIdent (parse ~pos:v.pos filter_ident v) + | Group g -> parse_filter ~pos:v.pos g.pelem + | Relop (op,e,f) -> FOp (aux e, op.pelem, aux f) + | Pfxop ({ pelem = `Not; _}, e) -> FNot (aux e) + | Pfxop ({ pelem = `Defined; _}, e) -> FDefined (aux e) + | Logop ({ pelem = `And; _}, e, f)-> FAnd (aux e, aux f) + | Logop ({ pelem = `Or; _}, e, f)-> FOr (aux e, aux f) | _ -> unexpected () in match l with | [] -> FBool true - | [Group (_, ([] | _::_::_))] | _::_::_ -> + | [{ pelem = Group { pelem = [] | _::_::_ ; _}; _}] + | _::_::_ -> bad_format ~pos "expected a single filter expression" - | [Group(_,[f])] | [f] -> aux f + | [{ pelem = Group { pelem = [f]; _}; _}] | [f] -> aux f in let print_filter f = let rec aux ?(context=`Or) f = let group_if ?(cond=false) f = if cond || OpamFormatConfig.(!r.all_parens) - then Group (pos_null, [f]) + then nullify_pos @@ Group (nullify_pos [f]) else f in match f with | FString s -> print string s | FIdent fid -> print filter_ident fid | FBool b -> print bool b - | FOp(e,s,f) -> - group_if ~cond:(context <> `Or && context <> `And) - (Relop (pos_null, s, aux ~context:`Relop e, aux ~context:`Relop f)) - | FOr(e,f) -> - group_if ~cond:(context <> `Or) - (Logop (pos_null, `Or, aux ~context:`Or e, aux ~context:`Or f)) - | FAnd(e,f) -> - group_if ~cond:(context <> `Or && context <> `And) - (Logop (pos_null, `And, aux ~context:`And e, aux ~context:`And f)) + | FOp (e,s,f) -> + group_if ~cond:(context <> `Or && context <> `And) @@ nullify_pos @@ + Relop (nullify_pos s, aux ~context:`Relop e, aux ~context:`Relop f) + | FOr (e,f) -> + group_if ~cond:(context <> `Or) @@ nullify_pos @@ + Logop (nullify_pos `Or, aux ~context:`Or e, aux ~context:`Or f) + | FAnd (e,f) -> + group_if ~cond:(context <> `Or && context <> `And) @@ nullify_pos @@ + Logop (nullify_pos `And, aux ~context:`And e, aux ~context:`And f) | FNot f -> - group_if ~cond:(context = `Relop) - (Pfxop (pos_null, `Not, aux ~context:`Not f)) + group_if ~cond:(context = `Relop) @@ nullify_pos @@ + Pfxop (nullify_pos `Not, aux ~context:`Not f) | FDefined f -> - group_if ~cond:(context = `Relop) - (Pfxop (pos_null, `Defined, aux ~context:`Defined f)) + group_if ~cond:(context = `Relop) @@ nullify_pos @@ + Pfxop (nullify_pos `Defined, aux ~context:`Defined f) | FUndef _ -> assert false in match f with @@ -340,18 +360,15 @@ let constraints version = let rec parse_constraints ~pos:_ l = - let rec aux = function - | Prefix_relop (pos, op, v) -> - Atom (op, parse version ~pos v) - | Logop (_, `And, l, r) -> - And (aux l, aux r) - | Logop (_, `Or, l, r) -> - Or (aux l, aux r) - | Pfxop (_,`Not,v) -> - OpamFormula.neg (fun (op, s) -> (OpamFormula.neg_relop op, s)) (aux v) - | Group (pos, g) -> - Block (parse_constraints ~pos g) - | v -> unexpected ~pos:(value_pos v) () + let rec aux v = match v.pelem with + | Prefix_relop (op, v) -> Atom (op.pelem, parse version ~pos:v.pos v) + | Logop ({ pelem = `And; _}, l, r) -> And (aux l, aux r) + | Logop ({ pelem = `Or; _}, l, r) -> Or (aux l, aux r) + | Pfxop ({ pelem = `Not; _}, v) -> + OpamFormula.neg (fun (op, s) -> + (OpamFormula.neg_relop op, s)) (aux v) + | Group g -> Block (parse_constraints ~pos:v.pos g.pelem) + | _ -> unexpected ~pos:(value_pos v) () in OpamFormula.ands (List.map aux l) in @@ -359,20 +376,22 @@ let rec aux ?(in_and=false) cs = let group_if ?(cond=false) f = if cond || OpamFormatConfig.(!r.all_parens) - then Group (pos_null, [f]) + then nullify_pos @@ Group (nullify_pos [f]) else f in match cs with | Empty -> assert false | Atom (r, v) -> - group_if (Prefix_relop (pos_null, r, print version v)) + group_if @@ nullify_pos @@ + Prefix_relop (nullify_pos r, print version v) | And (x, y) -> - group_if - (Logop (pos_null, `And, aux ~in_and:true x, aux ~in_and:true y)) + group_if @@ nullify_pos @@ + Logop (nullify_pos `And, aux ~in_and:true x, aux ~in_and:true y) | Or (x, y) -> - group_if ~cond:in_and - (Logop (pos_null, `Or, aux x, aux y)) - | Block g -> Group (pos_null, print_constraints g) + group_if ~cond:in_and @@ nullify_pos @@ + Logop (nullify_pos `Or, aux x, aux y) + | Block g -> + nullify_pos @@ Group (nullify_pos @@ print_constraints g) in match cs with | Empty -> [] @@ -383,50 +402,50 @@ let filtered_constraints version = let rec parse_cs ~pos:_ items = - let rec aux_parse = function - | Prefix_relop (pos, op, v) -> - Atom (Constraint (op, parse version ~pos v)) - | Logop (_, `And, a, b) -> OpamFormula.ands [aux_parse a; aux_parse b] - | Logop (_, `Or, a, b) -> OpamFormula.ors [aux_parse a; aux_parse b] - | Group (pos, g) -> OpamFormula.Block (parse_cs ~pos g) - | Pfxop (pos, `Not, v) -> - parse_cs ~pos [v] |> + let rec aux_parse v = match v.pelem with + | Prefix_relop (op, v) -> + Atom (Constraint (op.pelem, parse version ~pos:v.pos v)) + | Logop ({ pelem = `And; _}, a, b) -> + OpamFormula.ands [aux_parse a; aux_parse b] + | Logop ({ pelem = `Or; _}, a, b) -> + OpamFormula.ors [aux_parse a; aux_parse b] + | Group g -> OpamFormula.Block (parse_cs ~pos:v.pos g.pelem) + | Pfxop ({ pelem = `Not; _}, v') -> + parse_cs ~pos:v.pos [v'] |> OpamFormula.neg (function - | Constraint (op, v) -> - Constraint (OpamFormula.neg_relop op, v) - | Filter f -> - Filter (FNot f)) - | filt -> - let f = filter.parse ~pos:(value_pos filt) [filt] in - Atom (Filter f) + | Constraint (op, v) -> Constraint (OpamFormula.neg_relop op, v) + | Filter f -> Filter (FNot f)) + | _ -> + Atom (Filter (filter.parse ~pos:(value_pos v) [v])) in OpamFormula.ands (List.map aux_parse items) in - let rec print_cs cs = + let rec print_cs cs = (* form -> val list *) let rec aux ?(in_and=false) cs = let group_if ?(cond=false) f = if cond || OpamFormatConfig.(!r.all_parens) - then Group (pos_null, [f]) + then nullify_pos @@ Group (nullify_pos [f]) else f in match cs with | Empty -> assert false | And (x, y) -> - group_if - (Logop (pos_null, `And, aux ~in_and:true x, aux ~in_and:true y)) + group_if @@ nullify_pos @@ + Logop (nullify_pos `And, aux ~in_and:true x, aux ~in_and:true y) | Or (x, y) -> - group_if ~cond:in_and - (Logop (pos_null, `Or, aux x, aux y)) - | Block g -> Group (pos_null, print_cs g) + group_if ~cond:in_and @@ nullify_pos @@ + Logop (nullify_pos `Or, aux x, aux y) + | Block g -> nullify_pos @@ Group (nullify_pos @@ print_cs g) | Atom (Constraint (op,v)) -> - group_if (Prefix_relop (pos_null, op, print version v)) + group_if @@ nullify_pos @@ + Prefix_relop (nullify_pos op, print version v) | Atom (Filter flt) -> (match filter.print flt with | f1::fr -> - group_if - (List.fold_left (fun a b -> Logop (pos_null, `And, a, b)) - f1 fr) - | [] -> Group (pos_null, [])) + group_if (List.fold_left (fun a b -> + nullify_pos @@ Logop (nullify_pos `And, a, b)) + f1 fr) + | [] -> nullify_pos @@ Group (nullify_pos [])) in match cs with | Empty -> [] @@ -439,21 +458,21 @@ let ext_version = pp ~name:"version-expr" - (fun ~pos:_ -> function - | String (pos,s) -> + (fun ~pos:_ v -> match v.pelem with + | String s -> let _ = try OpamPackage.Version.of_string (OpamFilter.expand_string (fun _ -> Some (S "-")) s) with Failure msg -> - bad_format ~pos "Invalid version string %S: %s" s msg + bad_format ~pos:v.pos "Invalid version string %S: %s" s msg in FString s - | Ident (_,s) -> FIdent (filter_ident_of_string s) + | Ident s -> FIdent (filter_ident_of_string s) | _ -> unexpected ()) (function - | FString s -> String (pos_null, s) - | FIdent id -> Ident (pos_null, string_of_filter_ident id) + | FString s -> nullify_pos (String s) + | FIdent id -> nullify_pos @@ Ident (string_of_filter_ident id) | _ -> assert false) let pkgname = @@ -495,13 +514,15 @@ | `Disj -> ors_to_list, OpamFormula.ors in let rec parse_formula ~pos:_ l = - let rec aux = function - | String (pos,_) | Option (pos,_,_) as at -> - Atom (parse (package_atom constraints) ~pos at) - | Group (pos,g) -> Block (parse_formula ~pos g) - | Logop (_, `Or, e1, e2) -> let left = aux e1 in Or (left, aux e2) - | Logop (_, `And, e1, e2) -> let left = aux e1 in And (left, aux e2) - | v -> unexpected ~pos:(value_pos v) () + let rec aux v = match v.pelem with + | String _ | Option (_,_) -> + Atom (parse (package_atom constraints) ~pos:v.pos v) + | Group g -> Block (parse_formula ~pos:v.pos g.pelem) + | Logop ({ pelem = `Or; _}, e1, e2) -> + let left = aux e1 in Or (left, aux e2) + | Logop ({ pelem = `And; _}, e1, e2) -> + let left = aux e1 in And (left, aux e2) + | _ -> unexpected ~pos:(value_pos v) () in join (List.map aux l) in @@ -509,18 +530,19 @@ let rec aux ?(in_and=false) f = let group_if ?(cond=false) f = if cond || OpamFormatConfig.(!r.all_parens) - then Group (pos_null, [f]) + then nullify_pos @@ Group (nullify_pos [f]) else f in match f with | Empty -> assert false - | Block f -> Group (pos_null, print_formula ~inner:true f) + | Block f -> + nullify_pos @@ Group (nullify_pos @@ print_formula ~inner:true f) | And (e,f) -> - group_if - (Logop (pos_null, `And, aux ~in_and:true e, aux ~in_and:true f)) + group_if @@ nullify_pos @@ + Logop (nullify_pos `And, aux ~in_and:true e, aux ~in_and:true f) | Or (e,f) -> - group_if ~cond:in_and - (Logop (pos_null, `Or, aux e, aux f)) + group_if ~cond:in_and @@ nullify_pos @@ + Logop (nullify_pos `Or, aux e, aux f) | Atom at -> group_if (print (package_atom constraints) at) in let fl = if inner then [f] else split f in @@ -532,27 +554,30 @@ list -| package_formula_items kind constraints let env_binding = - let parse ~pos:_ = function - | Relop (_, `Eq, Ident (_,i), String (_,s)) -> i, Eq, s, None - | Env_binding (_, Ident (_,i), op, String (_,s)) -> i, op, s, None + let parse ~pos:_ v = match v.pelem with + | Relop ({ pelem = `Eq;_}, { pelem = Ident i;_}, { pelem = String s;_}) -> + i, OpamParserTypes.Eq, s, None + | Env_binding ({ pelem = Ident i; _}, op, { pelem = String s; _}) -> + i, op.pelem, s, None | _ -> unexpected () in let print (id, op, str, _) = - Env_binding (pos_null, print ident id, op, print string str) + nullify_pos @@ + Env_binding (print ident id, nullify_pos op, print string str) in list -| singleton -| pp ~name:"env-binding" parse print (* Only used by the deprecated "os" field *) let os_constraint = let rec parse_osc ~pos:_ l = - let rec aux = function - | Group (pos,g) -> Block (parse_osc ~pos g) - | String (_,os) -> Atom (true, os) - | Logop (_,`And,l,r) -> And (aux l, aux r) - | Logop (_,`Or,l,r) -> Or (aux l, aux r) - | Pfxop (_,`Not,v) -> + let rec aux v = match v.pelem with + | Group g -> Block (parse_osc ~pos:g.pos g.pelem) + | String os -> Atom (true, os) + | Logop ({ pelem = `And; _}, l, r) -> And (aux l, aux r) + | Logop ({ pelem = `Or; _}, l, r) -> Or (aux l, aux r) + | Pfxop ({ pelem = `Not; _}, v) -> OpamFormula.neg (fun (b, s) -> (not b, s)) (aux v) - | v -> unexpected ~pos:(value_pos v) () + | _ -> unexpected ~pos:(value_pos v) () in OpamFormula.ors (List.map aux l) in @@ -560,10 +585,11 @@ let rec aux = function | Empty -> assert false | Atom (true , os) -> print string os - | Atom (false, os) -> Pfxop (pos_null, `Not, print string os) - | Block g -> Group (pos_null, [aux g]) - | And(e,f) -> Logop (pos_null, `And, aux e, aux f) - | Or(e,f) -> Logop (pos_null, `Or, aux e, aux f) + | Atom (false, os) -> + nullify_pos @@ Pfxop (nullify_pos `Not, print string os) + | Block g -> nullify_pos @@ Group (nullify_pos [aux g]) + | And (e,f) -> nullify_pos @@ Logop (nullify_pos `And, aux e, aux f) + | Or (e,f) -> nullify_pos @@ Logop (nullify_pos `Or, aux e, aux f) in match f with | Empty -> [] @@ -590,19 +616,19 @@ let item = pp ~name:"field-binding" - (fun ~pos:_ -> function - | Section (pos,sec) -> - bad_format ~pos "Unexpected section %s" sec.section_kind - | Variable (_,k,v) -> k,v) - (fun (k,v) -> Variable (pos_null, k, v)) + (fun ~pos:_ v -> match v.pelem with + | Section (sec) -> + bad_format ~pos:v.pos "Unexpected section %s" sec.section_kind.pelem + | Variable (k,v) -> k.pelem, v) + (fun (k,v) -> nullify_pos @@ Variable (nullify_pos k, v)) let items = map_list ~posf:item_pos item let anonymous_section pp1 = pp ~name:pp1.ppname (fun ~pos -> function - | [None, contents] -> pp1.parse ~pos contents - | [Some _, _] -> + | [ None, contents ] -> pp1.parse ~pos contents + | [ Some _, _ ] -> bad_format ~pos "Unexpected section title" | [] -> bad_format ~pos "Missing section" @@ -612,15 +638,18 @@ let section kind = pp ~name:"file-section" - (fun ~pos:_ -> function - | Section (_, ({section_kind; _} as s)) when section_kind = kind -> - s.section_name, s.section_items - | Section (pos,sec) -> - bad_format ~pos "Unexpected section %s" sec.section_kind - | Variable (pos,k,_) -> - bad_format ~pos "Unexpected field %s" k) - (fun (section_name, section_items) -> - Section (pos_null, { section_kind=kind; section_name; section_items })) + (fun ~pos:_ v -> match v.pelem with + | Section ({section_kind; _} as s) when section_kind.pelem = kind -> + optelem s.section_name, s.section_items.pelem + | Section sec -> + bad_format ~pos:v.pos "Unexpected section %s" sec.section_kind.pelem + | Variable (k,_) -> + bad_format ~pos:v.pos "Unexpected field %s" k.pelem) + (fun (name, items) -> + nullify_pos @@ Section { + section_kind = nullify_pos kind; + section_name = nullposelem name; + section_items = nullify_pos items }) type ('a, 'value) fields_def = (string * ('a, 'value) field_parser) list @@ -635,24 +664,51 @@ let to_json (s,o) = `O (("kind", `String s) :: match o with None -> [] | Some s -> ["name", `String s]) + (* these serializers/deserializers are not accessible + from the OpamFormat.mli interface, so there are not currently + tested -- it's not clear if usage of the SEM functor touches + them in any way... *) + let of_json = function + | `O dict -> + begin try + match List.assoc "kind" dict with + | `String s -> + begin + let o = + if not (List.mem_assoc "name" dict) then None + else match List.assoc "name" dict with + | `String s -> Some s + | _ -> raise Not_found + in Some (s, o) + end + | _ -> raise Not_found + with Not_found -> None + end + | _ -> None end) in + (* XXX trasnform field map into set *) let errs, section_map, field_map = List.fold_left - (fun (errs, section_map, field_map) -> function - | Section - (pos, {section_kind=k; section_name=n; section_items=v}) -> + (fun (errs, section_map, field_map) it -> match it.pelem with + | Section sec -> + let k = sec.section_kind.pelem in + let v = sec.section_items.pelem in + let n = optelem sec.section_name in if List.mem_assoc k sections then try errs, - SEM.safe_add (k,n) (pos,v) section_map, field_map + SEM.safe_add (k, n) (pos,v) section_map, field_map with Failure _ -> (k,(Some pos,"Duplicate section "^k)) :: errs, section_map, field_map else (k,(Some pos,"Invalid section "^k)) :: errs, section_map, field_map - | Variable (pos, k, v) -> + | Variable (k, v) -> + let k = k.pelem in + let v = v.pelem in + let pos = it.pos in if List.mem_assoc k ppas then try errs, @@ -670,7 +726,7 @@ (fun (errs,acc) (field,ppa) -> try let pos, v = OpamStd.String.Map.find field field_map in - try errs, parse ppa ~pos (acc, Some v) with + try errs, parse ppa ~pos (acc, Some { pelem = v; pos }) with | Bad_format (pos, msg) -> (field, (pos, msg)) :: errs, acc with Not_found -> @@ -687,8 +743,8 @@ SEM.fold (fun (kind, name) (_, items) acc -> if kind = section_kind then (name, items) :: acc else acc) - section_map [] |> - List.rev + section_map [] + |> List.rev in if secs = [] then errs, acc else try errs, parse ppa ~pos (acc, Some secs) with @@ -702,16 +758,20 @@ OpamStd.List.filter_map (fun (field,ppa) -> match snd (ppa.print acc) with - | None | Some (List (_,[]) | Group (_,[])) -> None - | Some value -> Some (Variable (pos_null, field, value))) + | None | Some ({ pelem = List { pelem = []; _}; _} + | { pelem = Group { pelem = []; _}; _}) -> None + | Some value -> + Some (nullify_pos @@ Variable (nullify_pos field, value))) ppas @ (List.flatten @@ List.map - (fun (section_kind, ppa) -> + (fun (kind, ppa) -> OpamStd.Option.default [] (snd (ppa.print acc)) |> - List.map (fun (section_name, section_items) -> - Section - (pos_null, { section_kind; section_name; section_items }))) + List.map (fun (name, items) -> + nullify_pos @@ Section { + section_kind = nullify_pos kind; + section_name = nullposelem name; + section_items = nullify_pos items })) sections) in pp ?name parse print @@ -720,7 +780,8 @@ ?(strict=OpamFormatConfig.(!r.strict)) ?(condition=fun _ -> true) () = - let parse ~pos:(file,_,_) (t, errs) = + let parse ~pos (t, errs) = + let file = pos.filename in if errs = [] then t else if strict then raise (Bad_format_list (List.rev_map snd errs)) else @@ -754,24 +815,24 @@ (fun ~pos:_ -> List.partition filter) (fun (a,b) -> a @ b) - let partition_fields filter = - partition @@ function - | Variable (_,k,_) -> filter k - | _ -> false + let partition_fields ?(section=false) filter = + partition @@ fun v -> match v.pelem with + | Variable (k,_) -> filter k.pelem + | Section _ -> section let field name parse = pp (fun ~pos items -> match - OpamStd.List.filter_map (function - | Variable (_,k,v) when k = name -> Some v + OpamStd.List.filter_map (fun v -> match v.pelem with + | Variable (k,v) when k.pelem = name -> Some v | _ -> None) items with | [] -> None, items | _::_::_ -> bad_format ~pos "Duplicate '%s:' field" name | [v] -> Some (parse ~pos v), items) - (fun (_,x) -> x) + snd let extract_field name = @@ -784,25 +845,58 @@ let check_opam_version ?(optional=false) - ?(f=fun v -> OpamVersion.(compare current_nopatch (nopatch v) >= 0)) + ~format_version + ?(f=fun v -> OpamVersion.(compare format_version (nopatch v) >= 0)) () = let name = "opam-version" in - let opam_v = V.string -| of_module "opam-version" (module OpamVersion) in + let opam_v = V.string -| of_module name (module OpamVersion) in let f v = OpamFormatConfig.(!r.skip_version_checks) || match v with | Some v -> f v | None -> optional in + let errmsg = + Printf.sprintf + "unsupported or missing file format version; should be %s or older" + (OpamVersion.to_string format_version) + in field name (parse opam_v) -| - map_fst (check ~name ~raise:OpamPp.bad_version - ~errmsg:"unsupported or missing file format version; \ - should be 2.0 or older" f) -| - pp + map_fst (check ~name ~raise:OpamPp.bad_version ~errmsg f) -| + pp ~name (fun ~pos:_ (_,x) -> x) (fun x -> - (* re-extract the field using parse when printing, to check *) - parse ~pos:pos_null (field name (parse opam_v)) x) + (* re-extract the field using parse when printing, to check that it is + not undefined *) + match parse ~pos:pos_null (field name (parse opam_v)) x with + | None, _ -> failwith "opam version must be printed" + | v, l -> v, l) + + let opam_version ?(undefined=false) ~format_version () = + let name = "opam-version" in + pp + (fun ~pos:_ items -> + if not undefined then items else + List.filter (function + | { pelem = Variable ({ pelem = fname; _}, + { pelem = String _version; _}); _} + when fname = name -> + (* check opam version already called, we don't need to check + that it is the same version *) + false + | _ -> true) items) + (fun items -> + let opam_v = V.string -| of_module name (module OpamVersion) in + match parse ~pos:pos_null (field name (parse opam_v)) items with + | None, items -> + let opam_v = + nullify_pos @@ + Variable (nullify_pos name, + nullify_pos @@ + String (OpamVersion.to_string format_version)) + in + opam_v :: items + | Some _, items -> items) type signature = string * string * string @@ -813,6 +907,7 @@ exception Invalid_signature of pos * (string*string*string) list option let signed ~check = + let module OpamPrinter = OpamPrinter.FullPos in let pp_sig = V.map_list ~depth:2 signature in extract_field "signature" -| pp ~name:"signed-file" diff -Nru opam-2.0.10/src/format/opamFormat.mli opam-2.1.2/src/format/opamFormat.mli --- opam-2.0.10/src/format/opamFormat.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFormat.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -11,6 +11,7 @@ (** OPAM files syntax and conversion tools *) +open OpamParserTypes.FullPos open OpamTypes open OpamPp @@ -227,9 +228,10 @@ ('a * (string * bad_format) list, 'a) t (** Partitions items in an opamfile base on a condition on the variable - names *) + names. If a section is encountered, it is kept in the second list (as + filter returning false), unless [section] is true. *) val partition_fields : - (string -> bool) -> + ?section:bool -> (string -> bool) -> (opamfile_item list, opamfile_item list * opamfile_item list) t (** Partitions items in an opamfile base on a generic condition on the @@ -260,7 +262,14 @@ (** Checks the [opam_version] field; otherwise the identity *) val check_opam_version : - ?optional:bool -> ?f:(opam_version -> bool) -> unit -> + ?optional:bool -> format_version:opam_version -> ?f:(opam_version -> bool) + -> unit -> + (opamfile_item list, opamfile_item list) t + + (** Add [opam-version] field printing at printing, and removes it from parsed + fields if [undefined] (default is false) *) + val opam_version : + ?undefined:bool -> format_version:opam_version -> unit -> (opamfile_item list, opamfile_item list) t (** Signature handling (wip) *) diff -Nru opam-2.0.10/src/format/opamFormula.ml opam-2.1.2/src/format/opamFormula.ml --- opam-2.0.10/src/format/opamFormula.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFormula.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -19,6 +19,8 @@ | `Leq -> `Gt | `Lt -> `Geq +let string_of_relop = OpamPrinter.FullPos.relop_kind + type version_constraint = relop * OpamPackage.Version.t type atom = OpamPackage.Name.t * version_constraint option @@ -28,7 +30,7 @@ | n, Some (r,c) -> Printf.sprintf "%s (%s %s)" (OpamPackage.Name.to_string n) - (OpamPrinter.relop r) + (string_of_relop r) (OpamPackage.Version.to_string c) let short_string_of_atom = function @@ -40,12 +42,33 @@ | n, Some (r,c) -> Printf.sprintf "%s%s%s" (OpamPackage.Name.to_string n) - (OpamPrinter.relop r) + (string_of_relop r) (OpamPackage.Version.to_string c) let string_of_atoms atoms = OpamStd.List.concat_map " & " short_string_of_atom atoms +let atom_of_string str = + let re = lazy Re.(compile @@ whole_string @@ seq [ + group @@ rep1 @@ diff any (set ">=<.!"); + group @@ alt [ seq [ set "<>"; opt @@ char '=' ]; + set "=."; str "!="; ]; + group @@ rep1 any; + ]) + in + try + let sub = Re.exec (Lazy.force re) str in + let sname = Re.Group.get sub 1 in + let sop = Re.Group.get sub 2 in + let sversion = Re.Group.get sub 3 in + let name = OpamPackage.Name.of_string sname in + let sop = if sop = "." then "=" else sop in + let op = OpamLexer.FullPos.relop sop in + let version = OpamPackage.Version.of_string sversion in + name, Some (op, version) + with Not_found | Failure _ | OpamLexer.Error _ -> + OpamPackage.Name.of_string str, None + type 'a conjunction = 'a list let string_of_conjunction string_of_atom c = @@ -97,7 +120,7 @@ else s in match f with - | Empty -> "0" + | Empty -> "[]" | Atom a -> paren_if (string_of_a a) | Block x -> Printf.sprintf "(%s)" (aux x) | And(x,y) -> @@ -172,6 +195,60 @@ type t = (OpamPackage.Name.t * version_formula) formula +let rec compare_formula f x y = + let rec compare_atom x = function + | Empty -> 1 + | Atom y -> f x y + | Block y -> compare_atom x y + | And (y,z) | Or (y,z) -> + let r = compare_atom x y in + if r <> 0 then r else compare_atom x z + in + match x, y with + | Empty, Empty -> 0 + | Empty, _ -> -1 + | _ , Empty -> 1 + | Atom x, Atom y -> f x y + | Atom x, y -> compare_atom x y + | x , Atom y -> -1 * (compare_atom y x) + | Block x, y | x, Block y -> compare_formula f x y + | (And (x,y) | Or (x,y)) as lhs, ((And (x',y') | Or (x',y')) as rhs) -> + let l = compare_formula f x x' in + if l <> 0 then l else + let r = compare_formula f y y' in + if r <> 0 then r else + (match lhs, rhs with + | And _, And _ | Or _, Or _ -> 0 + | And _, Or _ -> 1 + | Or _, And _ -> -1 + | _ -> assert false) + +let compare_relop op1 op2 = + match op1, op2 with + | `Lt,`Lt | `Leq,`Leq | `Neq,`Neq | `Eq,`Eq | `Geq,`Geq | `Gt,`Gt -> 0 + | `Lt, _ -> -1 + | _, `Lt -> 1 + | `Leq, _ -> -1 + | _, `Leq -> 1 + | `Neq, _ -> -1 + | _, `Neq -> 1 + | `Eq, _ -> -1 + | _, `Eq -> 1 + | `Geq, _ -> -1 + | _, `Geq -> 1 + +let compare_version_formula = + compare_formula (fun (op1,v1) (op2,v2) -> + let c = compare v1 v2 in + if c <> 0 then c else + compare_relop op1 op2) + +let compare_nc (n1, c1) (n2, c2) = + let c = OpamPackage.Name.compare n1 n2 in + if c <> 0 then c else compare_version_formula c1 c2 + +let compare = compare_formula compare_nc + let rec eval atom = function | Empty -> true | Atom x -> atom x @@ -214,9 +291,10 @@ | None -> true | Some (relop, v) -> eval_relop relop (OpamPackage.version package) v -let packages_of_atoms pkgset atoms = - (* Conjunction for constraints over the same name, but disjunction on the - package names *) +let packages_of_atoms ?(disj=false) pkgset atoms = + (* Conjunction for constraints over the same name (unless [disj] is + specified), but disjunction on the package names *) + let ffilter = if disj then List.exists else List.for_all in let by_name = List.fold_left (fun acc (n,_ as atom) -> OpamPackage.Name.Map.update n (fun a -> atom::a) [] acc) @@ -225,13 +303,19 @@ OpamPackage.Name.Map.fold (fun name atoms acc -> OpamPackage.Set.union acc @@ OpamPackage.Set.filter - (fun nv -> List.for_all (fun a -> check a nv) atoms) + (fun nv -> ffilter (fun a -> check a nv) atoms) (OpamPackage.packages_of_name pkgset name)) by_name OpamPackage.Set.empty +let satisfies_depends pkgset f = + eval (fun (name, cstr) -> + OpamPackage.Set.exists (fun nv -> check_version_formula cstr nv.version) + (OpamPackage.packages_of_name pkgset name)) + f + let to_string t = let string_of_constraint (relop, version) = - Printf.sprintf "%s %s" (OpamPrinter.relop relop) + Printf.sprintf "%s %s" (string_of_relop relop) (OpamPackage.Version.to_string version) in let string_of_pkg = function | n, Empty -> OpamPackage.Name.to_string n @@ -381,6 +465,21 @@ in aux t +let rec sort comp f= + match f with + | (Empty | Atom _) as f -> f + | Block f -> Block (sort comp f) + | And _ as f -> + ands_to_list f + |> List.rev_map (sort comp) + |> List.sort (compare_formula comp) + |> ands + | Or _ as f -> + ors_to_list f + |> List.rev_map (sort comp) + |> List.sort (compare_formula comp) + |> ors + let atoms t = fold_right (fun accu x -> x::accu) [] (to_atom_formula t) @@ -538,10 +637,9 @@ | _ :: r -> Some (ors (aux2 r)) let formula_of_version_set set subset = + let module S = OpamPackage.Version.Set in match - gen_formula - (OpamPackage.Version.Set.elements set) - (fun x -> OpamPackage.Version.Set.mem x subset) + gen_formula (S.elements set) (fun x -> S.mem x subset) with | Some f -> f | None -> invalid_arg "Empty subset" diff -Nru opam-2.0.10/src/format/opamFormula.mli opam-2.1.2/src/format/opamFormula.mli --- opam-2.0.10/src/format/opamFormula.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamFormula.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -13,7 +13,7 @@ functions *) (** binary operations (compatible with the Dose type for Cudf operators!) *) -type relop = OpamParserTypes.relop (* = [ `Eq | `Neq | `Geq | `Gt | `Leq | `Lt ] *) +type relop = OpamParserTypes.FullPos.relop_kind (* = [ `Eq | `Neq | `Geq | `Gt | `Leq | `Lt ] *) (** Version constraints for OPAM *) type version_constraint = relop * OpamPackage.Version.t @@ -24,10 +24,14 @@ (** Pretty-printing of atoms *) val string_of_atom: atom -> string -(** The compact atom format used in requests, "pkgOPvers", with '.' allowed instead - of '=' *) +(** The compact atom format used in requests, "pkgOPvers", with '.' allowed + instead of '=' *) val short_string_of_atom: atom -> string +(** Parses a package or atom, in a format similar to [short_string_of_atom]. + @raise [Failure] if the format is incorrect *) +val atom_of_string: string -> atom + (** Prints atoms as a conjunction ("&") using the short format *) val string_of_atoms: atom list -> string @@ -36,8 +40,11 @@ (** Return all packages satisfying the given atoms from a set (i.e. name matching at least one of the atoms, version matching all atoms with the - appropriate name) *) -val packages_of_atoms: OpamPackage.Set.t -> atom list -> OpamPackage.Set.t + appropriate name). If [disj] is true, returns packages that satisfy at + least one of the constraint of a given name, otherwise that satisfy all + constraints. *) +val packages_of_atoms: + ?disj:bool -> OpamPackage.Set.t -> atom list -> OpamPackage.Set.t (** AND formulas *) type 'a conjunction = 'a list @@ -71,6 +78,8 @@ | And of 'a formula * 'a formula | Or of 'a formula * 'a formula +val compare_formula: ('a -> 'a -> int) -> 'a formula -> 'a formula -> int + (** Eval a formula *) val eval: ('a -> bool) -> 'a formula -> bool @@ -126,6 +135,10 @@ (** Fold function (bottom-up, right-to-left) *) val fold_right: ('a -> 'b -> 'a) -> 'a -> 'b formula -> 'a +(** Sort formula, using [compare] function. `Block` around `Or` and `And` \ + are removed. *) +val sort: ('a -> 'a -> int) -> 'a formula -> 'a formula + (** Expressions composed entirely of version constraints *) type version_formula = version_constraint formula @@ -138,15 +151,25 @@ - "foo" \{= "1" | > "4"\} | ("bar" "bouh") *) type t = (OpamPackage.Name.t * version_formula) formula +val compare: t -> t -> int + (** Returns [true] if [package] verifies [formula] (i.e. it is within at least one package set that is a solution of the formula, and is named in the formula) *) val verifies: t -> OpamPackage.t -> bool +(** Checks if a given set of (installed) packages satisfies a formula *) +val satisfies_depends: OpamPackage.Set.t -> t -> bool + (** Returns the subset of packages possibly matching the formula (i.e. including all disjunction cases) *) val packages: OpamPackage.Set.t -> t -> OpamPackage.Set.t +val compare_nc: + (OpamPackage.Name.t * version_formula) -> + (OpamPackage.Name.t * version_formula) -> + int + (** Convert a formula to CNF *) val cnf_of_formula: 'a formula -> 'a formula diff -Nru opam-2.0.10/src/format/opamInterpLexer.mli opam-2.1.2/src/format/opamInterpLexer.mli --- opam-2.0.10/src/format/opamInterpLexer.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/format/opamInterpLexer.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,18 @@ +(**************************************************************************) +(* *) +(* Copyright 2016 MetaStack Solutions Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** OPAM format variable interpolation processor *) + +val main: (string -> unit) -> (string -> unit) -> Lexing.lexbuf -> unit +(** [main unquoted quoted lexbuf] fully processes the given lexbuf. Strings are + applied to [unquoted] until a ["] or ["""] sequence is encountered when the + content within the single or triple-quoted string is applied to [quoted] + (note that the quote marks themselves are passed to [unquoted]). Within + either string type, backslash is the escape character. *) diff -Nru opam-2.0.10/src/format/opamInterpLexer.mll opam-2.1.2/src/format/opamInterpLexer.mll --- opam-2.0.10/src/format/opamInterpLexer.mll 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/format/opamInterpLexer.mll 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,42 @@ +(**************************************************************************) +(* *) +(* Copyright 2016 MetaStack Solutions Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +rule main unquoted quoted = parse +| [^ '"' '\n' ]+ + { unquoted @@ Lexing.lexeme lexbuf; + main unquoted quoted lexbuf } +| ("\"\"\"" | '"') as quote + { unquoted quote; + let triple = String.length quote = 3 in + string triple unquoted quoted lexbuf } +| '\n' { Lexing.new_line lexbuf; + unquoted "\n"; + main unquoted quoted lexbuf } +| eof { () } + + +and string triple unquoted quoted = parse +| ( [^ '"' '\\' '\n' ]+ | '\\' [^ '\n' ]? )+ + { quoted @@ Lexing.lexeme lexbuf; + string triple unquoted quoted lexbuf } +| "\"\"\"" + { unquoted "\"\"\""; + main unquoted quoted lexbuf } +| '"' { if triple then begin + quoted "\""; + string triple unquoted quoted lexbuf + end else begin + unquoted "\""; + main unquoted quoted lexbuf + end } +| '\n' { Lexing.new_line lexbuf; + unquoted "\n"; + string triple unquoted quoted lexbuf} +| eof { () } diff -Nru opam-2.0.10/src/format/opamLineLexer.mli opam-2.1.2/src/format/opamLineLexer.mli --- opam-2.0.10/src/format/opamLineLexer.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamLineLexer.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/format/opamLineLexer.mll opam-2.1.2/src/format/opamLineLexer.mll --- opam-2.0.10/src/format/opamLineLexer.mll 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamLineLexer.mll 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/format/opamPackage.ml opam-2.1.2/src/format/opamPackage.ml --- opam-2.0.10/src/format/opamPackage.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamPackage.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -36,14 +36,21 @@ let compare = OpamVersionCompare.compare + let equal v1 v2 = + compare v1 v2 = 0 + let to_json x = `String (to_string x) + let of_json = function + | `String x -> (try Some (of_string x) with _ -> None) + | _ -> None module O = struct type t = version let to_string = to_string let compare = compare let to_json = to_json + let of_json = of_json end module Set = OpamStd.Set.Make(O) @@ -76,18 +83,22 @@ | Some true -> x - let compare n1 n2 = - match compare (String.lowercase_ascii n1) (String.lowercase_ascii n2) with - | 0 -> compare n1 n2 - | i -> i + let compare = OpamStd.String.compare_case + + let equal n1 n2 = + compare n1 n2 = 0 let to_json x = `String x + let of_json = function + | `String s -> (try Some (of_string s) with _ -> None) + | _ -> None module O = struct type t = string let to_string = to_string let compare = compare let to_json = to_json + let of_json = of_json end module Set = OpamStd.Set.Make(O) @@ -145,6 +156,16 @@ `O [ ("name", Name.to_json (name nv)); ("version", Version.to_json (version nv)); ] +let of_json = function + | `O dict -> + begin try + let open OpamStd.Option.Op in + Name.of_json (List.assoc "name" dict) >>= fun name -> + Version.of_json (List.assoc "version" dict) >>= fun version -> + Some {name; version} + with Not_found -> None + end + | _ -> None module O = struct type tmp = t @@ -156,6 +177,7 @@ let equal = equal let to_string = to_string let to_json = to_json + let of_json = of_json end module Set = OpamStd.Set.Make (O) @@ -255,13 +277,23 @@ nvset Name.Set.empty -let packages_of_name nvset n = - if n = "" then Set.empty else +let package_of_name_aux empty split filter nv n = + if n = "" then empty else let inf = {name = String.sub n 0 (String.length n - 1); version= ""} in let sup = {name = n^"\000"; version = ""} in - let _, _, nvset = Set.split inf nvset in - let nvset, _, _ = Set.split sup nvset in - Set.filter (fun nv -> nv.name = n) nvset + let _, _, nv = split inf nv in + let nv, _, _ = split sup nv in + filter nv + +let packages_of_name nv n = + package_of_name_aux Set.empty Set.split + (Set.filter (fun nv -> nv.name = n)) + nv n + +let packages_of_name_map nv n = + package_of_name_aux Map.empty Map.split + (Map.filter (fun nv _ -> nv.name = n)) + nv n let package_of_name nvset n = Set.choose (packages_of_name nvset n) diff -Nru opam-2.0.10/src/format/opamPackage.mli opam-2.1.2/src/format/opamPackage.mli --- opam-2.0.10/src/format/opamPackage.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamPackage.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -21,6 +21,9 @@ (** Compare two versions using the Debian version scheme *) val compare: t -> t -> int + + (** Are two package versions equal? *) + val equal: t -> t -> bool end (** Names *) @@ -30,6 +33,8 @@ (** Compare two package names *) val compare: t -> t -> int + (** Are two package names equal? *) + val equal: t -> t -> bool end type t = private { @@ -93,6 +98,7 @@ (** Return all the packages with the given name *) val packages_of_name: Set.t -> Name.t -> Set.t +val packages_of_name_map: 'a Map.t -> Name.t -> 'a Map.t (** Return a package with the given name *) val package_of_name: Set.t -> Name.t -> t diff -Nru opam-2.0.10/src/format/opamPath.ml opam-2.1.2/src/format/opamPath.ml --- opam-2.0.10/src/format/opamPath.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamPath.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -25,15 +25,18 @@ OpamFilename.Dir.of_string (OpamStd.Sys.home ()) // ".opamrc"; ] -let state_cache t = t / "repo" // "state.cache" +let state_cache_dir t = t / "repo" + +let state_cache t = state_cache_dir t // Printf.sprintf "state-%s.cache" (OpamVersion.magic ()) let lock t = t // "lock" let config_lock t = t // "config.lock" +(* let archives_dir t = t / "archives" - let archive t nv = archives_dir t // (OpamPackage.to_string nv ^ "+opam.tar.gz") +*) let repos_lock t = t / "repo" // "lock" @@ -57,15 +60,17 @@ let backup t = backup_dir t /- backup_file () +let plugin_prefix = "opam-" + let plugins t = t / "plugins" let plugins_bin t = plugins t / "bin" let plugin_bin t name = - let sname = OpamPackage.Name.to_string name in + let sname = OpamPackage.Name.to_string name ^ OpamStd.Sys.executable_name "" in let basename = - if OpamStd.String.starts_with ~prefix:"opam-" sname then sname - else "opam-" ^ sname + if OpamStd.String.starts_with ~prefix:plugin_prefix sname then sname + else plugin_prefix ^ sname in plugins_bin t // basename @@ -74,6 +79,12 @@ assert (sname <> "bin"); plugins t / sname +module type LAYOUT = sig + type ctx + val root : dirname -> ctx -> dirname + val lib_dir : dirname -> ctx -> dirname +end + module Switch = struct let root t a = OpamSwitch.get_root t a @@ -117,6 +128,10 @@ let sources_dir t a = meta t a / "sources" + let extra_files_dir t a = meta t a / "extra-files-cache" + + let extra_file t a h = extra_files_dir t a // OpamHash.contents h + let sources t a nv = sources_dir t a / OpamPackage.to_string nv let pinned_package t a name = sources_dir t a / OpamPackage.Name.to_string name @@ -129,6 +144,8 @@ let installed_opams t a = meta t a / "packages" + let installed_opams_cache t a = meta t a / "packages" // "cache" + let installed_package_dir t a nv = installed_opams t a / OpamPackage.to_string nv @@ -138,39 +155,45 @@ let installed_opam_files_dir t a nv = installed_package_dir t a nv / "files" - module Default = struct - - (** Visible files that can be redirected using - [config/global-config.config] *) - - let lib_dir t a = root t a / "lib" + module DefaultF(L:LAYOUT) = struct + let lib_dir = L.lib_dir - let lib t a n = lib_dir t a / OpamPackage.Name.to_string n + let lib t a n = L.lib_dir t a / OpamPackage.Name.to_string n - let stublibs t a = lib_dir t a / "stublibs" + let stublibs t a = L.lib_dir t a / "stublibs" - let toplevel t a = lib_dir t a / "toplevel" + let toplevel t a = L.lib_dir t a / "toplevel" - let doc_dir t a = root t a / "doc" + let doc_dir t a = L.root t a / "doc" let man_dir ?num t a = match num with - | None -> root t a / "man" - | Some n -> root t a / "man" / ("man" ^ n) + | None -> L.root t a / "man" + | Some n -> L.root t a / "man" / ("man" ^ n) - let share_dir t a = root t a / "share" + let share_dir t a = L.root t a / "share" let share t a n = share_dir t a / OpamPackage.Name.to_string n - let etc_dir t a = root t a / "etc" + let etc_dir t a = L.root t a / "etc" let etc t a n = etc_dir t a / OpamPackage.Name.to_string n let doc t a n = doc_dir t a / OpamPackage.Name.to_string n - let bin t a = root t a / "bin" + let bin t a = L.root t a / "bin" - let sbin t a = root t a / "sbin" + let sbin t a = L.root t a / "sbin" + end + + (** Visible files that can be redirected using + [config/global-config.config] *) + module Default = struct + include DefaultF(struct + type ctx = switch + let root = root + let lib_dir t a = root t a / "lib" + end) end diff -Nru opam-2.0.10/src/format/opamPath.mli opam-2.1.2/src/format/opamPath.mli --- opam-2.0.10/src/format/opamPath.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamPath.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -21,6 +21,9 @@ (** State cache *) val state_cache: t -> filename +(** Directory containing state cache *) +val state_cache_dir: t -> dirname + (** Global lock file for the whole opamroot. Opam should generally read-lock this (e.g. initialisation and format upgrades require a write lock) *) val lock: t -> filename @@ -36,12 +39,6 @@ switches, repositories lists are expected. No lock needed otherwise) *) val config_lock: t -> filename -(** Archives dir *) -val archives_dir: t -> dirname - -(** Archive file: {i $opam/archives/$NAME.$VERSION+opam.tar.gz} *) -val archive: t -> package -> filename - (** Global lock file for the repositories mirrors: {i $opam/repo/lock} *) val repos_lock: t -> filename @@ -64,6 +61,9 @@ (** Backup file for state export *) val backup: t -> switch_selections OpamFile.t +(** The prefix for plugin commands (["opam-"]) *) +val plugin_prefix : string + (** The directory for plugins data {i $opam/plugins} *) val plugins: t -> dirname @@ -78,6 +78,12 @@ forbidden. *) val plugin: t -> name -> dirname +module type LAYOUT = sig + type ctx + val root : dirname -> ctx -> dirname + val lib_dir : dirname -> ctx -> dirname +end + (** Switch related paths *) module Switch: sig @@ -153,6 +159,14 @@ $meta/sources/$name.$version/} *) val sources: t -> switch -> package -> dirname + (** Temporary switch-local directory where a by-hash map of extra files may be stored. + This is used for switch-imports: {i $meta/extra-files-cache} *) + val extra_files_dir: t -> switch -> dirname + + (** Extra file with the given hash from the temporary switch-import cache: + {i $meta/extra-files-cache/HASH} *) + val extra_file: t -> switch -> OpamHash.t -> filename + (** Mirror of the sources for a given pinned package: {i $meta/sources/$name/} (without version) *) val pinned_package: t -> switch -> name -> dirname @@ -167,6 +181,10 @@ {i $meta/packages/} *) val installed_opams: t -> switch -> dirname + (** Cache file for the mirrored installed package metadata + {i $meta/packages/cache} *) + val installed_opams_cache: t -> switch -> filename + (** The mirror of the package definition for the given installed package {i $meta/packages/$name.$version/} *) val installed_package_dir: t -> switch -> package -> dirname @@ -229,6 +247,37 @@ val sbin: t -> switch -> dirname end + (** Fuctorised version of Default, for replicating + a switch's layout in non-switch contexts *) + module DefaultF : functor (L:LAYOUT) -> sig + val lib: t -> L.ctx -> name -> dirname + + val lib_dir: t -> L.ctx -> dirname + + val stublibs: t -> L.ctx -> dirname + + val toplevel: t -> L.ctx -> dirname + + val doc: t -> L.ctx -> name -> dirname + + val doc_dir: t -> L.ctx -> dirname + + val share_dir: t -> L.ctx -> dirname + + val share: t -> L.ctx -> name -> dirname + + val etc_dir: t -> L.ctx -> dirname + + val etc: t -> L.ctx -> name -> dirname + + val man_dir: ?num:string -> t -> L.ctx -> dirname + + val bin: t -> L.ctx -> dirname + + val sbin: t -> L.ctx -> dirname + end + + (** Actual config handling the global-config.config indirections *) (** Package-independent dirs *) diff -Nru opam-2.0.10/src/format/opamPp.ml opam-2.1.2/src/format/opamPp.ml --- opam-2.0.10/src/format/opamPp.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamPp.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,7 +9,7 @@ (* *) (**************************************************************************) -open OpamTypes +open OpamParserTypes.FullPos open OpamTypesBase open OpamStd.Op @@ -45,11 +45,11 @@ let rec string_of_bad_format ?file e = match e, file with - | Bad_version (None, msg), Some f - | Bad_format (None, msg), Some f - | Bad_version (Some (f, -1, -1), msg), _ - | Bad_format (Some (f, -1, -1), msg), _ -> - Printf.sprintf "In %s:\n%s" f msg + | Bad_version (None, msg), Some filename + | Bad_format (None, msg), Some filename + | Bad_version (Some {filename; start = -1, -1 ; stop = -1,-1 }, msg), _ + | Bad_format (Some {filename; start = -1, -1 ; stop = -1,-1 }, msg), _ -> + Printf.sprintf "In %s:\n%s" filename msg | Bad_version (Some pos, msg), _ | Bad_format (Some pos, msg), _ -> Printf.sprintf "At %s:\n%s" (string_of_pos pos) msg diff -Nru opam-2.0.10/src/format/opamPp.mli opam-2.1.2/src/format/opamPp.mli --- opam-2.0.10/src/format/opamPp.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamPp.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -11,7 +11,7 @@ (** Generic bidirectional transformation toolbox for parsing/printing *) -open OpamTypes +open OpamParserTypes.FullPos (** {2 Parsing positions and error reporting helpers} *) diff -Nru opam-2.0.10/src/format/opamRepositoryName.ml opam-2.1.2/src/format/opamRepositoryName.ml --- opam-2.0.10/src/format/opamRepositoryName.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamRepositoryName.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/format/opamRepositoryName.mli opam-2.1.2/src/format/opamRepositoryName.mli --- opam-2.0.10/src/format/opamRepositoryName.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamRepositoryName.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/format/opamSwitch.ml opam-2.1.2/src/format/opamSwitch.ml --- opam-2.0.10/src/format/opamSwitch.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamSwitch.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -19,12 +19,29 @@ let external_dirname = "_opam" +let check s = + if String.compare s "" = 0 && + let re = + Re.(compile @@ + seq [ + bol; + opt @@ seq [ wordc ; char ':'; set "/\\" ]; + rep @@ diff any @@ set "<>!`$():"; + eol + ]) + in + (try ignore @@ Re.exec re s; true with Not_found -> false) then + failwith (Printf.sprintf "Invalid character in switch name %S" s); + s + let of_string s = + check @@ if is_external s then OpamFilename.Dir.(to_string (of_string s)) else s let of_dirname d = let s = OpamFilename.Dir.to_string d in + check @@ try let swdir = Unix.readlink (Filename.concat s external_dirname) in let swdir = diff -Nru opam-2.0.10/src/format/opamSwitch.mli opam-2.1.2/src/format/opamSwitch.mli --- opam-2.0.10/src/format/opamSwitch.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamSwitch.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2017 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/format/opamSysPkg.ml opam-2.1.2/src/format/opamSysPkg.ml --- opam-2.0.10/src/format/opamSysPkg.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/format/opamSysPkg.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,65 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(* basic ops *) + +type t = string + +let of_string s = s +let to_string s = s +let compare = OpamStd.String.compare_case + +let to_json s = + `O [ ("sys_package", `String s) ] +let of_json = function + | `O dict -> + (match List.assoc "sys_package" dict with + | `String s -> Some (of_string s) + | _ -> None + | exception Not_found -> None) + | _ -> None + +module O = struct + type tmp = t + type t = tmp + let compare = compare + let to_string = to_string + let to_json = to_json + let of_json = of_json +end + +module Set = OpamStd.Set.Make(O) + +module Map = OpamStd.Map.Make(O) + +let raw_set set = + OpamStd.String.Set.fold (fun spkg set-> Set.add (of_string spkg) set) + set Set.empty + +type status = + { + s_available : Set.t; + (** Package available but not installed *) + + s_not_found : Set.t; + (** Package unavailable on this system *) + } + + +let status_empty = + { + s_available = Set.empty; + s_not_found = Set.empty; + } + +let string_of_status sp = + Printf.sprintf "available: %s; not_found: %s" + (Set.to_string sp.s_available) + (Set.to_string sp.s_not_found) diff -Nru opam-2.0.10/src/format/opamSysPkg.mli opam-2.1.2/src/format/opamSysPkg.mli --- opam-2.0.10/src/format/opamSysPkg.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/format/opamSysPkg.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,29 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** System package *) +type t +include OpamStd.ABSTRACT with type t := t + +val raw_set: OpamStd.String.Set.t -> Set.t + +(** System packages status *) +type status = + { + s_available : Set.t; + (** Package available but not installed *) + + s_not_found : Set.t; + (** Package unavailable on this system *) + } + +val status_empty: status + +val string_of_status: status -> string diff -Nru opam-2.0.10/src/format/opamTypesBase.ml opam-2.1.2/src/format/opamTypesBase.ml --- opam-2.0.10/src/format/opamTypesBase.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamTypesBase.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,6 +9,7 @@ (* *) (**************************************************************************) +open OpamParserTypes.FullPos open OpamTypes include OpamCompat @@ -49,22 +50,42 @@ | SH_bash -> "bash" let file_null = "" -let pos_file filename = OpamFilename.to_string filename, -1, -1 -let pos_null = file_null, -1, -1 +let pos_file filename = + { filename = OpamFilename.to_string filename; + start = -1, -1; + stop = -1, -1; + } +let pos_null = + { filename = file_null; + start = -1, -1; + stop = -1, -1; + } +let nullify_pos pelem = {pelem; pos = pos_null} + +(* XXX update *) +let pos_best pos1 pos2 = + match pos1, pos2 with + | { filename = ""; _ }, _ -> pos2 + | _, { filename = ""; _ } -> pos1 + | { start = (-1,_) ; _ }, _ -> pos2 + | _, { start = (-1,_) ; _ } -> pos1 + | _, _ -> pos1 -let pos_best (f1,_li1,col1 as pos1) (f2,_li2,_col2 as pos2) = +(* if f1 = file_null then pos2 else if f2 = file_null then pos1 else if col1 = -1 then pos2 else pos1 +*) -let string_of_pos (file,line,col) = - file ^ - if line >= 0 then - ":" ^ string_of_int line ^ - if col >= 0 then ":" ^ string_of_int col - else "" - else "" +let string_of_pos pos = + let check x = if x >= 0 then string_of_int x else "-" in + Printf.sprintf "%s:%s:%s-%s:%s:" + pos.filename + (check (fst pos.start)) + (check (snd pos.start)) + (check (fst pos.stop)) + (check (snd pos.stop)) let string_of_user_action = function | Query -> "query" @@ -136,6 +157,7 @@ | Pkgflag_Plugin -> "plugin" | Pkgflag_Compiler -> "compiler" | Pkgflag_Conf -> "conf" + | Pkgflag_AvoidVersion -> "avoid-version" | Pkgflag_Unknown s -> s let pkg_flag_of_string = function @@ -144,14 +166,15 @@ | "plugin" -> Pkgflag_Plugin | "compiler" -> Pkgflag_Compiler | "conf" -> Pkgflag_Conf + | "avoid-version" -> Pkgflag_AvoidVersion | s -> Pkgflag_Unknown s let action_contents = function - | `Remove p | `Install p | `Reinstall p | `Build p -> p + | `Remove p | `Install p | `Reinstall p | `Build p | `Fetch p -> p | `Change (_,_,p) -> p let full_action_contents = function - | `Remove p | `Install p | `Reinstall p | `Build p -> [p] + | `Remove p | `Install p | `Reinstall p | `Build p | `Fetch p -> [p] | `Change (_,p1,p2) -> [p1; p2] let map_atomic_action f = function @@ -166,6 +189,7 @@ let map_concrete_action f = function | #atomic_action as a -> map_atomic_action f a | `Build p -> `Build (f p) + | `Fetch p -> `Fetch (f p) let map_action f = function | #highlevel_action as a -> map_highlevel_action f a @@ -176,7 +200,7 @@ | a::b::c::_::_::_ -> Printf.sprintf "%s, %s, %s, etc." a b c | l -> String.concat ", " l in function - | Upstream_changes -> "upstream changes" + | Upstream_changes -> "upstream or system changes" | Use pkgs -> Printf.sprintf "uses %s" (list_to_string pkgs) | Required_by pkgs -> Printf.sprintf "required by %s" (list_to_string pkgs) diff -Nru opam-2.0.10/src/format/opamTypesBase.mli opam-2.1.2/src/format/opamTypesBase.mli --- opam-2.0.10/src/format/opamTypesBase.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamTypesBase.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -13,6 +13,8 @@ (** This module contains basic utility functions and stringifiers for the basic OPAM types present in OpamTypes.ml *) + +open OpamParserTypes.FullPos open OpamTypes include module type of struct include OpamCompat end @@ -41,6 +43,7 @@ (** The empty file position *) val pos_null: pos +val nullify_pos : 'a -> 'a with_pos (** [pos_best pos1 pos2] returns the most detailed position between [pos1] and [pos2] (defaulting to [pos1]) *) diff -Nru opam-2.0.10/src/format/opamTypes.mli opam-2.1.2/src/format/opamTypes.mli --- opam-2.0.10/src/format/opamTypes.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamTypes.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -23,10 +23,6 @@ | Left of 'a | Right of 'b -(** {2 Untyped generic file format} *) - -include module type of struct include OpamParserTypes end - (** {2 Filenames} *) (** Basenames *) @@ -119,6 +115,8 @@ | Pkgflag_Compiler (** Package may be used for 'opam switch' *) | Pkgflag_Conf (** Virtual package: no source, no install or remove instructions, .install, but likely has depexts *) + | Pkgflag_AvoidVersion (** This version of the package will only be installed if + strictly required *) | Pkgflag_Unknown of string (** Used for error reporting, otherwise ignored *) (** At some point we want to abstract so that the same functions can be used @@ -174,13 +172,14 @@ (** Repositories *) type repository = { repo_name : repository_name; - repo_root : dirname; (** The root of opam's local mirror for this repo *) repo_url : url; repo_trust : trust_anchors option; } (** {2 Variable-based filters} *) +type relop = OpamParserTypes.FullPos.relop_kind + type filter = | FBool of bool | FString of string @@ -238,6 +237,7 @@ type 'a concrete_action = [ | 'a atomic_action | `Build of 'a + | `Fetch of 'a ] type 'a action = [ @@ -256,14 +256,17 @@ | Unknown (** Solver result *) -type solver_result = +type actions_result = { + actions_successes : package action list; + actions_errors : (package action * exn) list; + actions_aborted : package action list; +} + +type solution_result = | Nothing_to_do | OK of package action list (** List of successful actions *) | Aborted - | No_solution - | Partial_error of package action list * package action list * package action list - (** List of successful actions, list of actions with errors, - list of remaining undone actions *) + | Partial_error of actions_result (** Solver result *) type ('a, 'b) result = @@ -297,6 +300,7 @@ u_installed_roots: package_set; u_pinned : package_set; u_base : package_set; + u_invariant: formula; u_reinstall: package_set; u_attrs : (string * package_set) list; } @@ -389,7 +393,7 @@ type env = (string * string * string option) list (** Environment updates *) -type env_update = string * env_update_op * string * string option +type env_update = string * OpamParserTypes.FullPos.env_update_op_kind * string * string option (** var, update_op, value, comment *) (** Tags *) @@ -402,3 +406,7 @@ (** {2 JSON} *) type json = OpamJson.t + +type sys_package = OpamSysPkg.t + +type sys_pkg_status = OpamSysPkg.status diff -Nru opam-2.0.10/src/format/opamVariable.ml opam-2.1.2/src/format/opamVariable.ml --- opam-2.0.10/src/format/opamVariable.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamVariable.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -104,12 +104,17 @@ let to_json x = `String (to_string x) + let of_json = function + | `String s -> (try Some (of_string s) with _ -> None) + | _ -> None + module O = struct type tmp = t type t = tmp let compare = compare let to_string = to_string let to_json = to_json + let of_json = of_json end module Set = OpamStd.Set.Make(O) diff -Nru opam-2.0.10/src/format/opamVariable.mli opam-2.1.2/src/format/opamVariable.mli --- opam-2.0.10/src/format/opamVariable.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/format/opamVariable.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2017 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/manifest/default-manifest.xmlf opam-2.1.2/src/manifest/default-manifest.xmlf --- opam-2.0.10/src/manifest/default-manifest.xmlf 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/default-manifest.xmlf 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + diff -Nru opam-2.0.10/src/manifest/dune-manifest opam-2.1.2/src/manifest/dune-manifest --- opam-2.0.10/src/manifest/dune-manifest 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/dune-manifest 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,36 @@ +(library + (name opam_manifest) + (public_name opam-client.manifest) + (c_names dummy) + (c_library_flags ("src/manifest/opam-manifest.o")) + (synopsis "mingw-w64 runtime manifest")) + +(rule + (targets dummy.c) + (deps opam-manifest.o) + (action (with-stdout-to %{targets} (echo "")))) + +(rule + (targets opam-manifest.o) + (deps (:rcfile opam.rc)) + (action (system "%{read-lines:tool-arch}-w64-mingw32-windres %{rcfile} %{targets}"))) + +(rule + (with-stdout-to tool-arch (run ocaml %{dep:../../shell/context_flags.ml} mingw-arch))) + +(rule + (targets opam.exe.manifest) + (deps opam-mingw.xmlf opam-mingw64.xmlf) + (action (with-stdout-to %{targets} + (progn (echo "\n") + (echo "\n") + (system "cat opam-%{ocaml-config:system}.xmlf 2> %{null} || type opam-%{ocaml-config:system}.xmlf") + (cat default-manifest.xmlf) + (echo ""))))) + +(rule + (targets opam.rc) + (deps (:manifest opam.exe.manifest)) + (action (with-stdout-to opam.rc (echo "#include \nCREATEPROCESS_MANIFEST_RESOURCE_ID RT_MANIFEST %{manifest}")))) + +(include install.inc) diff -Nru opam-2.0.10/src/manifest/install.amd64 opam-2.1.2/src/manifest/install.amd64 --- opam-2.0.10/src/manifest/install.amd64 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/install.amd64 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,12 @@ +(rule + (targets libstdc++-6.dll libwinpthread-1.dll libgcc_s_seh-1.dll) + (deps (:link ../../shell/link_runtime.ml)) + (action (run ocaml %{link} %{targets}))) + +(install + (section bin) + (files (Opam.Runtime.amd64.manifest as Opam.Runtime.amd64\Opam.Runtime.amd64.manifest) + (libstdc++-6.dll as Opam.Runtime.amd64\libstdc++-6.dll) + (libwinpthread-1.dll as Opam.Runtime.amd64\libwinpthread-1.dll) + (libgcc_s_seh-1.dll as Opam.Runtime.amd64\libgcc_s_seh-1.dll)) + (package opam)) diff -Nru opam-2.0.10/src/manifest/install.i386 opam-2.1.2/src/manifest/install.i386 --- opam-2.0.10/src/manifest/install.i386 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/install.i386 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,12 @@ +(rule + (targets libstdc++-6.dll libwinpthread-1.dll libgcc_s_sjlj-1.dll) + (deps (:link ../../shell/link_runtime.ml)) + (action (run ocaml %{link} %{targets}))) + +(install + (section bin) + (files (Opam.Runtime.x86.manifest as Opam.Runtime.x86\Opam.Runtime.x86.manifest) + (libstdc++-6.dll as Opam.Runtime.x86\libstdc++-6.dll) + (libwinpthread-1.dll as Opam.Runtime.x86\libwinpthread-1.dll) + (libgcc_s_sjlj-1.dll as Opam.Runtime.x86\libgcc_s_sjlj-1.dll)) + (package opam)) diff -Nru opam-2.0.10/src/manifest/opam-mingw64.xmlf opam-2.1.2/src/manifest/opam-mingw64.xmlf --- opam-2.0.10/src/manifest/opam-mingw64.xmlf 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/opam-mingw64.xmlf 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,7 @@ + + OCaml Package Manager + + + + + diff -Nru opam-2.0.10/src/manifest/opam-mingw.xmlf opam-2.1.2/src/manifest/opam-mingw.xmlf --- opam-2.0.10/src/manifest/opam-mingw.xmlf 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/opam-mingw.xmlf 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,7 @@ + + OCaml Package Manager + + + + + diff -Nru opam-2.0.10/src/manifest/Opam.Runtime.amd64.manifest opam-2.1.2/src/manifest/Opam.Runtime.amd64.manifest --- opam-2.0.10/src/manifest/Opam.Runtime.amd64.manifest 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/Opam.Runtime.amd64.manifest 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + + + diff -Nru opam-2.0.10/src/manifest/Opam.Runtime.x86.manifest opam-2.1.2/src/manifest/Opam.Runtime.x86.manifest --- opam-2.0.10/src/manifest/Opam.Runtime.x86.manifest 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/manifest/Opam.Runtime.x86.manifest 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,7 @@ + + + + + + + diff -Nru opam-2.0.10/src/ocaml-flags-configure.sexp.in opam-2.1.2/src/ocaml-flags-configure.sexp.in --- opam-2.0.10/src/ocaml-flags-configure.sexp.in 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/ocaml-flags-configure.sexp.in 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +(@CONF_OCAMLFLAGS@) diff -Nru opam-2.0.10/src/ocaml-flags-standard.sexp opam-2.1.2/src/ocaml-flags-standard.sexp --- opam-2.0.10/src/ocaml-flags-standard.sexp 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/ocaml-flags-standard.sexp 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +(-w +a-4-40-42-44-48 -safe-string) diff -Nru opam-2.0.10/src/ocaml-flags-standard.sexp.in opam-2.1.2/src/ocaml-flags-standard.sexp.in --- opam-2.0.10/src/ocaml-flags-standard.sexp.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/ocaml-flags-standard.sexp.in 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -(-w +a-4-40-42-44-48 -safe-string @CONF_OCAMLFLAGS@) diff -Nru opam-2.0.10/src/repository/dune opam-2.1.2/src/repository/dune --- opam-2.0.10/src/repository/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/dune 2021-12-07 16:09:27.000000000 +0000 @@ -5,5 +5,6 @@ (libraries opam-format) (flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp))) (wrapped false)) diff -Nru opam-2.0.10/src/repository/opamDarcs.ml opam-2.1.2/src/repository/opamDarcs.ml --- opam-2.0.10/src/repository/opamDarcs.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamDarcs.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -48,15 +48,20 @@ (* Marks the current state, in the form of a reversing patch on top of the fetched state *) - let fetch ?cache_dir:_ repo_root repo_url = + let fetch ?cache_dir:_ ?subpath:_ repo_root repo_url = (* Just do a fresh pull into a temp directory, and replace _darcs/ There is no easy way to diff or make sure darcs forgets about local patches otherwise. *) + let repo_str = + match OpamUrl.local_dir repo_url with + | Some path -> OpamFilename.Dir.to_string path + | None -> OpamUrl.base_url repo_url + in OpamFilename.with_tmp_dir_job @@ fun d -> let repodir = d / "repo" in darcs repo_root (with_tag repo_url - [ "get"; OpamUrl.base_url repo_url; + [ "get"; repo_str; "--repodir"; OpamFilename.Dir.to_string repodir; "--quiet"; "--lazy" ]) (* --no-working-dir would be fine, except it is stored in _darcs/format *) @@ -152,10 +157,48 @@ let current_branch _dir = Done None (* No branches in Darcs *) - let is_dirty dir = + let is_dirty ?subpath:_ dir = darcs dir [ "whatsnew"; "--quiet"; "--summary" ] @@> fun r -> Done (OpamProcess.check_success_and_cleanup r) + let modified_files repo_root = + darcs repo_root [ "whatsnew"; "--summary" ] @@> fun r -> + OpamSystem.raise_on_process_error r; + let files = + OpamStd.List.filter_map (fun line -> + match OpamStd.String.split line ' ' with + | ("A" | "M")::file::[] + | _::"->"::file::[] -> Some file + | _ -> None) r.OpamProcess.r_stdout + in + Done (List.sort_uniq compare files) + + let get_remote_url ?hash:_ repo_root = + darcs repo_root [ "show"; "repo" ] + @@> function + | { OpamProcess.r_code = 0; _ } as r -> + let res = + (let valid c e = + match OpamStd.String.cut_at (OpamStd.String.strip e) ':' with + | Some (p,rhs) when p = c -> Some rhs + | _ -> None + in + match OpamStd.List.filter_map (valid "Cache") r.r_stdout with + | [line] -> + (let repo = + OpamStd.List.filter_map (valid "repo") + (OpamStd.String.split line ',') + in + match repo with + | [repo] -> Some repo + | _ -> None) + | _ -> None) + in + Done (OpamStd.Option.map (fun u -> + OpamUrl.parse ~backend:`darcs u) res) + | { OpamProcess.r_code = 1; _ } -> Done None + | r -> OpamSystem.process_error r + end module B = OpamVCS.Make(VCS) diff -Nru opam-2.0.10/src/repository/opamDarcs.mli opam-2.1.2/src/repository/opamDarcs.mli --- opam-2.0.10/src/repository/opamDarcs.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamDarcs.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/repository/opamDownload.ml opam-2.1.2/src/repository/opamDownload.ml --- opam-2.0.10/src/repository/opamDownload.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamDownload.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -30,6 +30,7 @@ CString "--user-agent", None; user_agent, None; CString "-L", None; CString "-o", None; CIdent "out", None; + CString "--", None; (* End list of options *) CIdent "url", None; ] @@ -37,23 +38,39 @@ CString "--content-disposition", None; CString "-t", None; CIdent "retry", None; CString "-O", None; CIdent "out", None; + CString "-U", None; user_agent, None; + CString "--", None; (* End list of options *) + CIdent "url", None; +] + +let fetch_args = [ + CString "-o", None; CIdent "out", None; + CString "--user-agent", None; user_agent, None; + CString "--", None; (* End list of options *) CIdent "url", None; +] + +let ftp_args = [ + CString "-o", None; CIdent "out", None; CString "-U", None; user_agent, None; + CString "--", None; (* End list of options *) + CIdent "url", None; ] -let download_args ~url ~out ~retry ?checksum ~compress = +let download_args ~url ~out ~retry ?checksum ~compress () = let cmd, _ = Lazy.force OpamRepositoryConfig.(!r.download_tool) in let cmd = match cmd with | [(CIdent "wget"), _] -> cmd @ wget_args + | [(CIdent "fetch"), _] -> cmd @ fetch_args + | [(CIdent "ftp"), _] -> cmd @ ftp_args | [_] -> cmd @ curl_args (* Assume curl if the command is a single arg *) | _ -> cmd in OpamFilter.single_command (fun v -> if not (OpamVariable.Full.is_global v) then None else match OpamVariable.to_string (OpamVariable.Full.variable v) with - | "curl" -> Some (S "curl") - | "wget" -> Some (S "wget") + | ("curl" | "wget" | "fetch" | "ftp") as dl_tool-> Some (S dl_tool) | "url" -> Some (S (OpamUrl.to_string url)) | "out" -> Some (S out) | "retry" -> Some (S (string_of_int retry)) @@ -99,7 +116,7 @@ code (OpamUrl.to_string url)) else Done () -let download_command ~compress ?checksum ~url ~dst = +let download_command ~compress ?checksum ~url ~dst () = let cmd, args = match download_args @@ -108,6 +125,7 @@ ~retry:OpamRepositoryConfig.(!r.retries) ?checksum ~compress + () with | cmd::args -> cmd, args | [] -> @@ -118,7 +136,7 @@ let really_download ?(quiet=false) ~overwrite ?(compress=false) ?checksum ?(validate=true) - ~url ~dst = + ~url ~dst () = assert (url.OpamUrl.backend = `http); let tmp_dst = dst ^ ".part" in if Sys.file_exists tmp_dst then OpamSystem.remove tmp_dst; @@ -134,7 +152,7 @@ log "Could not download file at %s." (OpamUrl.to_string url); raise e) @@ fun () -> - download_command ~compress ?checksum ~url ~dst:tmp_dst + download_command ~compress ?checksum ~url ~dst:tmp_dst () @@+ fun () -> if not (Sys.file_exists tmp_dst) then fail (Some "Downloaded file not found", @@ -167,6 +185,7 @@ really_download ?quiet ~overwrite ?compress ?checksum ?validate ~url ~dst:(OpamFilename.to_string dst) + () let download ?quiet ?validate ~overwrite ?compress ?checksum url dstdir = let dst = diff -Nru opam-2.0.10/src/repository/opamDownload.mli opam-2.1.2/src/repository/opamDownload.mli --- opam-2.0.10/src/repository/opamDownload.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamDownload.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/repository/opamGit.ml opam-2.1.2/src/repository/opamGit.ml --- opam-2.0.10/src/repository/opamGit.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamGit.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -22,10 +22,15 @@ OpamFilename.exists_dir (repo_root / ".git") || OpamFilename.exists (repo_root // ".git") + let cygpath = OpamSystem.get_cygpath_function ~command:"git" + let git repo_root = let dir = OpamFilename.Dir.to_string repo_root in - fun ?verbose ?env ?stdout args -> - OpamSystem.make_command ~dir ?verbose ?env ?stdout "git" args + (* If the ?env arg is restored here, then the caching for the Cygwin-ness + of git will need to change, as altering PATH could select a different + Git *) + fun ?verbose ?stdout args -> + OpamSystem.make_command ~dir ?verbose ?stdout "git" args let init repo_root repo_url = OpamFilename.mkdir repo_root; @@ -35,6 +40,16 @@ git repo_root [ "config" ; "--local" ; "fetch.prune"; "false"]; (* We reset diff.noprefix to ensure we get a `-p1` patch and avoid . *) git repo_root [ "config" ; "--local" ; "diff.noprefix"; "false"]; + (* Disable automatic line-ending conversion and switch core.eol to Unix. + THIS DOES NOT MEAN ALL FILES GET LF-ONLY LINE-ENDINGS! + This combination of settings means that files will be checked out + exactly as they appear in the repository, so if files are checked in + with CRLF line-endings (either by not having .gitattributes with + core.autocrlf = false, or having an explicit eol=crlf in + .gitattributes), then they will still be checked out with CRLF endings. + *) + git repo_root [ "config" ; "--local" ; "core.autocrlf"; "false"]; + git repo_root [ "config" ; "--local" ; "core.eol"; "lf"]; (* Document the remote for user-friendliness (we don't use it) *) git repo_root [ "remote"; "add"; "origin"; OpamUrl.base_url repo_url ]; ] @@+ function @@ -46,7 +61,15 @@ | Some h -> "refs/remotes/opam-ref-"^h | None -> "refs/remotes/opam-ref" - let fetch ?cache_dir repo_root repo_url = + let fetch ?cache_dir ?subpath repo_root repo_url = + (match subpath with + | Some sp -> + git repo_root [ "config"; "--local"; "core.sparseCheckout"; "true" ] + @@> fun r -> OpamSystem.raise_on_process_error r; + OpamFilename.write (repo_root / ".git" / "info" // "sparse-checkout") sp; + Done() + | None -> Done()) + @@+ fun _ -> (match cache_dir with | Some c when OpamUrl.local_dir repo_url = None -> let dir = c / "git" in @@ -58,6 +81,7 @@ else Done (Some dir) | _ -> Done None) @@+ fun global_cache -> + let repo_url = OpamUrl.map_file_url (Lazy.force cygpath) repo_url in let origin = OpamUrl.base_url repo_url in let branch = OpamStd.Option.default "HEAD" repo_url.OpamUrl.hash in let opam_ref = remote_ref repo_url in @@ -111,7 +135,17 @@ if OpamProcess.check_success_and_cleanup r then failwith "Commit found, but unreachable: enable uploadpack.allowReachableSHA1InWant on server" else failwith "Commit not found on repository")) - else OpamSystem.process_error r + else + let error = r in + git repo_root ["ls-files"] @@> function + | { OpamProcess.r_code = 0; OpamProcess.r_stdout = []; _ } -> + git repo_root ["show"] @@> fun r -> + if OpamProcess.is_failure r then + failwith "Git repository seems just initialized, \ + try again after your first commit" + else + OpamSystem.process_error error + | _ -> OpamSystem.process_error error let revision repo_root = git repo_root ~verbose:false [ "rev-parse"; "HEAD" ] @@> @@ -135,6 +169,11 @@ if OpamProcess.is_failure r then OpamSystem.internal_error "Git error: %s not found." rref else + git repo_root [ "clean"; "-fdx" ] + @@> fun r -> + if OpamProcess.is_failure r then + OpamSystem.internal_error "Git error: %s not found." rref + else if OpamFilename.exists (repo_root // ".gitmodules") then git repo_root [ "submodule"; "update"; "--init"; "--recursive" ] @@> fun r -> @@ -159,7 +198,7 @@ unregistered directories. *) OpamSystem.raise_on_process_error r; (* We also reset diff.noprefix here to handle already existing repo. *) - git repo_root ~stdout:patch_file [ "-c" ; "diff.noprefix=false" ; "diff" ; "--no-ext-diff" ; "-R" ; "-p" ; rref; "--" ] + git repo_root ~stdout:patch_file [ "-c" ; "diff.noprefix=false" ; "diff" ; "--text" ; "--no-ext-diff" ; "-R" ; "-p" ; rref; "--" ] @@> fun r -> if not (OpamProcess.check_success_and_cleanup r) then (finalise (); @@ -193,14 +232,112 @@ | _ -> Done (Some "HEAD") - let is_dirty dir = - git dir [ "diff" ; "--no-ext-diff" ; "--quiet" ] + let is_dirty ?subpath dir = + let subpath = + match subpath with + | None -> [] + | Some dir -> ["--" ; dir] in + git dir ([ "diff"; "--no-ext-diff"; "--quiet" ; "HEAD" ] @ subpath) @@> function - | { OpamProcess.r_code = 0; _ } -> Done false + | { OpamProcess.r_code = 0; _ } -> + (git dir ["ls-files"; "--others"; "--exclude-standard"] + @@> function + | { OpamProcess.r_code = 0; OpamProcess.r_stdout = []; _ } -> + Done false + | { OpamProcess.r_code = 0; _ } + | { OpamProcess.r_code = 1; _ } as r -> + OpamProcess.cleanup ~force:true r; Done true + | r -> OpamSystem.process_error r + ) | { OpamProcess.r_code = 1; _ } as r -> OpamProcess.cleanup ~force:true r; Done true | r -> OpamSystem.process_error r + let modified_files repo_root = + git repo_root ~verbose:false [ "status" ; "--short" ] @@> fun r -> + OpamSystem.raise_on_process_error r; + let files = + OpamStd.List.filter_map (fun line -> + match OpamStd.String.split line ' ' with + | ("A" | "M" | "AM")::file::[] + | ("R"|"RM"|"C"|"CM")::_::"->"::file::[] -> Some file + | _ -> None) r.OpamProcess.r_stdout + in + Done files + + let origin = "origin" + + (** check if a hash or branch is present in remote origin and returns *) + let check_remote repo_root hash_or_b = + let is_hex str = + OpamStd.String.fold_left (fun hex ch -> + hex && match ch with + | '0'..'9' | 'A'..'F' | 'a'..'f' -> true + | _ -> false + ) true str + in + (* get the hash of the branch *) + let hash = + git repo_root ["branch"] @@> fun r -> + if OpamProcess.is_success r then + let is_branch = + List.exists (OpamStd.String.contains ~sub:hash_or_b) r.r_stdout + in + if is_branch then + git repo_root [ "rev-list"; hash_or_b; "-1" ] @@> fun r -> + if OpamProcess.is_success r then + (match List.filter is_hex r.r_stdout with + | [hash] -> + Done (Some hash) + | _ -> Done None) + else Done None + else + if is_hex hash_or_b then + Done (Some hash_or_b) + else Done None + else + Done None + in + hash @@+ function + | Some hash -> + (* check if hash / branch is present in remote *) + (git repo_root ["branch"; "-r"; "--contains"; hash] + @@> function + | { OpamProcess.r_code = 0; _ } as r -> + if r.r_stdout <> [] && + (List.exists (OpamStd.String.contains ~sub:origin) r.r_stdout) then + Done (Some hash_or_b) + else + Done None + | { OpamProcess.r_code = 1; _ } -> + Done None + | r -> OpamSystem.process_error r) + | None -> Done None + + let get_remote_url ?hash repo_root = + git repo_root ["remote"; "get-url"; origin] + @@> function + | { OpamProcess.r_code = 0; OpamProcess.r_stdout = [url]; _ } -> + (let u = OpamUrl.parse ~backend:`git url in + if OpamUrl.local_dir u <> None then Done None else + let hash_in_remote = + match hash with + | None -> + (current_branch repo_root @@+ function + | None | Some "HEAD" -> Done None + | Some hash -> check_remote repo_root hash) + | Some hash -> check_remote repo_root hash + in + hash_in_remote @@+ function + | Some _ as hash -> + Done (Some { u with OpamUrl.hash = hash }) + | None -> + Done (Some { u with OpamUrl.hash = None }) + ) + | { OpamProcess.r_code = 0; _ } + | { OpamProcess.r_code = 1; _ } -> Done None + | r -> OpamSystem.process_error r + end module B = OpamVCS.Make(VCS) diff -Nru opam-2.0.10/src/repository/opamGit.mli opam-2.1.2/src/repository/opamGit.mli --- opam-2.0.10/src/repository/opamGit.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamGit.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/repository/opamHg.ml opam-2.1.2/src/repository/opamHg.ml --- opam-2.0.10/src/repository/opamHg.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamHg.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -37,7 +37,7 @@ | None -> mark_prefix | Some fragment -> mark_prefix ^ "-" ^ fragment - let fetch ?cache_dir:_ repo_root repo_url = + let fetch ?cache_dir:_ ?subpath:_ repo_root repo_url = let src = OpamUrl.base_url repo_url in let rev = OpamStd.Option.default "default" repo_url.OpamUrl.hash in let mark = mark_from_url repo_url in @@ -69,7 +69,7 @@ let finalise () = OpamSystem.remove_file patch_file in OpamProcess.Job.catch (fun e -> finalise (); raise e) @@ fun () -> let mark = mark_from_url repo_url in - hg repo_root ~stdout:patch_file [ "diff"; "--subrepos"; "--reverse"; + hg repo_root ~stdout:patch_file [ "diff"; "--text"; "--subrepos"; "--reverse"; "--rev"; mark ] @@> fun r -> if OpamProcess.is_failure r then (finalise (); @@ -111,11 +111,51 @@ | branch::_ when branch <> "default" -> Done (Some branch) | _ -> Done None - let is_dirty repo_root = + let is_dirty ?subpath:_ repo_root = hg repo_root [ "status"; "--subrepos" ] @@> fun r -> OpamSystem.raise_on_process_error r; Done (r.OpamProcess.r_stdout = []) + let modified_files repo_root = + hg repo_root [ "status"; "--subrepos" ] @@> fun r -> + OpamSystem.raise_on_process_error r; + let files = + OpamStd.List.filter_map (fun line -> + match OpamStd.String.split line ' ' with + | ("A" | "M")::file::[] -> Some file + | _ -> None) r.OpamProcess.r_stdout + in + Done files + + let get_remote_url ?hash repo_root = + hg repo_root [ "paths"; "default" ] + @@> function + | { OpamProcess.r_code = 0; _ } as r -> + (match r.r_stdout with + | [url] -> + (let url = OpamUrl.parse ~backend:`hg url in + if OpamUrl.local_dir url <> None then Done None else + let check_remote hash = + hg repo_root [ "id"; "-r"; hash; "default" ] + @@> fun r -> + if OpamProcess.is_success r then + Done (Some { url with hash = Some hash }) + (* default branch of hg is default *) + else Done (Some { url with hash = None}) + in + match hash with + | None -> + (hg repo_root ["branch"] @@> function + | { OpamProcess.r_code = 0; OpamProcess.r_stdout = [hash]; _ } -> + check_remote hash + | { OpamProcess.r_code = 0; _ } + | { OpamProcess.r_code = 1; _ } -> Done (Some url) + | r -> OpamSystem.process_error r) + | Some hash -> check_remote hash) + | _ -> Done None) + | { OpamProcess.r_code = 1; _ } -> Done None + | r -> OpamSystem.process_error r + end module B = OpamVCS.Make(VCS) diff -Nru opam-2.0.10/src/repository/opamHg.mli opam-2.1.2/src/repository/opamHg.mli --- opam-2.0.10/src/repository/opamHg.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamHg.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/repository/opamHTTP.ml opam-2.1.2/src/repository/opamHTTP.ml --- opam-2.0.10/src/repository/opamHTTP.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamHTTP.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -68,7 +68,7 @@ let repo_update_complete _ _ = Done () - let pull_url ?cache_dir:_ dirname checksum remote_url = + let pull_url ?cache_dir:_ ?subpath:_ dirname checksum remote_url = log "pull-file into %a: %a" (slog OpamFilename.Dir.to_string) dirname (slog OpamUrl.to_string) remote_url; @@ -89,7 +89,11 @@ let revision _ = Done None - let sync_dirty dir url = pull_url dir None url + let sync_dirty ?subpath:_ dir url = pull_url dir None url + (* do not propagate *) + + let get_remote_url ?hash:_ _ = + Done None end diff -Nru opam-2.0.10/src/repository/opamHTTP.mli opam-2.1.2/src/repository/opamHTTP.mli --- opam-2.0.10/src/repository/opamHTTP.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamHTTP.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/repository/opamLocal.ml opam-2.1.2/src/repository/opamLocal.ml --- opam-2.0.10/src/repository/opamLocal.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamLocal.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -15,7 +15,14 @@ let log fmt = OpamConsole.log "RSYNC" fmt -let rsync_arg = "-rLptgoDrvc" +(* Rsync args recap: + - recurse into directories (r) + - skip based on checksum, not mod-time & size (c) + - preserve permissions (p), times (t), group (g), owner (o), + device & special files (D) + - transform symlink into referent file/dir (L) + *) +let rsync_arg = "-rLptgoDvc" (* if rsync -arv return 4 lines, this means that no files have changed *) let rsync_trim = function @@ -25,6 +32,9 @@ | _ :: _ :: _ :: l -> List.filter ((<>) "./") l | _ -> [] +let convert_path = + OpamSystem.get_cygpath_function ~command:"rsync" + let call_rsync check args = OpamSystem.make_command "rsync" args @@> fun r -> @@ -52,22 +62,20 @@ let overlap src dst = let norm d = Filename.concat d "" in OpamStd.String.starts_with ~prefix:(norm src) (norm dst) && - not (OpamStd.String.starts_with - ~prefix:(norm (Filename.concat src OpamSwitch.external_dirname)) - (norm dst)) || + not (OpamStd.String.contains ~sub:OpamSwitch.external_dirname (norm dst)) || OpamStd.String.starts_with ~prefix:(norm dst) (norm src) in + (* See also [OpamVCS.sync_dirty] *) let exclude_args = - if exclude_vcdirs then [ - "--exclude"; ".git"; - "--exclude"; "_darcs"; - "--exclude"; ".hg"; - "--exclude"; ".#*"; - "--exclude"; OpamSwitch.external_dirname ^ "*"; - ] - else [ + (if not exclude_vcdirs then [] else + [ "--exclude"; ".git"; + "--exclude"; "_darcs"; + "--exclude"; ".hg"; + ]) + @ [ "--exclude"; ".#*"; "--exclude"; OpamSwitch.external_dirname ^ "*"; + "--exclude"; "_build"; ] in if not(remote || Sys.file_exists src) then @@ -79,9 +87,10 @@ Done (Not_available (None, src))) else ( OpamSystem.mkdir dst; + let convert_path = Lazy.force convert_path in call_rsync (fun () -> not (OpamSystem.dir_is_empty dst)) ( rsync_arg :: args @ exclude_args @ - [ "--delete"; "--delete-excluded"; src; dst; ]) + [ "--delete"; "--delete-excluded"; convert_path src; convert_path dst; ]) @@| function | None -> Not_available (None, src) | Some [] -> Up_to_date [] @@ -115,8 +124,9 @@ Done (Up_to_date dst) else (OpamFilename.mkdir (OpamFilename.dirname dst); + let convert_path = Lazy.force convert_path in call_rsync (fun () -> Sys.file_exists dst_s) - ( rsync_arg :: args @ [ src_s; dst_s ]) + ( rsync_arg :: args @ [ convert_path src_s; convert_path dst_s ]) @@| function | None -> Not_available (None, src_s) | Some [] -> Up_to_date dst @@ -181,10 +191,18 @@ let repo_update_complete _ _ = Done () - let pull_url ?cache_dir:_ local_dirname _checksum remote_url = + let pull_url ?cache_dir:_ ?subpath local_dirname _checksum remote_url = + let local_dirname = + OpamStd.Option.map_default (fun x -> OpamFilename.Op.(local_dirname / x)) + local_dirname subpath + in OpamFilename.mkdir local_dirname; let dir = OpamFilename.Dir.to_string local_dirname in let remote_url = + OpamStd.Option.map_default (fun x -> OpamUrl.Op.(remote_url / x)) + remote_url subpath + in + let remote_url = match OpamUrl.local_dir remote_url with | Some _ -> (* ensure that rsync doesn't recreate a subdir: add trailing '/' *) @@ -219,6 +237,9 @@ let revision _ = Done None - let sync_dirty dir url = pull_url dir None url + let sync_dirty ?subpath dir url = pull_url ?subpath dir None url + + let get_remote_url ?hash:_ _ = + Done None end diff -Nru opam-2.0.10/src/repository/opamLocal.mli opam-2.1.2/src/repository/opamLocal.mli --- opam-2.0.10/src/repository/opamLocal.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamLocal.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2016 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/repository/opamRepositoryBackend.ml opam-2.1.2/src/repository/opamRepositoryBackend.ml --- opam-2.0.10/src/repository/opamRepositoryBackend.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepositoryBackend.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -22,31 +22,27 @@ module type S = sig val name: OpamUrl.backend val pull_url: - ?cache_dir:dirname -> dirname -> OpamHash.t option -> url -> + ?cache_dir:dirname -> ?subpath:string -> dirname -> OpamHash.t option -> url -> filename option download OpamProcess.job val fetch_repo_update: repository_name -> ?cache_dir:dirname -> dirname -> url -> update OpamProcess.job val repo_update_complete: dirname -> url -> unit OpamProcess.job val revision: dirname -> version option OpamProcess.job - val sync_dirty: dirname -> url -> filename option download OpamProcess.job + val sync_dirty: + ?subpath:string -> dirname -> url -> filename option download OpamProcess.job + val get_remote_url: + ?hash:string -> dirname -> + url option OpamProcess.job end let compare r1 r2 = compare r1.repo_name r2.repo_name let to_string r = - Printf.sprintf "%s at %s from %s" + Printf.sprintf "%s from %s" (OpamRepositoryName.to_string r.repo_name) - (OpamFilename.Dir.to_string r.repo_root) (OpamUrl.to_string r.repo_url) -let local dirname = { - repo_name = OpamRepositoryName.of_string "local"; - repo_root = dirname; - repo_url = OpamUrl.empty; - repo_trust = None; -} - let to_json r = `O [ ("name", OpamRepositoryName.to_json r.repo_name); ("kind", `String (OpamUrl.string_of_backend r.repo_url.OpamUrl.backend)); diff -Nru opam-2.0.10/src/repository/opamRepositoryBackend.mli opam-2.1.2/src/repository/opamRepositoryBackend.mli --- opam-2.0.10/src/repository/opamRepositoryBackend.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepositoryBackend.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -45,7 +45,7 @@ [checksum] can be used for retrieval but is NOT checked by this function. *) val pull_url: - ?cache_dir:dirname -> dirname -> OpamHash.t option -> url -> + ?cache_dir:dirname -> ?subpath:string -> dirname -> OpamHash.t option -> url -> filename option download OpamProcess.job (** [pull_repo_update] fetches the remote update from [url] to the local @@ -69,10 +69,18 @@ val revision: dirname -> version option OpamProcess.job (** Like [pull_url], except for locally-bound version control backends, where - it should get the latest, uncommitted source. *) + it should get the latest, uncommitted source. First, it performs a + [pull_url], then remove deleted files, and finally copy via rsync + unversioned & modified-uncommitted files. *) val sync_dirty: - dirname -> url -> filename option download OpamProcess.job + ?subpath:string -> dirname -> url -> filename option download OpamProcess.job + (** [get_remote_url ?hash dirname] return the distant url of repo [dirname], \ + if found. When [hash] is specified, it checks that this hash (branch or \ + commit) is present in the distant repository and returns the url with \ + this hash. If the hash is absent it returns the remote url with no hash. *) + val get_remote_url: + ?hash:string -> dirname -> url option OpamProcess.job end (** Pretty-print *) @@ -82,10 +90,6 @@ (** Compare repositories *) val compare: repository -> repository -> int -(** Create a local repository on a given path, without remote (only for external - tools, not to be mistaken for an opam repo with a local url) *) -val local: dirname -> repository - (** [check_digest file expected] check that the [file] digest is the one [expected]. *) val check_digest: filename -> OpamHash.t option -> bool diff -Nru opam-2.0.10/src/repository/opamRepositoryConfig.ml opam-2.1.2/src/repository/opamRepositoryConfig.ml --- opam-2.0.10/src/repository/opamRepositoryConfig.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepositoryConfig.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -10,6 +10,26 @@ open OpamTypes +module E = struct + + type OpamStd.Config.E.t += + | CURL of string option + | FETCH of string option + | NOCHECKSUMS of bool option + | REQUIRECHECKSUMS of bool option + | RETRIES of int option + | VALIDATIONHOOK of string option + + open OpamStd.Config.E + let curl = value (function CURL s -> s | _ -> None) + let fetch = value (function FETCH s -> s | _ -> None) + let nochecksums = value (function NOCHECKSUMS b -> b | _ -> None) + let requirechecksums = value (function REQUIRECHECKSUMS b -> b | _ -> None) + let retries = value (function RETRIES i -> i | _ -> None) + let validationhook = value (function VALIDATIONHOOK s -> s | _ -> None) + +end + type dl_tool_kind = [ `Curl | `Default ] type t = { @@ -28,11 +48,15 @@ let default = { download_tool = lazy ( + let os = OpamStd.Sys.os () in try + let curl = "curl", `Curl in let tools = - if OpamStd.Sys.(os () = Darwin) - then ["wget", `Default; "curl", `Curl] - else ["curl", `Curl; "wget", `Default] + match os with + | Darwin -> ["wget", `Default; curl] + | FreeBSD -> ["fetch", `Default ; curl] + | OpenBSD -> ["ftp", `Default; curl] + | _ -> [curl; "wget", `Default] in let cmd, kind = List.find (fun (c,_) -> OpamSystem.resolve_command c <> None) tools @@ -41,8 +65,12 @@ with Not_found -> OpamConsole.error_and_exit `Configuration_error "Could not find a suitable download command. Please make sure you \ - have either \"curl\" or \"wget\" installed, or specify a custom \ - command through variable OPAMFETCH." + have %s installed, or specify a custom command through variable \ + OPAMFETCH." + (match os with + | FreeBSD -> "fetch" + | OpenBSD -> "ftp" + | _ -> "either \"curl\" or \"wget\"") ); validation_hook = None; retries = 3; @@ -70,10 +98,9 @@ let update ?noop:_ = setk (fun cfg () -> r := cfg) !r let initk k = - let open OpamStd.Config in let open OpamStd.Option.Op in let download_tool = - env_string "FETCH" >>= (fun s -> + E.fetch () >>= (fun s -> let args = OpamStd.String.split s ' ' in match args with | cmd::a -> @@ -91,17 +118,17 @@ None ) >>+ fun () -> - env_string "CURL" >>| (fun s -> + E.curl () >>| (fun s -> lazy ([CString s, None], `Curl)) in let validation_hook = - env_string "VALIDATIONHOOK" >>| fun s -> + E.validationhook () >>| fun s -> match List.map (fun s -> CString s, None) (OpamStd.String.split s ' ') with | [] -> None | l -> Some l in let force_checksums = - match env_bool "REQUIRECHECKSUMS", env_bool "NOCHECKSUMS" with + match E.requirechecksums (), E.nochecksums () with | Some true, _ -> Some (Some true) | _, Some true -> Some (Some false) | None, None -> None @@ -110,7 +137,7 @@ setk (setk (fun c -> r := c; k)) !r ?download_tool ?validation_hook - ?retries:(env_int "RETRIES") + ?retries:(E.retries ()) ?force_checksums let init ?noop:_ = initk (fun () -> ()) diff -Nru opam-2.0.10/src/repository/opamRepositoryConfig.mli opam-2.1.2/src/repository/opamRepositoryConfig.mli --- opam-2.0.10/src/repository/opamRepositoryConfig.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepositoryConfig.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2016 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -11,6 +11,19 @@ (** Configuration options for the repository lib (record, global reference, setter, initialisation) *) +module E : sig + type OpamStd.Config.E.t += + | CURL of string option + | FETCH of string option + | NOCHECKSUMS of bool option + | REQUIRECHECKSUMS of bool option + | RETRIES of int option + | VALIDATIONHOOK of string option + + val curl: unit -> string option + val fetch: unit -> string option +end + (** Toggles parsing of the tool's output to detect errors (curl returns 0 on a 404) *) type dl_tool_kind = [ `Curl | `Default ] diff -Nru opam-2.0.10/src/repository/opamRepository.ml opam-2.1.2/src/repository/opamRepository.ml --- opam-2.0.10/src/repository/opamRepository.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepository.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -32,14 +32,6 @@ let find_backend r = url_backend r.repo_url -(* initialize the current directory *) -let init root name = - log "init local repo mirror at %s" (OpamRepositoryName.to_string name); - (* let module B = (val find_backend repo: OpamRepositoryBackend.S) in *) - let dir = OpamRepositoryPath.create root name in - OpamFilename.cleandir dir; - Done () - let cache_url root_cache_url checksum = List.fold_left OpamUrl.Op.(/) root_cache_url (OpamHash.to_path checksum) @@ -88,9 +80,15 @@ ~quiet:true ~validate:false ~overwrite:true ~checksum url file | `rsync -> - (OpamLocal.rsync_file url file @@| function - | Result _ | Up_to_date _-> () - | Not_available (s,l) -> raise (OpamDownload.Download_fail (s,l))) + begin match OpamUrl.local_file url with + | Some src -> + OpamFilename.copy ~src ~dst:file; + OpamProcess.Job.Op.Done () + | None -> + (OpamLocal.rsync_file url file @@| function + | Result _ | Up_to_date _-> () + | Not_available (s,l) -> raise (OpamDownload.Download_fail (s,l))) + end | #OpamUrl.version_control -> failwith "Version control not allowed as cache URL" in @@ -160,7 +158,7 @@ (* [cache_dir] used to add to cache only *) let pull_from_upstream - label ?(working_dir=false) cache_dir destdir checksums url = + label ?(working_dir=false) ?subpath cache_dir destdir checksums url = let module B = (val url_backend url: OpamRepositoryBackend.S) in let cksum = match checksums with [] -> None | c::_ -> Some c in let text = @@ -168,7 +166,7 @@ (OpamUrl.string_of_backend url.OpamUrl.backend) in OpamProcess.Job.with_text text @@ - (if working_dir then B.sync_dirty destdir url + (if working_dir then B.sync_dirty ?subpath destdir url else let pin_cache_dir = OpamRepositoryPath.pin_cache url in let url, pull = @@ -177,7 +175,7 @@ (log "Pin cache existing for %s : %s\n" (OpamUrl.to_string url) @@ OpamFilename.Dir.to_string pin_cache_dir; let rsync = - OpamUrl.parse ~backend:`rsync + OpamUrl.parse ~backend:`rsync ~from_file:false @@ OpamFilename.Dir.to_string pin_cache_dir in let pull = @@ -186,60 +184,72 @@ in rsync, pull ) + else if OpamUrl.(match url.backend with | `git -> true | _ -> false) + && OpamFilename.exists_dir pin_cache_dir then + (log "Pin cache (git) existing for %s : %s\n" + (OpamUrl.to_string url) @@ OpamFilename.Dir.to_string pin_cache_dir; + let git_cached = + OpamUrl.parse ~backend:`git + @@ OpamFilename.Dir.to_string pin_cache_dir + in + let pull = + let module BR = (val url_backend git_cached: OpamRepositoryBackend.S) in + BR.pull_url + in + git_cached, pull + ) else url, B.pull_url in - pull ?cache_dir destdir cksum url + pull ?cache_dir ?subpath destdir cksum url ) @@| function | (Result (Some file) | Up_to_date (Some file)) as ret -> if OpamRepositoryConfig.(!r.force_checksums) = Some false - || validate_and_add_to_cache label url cache_dir file checksums then - (OpamConsole.msg "[%s] %s from %s\n" - (OpamConsole.colorise `green label) - (match ret with Up_to_date _ -> "no changes" | _ -> "downloaded") - (OpamUrl.to_string url); - ret) + || validate_and_add_to_cache label url cache_dir file checksums + then ret else let m = "Checksum mismatch" in Not_available (Some m, m) - | (Result None | Up_to_date None) as ret -> - if checksums = [] then - (OpamConsole.msg "[%s] %s from %s\n" - (OpamConsole.colorise `green label) - (match ret with Up_to_date _ -> "no changes" | _ -> "synchronised") - (OpamUrl.to_string url); - ret) - else - (OpamConsole.error "%s: file checksum specified, but a directory was \ - retrieved from %s" - label (OpamUrl.to_string url); - OpamFilename.rmdir destdir; - let m = "can't check directory checksum" in - Not_available (Some m, m)) + | (Result None | Up_to_date None) as ret -> ret | Not_available _ as na -> na -let rec pull_from_mirrors label ?working_dir cache_dir destdir checksums = function - | [] -> invalid_arg "pull_from_mirrors: empty mirror list" - | [url] -> - pull_from_upstream label ?working_dir cache_dir destdir checksums url - | url::mirrors -> - pull_from_upstream label ?working_dir cache_dir destdir checksums url - @@+ function - | Not_available (_,s) -> - OpamConsole.warning "%s: download of %s failed (%s), trying mirror" - label (OpamUrl.to_string url) s; - pull_from_mirrors label cache_dir destdir checksums mirrors - | r -> Done r +let pull_from_mirrors label ?working_dir ?subpath cache_dir destdir checksums urls = + let rec aux = function + | [] -> invalid_arg "pull_from_mirrors: empty mirror list" + | [url] -> + pull_from_upstream label ?working_dir ?subpath cache_dir destdir checksums url + @@| fun r -> url, r + | url::mirrors -> + pull_from_upstream label ?working_dir ?subpath cache_dir destdir checksums url + @@+ function + | Not_available (_,s) -> + OpamConsole.warning "%s: download of %s failed (%s), trying mirror" + label (OpamUrl.to_string url) s; + aux mirrors + | r -> Done (url, r) + in + aux urls @@| function + | url, (Result None | Up_to_date None) when checksums <> [] -> + OpamConsole.error "%s: file checksum specified, but a directory was \ + retrieved from %s" + label (OpamUrl.to_string url); + OpamFilename.rmdir destdir; + let m = "can't check directory checksum" in + url, Not_available (Some m, m) + | ret -> ret let pull_tree - label ?cache_dir ?(cache_urls=[]) ?working_dir + label ?cache_dir ?(cache_urls=[]) ?working_dir ?subpath local_dirname checksums remote_urls = - let extract_archive f = + let extract_archive f s = OpamFilename.cleandir local_dirname; OpamFilename.extract_job f local_dirname @@+ function - | None -> Done (Up_to_date ()) + | None -> Done (Up_to_date s) | Some (Failure s) -> - Done (Not_available (Some "Could not extract archive", s)) + Done (Not_available (Some s, "Could not extract archive:\n"^s)) + | Some (OpamSystem.Process_error pe) -> + Done (Not_available (Some (OpamProcess.result_summary pe), + OpamProcess.string_of_result pe)) | Some e -> Done (Not_available (None, Printexc.to_string e)) in (match cache_dir with @@ -253,34 +263,32 @@ Done (Not_available (Some m, m))) @@+ function | Up_to_date (archive, _) -> - OpamConsole.msg "[%s] found in cache\n" - (OpamConsole.colorise `green label); - extract_archive archive + extract_archive archive "cached" | Result (archive, url) -> - OpamConsole.msg "[%s] %s\n" - (OpamConsole.colorise `green label) - (match url.OpamUrl.backend with - | `http -> "downloaded from cache at "^OpamUrl.to_string url - | `rsync -> "found in external cache at "^url.OpamUrl.path - | _ -> "found in external cache "^OpamUrl.to_string url); - extract_archive archive + let msg = match url.OpamUrl.backend with + | `rsync -> url.OpamUrl.path + | _ -> OpamUrl.to_string url + in + extract_archive archive msg | Not_available _ -> if checksums = [] && OpamRepositoryConfig.(!r.force_checksums = Some true) then - OpamConsole.error_and_exit `File_error - "%s: Missing checksum, and `--require-checksums` was set." - label; - pull_from_mirrors label ?working_dir cache_dir local_dirname checksums - remote_urls - @@+ function - | Up_to_date None -> Done (Up_to_date ()) - | Up_to_date (Some archive) | Result (Some archive) -> - OpamFilename.with_tmp_dir_job @@ fun tmpdir -> - let tmp_archive = OpamFilename.(create tmpdir (basename archive)) in - OpamFilename.move ~src:archive ~dst:tmp_archive; - extract_archive tmp_archive - | Result None -> Done (Result ()) - | Not_available _ as na -> Done na + Done ( + Not_available ( + Some ("missing checksum"), + label ^ ": Missing checksum, and `--require-checksums` was set.")) + else + pull_from_mirrors label ?working_dir ?subpath cache_dir local_dirname checksums + remote_urls + @@+ function + | _, Up_to_date None -> Done (Up_to_date "no changes") + | url, (Up_to_date (Some archive) | Result (Some archive)) -> + OpamFilename.with_tmp_dir_job @@ fun tmpdir -> + let tmp_archive = OpamFilename.(create tmpdir (basename archive)) in + OpamFilename.move ~src:archive ~dst:tmp_archive; + extract_archive tmp_archive (OpamUrl.to_string url) + | url, Result None -> Done (Result (OpamUrl.to_string url)) + | _, (Not_available _ as na) -> Done na let revision dirname url = let kind = url.OpamUrl.backend in @@ -314,43 +322,43 @@ | Not_available _ -> if checksums = [] && OpamRepositoryConfig.(!r.force_checksums = Some true) then - OpamConsole.error_and_exit `File_error - "%s: Missing checksum, and `--require-checksums` was set." - label; - OpamFilename.with_tmp_dir_job (fun tmpdir -> - pull_from_mirrors label cache_dir tmpdir checksums remote_urls - @@| function - | Up_to_date _ -> assert false - | Result (Some f) -> OpamFilename.move ~src:f ~dst:file; Result () - | Result None -> let m = "is a directory" in Not_available (Some m, m) - | Not_available _ as na -> na) + Done ( + Not_available + (Some "missing checksum", + label ^ ": Missing checksum, and `--require-checksums` was set.")) + else + OpamFilename.with_tmp_dir_job (fun tmpdir -> + pull_from_mirrors label cache_dir tmpdir checksums remote_urls + @@| function + | _, Up_to_date _ -> assert false + | _, Result (Some f) -> OpamFilename.move ~src:f ~dst:file; Result () + | _, Result None -> let m = "is a directory" in Not_available (Some m, m) + | _, (Not_available _ as na) -> na) let pull_file_to_cache label ~cache_dir ?(cache_urls=[]) checksums remote_urls = let text = OpamProcess.make_command_text label "dl" in OpamProcess.Job.with_text text @@ fetch_from_cache cache_dir cache_urls checksums @@+ function - | Up_to_date _ -> Done (Up_to_date ()) + | Up_to_date (_, _) -> + Done (Up_to_date "cached") | Result (_, url) -> - OpamConsole.msg "[%s] downloaded from %s\n" - (OpamConsole.colorise `green label) - (OpamUrl.to_string url); - Done (Result ()) + Done (Result (OpamUrl.to_string url)) | Not_available _ -> OpamFilename.with_tmp_dir_job (fun tmpdir -> pull_from_mirrors label (Some cache_dir) tmpdir checksums remote_urls @@| function - | Up_to_date _ -> assert false - | Result (Some _) -> Result () - | Result None -> let m = "is a directory" in Not_available (Some m, m) - | Not_available _ as na -> na) + | _, Up_to_date _ -> assert false + | url, Result (Some _) -> Result (OpamUrl.to_string url) + | _, Result None -> let m = "is a directory" in Not_available (Some m, m) + | _, (Not_available _ as na) -> na) -let packages r = - OpamPackage.list (OpamRepositoryPath.packages_dir r.repo_root) +let packages repo_root = + OpamPackage.list (OpamRepositoryPath.packages_dir repo_root) -let packages_with_prefixes r = - OpamPackage.prefixes (OpamRepositoryPath.packages_dir r.repo_root) +let packages_with_prefixes repo_root = + OpamPackage.prefixes (OpamRepositoryPath.packages_dir repo_root) -let validate_repo_update repo update = +let validate_repo_update repo repo_root update = match repo.repo_trust, OpamRepositoryConfig.(!r.validation_hook), @@ -370,7 +378,7 @@ let env v = match OpamVariable.Full.to_string v, update with | "anchors", _ -> Some (S (String.concat "," ta.fingerprints)) | "quorum", _ -> Some (S (string_of_int ta.quorum)) - | "repo", _ -> Some (S (OpamFilename.Dir.to_string repo.repo_root)) + | "repo", _ -> Some (S (OpamFilename.Dir.to_string repo_root)) | "patch", Update_patch f -> Some (S (OpamFilename.to_string f)) | "incremental", Update_patch _ -> Some (B true) | "incremental", _ -> Some (B false) @@ -391,17 +399,17 @@ open OpamRepositoryBackend -let apply_repo_update repo = function +let apply_repo_update repo repo_root = function | Update_full d -> log "%a: applying update from scratch at %a" (slog OpamRepositoryName.to_string) repo.repo_name (slog OpamFilename.Dir.to_string) d; - OpamFilename.rmdir repo.repo_root; + OpamFilename.rmdir repo_root; if OpamFilename.is_symlink_dir d then - (OpamFilename.copy_dir ~src:d ~dst:repo.repo_root; + (OpamFilename.copy_dir ~src:d ~dst:repo_root; OpamFilename.rmdir d) else - OpamFilename.move_dir ~src:d ~dst:repo.repo_root; + OpamFilename.move_dir ~src:d ~dst:repo_root; OpamConsole.msg "[%s] Initialised\n" (OpamConsole.colorise `green (OpamRepositoryName.to_string repo.repo_name)); @@ -419,7 +427,7 @@ | `http | `rsync -> false | _ -> true in - (OpamFilename.patch ~preprocess f repo.repo_root @@+ function + (OpamFilename.patch ~preprocess f repo_root @@+ function | Some e -> if not (OpamConsole.debug ()) then OpamFilename.remove f; raise e @@ -441,23 +449,27 @@ | Update_patch f -> OpamFilename.remove f | _ -> () -let update repo = +let update repo repo_root = log "update %a" (slog OpamRepositoryBackend.to_string) repo; let module B = (val find_backend repo: OpamRepositoryBackend.S) in - B.fetch_repo_update repo.repo_name repo.repo_root repo.repo_url @@+ function + B.fetch_repo_update repo.repo_name repo_root repo.repo_url @@+ function | Update_err e -> raise e - | (Update_empty | Update_full _ | Update_patch _) as upd -> + | Update_empty -> + log "update empty, no validation performed"; + apply_repo_update repo repo_root Update_empty @@+ fun () -> + B.repo_update_complete repo_root repo.repo_url + | (Update_full _ | Update_patch _) as upd -> OpamProcess.Job.catch (fun exn -> cleanup_repo_update upd; raise exn) @@ fun () -> - validate_repo_update repo upd @@+ function + validate_repo_update repo repo_root upd @@+ function | false -> cleanup_repo_update upd; failwith "Invalid repository signatures, update aborted" | true -> - apply_repo_update repo upd @@+ fun () -> - B.repo_update_complete repo.repo_root repo.repo_url + apply_repo_update repo repo_root upd @@+ fun () -> + B.repo_update_complete repo_root repo.repo_url let on_local_version_control url ~default f = match url.OpamUrl.backend with @@ -472,6 +484,25 @@ on_local_version_control url ~default:(Done None) @@ fun dir (module VCS) -> VCS.current_branch dir -let is_dirty url = +let is_dirty ?subpath url = on_local_version_control url ~default:(Done false) @@ - fun dir (module VCS) -> VCS.is_dirty dir + fun dir (module VCS) -> VCS.is_dirty ?subpath dir + +let report_fetch_result pkg = function + | Result msg -> + OpamConsole.msg + "[%s] synchronised (%s)\n" + (OpamConsole.colorise `green (OpamPackage.to_string pkg)) + msg; + Result () + | Up_to_date msg -> + OpamConsole.msg + "[%s] synchronised (%s)\n" + (OpamConsole.colorise `green (OpamPackage.to_string pkg)) + msg; + Up_to_date () + | Not_available (s, l) -> + let msg = match s with None -> l | Some s -> s in + OpamConsole.msg "[%s] fetching sources failed: %s\n" + (OpamConsole.colorise `red (OpamPackage.to_string pkg)) msg; + Not_available (s, l) diff -Nru opam-2.0.10/src/repository/opamRepository.mli opam-2.1.2/src/repository/opamRepository.mli --- opam-2.0.10/src/repository/opamRepository.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepository.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -15,19 +15,16 @@ open OpamTypes (** Get the list of packages *) -val packages: repository -> package_set +val packages: dirname -> package_set (** Get the list of packages (and their possible prefix) *) -val packages_with_prefixes: repository -> string option package_map +val packages_with_prefixes: dirname -> string option package_map (** {2 Repository backends} *) -(** Initialize {i $opam/repo/$repo} *) -val init: dirname -> repository_name -> unit OpamProcess.job - (** Update {i $opam/repo/$repo}. Raises [Failure] in case the update couldn't be achieved. *) -val update: repository -> unit OpamProcess.job +val update: repository -> dirname -> unit OpamProcess.job (** Fetch an URL and put the resulting tree into the supplied directory. The URL must either point to a tree (VCS, rsync) or to a known archive type. In case @@ -36,8 +33,8 @@ or synchronised directly if local and [working_dir] was set. *) val pull_tree: string -> ?cache_dir:dirname -> ?cache_urls:url list -> ?working_dir:bool -> - dirname -> OpamHash.t list -> url list -> - unit download OpamProcess.job + ?subpath:string -> dirname -> OpamHash.t list -> url list -> + string download OpamProcess.job (** Same as [pull_tree], but for fetching a single file. *) val pull_file: @@ -49,7 +46,7 @@ is present in the cache. *) val pull_file_to_cache: string -> cache_dir:dirname -> ?cache_urls:url list -> - OpamHash.t list -> url list -> unit download OpamProcess.job + OpamHash.t list -> url list -> string download OpamProcess.job (** The file where the file with the given hash is stored under cache at given dirname. *) @@ -64,8 +61,11 @@ (** Returns true if the url points to a local, version-controlled directory that has uncommitted changes *) -val is_dirty: url -> bool OpamProcess.job +val is_dirty: ?subpath:string -> url -> bool OpamProcess.job (** Find a backend *) val find_backend: repository -> (module OpamRepositoryBackend.S) val find_backend_by_kind: OpamUrl.backend -> (module OpamRepositoryBackend.S) + +(** Prints user messages upon the result of a download *) +val report_fetch_result: package -> string download -> unit download diff -Nru opam-2.0.10/src/repository/opamRepositoryPath.ml opam-2.1.2/src/repository/opamRepositoryPath.ml --- opam-2.0.10/src/repository/opamRepositoryPath.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepositoryPath.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -11,7 +11,9 @@ open OpamFilename.Op -let create root name = root / "repo" / OpamRepositoryName.to_string name +let root root name = root / "repo" / OpamRepositoryName.to_string name + +let tar root name = root / "repo" // (OpamRepositoryName.to_string name ^ ".tar.gz") let download_cache root = root / "download-cache" diff -Nru opam-2.0.10/src/repository/opamRepositoryPath.mli opam-2.1.2/src/repository/opamRepositoryPath.mli --- opam-2.0.10/src/repository/opamRepositoryPath.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamRepositoryPath.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -14,7 +14,9 @@ open OpamTypes (** Repository local path: {i $opam/repo/} *) -val create: dirname -> repository_name -> dirname +val root: dirname -> repository_name -> dirname + +val tar: dirname -> repository_name -> filename (** Prefix where to store the downloaded files cache: {i $opam/download-cache}. Warning, this is relative to the opam root, not a repository root. *) diff -Nru opam-2.0.10/src/repository/opamVCS.ml opam-2.1.2/src/repository/opamVCS.ml --- opam-2.0.10/src/repository/opamVCS.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamVCS.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -17,7 +17,7 @@ val name: OpamUrl.backend val exists: dirname -> bool val init: dirname -> url -> unit OpamProcess.job - val fetch: ?cache_dir:dirname -> dirname -> url -> unit OpamProcess.job + val fetch: ?cache_dir:dirname -> ?subpath:string -> dirname -> url -> unit OpamProcess.job val reset_tree: dirname -> url -> unit OpamProcess.job val patch_applied: dirname -> url -> unit OpamProcess.job val diff: dirname -> url -> filename option OpamProcess.job @@ -26,9 +26,13 @@ val versioned_files: dirname -> string list OpamProcess.job val vc_dir: dirname -> dirname val current_branch: dirname -> string option OpamProcess.job - val is_dirty: dirname -> bool OpamProcess.job + val is_dirty: ?subpath:string -> dirname -> bool OpamProcess.job + val modified_files: dirname -> string list OpamProcess.job + val get_remote_url: ?hash:string -> dirname -> url option OpamProcess.job end +let convert_path = + OpamSystem.get_cygpath_function ~command:"rsync" module Make (VCS: VCS) = struct @@ -68,7 +72,7 @@ VCS.patch_applied dirname url @@+ fun () -> Done () - let pull_url ?cache_dir dirname checksum url = + let pull_url ?cache_dir ?subpath dirname checksum url = if checksum <> None then invalid_arg "VC pull_url doesn't allow checksums"; OpamProcess.Job.catch (fun e -> @@ -79,7 +83,7 @@ Done (Not_available (None, OpamUrl.to_string url))) @@ fun () -> if VCS.exists dirname then - VCS.fetch ?cache_dir dirname url @@+ fun () -> + VCS.fetch ?cache_dir ?subpath dirname url @@+ fun () -> VCS.is_up_to_date dirname url @@+ function | true -> Done (Up_to_date None) | false -> @@ -88,7 +92,7 @@ else (OpamFilename.mkdir dirname; VCS.init dirname url @@+ fun () -> - VCS.fetch ?cache_dir dirname url @@+ fun () -> + VCS.fetch ?cache_dir ?subpath dirname url @@+ fun () -> VCS.reset_tree dirname url @@+ fun () -> Done (Result None)) @@ -96,41 +100,82 @@ VCS.revision repo_root @@+ fun r -> Done (OpamStd.Option.map OpamPackage.Version.of_string r) - let sync_dirty repo_root repo_url = + let sync_dirty ?subpath repo_root repo_url = + let filter_subpath files = + match subpath with + | None -> files + | Some sp -> + OpamStd.List.filter_map + (fun f -> + if OpamStd.String.remove_prefix ~prefix:(sp ^ Filename.dir_sep) f + <> f then Some f else None) + files + in + pull_url ?subpath repo_root None repo_url @@+ fun result -> match OpamUrl.local_dir repo_url with - | None -> pull_url repo_root None repo_url + | None -> Done (result) | Some dir -> - VCS.versioned_files dir - @@+ fun files -> + VCS.versioned_files dir @@+ fun vc_files -> + VCS.modified_files dir @@+ fun vc_dirty_files -> let files = - List.map OpamFilename.(remove_prefix dir) - (OpamFilename.rec_files (VCS.vc_dir dir)) - @ files + filter_subpath + (List.map OpamFilename.(remove_prefix dir) + (OpamFilename.rec_files dir)) + in + (* Remove non-listed files from destination *) + (* fixme: doesn't clean directories *) + let fset = OpamStd.String.Set.of_list files in + let rm_list = + List.filter (fun f -> + let basename = OpamFilename.remove_prefix repo_root f in + not (OpamFilename.(starts_with (VCS.vc_dir repo_root) f) + || OpamStd.String.Set.mem basename fset)) + (OpamFilename.rec_files repo_root) + in + List.iter OpamFilename.remove rm_list; + (* We do the list cleaning here because of rsync options: with + `--files-from`, `--exclude` need to be explicitly given directory + descendants, e.g `--exclude _build/**` + *) + let excluded = + (* from [OpamLocal.rsync] exclude list *) + let exc = + [ OpamSwitch.external_dirname; "_build"; + ".git"; "_darcs"; ".hg" ] + in + OpamStd.String.Set.filter (fun f -> + List.exists (fun prefix -> + OpamStd.String.starts_with ~prefix f) + exc) + fset + in + let vcset = OpamStd.String.Set.of_list (filter_subpath vc_files) in + let vc_dirty_set = + OpamStd.String.Set.of_list (filter_subpath vc_dirty_files) + in + let final_set = + OpamStd.String.Set.Op.(fset -- vcset ++ vc_dirty_set -- excluded) in let stdout_file = let f = OpamSystem.temp_file "rsync-files" in let fd = open_out f in - List.iter (fun s -> output_string fd s; output_char fd '\n') files; + (* Using the set here to keep the list file sorted, it helps rsync *) + OpamStd.String.Set.iter (fun s -> + output_string fd s; output_char fd '\n') + final_set; close_out fd; f in - (* Remove non-versionned files from destination *) - (* fixme: doesn't clean directories *) - let fset = OpamStd.String.Set.of_list files in - List.iter (fun f -> - let basename = OpamFilename.remove_prefix repo_root f in - if not (OpamFilename.(starts_with (VCS.vc_dir repo_root) f) || - OpamStd.String.Set.mem basename fset) - then OpamFilename.remove f) - (OpamFilename.rec_files repo_root); - OpamLocal.rsync_dirs ~args:["--files-from"; stdout_file] - ~exclude_vcdirs:false - repo_url repo_root - @@+ fun result -> + let args = [ + "--files-from"; (Lazy.force convert_path) stdout_file; + ] in + OpamLocal.rsync_dirs ~args repo_url repo_root @@+ fun result -> OpamSystem.remove stdout_file; Done (match result with - | Up_to_date _ -> Up_to_date None - | Result _ -> Result None + | Up_to_date _ when rm_list = [] -> Up_to_date None + | Up_to_date _ | Result _ -> Result None | Not_available _ as na -> na) + let get_remote_url = VCS.get_remote_url + end diff -Nru opam-2.0.10/src/repository/opamVCS.mli opam-2.1.2/src/repository/opamVCS.mli --- opam-2.0.10/src/repository/opamVCS.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/repository/opamVCS.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -28,7 +28,7 @@ in a staging area. Be aware that the remote URL might have been changed, so make sure to update accordingly. *) - val fetch: ?cache_dir:dirname -> dirname -> url -> unit OpamProcess.job + val fetch: ?cache_dir:dirname -> ?subpath:string -> dirname -> url -> unit OpamProcess.job (** Reset the master branch of the repository to match the remote repository state. This might still fetch more data (git submodules...), so is @@ -65,8 +65,14 @@ recorded in the VCS as current. This differs from [is_up_to_date], which compares specifically to the last fetched state. This should always be [false] after [reset] has been called. *) - val is_dirty: dirname -> bool OpamProcess.job + val is_dirty: ?subpath:string -> dirname -> bool OpamProcess.job + + (** Returns the list of files under version control, modified in the working + tree but not comitted *) + val modified_files: dirname -> string list OpamProcess.job + + val get_remote_url: ?hash:string -> dirname -> url option OpamProcess.job end (** Create a backend from a [VCS] implementation. *) -module Make(VCS: VCS): OpamRepositoryBackend.S +module Make(VCS : VCS) : OpamRepositoryBackend.S [@@ocaml.warning "-67"] (* TODO: Remove this once we get past OCaml 4.02 *) diff -Nru opam-2.0.10/src/solver/dune opam-2.1.2/src/solver/dune --- opam-2.0.10/src/solver/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/dune 2021-12-07 16:09:27.000000000 +0000 @@ -5,8 +5,15 @@ (libraries opam-format cudf dose3.algo (select opamBuiltinMccs.ml from (mccs -> opamBuiltinMccs.ml.real) - ( -> opamBuiltinMccs.ml.dummy))) + ( -> opamBuiltinMccs.ml.dummy)) + (select opamBuiltinZ3.ml from + (z3 -> opamBuiltinZ3.ml.real) + ( -> opamBuiltinZ3.ml.dummy)) + (select opamBuiltin0install.ml from + (opam-0install-cudf -> opamBuiltin0install.ml.real) + ( -> opamBuiltin0install.ml.dummy))) (flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp))) (wrapped false)) diff -Nru opam-2.0.10/src/solver/opamActionGraph.ml opam-2.1.2/src/solver/opamActionGraph.ml --- opam-2.0.10/src/solver/opamActionGraph.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamActionGraph.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2014 OCamlPro *) +(* Copyright 2014-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -29,6 +29,7 @@ | `Change (`Down,_,_) -> "downgrade" | `Reinstall _ -> "recompile" | `Build _ -> "build" + | `Fetch _ -> "fetch" let symbol_of_action = let open OpamConsole in @@ -51,6 +52,10 @@ | `Build _ -> utf8_symbol Symbols.greek_small_letter_lambda ~alternates:[Symbols.six_pointed_black_star] "B" + | `Fetch _ -> + utf8_symbol Symbols.downwards_black_arrow + ~alternates:[Symbols.downwards_double_arrow; + Symbols.black_down_pointing_triangle] "F" let action_strings ?utf8 a = if utf8 = None && (OpamConsole.utf8 ()) || utf8 = Some true @@ -62,7 +67,7 @@ | `Install _ | `Change (`Up,_,_) -> `green | `Remove _ | `Change (`Down,_,_) -> `red | `Reinstall _ -> `yellow - | `Build _ -> `cyan) + | `Build _ | `Fetch _ -> `cyan) module MakeAction (P: GenericPackage) : ACTION with type package = P.t = struct @@ -71,12 +76,13 @@ type t = package action let compare t1 t2 = - (* `Install > `Build > `Upgrade > `Reinstall > `Downgrade > `Remove *) + (* `Install > `Build > `Fetch > `Upgrade > `Reinstall > `Downgrade > `Remove *) match t1,t2 with | `Remove p, `Remove q | `Install p, `Install q | `Reinstall p, `Reinstall q | `Build p, `Build q + | `Fetch p, `Fetch q -> P.compare p q | `Change (`Up,p0,p), `Change (`Up,q0,q) | `Change (`Down,p0,p), `Change (`Down,q0,q) @@ -86,16 +92,16 @@ | `Install _, _ | _, `Remove _ -> 1 | _, `Install _ | `Remove _, _ -> -1 | `Build _, _ | _, `Change (`Down,_,_) -> 1 - | `Change (`Down,_,_), _ | _, `Build _ -> -1 - | `Change (`Up,_,_), `Reinstall _ -> 1 - | `Reinstall _, `Change(`Up,_,_) -> -1 + | _, `Build _ | `Change (`Down,_,_), _ -> -1 + | `Fetch _, _ | _, `Reinstall _ -> 1 + | _, `Fetch _ | `Reinstall _, _ -> -1 let hash a = Hashtbl.hash (OpamTypesBase.map_action P.hash a) let equal t1 t2 = compare t1 t2 = 0 let to_string a = match a with - | `Remove p | `Install p | `Reinstall p | `Build p -> + | `Remove p | `Install p | `Reinstall p | `Build p | `Fetch p -> Printf.sprintf "%s %s" (action_strings a) (P.to_string p) | `Change (_,p0,p) -> Printf.sprintf "%s.%s %s %s" @@ -114,7 +120,7 @@ :: OpamConsole.colorise `bold (P.name_to_string (OpamTypesBase.action_contents a)) :: match a with - | `Remove p | `Install p | `Reinstall p | `Build p -> + | `Remove p | `Install p | `Reinstall p | `Build p | `Fetch p -> (P.version_to_string p ^ append p) :: [] | `Change (_,p0,p) -> Printf.sprintf "%s to %s" @@ -126,16 +132,40 @@ let to_json = function | `Remove p -> `O ["remove", P.to_json p] | `Install p -> `O ["install", P.to_json p] - | `Change (_, o, p) -> - `O ["change", `A [P.to_json o;P.to_json p]] + | `Change (d, o, p) -> + let dir_to_json = function + | `Up -> `String "up" + | `Down -> `String "down" in + `O ["change", `A [dir_to_json d; P.to_json o;P.to_json p]] | `Reinstall p -> `O ["recompile", P.to_json p] | `Build p -> `O ["build", P.to_json p] + | `Fetch p -> `O ["fetch", P.to_json p] + + let of_json = + let open OpamStd.Option.Op in + function + | `O ["remove", p] -> P.of_json p >>= (fun p -> Some (`Remove p)) + | `O ["install", p] -> P.of_json p >>= (fun p -> Some (`Install p)) + | `O ["change", `A [dj; oj; pj]] -> + let json_of_dir = function + | `String "up" -> Some `Up + | `String "down" -> Some `Down + | _ -> None in + json_of_dir dj >>= fun d -> + P.of_json oj >>= fun o -> + P.of_json pj >>= fun p -> + Some (`Change(d, o, p)) + | `O ["recompile", p] -> P.of_json p >>= (fun p -> Some (`Reinstall p)) + | `O ["build", p] -> P.of_json p >>= (fun p -> Some (`Build p)) + | `O ["fetch", p] -> P.of_json p >>= (fun p -> Some (`Fetch p)) + | _ -> None module O = struct type t = package action let compare = compare let to_string = to_string let to_json = to_json + let of_json = of_json end module Set = OpamStd.Set.Make(O) @@ -147,7 +177,8 @@ type package include OpamParallel.GRAPH with type V.t = package OpamTypes.action val reduce: t -> t - val explicit: ?noop_remove:(package -> bool) -> t -> t + val explicit: + ?noop_remove:(package -> bool) -> sources_needed:(package -> bool) -> t -> t val fold_descendants: (V.t -> 'a -> 'a) -> 'a -> t -> V.t -> 'a end @@ -208,6 +239,8 @@ ) !reduced; g + let same_name p1 p2 = A.Pkg.(name_to_string p1 = name_to_string p2) + let compute_closed_predecessors noop_remove g = let closed_g = copy g in transitive_closure closed_g; @@ -234,7 +267,12 @@ let preds = List.filter (function - | `Build p -> Set.mem p closed_packages + | `Build q as b -> + Set.mem q closed_packages && + not (List.exists (function + | `Remove r -> same_name p r + | _ -> false) + (pred closed_g b)) | _ -> false) (pred closed_g a) in OpamStd.String.Map.add (A.Pkg.name_to_string p) preds acc @@ -248,9 +286,8 @@ | None -> [] | Some pred -> pred - let explicit ?(noop_remove = (fun _ -> false)) g0 = + let explicit ?(noop_remove = (fun _ -> false)) ~sources_needed g0 = let g = copy g0 in - let same_name p1 p2 = A.Pkg.(name_to_string p1 = name_to_string p2) in (* We insert a "build" action before any "install" action. Except, between the removal and installation of the same package (the removal might be postponed after a succesfull build. *) @@ -264,7 +301,7 @@ g0 a; add_edge g b a | `Remove _ -> () - | `Build _ -> assert false) + | `Build _ | `Fetch _ -> assert false) g0; (* For delaying removal a little bit, for each action "remove A" we add a constraint "build B -> remove A" for transitive predecessors @@ -278,8 +315,26 @@ List.iter (fun b -> add_edge g b a) (closed_predecessors p) - | `Install _ | `Reinstall _ | `Change _ | `Build _ -> ()) + | `Install _ | `Reinstall _ | `Change _ | `Build _ | `Fetch _ -> ()) g; + (* Add a "fetch" action as a dependency for all "build" and "remove" actions + that require it (via [sources_needed]). *) + let acc_add_action (acc: vertex list Map.t) + (p: A.package) (a: vertex) : vertex list Map.t = + let acts = try Map.find p acc with Not_found -> [] in + Map.add p (a :: acts) acc + in + let m = fold_vertex (fun a acc -> + match a with + | `Build p | `Remove p -> + if sources_needed p then acc_add_action acc p a else acc + | `Install _ | `Reinstall _ | `Change _ -> acc + | `Fetch _ -> assert false + ) g Map.empty in + Map.iter (fun p acts -> + let f = `Fetch p in + List.iter (fun a -> add_edge g f a) acts + ) m; g let fold_descendants f acc t v = diff -Nru opam-2.0.10/src/solver/opamActionGraph.mli opam-2.1.2/src/solver/opamActionGraph.mli --- opam-2.0.10/src/solver/opamActionGraph.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamActionGraph.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2014 OCamlPro *) +(* Copyright 2014-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -42,8 +42,14 @@ (** Expand install actions, adding a build action preceding them. The argument [noop_remove] is a function that should return `true` for package where the `remove` action is known not to modify the - filesystem (such as `conf-*` package). *) - val explicit: ?noop_remove:(package -> bool) -> t -> t + filesystem (such as `conf-*` package). + The argument [sources_needed] is a function that should return `true` + for packages that require fetching sources (packages that do not + require it are typically up-to-date pins or "in-place" builds). *) + val explicit: + ?noop_remove:(package -> bool) -> + sources_needed:(package -> bool) -> + t -> t (** Folds on all recursive successors of the given action, including itself, depth-first. *) diff -Nru opam-2.0.10/src/solver/opamBuiltin0install.ml.dummy opam-2.1.2/src/solver/opamBuiltin0install.ml.dummy --- opam-2.0.10/src/solver/opamBuiltin0install.ml.dummy 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltin0install.ml.dummy 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,29 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 Kate Deplaix *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamCudfSolverSig + +let name = "builtin-dummy-0install-solver" + +let is_present () = false + +let ext = ref None + +let command_name = None + +let default_criteria = { + crit_default = ""; + crit_upgrade = ""; + crit_fixup = ""; + crit_best_effort_prefix = None; +} + +let call ~criteria:_ ?timeout:_ _cudf = + failwith "This opam was compiled without the opam-0install solver built in" diff -Nru opam-2.0.10/src/solver/opamBuiltin0install.mli opam-2.1.2/src/solver/opamBuiltin0install.mli --- opam-2.0.10/src/solver/opamBuiltin0install.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltin0install.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +include OpamCudfSolverSig.S diff -Nru opam-2.0.10/src/solver/opamBuiltin0install.ml.real opam-2.1.2/src/solver/opamBuiltin0install.ml.real --- opam-2.0.10/src/solver/opamBuiltin0install.ml.real 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltin0install.ml.real 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,118 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 Kate Deplaix *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamCudfSolverSig + +let log ?level f = OpamConsole.log "0install" ?level f + +let name = "builtin-0install" + +let ext = ref None + +let is_present () = true + +let command_name = None + +let default_criteria = { + crit_default = ""; + crit_upgrade = ""; + crit_fixup = ""; + crit_best_effort_prefix = None; +} + +let not_relop = function + | `Eq -> `Neq + | `Neq -> `Eq + | `Geq -> `Lt + | `Gt -> `Leq + | `Leq -> `Gt + | `Lt -> `Geq + +let keep_installed ~drop_installed_packages request pkgname = + not drop_installed_packages && + not (List.exists (fun (pkg, _) -> String.equal pkg pkgname) request.Cudf.install) && + not (List.exists (fun (pkg, _) -> String.equal pkg pkgname) request.Cudf.upgrade) && + not (List.exists (fun (pkg, _) -> String.equal pkg pkgname) request.Cudf.remove) + +let add_spec pkg req c (pkgs, constraints) = + let pkgs = (pkg, req) :: pkgs in + let constraints = match c with + | None -> constraints + | Some c -> (pkg, c) :: constraints + in + (pkgs, constraints) + +let essential spec (pkg, c) = add_spec pkg `Essential c spec +let recommended spec (pkg, c) = add_spec pkg `Recommended c spec + +let restricts (pkgs, constraints) (pkg, c) = + let constraints = match c with + | None -> (pkg, (`Lt, 1)) :: (pkg, (`Gt, 1)) :: constraints (* pkg < 1 & pkg > 1 is always false *) + | Some (relop, v) -> (pkg, (not_relop relop, v)) :: constraints + in + (pkgs, constraints) + +let create_spec ~drop_installed_packages universe request = + let spec = ([], []) in + let spec = List.fold_left essential spec request.Cudf.install in + let spec = List.fold_left essential spec request.Cudf.upgrade in + let spec = List.fold_left restricts spec request.Cudf.remove in + Cudf.fold_packages_by_name (fun spec pkgname pkgs -> + match List.find_opt (fun pkg -> pkg.Cudf.installed) pkgs with + | Some {Cudf.keep = `Keep_version; version; _} -> essential spec (pkgname, Some (`Eq, version)) + | Some {Cudf.keep = `Keep_package; _} -> essential spec (pkgname, None) + | Some {Cudf.keep = `Keep_feature; _} -> assert false (* NOTE: Opam has no support for features *) + | Some {Cudf.keep = `Keep_none; _} -> + if keep_installed ~drop_installed_packages request pkgname then + recommended spec (pkgname, None) + else + spec + | None -> spec + ) spec universe + +let reconstruct_universe universe selections = + Opam_0install_cudf.packages_of_result selections |> + List.fold_left (fun pkgs (pkg, v) -> + let pkg = Cudf.lookup_package universe (pkg, v) in + {pkg with was_installed = pkg.installed; installed = true} :: pkgs + ) [] |> + Cudf.load_universe + +type options = { + drop_installed_packages : bool; + prefer_oldest : bool; +} + +let parse_criteria criteria = + let default = {drop_installed_packages = false; prefer_oldest = false} in + match criteria with + | "" -> default + | "+removed" -> {drop_installed_packages = true; prefer_oldest = false} + | "+count[version-lag,solution]" -> {drop_installed_packages = false; prefer_oldest = true} + | "+removed,+count[version-lag,solution]" -> + {drop_installed_packages = true; prefer_oldest = true} + | _ -> + OpamConsole.warning "Criteria '%s' is not supported by the 0install solver" criteria; + default + +let call ~criteria ?timeout:_ (preamble, universe, request) = + let {drop_installed_packages; prefer_oldest} = parse_criteria criteria in + let timer = OpamConsole.timer () in + let pkgs, constraints = create_spec ~drop_installed_packages universe request in + let context = Opam_0install_cudf.create ~prefer_oldest ~constraints universe in + match Opam_0install_cudf.solve context pkgs with + | Ok selections -> + let universe = reconstruct_universe universe selections in + log "Solution found. Solve took %.2f s" (timer ()); + (Some preamble, universe) + | Error problem -> + log "No solution. Solve took %.2f s" (timer ()); + log ~level:3 "%a" (OpamConsole.slog Opam_0install_cudf.diagnostics) problem; + raise Common.CudfSolver.Unsat diff -Nru opam-2.0.10/src/solver/opamBuiltinMccs.ml.dummy opam-2.1.2/src/solver/opamBuiltinMccs.ml.dummy --- opam-2.0.10/src/solver/opamBuiltinMccs.ml.dummy 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltinMccs.ml.dummy 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) diff -Nru opam-2.0.10/src/solver/opamBuiltinMccs.mli opam-2.1.2/src/solver/opamBuiltinMccs.mli --- opam-2.0.10/src/solver/opamBuiltinMccs.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltinMccs.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) diff -Nru opam-2.0.10/src/solver/opamBuiltinMccs.ml.real opam-2.1.2/src/solver/opamBuiltinMccs.ml.real --- opam-2.0.10/src/solver/opamBuiltinMccs.ml.real 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltinMccs.ml.real 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -14,13 +14,20 @@ let default_criteria = { crit_default = "-removed,\ + -count[avoid-version,changed],\ -count[version-lag,request],\ -count[version-lag,changed],\ + -count[missing-depexts,changed],\ -changed"; crit_upgrade = "-removed,\ + -count[avoid-version,changed],\ -count[version-lag,solution],\ + -count[missing-depexts,changed],\ -new"; - crit_fixup = "-changed,-count[version-lag:,false]"; + crit_fixup = "-changed,\ + -count[avoid-version:,true],\ + -count[version-lag:,false],\ + -count[missing-depexts:,true]"; crit_best_effort_prefix = Some "+count[opam-query:,false],"; } @@ -32,12 +39,12 @@ match Mccs.resolve_cudf ~solver - ~verbose:OpamCoreConfig.(!r.debug_level >= 2) + ~verbose:OpamCoreConfig.(abs !r.debug_level >= 2) ?timeout criteria cudf with | None -> raise Common.CudfSolver.Unsat | Some (preamble, univ) -> Some preamble, univ - | exception Mccs.Timeout -> raise Timeout + | exception Mccs.Timeout -> raise (Timeout None) let of_backend backend : (module OpamCudfSolverSig.S) = (module struct diff -Nru opam-2.0.10/src/solver/opamBuiltinZ3.ml.dummy opam-2.1.2/src/solver/opamBuiltinZ3.ml.dummy --- opam-2.0.10/src/solver/opamBuiltinZ3.ml.dummy 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltinZ3.ml.dummy 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,29 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamCudfSolverSig + +let name = "builtin-dummy-z3-solver" + +let is_present () = false + +let ext = ref None + +let command_name = None + +let default_criteria = { + crit_default = ""; + crit_upgrade = ""; + crit_fixup = ""; + crit_best_effort_prefix = None; +} + +let call ~criteria:_ ?timeout:_ _cudf = + failwith "This opam was compiled without the Z3 solver built in" diff -Nru opam-2.0.10/src/solver/opamBuiltinZ3.mli opam-2.1.2/src/solver/opamBuiltinZ3.mli --- opam-2.0.10/src/solver/opamBuiltinZ3.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltinZ3.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +include OpamCudfSolverSig.S diff -Nru opam-2.0.10/src/solver/opamBuiltinZ3.ml.real opam-2.1.2/src/solver/opamBuiltinZ3.ml.real --- opam-2.0.10/src/solver/opamBuiltinZ3.ml.real 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/solver/opamBuiltinZ3.ml.real 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,440 @@ +(**************************************************************************) +(* *) +(* Copyright 2017-2019 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +open OpamCudfSolverSig + +let log f = OpamConsole.log "Z3" f + +let name = "builtin-z3" + +let ext = ref None + +let is_present () = true + +let command_name = None + +let default_criteria = { + crit_default = "-removed,\ + -count[avoid-version,changed],\ + -count[version-lag,request],\ + -count[version-lag,changed],\ + -count[missing-depexts,changed],\ + -changed"; + crit_upgrade = "-removed,\ + -count[avoid-version,changed],\ + -count[version-lag,solution],\ + -count[missing-depexts,changed],\ + -new"; + crit_fixup = "-changed,\ + -count[avoid-version,changed],\ + -count[version-lag,solution],\ + -count[missing-depexts,changed]"; + crit_best_effort_prefix = Some "+count[opam-query,solution],"; +} + +let mk_or ctx = function + | None -> None + | Some [] -> None + | Some [p] -> Some p + | Some l -> Some (Z3.Boolean.mk_or ctx l) + +let mk_and ctx = function + | None -> None + | Some [] -> None + | Some [p] -> Some p + | Some l -> Some (Z3.Boolean.mk_and ctx l) + +let ( @^ ) opt l = match opt with + | None -> l + | Some x -> x :: l + +let (@@^) o l = match o with + | None -> l + | Some l1 -> List.rev_append l1 l + +let xrmap f l = + match List.fold_left (fun acc x -> f x @^ acc) [] l with + | [] -> None + | l -> Some l + +(* +let xmap f l = match xrmap f l with + | Some l -> Some (List.rev l) + | None -> None +*) +open OpamStd.Option.Op + +let def_packages ctx (_preamble, universe, _request) = + let syms = Hashtbl.create 2731 in + let psym p = Hashtbl.find_opt syms p in + let psym_exn p = match psym p with None -> raise Not_found | Some p -> p in + (* variable definitions *) + Cudf.iter_packages (fun pkg -> + Hashtbl.add syms pkg + (Z3.Boolean.mk_const_s ctx + (Printf.sprintf "%s.%d" pkg.Cudf.package pkg.Cudf.version))) + universe; + let def_exprs = [] in + let def_exprs = + (* "keep" flags *) + Cudf.fold_packages_by_name (fun e _name pkgs -> + let keep = + match List.find (fun p -> p.Cudf.keep = `Keep_version) pkgs with + | p -> psym p + | exception Not_found -> + if List.exists (fun p -> p.Cudf.keep = `Keep_package) pkgs then + mk_or ctx @@ xrmap psym pkgs + else None + in + keep @^ e) + def_exprs + universe + in + let expand_constraint pkg (name, constr) = + mk_or ctx + (xrmap (fun p -> if Cudf.( =% ) pkg p then None else psym p) + (Cudf.lookup_packages universe ~filter:constr name)) + in + let def_exprs = + Cudf.fold_packages (fun e pkg -> + let module SM = OpamStd.String.Map in + let cudf_depends, cudf_depends_map = + List.fold_left (fun (rem, map) -> function + | (name, _) :: r as disj + when List.for_all (fun (n1, _) -> n1 = name) r -> + rem, SM.update name (fun conj -> disj :: conj) [] map + | disj -> disj :: rem, map) + ([], SM.empty) + pkg.Cudf.depends + in + let depends = + xrmap + (fun disj -> mk_or ctx @@ xrmap (expand_constraint pkg) disj) + cudf_depends @@^ + SM.fold (fun name conj e -> + (match + xrmap psym @@ + List.fold_left (fun plist disj -> + let r = + List.filter (fun p -> + List.exists + (fun (_, cstr) -> + Cudf.version_matches p.Cudf.version cstr) + disj) + plist + in + r) + (Cudf.lookup_packages universe name) + conj + with + | None -> Some (Z3.Boolean.mk_false ctx) + | some -> mk_or ctx some) + @^ e) + cudf_depends_map + [] + |> OpamStd.Option.some + |> mk_and ctx + >>| Z3.Boolean.mk_implies ctx (psym_exn pkg) + in + let conflicts = + mk_or ctx @@ xrmap + (expand_constraint pkg) + pkg.Cudf.conflicts + >>| fun c -> + Z3.Boolean.mk_implies ctx (psym_exn pkg) (Z3.Boolean.mk_not ctx c) + in + depends @^ conflicts @^ e) + def_exprs + universe + in + List.rev def_exprs, + psym + +let def_request ctx (_preamble, universe, request) psym = + let expand_constraint (name, constr) = + mk_or ctx @@ xrmap psym + (Cudf.lookup_packages universe ~filter:constr name) + in + let inst = + xrmap expand_constraint request.Cudf.install + in + let rem = + xrmap + (fun vpkg -> expand_constraint vpkg >>| Z3.Boolean.mk_not ctx) + request.Cudf.remove + in + let up = + xrmap (fun (name, constr) -> + match Cudf.get_installed universe name with + | [] -> + mk_or ctx @@ xrmap psym + (Cudf.lookup_packages universe ~filter:constr name) + | p::l -> + let vmin = + List.fold_left (fun vmin p -> max vmin p.Cudf.version) p.Cudf.version l + in + Cudf.lookup_packages universe ~filter:constr name |> + List.filter (fun p -> p.Cudf.version >= vmin) |> + xrmap psym |> + (* fixme: the spec states that an 'upgrade' request should guarantee + that only one version of the package will be installed. Since it's + already a constraint in opam, and it's non trivial to encode, we + ignore it here. *) + mk_or ctx) + request.Cudf.upgrade + in + inst @@^ rem @@^ up @@^ [] + +let sum ctx (_, universe, _) filter value = + let ite filt iftrue iffalse = + Z3.Boolean.mk_ite ctx filt + (Z3.Arithmetic.Integer.mk_numeral_i ctx iftrue) + (Z3.Arithmetic.Integer.mk_numeral_i ctx iffalse) + in + Cudf.fold_packages (fun e pkg -> + match filter pkg with + | None -> e + | Some filt -> + match value pkg with + | 0 -> e + | n -> + if Z3.Boolean.is_not filt then + match Z3.Expr.get_args filt with + | [filt] -> ite filt 0 n :: e + | _ -> assert false + else + ite filt n 0 :: e) + [] + universe + +type filter = Installed | Changed | Removed | New | Upgraded | Downgraded | Requested +type property = string option +type sign = Plus | Minus + +type criterion = sign * filter * property + +let def_criterion ctx opt (preamble, universe, request as cudf) psym + (sign, filter, property : criterion) = + let filter_f = match filter with + | Installed -> fun p -> psym p + | Changed -> + fun p -> + if p.Cudf.installed then psym p >>| Z3.Boolean.mk_not ctx + else psym p + | Removed -> + fun p -> + if p.Cudf.installed then + mk_or ctx @@ xrmap psym (Cudf.lookup_packages universe p.Cudf.package) + >>| Z3.Boolean.mk_not ctx + else None + | New -> + fun p -> + if p.Cudf.installed then None + else + mk_or ctx @@ xrmap psym (Cudf.lookup_packages universe p.Cudf.package) + | Upgraded -> + fun p -> + if p.Cudf.installed then None + else (match Cudf.get_installed universe p.Cudf.package with + | [] -> None + | l when List.for_all (fun p1 -> p1.Cudf.version < p.Cudf.version) l + -> psym p + | _ -> None) + | Downgraded -> + fun p -> + if p.Cudf.installed then None + else (match Cudf.get_installed universe p.Cudf.package with + | [] -> None + | l when List.exists (fun p1 -> p1.Cudf.version > p.Cudf.version) l + -> psym p + | _ -> None) + | Requested -> + fun p -> + if + List.exists (fun (name, cstr) -> + p.Cudf.package = name && Cudf.version_matches p.Cudf.version cstr) + request.Cudf.install || + List.exists (fun (name, cstr) -> + p.Cudf.package = name && Cudf.version_matches p.Cudf.version cstr) + request.Cudf.upgrade + then psym p + else None + in + let value_f = match property with + | None -> fun _ -> 1 + | Some prop -> + fun p -> + match Cudf.lookup_typed_package_property p prop with + | `Int n | `Nat n -> n + | `Bool true -> 1 + | `Bool false -> 0 + | _ -> 0 + | exception Not_found -> + match List.assoc prop preamble.Cudf.property with + | `Int (Some n) | `Nat (Some n) -> n + | `Bool (Some true) -> 1 + | `Bool (Some false) -> 0 + | _ -> 0 + | exception Not_found -> + failwith ("Undefined CUDF property: "^prop) + in + match sum ctx cudf filter_f value_f with + | [] -> None + | sum -> + OpamStd.Option.some @@ + (match sign with Plus -> Z3.Optimize.maximize | Minus -> Z3.Optimize.minimize) + opt + (Z3.Arithmetic.mk_add ctx sum) + +let def_criteria ctx opt cudf psym crits = + List.map (def_criterion ctx opt cudf psym) crits + +module Syntax = struct + + let criterion_of_string (s,params) = + let sign = match s.[0] with + | '+' -> Plus + | '-' -> Minus + | c -> failwith (Printf.sprintf "criteria_of_string sign=%c" c) + | exception Invalid_argument _ -> + failwith "criteria_of_string sign=EOF" + in + let s = String.sub s 1 (String.length s - 1) in + let subset_of_string = function + | "new" -> New + | "removed" -> Removed + | "changed" -> Changed + | "up" -> Upgraded + | "down" -> Downgraded + | "installed" | "solution" -> Installed + | "request" -> Requested + | s -> failwith ("criteria_of_string subset="^s) + in + match s, params with + | "count", [field; subset] -> + sign, subset_of_string subset, Some field + | s, [] -> sign, subset_of_string s, None + | s, _ -> failwith ("criteria_of_string s="^s) +(* + let string_of_criterion (sign, filter, property: criterion) = + Printf.sprintf "%c%s%s" + (match sign with Plus -> '+' | Minus -> '-') + (match filter with + | Installed -> "installed" + | Changed -> "changed" + | Removed -> "removed" + | New -> "new" + | Upgraded -> "up" + | Downgraded -> "down" + | Requested -> "request") + (match property with None -> "" | Some p -> "["^p^"]") +*) + let criteria_of_string s = + let start = ref 0 in + let crits = ref [] in + let params = ref None in + for i = 0 to String.length s - 1 do + match s.[i] with + | ',' -> + let sub = String.sub s !start (i - !start) in + start := i + 1; + if sub <> "" then + (match !params with + | None -> crits := (sub, []) :: !crits + | Some (name, ps) -> params := Some (name, sub :: ps)) + | '[' -> + let sub = String.sub s !start (i - !start) in + start := i + 1; + if !params <> None then failwith "criteria_of_string"; + params := Some (sub, []) + | ']' -> + let sub = String.sub s !start (i - !start) in + start := i + 1; + (match !params with + | None -> failwith "criteria_of_string" + | Some (name, ps) -> + params := None; + crits := (name, List.rev (sub::ps)) :: !crits) + | _ -> () + done; + if !start < String.length s then + crits := (String.sub s !start (String.length s - !start), []) :: !crits; + if !params <> None then failwith "criteria_of_string"; + let r = List.rev_map criterion_of_string !crits in + r + +end + +let extract_solution_packages universe opt = + match Z3.Optimize.get_model opt with + | Some model -> + Z3.Model.get_const_decls model |> + List.fold_left (fun pkgs decl -> + match Z3.Model.get_const_interp model decl with + | Some p when Z3.Boolean.is_true p -> + (match OpamStd.String.cut_at + (Z3.Symbol.get_string (Z3.FuncDecl.get_name decl)) + '.' + with + | None -> pkgs + | Some (p, v) -> + let p = Cudf.lookup_package universe (p, int_of_string v) in + {p with + Cudf.was_installed = p.installed; + Cudf.installed = true} + :: pkgs) + | _ -> pkgs) + [] + | None -> failwith "no model ??" + +let call ~criteria ?timeout (preamble, universe, _ as cudf) = + (* try *) + log "Generating problem..."; + let cfg = match timeout with + | None -> [] + | Some secs -> ["timeout", string_of_int (int_of_float (1000. *. secs))] + in + let ctx = Z3.mk_context cfg in + let opt = Z3.Optimize.mk_opt ctx in + log "Generating package definitions"; + let expr, psym = def_packages ctx cudf in + Z3.Optimize.add opt expr; + log "Generating request"; + Z3.Optimize.add opt (def_request ctx cudf psym); + log "Generating optimization criteria"; + let _objs = + def_criteria ctx opt cudf psym (Syntax.criteria_of_string criteria) + in + log "Resolving..."; + match Z3.Optimize.check opt with + | UNSATISFIABLE -> + log "UNSAT"; + raise Common.CudfSolver.Unsat + | UNKNOWN -> + log "UNKNOWN"; + (try + let universe = + Cudf.load_universe (extract_solution_packages universe opt) + in + raise (Timeout (Some (Some preamble, universe))) + with Failure _ -> + raise (Timeout None)) + | SATISFIABLE -> + log "SAT: extracting model"; + let universe = + Cudf.load_universe (extract_solution_packages universe opt) + in + Some preamble, universe + (* with + * | (Timeout | Common.CudfSolver.Unsat | Failure _) as e -> raise e + * | e -> + * OpamConsole.error "Z3 error: %s" (Printexc.to_string e); + * OpamConsole.errmsg "%s" (Printexc.get_backtrace ()); + * raise e *) diff -Nru opam-2.0.10/src/solver/opamCudf.ml opam-2.1.2/src/solver/opamCudf.ml --- opam-2.0.10/src/solver/opamCudf.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamCudf.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -23,7 +23,22 @@ let s_pinned = "pinned" let s_version_lag = "version-lag" +let opam_invariant_package_name = + Common.CudfAdd.encode "=opam-invariant" + +let opam_invariant_package_version = 1 + +let opam_invariant_package = + opam_invariant_package_name, opam_invariant_package_version + +let is_opam_invariant p = + p.Cudf.package = opam_invariant_package_name + let cudf2opam cpkg = + if is_opam_invariant cpkg then + OpamConsole.error_and_exit `Internal_error + "Internal error: tried to access the CUDF opam invariant as an opam \ + package"; let sname = Cudf.lookup_package_property cpkg s_source in let name = OpamPackage.Name.of_string sname in let sver = Cudf.lookup_package_property cpkg s_source_number in @@ -60,23 +75,334 @@ let string_of_packages l = OpamStd.List.to_string string_of_package l -let to_json p = - `O [ ("name", `String p.Cudf.package); - ("version", `String (string_of_int p.Cudf.version)); - ("installed", `String (string_of_bool p.Cudf.installed)); - ] +module Json = struct + + let (>>=) = OpamStd.Option.Op.(>>=) + + let int_to_json n : OpamJson.t = `Float (float_of_int n) + let int_of_json = function + | `Float x -> Some (int_of_float x) + | _ -> None + + let string_to_json s : OpamJson.t = `String s + let string_of_json = function + | `String s -> Some s + | _ -> None + + let pkgname_to_json name : OpamJson.t = string_to_json name + let pkgname_of_json json = string_of_json json + + let bool_to_json bool : OpamJson.t = `Bool bool + let bool_of_json = function + | `Bool b -> Some b + | _ -> None + + let list_to_json elem_to_json li : OpamJson.t = + `A (List.map elem_to_json li) + let list_of_json elem_of_json = function + | `A jsons -> + begin try + let get = function + | None -> raise Not_found + | Some v -> v + in + Some (List.map (fun json -> get (elem_of_json json)) jsons) + with Not_found -> None + end + | _ -> None + + let option_to_json elem_to_json = function + | None -> `Null + | Some elem -> + let json = elem_to_json elem in + assert (json <> `Null); + json + let option_of_json elem_of_json = function + | `Null -> Some None + | other -> + elem_of_json other >>= fun elem -> Some (Some elem) + + let pair_to_json + fst_field fst_to_json + snd_field snd_to_json (fst, snd) = + `O [(fst_field, fst_to_json fst); + (snd_field, snd_to_json snd)] + + let pair_of_json + fst_field fst_of_json + snd_field snd_of_json : OpamJson.t -> _ = function + | `O dict -> + begin try + fst_of_json (List.assoc fst_field dict) >>= fun fst -> + snd_of_json (List.assoc snd_field dict) >>= fun snd -> + Some (fst, snd) + with Not_found -> None + end + | _ -> None + + let version_to_json n = int_to_json n + let version_of_json json = int_of_json json + + let relop_to_json : Cudf_types.relop -> _ = function + | `Eq -> `String "eq" + | `Neq -> `String "neq" + | `Geq -> `String "geq" + | `Gt -> `String "gt" + | `Leq -> `String "leq" + | `Lt -> `String "lt" + + let relop_of_json : _ -> Cudf_types.relop option = function + | `String "eq" -> Some `Eq + | `String "neq" -> Some `Neq + | `String "geq" -> Some `Geq + | `String "gt" -> Some `Gt + | `String "leq" -> Some `Leq + | `String "lt" -> Some `Lt + | _ -> None + + let enum_keep_to_json = function + | `Keep_version -> `String "keep_version" + | `Keep_package -> `String "keep_package" + | `Keep_feature -> `String "keep_feature" + | `Keep_none -> `String "keep_none" + let enum_keep_of_json = function + | `String "keep_version" -> Some (`Keep_version) + | `String "keep_package" -> Some (`Keep_package) + | `String "keep_feature" -> Some (`Keep_feature) + | `String "keep_none" -> Some (`Keep_none) + | _ -> None + + let constr_to_json constr = + option_to_json + (pair_to_json "relop" relop_to_json "version" version_to_json) + constr + let constr_of_json json = + option_of_json + (pair_of_json "relop" relop_of_json "version" version_of_json) + json + + let vpkg_to_json v = + pair_to_json "pkgname" pkgname_to_json "constr" constr_to_json v + let vpkg_of_json json = + pair_of_json "pkgname" pkgname_of_json "constr" constr_of_json json + + let vpkglist_to_json (vpkglist : Cudf_types.vpkglist) = + list_to_json vpkg_to_json vpkglist + let vpkglist_of_json jsons : Cudf_types.vpkglist option = + list_of_json vpkg_of_json jsons + + let veqpkg_to_json veqpkg = vpkg_to_json (veqpkg :> Cudf_types.vpkg) + let veqpkg_of_json json = + vpkg_of_json json >>= function + | (pkgname, None) -> Some (pkgname, None) + | (pkgname, Some (`Eq, version)) -> Some (pkgname, Some (`Eq, version)) + | (_pkgname, Some (_, _version)) -> None + + let veqpkglist_to_json veqpkglist = list_to_json veqpkg_to_json veqpkglist + let veqpkglist_of_json jsons = list_of_json veqpkg_of_json jsons + + let vpkgformula_to_json formula = + list_to_json (list_to_json vpkg_to_json) formula + let vpkgformula_of_json json = + list_of_json (list_of_json vpkg_of_json) json + + let binding_to_json value_to_json v = + pair_to_json "key" string_to_json "value" value_to_json v + let binding_of_json value_of_json v = + pair_of_json "key" string_of_json "value" value_of_json v + + let stanza_to_json value_to_json stanza = + list_to_json (binding_to_json value_to_json) stanza + let stanza_of_json value_of_json json = + list_of_json (binding_of_json value_of_json) json + + let type_schema_to_json tag value_to_json value = + pair_to_json + "type" string_to_json + "default" (option_to_json value_to_json) + (tag, value) + let rec typedecl1_to_json = function + | `Int n -> + type_schema_to_json "int" int_to_json n + | `Posint n -> + type_schema_to_json "posint" int_to_json n + | `Nat n -> + type_schema_to_json "nat" int_to_json n + | `Bool b -> + type_schema_to_json "bool" bool_to_json b + | `String s -> + type_schema_to_json "string" string_to_json s + | `Pkgname s -> + type_schema_to_json "pkgname" pkgname_to_json s + | `Ident s -> + type_schema_to_json "ident" string_to_json s + | `Enum (enums, v) -> + pair_to_json + "type" string_to_json + "default" (pair_to_json + "set" (list_to_json string_to_json) + "default" (option_to_json string_to_json)) + ("enum", (enums, v)) + | `Vpkg v -> + type_schema_to_json "vpkg" vpkg_to_json v + | `Vpkgformula v -> + type_schema_to_json "vpkgformula" vpkgformula_to_json v + | `Vpkglist v -> + type_schema_to_json "vpkglist" vpkglist_to_json v + | `Veqpkg v -> + type_schema_to_json "veqpkg" veqpkg_to_json v + | `Veqpkglist v -> + type_schema_to_json "veqpkglist" veqpkglist_to_json v + | `Typedecl td -> + type_schema_to_json "typedecl" typedecl_to_json td + and typedecl_to_json td = + stanza_to_json typedecl1_to_json td + + let rec typedecl1_of_json json = + pair_of_json "type" string_of_json "default" (fun x -> Some x) json >>= + fun (tag, json) -> + match tag with + | "int" -> option_of_json int_of_json json >>= fun x -> Some (`Int x) + | "posint" -> option_of_json int_of_json json >>= fun x -> Some (`Posint x) + | "nat" -> option_of_json int_of_json json >>= fun x -> Some (`Nat x) + | "bool" -> option_of_json bool_of_json json >>= fun x -> Some (`Bool x) + | "string" -> option_of_json string_of_json json >>= fun x -> Some (`String x) + | "pkgname" -> option_of_json string_of_json json >>= fun x -> Some (`Pkgname x) + | "ident" -> option_of_json string_of_json json >>= fun x -> Some (`Ident x) + | "enum" -> + pair_of_json + "set" (list_of_json string_of_json) + "default" (option_of_json string_of_json) + json >>= fun x -> Some (`Enum x) + | "vpkg" -> + option_of_json vpkg_of_json json >>= fun x -> Some (`Vpkg x) + | "vpkgformula" -> + option_of_json vpkgformula_of_json json >>= fun x -> Some (`Vpkgformula x) + | "vpkglist" -> + option_of_json vpkglist_of_json json >>= fun x -> Some (`Vpkglist x) + | "veqpkg" -> + option_of_json veqpkg_of_json json >>= fun x -> Some (`Veqpkg x) + | "veqpkglist" -> + option_of_json veqpkglist_of_json json >>= fun x -> Some (`Veqpkglist x) + | "typedecl" -> + option_of_json typedecl_of_json json >>= fun x -> Some (`Typedecl x) + | _ -> None + and typedecl_of_json json = + stanza_of_json typedecl1_of_json json + + let type_tagged_to_json tag value_to_json value = + pair_to_json "type" string_to_json "value" value_to_json (tag, value) + + let typed_value_to_json : Cudf_types.typed_value -> _ = function + | `Int n -> + type_tagged_to_json "int" int_to_json n + | `Posint n -> + type_tagged_to_json "posint" int_to_json n + | `Nat n -> + type_tagged_to_json "nat" int_to_json n + | `Bool b -> + type_tagged_to_json "bool" bool_to_json b + | `String s -> + type_tagged_to_json "string" string_to_json s + | `Pkgname name -> + type_tagged_to_json "pkgname" pkgname_to_json name + | `Ident id -> + type_tagged_to_json "ident" string_to_json id + | `Enum (enums, value) -> + type_tagged_to_json "enum" + (pair_to_json + "set" (list_to_json string_to_json) + "choice" string_to_json) (enums, value) + | `Vpkg vpkg -> + type_tagged_to_json "vpkg" vpkg_to_json vpkg + | `Vpkgformula vpkgformula -> + type_tagged_to_json "vpkgformula" vpkgformula_to_json vpkgformula + | `Vpkglist vpkglist -> + type_tagged_to_json "vpkglist" vpkglist_to_json vpkglist + | `Veqpkg veqpkg -> + type_tagged_to_json "veqpkg" veqpkg_to_json veqpkg + | `Veqpkglist veqpkglist -> + type_tagged_to_json "veqpkglist" veqpkglist_to_json veqpkglist + | `Typedecl typedecl -> + type_tagged_to_json "typedecl" typedecl_to_json typedecl + + let typed_value_of_json json : Cudf_types.typed_value option = + pair_of_json "type" string_of_json "value" (fun x -> Some x) json >>= + fun (tag, json) -> + match tag with + | "int" -> int_of_json json >>= fun x -> Some (`Int x) + | "posint" -> int_of_json json >>= fun x -> Some (`Posint x) + | "nat" -> int_of_json json >>= fun x -> Some (`Nat x) + | "bool" -> bool_of_json json >>= fun x -> Some (`Bool x) + | "string" -> string_of_json json >>= fun x -> Some (`String x) + | "pkgname" -> string_of_json json >>= fun x -> Some (`Pkgname x) + | "ident" -> string_of_json json >>= fun x -> Some (`Ident x) + | "enum" -> pair_of_json + "set" (list_of_json string_of_json) + "choice" string_of_json + json >>= fun p -> Some (`Enum p) + | "vpkg" -> vpkg_of_json json >>= fun x -> Some (`Vpkg x) + | "vpkgformula" -> vpkgformula_of_json json >>= fun x -> Some (`Vpkgformula x) + | "vpkglist" -> vpkglist_of_json json >>= fun x -> Some (`Vpkglist x) + | "veqpkg" -> veqpkg_of_json json >>= fun x -> Some (`Veqpkg x) + | "veqpkglist" -> veqpkglist_of_json json >>= fun x -> Some (`Veqpkglist x) + | "typedecl" -> typedecl_of_json json >>= fun x -> Some (`Typedecl x) + | _ -> None + + let package_to_json p = + `O [ ("name", pkgname_to_json p.Cudf.package); + ("version", version_to_json p.Cudf.version); + ("depends", vpkgformula_to_json p.Cudf.depends); + ("conflicts", vpkglist_to_json p.Cudf.conflicts); + ("provides", veqpkglist_to_json p.Cudf.provides); + ("installed", bool_to_json p.Cudf.installed); + ("was_installed", bool_to_json p.Cudf.was_installed); + ("keep", enum_keep_to_json p.Cudf.keep); + ("pkg_extra", stanza_to_json typed_value_to_json p.Cudf.pkg_extra); + ] + + let package_of_json = function + | `O dict -> + begin try + pkgname_of_json (List.assoc "name" dict) >>= fun package -> + version_of_json (List.assoc "version" dict) >>= fun version -> + vpkgformula_of_json (List.assoc "depends" dict) >>= fun depends -> + vpkglist_of_json (List.assoc "conflicts" dict) >>= fun conflicts -> + veqpkglist_of_json (List.assoc "provides" dict) >>= fun provides -> + bool_of_json (List.assoc "installed" dict) >>= fun installed -> + bool_of_json (List.assoc "was_installed" dict) >>= fun was_installed -> + enum_keep_of_json (List.assoc "keep" dict) >>= fun keep -> + stanza_of_json typed_value_of_json (List.assoc "pkg_extra" dict) >>= fun pkg_extra -> + Some { Cudf.package = package; + version; + depends; + conflicts; + provides; + installed; + was_installed; + keep; + pkg_extra; + } + with Not_found -> None + end + | _ -> None +end + +let to_json = Json.package_to_json +let of_json = Json.package_of_json (* Graph of cudf packages *) -module Pkg = struct +module Package = struct type t = Cudf.package include Common.CudfAdd let to_string = string_of_package let name_to_string t = t.Cudf.package let version_to_string t = string_of_int t.Cudf.version let to_json = to_json + let of_json = of_json end -module Action = OpamActionGraph.MakeAction(Pkg) +module Action = OpamActionGraph.MakeAction(Package) module ActionGraph = OpamActionGraph.Make(Action) let string_of_action = Action.to_string @@ -93,8 +419,101 @@ type conflict = Cudf.universe * int package_map * conflict_case -module Map = OpamStd.Map.Make(Pkg) -module Set = OpamStd.Set.Make(Pkg) +module Map = OpamStd.Map.Make(Package) +module Set = OpamStd.Set.Make(Package) + +let strong_and_weak_deps u deps = + (* strong deps are mandatory (constraint appearing in the top conjunction) + weak deps correspond to optional occurrences of a package, as part of a + disjunction: e.g. in (A>=4 & (B | A<5)), A>=4 is strong, and the other two + are weak. In the end we want to retain B and A>=4. *) + List.fold_left (fun (strong_deps, weak_deps) l -> + let names = + List.fold_left (fun acc (n, _) -> + OpamStd.String.Map.add n Set.empty acc) + OpamStd.String.Map.empty l + in + let set = + List.fold_left (fun acc (n, cstr) -> + List.fold_left (fun s x -> Set.add x s) + acc (Cudf.lookup_packages ~filter:cstr u n)) + Set.empty l + in + let by_name = + Set.fold (fun p -> + OpamStd.String.Map.update + p.Cudf.package (Set.add p) Set.empty) + set names + in + if OpamStd.String.Map.is_singleton by_name then + let name, versions = OpamStd.String.Map.choose by_name in + OpamStd.String.Map.update name (Set.inter versions) versions + strong_deps, + OpamStd.String.Map.remove name weak_deps + else + let by_name = + OpamStd.String.Map.filter + (fun name _ -> not (OpamStd.String.Map.mem name strong_deps)) + by_name + in + strong_deps, OpamStd.String.Map.union Set.union weak_deps by_name) + (OpamStd.String.Map.empty, OpamStd.String.Map.empty) + deps + +(* From a CUDF dependency CNF, extract the set of packages that can possibly be + part of a solution. + + This is much finer than [Common.CudfAdd.resolve_deps] which doesn't handle + conjunctions of versions (see [Graph.of_universe] below) *) +let dependency_set u deps = + let strong_deps, weak_deps = strong_and_weak_deps u deps in + OpamStd.String.Map.fold (fun _ -> Set.union) strong_deps @@ + OpamStd.String.Map.fold (fun _ -> Set.union) weak_deps @@ + Set.empty + +(* From a CUDF dependency CNF, extract the set of packages that will necessarily + be part of any solution (with a choice among packages of the same name of + course) *) +let _strong_dependency_set u deps = + let strong_deps, _ = strong_and_weak_deps u deps in + OpamStd.String.Map.fold (fun _ -> Set.union) strong_deps Set.empty + +let rec_strong_dependency_map u deps = + let module SM = OpamStd.String.Map in + let rec aux seen deps = + let strong_deps, _ = strong_and_weak_deps u deps in + OpamStd.String.Map.fold (fun name ps (seen, acc) -> + let seen, common_strong_deps = + Set.fold (fun p (seen, acc) -> + let seen, dmap = + try seen, Map.find p seen with Not_found -> + let seen, r = aux (Map.add p SM.empty seen) p.Cudf.depends in + Map.add p r seen, r + in + seen, + Some (match acc with + | None -> dmap + | Some m -> + SM.merge (fun _ a b -> match a, b with + | Some a, Some b -> Some (Set.union a b) + | _ -> None) + m dmap)) + ps (seen, None) + in + let strong_deps = + SM.add name ps + (OpamStd.Option.default SM.empty common_strong_deps) + in + seen, SM.union Set.inter acc strong_deps) + strong_deps (seen, SM.empty) + in + snd (aux Map.empty deps) + +let _rec_strong_dependency_set u deps = + OpamStd.String.Map.fold (fun _ -> Set.union) + (rec_strong_dependency_map u deps) + Set.empty + module Graph = struct module PG = struct @@ -119,44 +538,7 @@ let g = PG.create ~size:(Cudf.universe_size u) () in let iter_deps f deps = (* List.iter (fun d -> List.iter f (Common.CudfAdd.resolve_deps u d)) deps *) - let strong_deps, weak_deps = - (* strong deps are mandatory (constraint appearing in the top - conjunction) - weak deps correspond to optional occurrences of a package, as part of - a disjunction: e.g. in (A>=4 & (B | A<5)), A>=4 is strong, and the - other two are weak. In the end we want to retain B and A>=4. *) - List.fold_left (fun (strong_deps, weak_deps) l -> - let names = - List.fold_left (fun acc (n, _) -> - OpamStd.String.Map.add n Set.empty acc) - OpamStd.String.Map.empty l - in - let set = - List.fold_left (fun acc (n, cstr) -> - List.fold_left (fun s x -> Set.add x s) - acc (Cudf.lookup_packages ~filter:cstr u n)) - Set.empty l - in - let by_name = - Set.fold (fun p -> - OpamStd.String.Map.update - p.Cudf.package (Set.add p) Set.empty) - set names - in - if OpamStd.String.Map.is_singleton by_name then - let name, versions = OpamStd.String.Map.choose by_name in - OpamStd.String.Map.update name (Set.inter versions) versions - strong_deps, - OpamStd.String.Map.remove name weak_deps - else - strong_deps, OpamStd.String.Map.union Set.union weak_deps by_name) - (OpamStd.String.Map.empty, OpamStd.String.Map.empty) deps - in - OpamStd.String.Map.iter (fun _ p -> Set.iter f p) strong_deps; - OpamStd.String.Map.iter (fun name p -> - if not (OpamStd.String.Map.mem name strong_deps) - then Set.iter f p) - weak_deps + Set.iter f (dependency_set u deps) in Cudf.iter_packages (fun p -> @@ -174,19 +556,8 @@ let transitive_closure g = PO.O.add_transitive_closure g - let close_and_linearize g pkgs = - let _, l = - Topo.fold - (fun pkg (closure, topo) -> - if Set.mem pkg closure then - closure, pkg :: topo - else if List.exists (fun p -> Set.mem p closure) (PG.pred g pkg) then - Set.add pkg closure, pkg :: topo - else - closure, topo) - g - (pkgs, []) in - l + let linearize g pkgs = + Topo.fold (fun p acc -> if Set.mem p pkgs then p::acc else acc) g [] let mirror = PO.O.mirror @@ -195,28 +566,28 @@ (** Special package used by Dose internally, should generally be filtered out *) let dose_dummy_request = Algo.Depsolver.dummy_request.Cudf.package -let is_dose_request cpkg = cpkg.Cudf.package = dose_dummy_request - -let filter_dependencies f_direction universe packages = - log ~level:3 "filter deps: build graph"; - let graph = f_direction (Graph.of_universe universe) in - let packages = Set.of_list packages in - log ~level:3 "filter deps: close_and_linearize"; - let r = Graph.close_and_linearize graph packages in - log ~level:3 "filter deps: done"; - r +let is_artefact cpkg = + is_opam_invariant cpkg || + cpkg.Cudf.package = dose_dummy_request -let dependencies = filter_dependencies (fun x -> x) +let dependencies universe packages = + Set.fixpoint (fun p -> dependency_set universe p.Cudf.depends) packages (* similar to Algo.Depsolver.dependency_closure but with finer results on version sets *) -let reverse_dependencies = filter_dependencies Graph.mirror +let reverse_dependencies universe packages = + let graph = Graph.of_universe universe in + Set.fixpoint (fun p -> Set.of_list (Graph.pred graph p)) packages (* similar to Algo.Depsolver.reverse_dependency_closure but more reliable *) +let dependency_sort universe packages = + let graph = Graph.of_universe universe in + Graph.linearize graph packages |> List.rev + let string_of_atom (p, c) = let const = function | None -> "" - | Some (r,v) -> Printf.sprintf " (%s %d)" (OpamPrinter.relop r) v in + | Some (r,v) -> Printf.sprintf " (%s %d)" (OpamPrinter.FullPos.relop_kind r) v in Printf.sprintf "%s%s" p (const c) let string_of_vpkgs constr = @@ -262,11 +633,6 @@ }] *) -let vpkg2opam cudfnv2opam vpkg = - match vpkg2atom cudfnv2opam vpkg with - | p, None -> p, Empty - | p, Some (relop,v) -> p, Atom (relop, v) - let conflict_empty ~version_map univ = Conflicts (univ, version_map, Conflict_dep (fun () -> [])) let make_conflicts ~version_map univ = function @@ -284,230 +650,393 @@ in String.concat (OpamConsole.colorise `yellow arrow) sl -let strings_of_reasons packages cudfnv2opam unav_reasons rs = - let open Algo.Diagnostic in - let is_base cpkg = cpkg.Cudf.keep = `Keep_version in - let rec aux = function - | [] -> [] - | Conflict (i,j,jc)::rs -> - if is_dose_request i || is_dose_request j then - let a = if is_dose_request i then j else i in - if is_dose_request a then aux rs else - if is_base a then - let str = - Printf.sprintf "Package %s is part of the base for this compiler \ - and can't be changed" - (OpamPackage.name_to_string (cudf2opam a)) in - str :: aux rs - else - let str = - Printf.sprintf "Conflicting query for package %s" - (OpamPackage.to_string (cudf2opam a)) in - str :: aux rs - else - if i.Cudf.package = j.Cudf.package then - if is_base i || is_base j then - let str = - Printf.sprintf "Package %s is part of the base for this compiler \ - and can't be changed" - (OpamPackage.name_to_string (cudf2opam i)) in - str :: aux rs - else - let str = Printf.sprintf "No available version of %s satisfies the \ - constraints" - (OpamPackage.name_to_string (cudf2opam i)) in - str :: aux rs - else - let nva = cudf2opam i in - let versions, rs = - List.fold_left (fun (versions, rs) -> function - | Conflict (i1, _, jc1) when - (cudf2opam i1).name = nva.name && jc1 = jc -> - OpamPackage.Version.Set.add (cudf2opam i1).version versions, rs - | r -> versions, r::rs) - (OpamPackage.Version.Set.singleton nva.version, []) rs - in - let rs = List.rev rs in - let formula = - OpamFormula.formula_of_version_set - (OpamPackage.versions_of_name packages nva.name) versions - in - let str = Printf.sprintf "%s is in conflict with %s" - (OpamFormula.to_string (Atom (nva.name, formula))) - (OpamFormula.to_string - (OpamFormula.of_atom_formula (Atom (vpkg2atom cudfnv2opam jc)))) - in - str :: aux rs - | Missing (p,missing) :: rs when is_dose_request p -> - (* Requested pkg missing *) - let atoms = - List.map (fun vp -> - try vpkg2atom cudfnv2opam vp - with Not_found -> - OpamPackage.Name.of_string (Common.CudfAdd.decode (fst vp)), None) - missing - in - let names = OpamStd.List.sort_nodup compare (List.map fst atoms) in - List.map (fun name -> - let formula = - OpamFormula.ors (List.map (function - | n, Some atom when n = name -> Atom atom - | _ -> Empty) - atoms) - in - let all_versions = OpamPackage.versions_of_name packages name in - let formula = OpamFormula.simplify_version_set all_versions formula in - unav_reasons (name, formula)) - names @ - aux rs - | Missing _ :: rs (* dependency missing, treated in strings_of_chains *) - | Dependency _ :: rs -> aux rs +let formula_of_vpkgl cudfnv2opam all_packages vpkgl = + let atoms = + List.map (fun vp -> + try vpkg2atom cudfnv2opam vp + with Not_found -> + OpamPackage.Name.of_string (Common.CudfAdd.decode (fst vp)), None) + vpkgl in - aux rs + let names = OpamStd.List.sort_nodup compare (List.map fst atoms) in + let by_name = + List.map (fun name -> + let formula = + OpamFormula.ors (List.map (function + | n, Some atom when n = name -> Atom atom + | _ -> Empty) + atoms) + in + let all_versions = OpamPackage.versions_of_name all_packages name in + let formula = OpamFormula.simplify_version_set all_versions formula in + Atom (name, formula)) + names + in + OpamFormula.ors by_name + +(* - Conflict message handling machinery - *) + +(* chain sets: sets of package lists *) +module ChainSet = struct -let make_chains packages cudfnv2opam depends = + include OpamStd.Set.Make (struct + type t = Package.t list + let rec compare t1 t2 = match t1, t2 with + | [], [] -> 0 + | [], _ -> -1 + | _, [] -> 1 + | p1::r1, p2::r2 -> + match Package.compare p1 p2 with 0 -> compare r1 r2 | n -> n + let to_string t = + arrow_concat (List.rev_map Package.to_string t) + let to_json t = Json.list_to_json Package.to_json t + let of_json j = Json.list_of_json Package.of_json j + end) + + (** Turns a set of lists into a list of sets *) + let rec transpose cs = + let hds, tls = + fold (fun c (hds, tls) -> match c with + | hd::tl -> Set.add hd hds, add tl tls + | [] -> hds, tls) + cs (Set.empty, empty) + in + if Set.is_empty hds then [] + else hds :: transpose tls + + (** cs1 precludes cs2 if it contains a list that is prefix to all elements of + cs2 *) + let precludes cs1 cs2 = + let rec list_is_prefix pfx l = match pfx, l with + | [], _ -> true + | a::r1, b::r2 when Package.equal a b -> list_is_prefix r1 r2 + | _ -> false + in + exists (fun pfx -> for_all (fun l -> list_is_prefix pfx l) cs2) cs1 + + let length cs = fold (fun l acc -> min (List.length l) acc) cs max_int +end + +type explanation = + [ `Conflict of string option * string list * bool + | `Missing of string option * string * + (OpamPackage.Name.t * OpamFormula.version_formula) + OpamFormula.formula + ] + +let extract_explanations packages cudfnv2opam reasons : explanation list = + log "Conflict reporting"; let open Algo.Diagnostic in - let map_addlist k v map = - try Map.add k (v @ Map.find k map) map - with Not_found -> Map.add k v map in - let roots,notroots,deps,vpkgs = - List.fold_left (fun (roots,notroots,deps,vpkgs) -> function - | Dependency (i, vpkgl, jl) when not (is_dose_request i) -> - Set.add i roots, - List.fold_left (fun notroots j -> Set.add j notroots) notroots jl, - map_addlist i jl deps, - map_addlist i vpkgl vpkgs - | Missing (i, vpkgl) when not (is_dose_request i) -> - let jl = - List.map (fun (package,_) -> - {Cudf.default_package with Cudf.package}) - vpkgl in - Set.add i roots, - notroots, - map_addlist i jl deps, - map_addlist i vpkgl vpkgs - | _ -> roots, notroots, deps, vpkgs) - (Set.empty,Set.empty,Map.empty,Map.empty) - depends - in - let roots = Set.diff roots notroots in - if Set.is_empty roots then [] else - let children cpkgs = - Set.fold (fun c acc -> - List.fold_left (fun m a -> Set.add a m) acc - (try Map.find c deps with Not_found -> [])) - cpkgs Set.empty - in - let rec aux constrs direct_deps = - if Set.is_empty direct_deps then [[]] else - let depnames = - Set.fold (fun p set -> OpamStd.String.Set.add p.Cudf.package set) - direct_deps OpamStd.String.Set.empty in - OpamStd.String.Set.fold (fun name acc -> - let name_deps = (* Gather all deps with the given name *) - Set.filter (fun p -> p.Cudf.package = name) direct_deps in - let name_constrs = - List.map (List.filter (fun (n,_) -> n = name)) constrs in - let to_opam_constr p = - snd (vpkg2opam cudfnv2opam p) - in - let formula = - OpamFormula.ors - (List.map (fun conj -> - OpamFormula.ands (List.map to_opam_constr conj)) - name_constrs) + let open Set.Op in + let module CS = ChainSet in + (* Definitions and printers *) + let all_opam = + let add p set = + if is_artefact p then set + else OpamPackage.Set.add (cudf2opam p) set + in + List.fold_left (fun acc -> function + | Conflict (l, r, _) -> add l @@ add r @@ acc + | Dependency (l, _, rs) -> + List.fold_left (fun acc p -> add p acc) (add l acc) rs + | Missing (p, _) -> add p acc) + OpamPackage.Set.empty + reasons + in + let print_set pkgs = + if Set.exists is_artefact pkgs then + if Set.exists is_opam_invariant pkgs then "(invariant)" + else "(request)" + else + let nvs = + OpamPackage.to_map @@ + Set.fold (fun p s -> OpamPackage.Set.add (cudf2opam p) s) + pkgs OpamPackage.Set.empty + in + let strs = + OpamPackage.Name.Map.mapi (fun name versions -> + let all_versions = OpamPackage.versions_of_name all_opam name in + let formula = + OpamFormula.formula_of_version_set all_versions versions + in + OpamFormula.to_string (Atom (name, formula))) + nvs + in + String.concat ", " (OpamPackage.Name.Map.values strs) + in + let cs_to_string ?(hl_last=true) cs = + let rec aux vpkgl = function + | [] -> [] + | pkgs :: r -> + let vpkgl1 = + List.fold_left (fun acc -> function + | Dependency (p1, vpl, _) when Set.mem p1 pkgs -> + List.rev_append vpl acc + | _ -> acc) + [] reasons in - let opam_name = - OpamPackage.Name.of_string (Common.CudfAdd.decode name) + if Set.exists is_artefact pkgs then + if Set.exists is_opam_invariant pkgs then + Printf.sprintf "(invariant)" + :: aux vpkgl1 r + else if r = [] then ["(request)"] + else aux vpkgl1 r (* request *) + else if vpkgl = [] then + print_set pkgs :: aux vpkgl1 r + else + let f = + let vpkgl = + List.filter + (fun (n, _) -> Set.exists (fun p -> p.package = n) pkgs) + vpkgl + in + formula_of_vpkgl cudfnv2opam packages vpkgl in - let all_versions = OpamPackage.versions_of_name packages opam_name in - let formula = OpamFormula.simplify_version_set all_versions formula in - let formula = opam_name, formula in - let children_constrs = - List.map (fun p -> try Map.find p vpkgs with Not_found -> []) - (Set.elements name_deps) in - let chains = aux children_constrs (children name_deps) in - List.fold_left - (fun acc chain -> (formula :: chain) :: acc) - acc chains - ) - depnames [] - in - let start_constrs = - let set = - Set.fold (fun p acc -> OpamStd.String.Set.add p.Cudf.package acc) - roots OpamStd.String.Set.empty in - List.map (fun name -> [name,None]) (OpamStd.String.Set.elements set) in - aux start_constrs roots - -let strings_of_final_reasons packages cudfnv2opam unav_reasons reasons = + let s = OpamFormula.to_string f in + (if hl_last && r = [] then OpamConsole.colorise' [`red;`bold] s else s) + :: aux vpkgl1 r + in + arrow_concat (aux [] (CS.transpose (CS.map List.rev cs))) + in + let get t x = try Hashtbl.find t x with Not_found -> Set.empty in + let add_set t l set = + match Hashtbl.find t l with + | exception Not_found -> Hashtbl.add t l set + | s -> Hashtbl.replace t l (Set.union set s) + in + (* Gather all data in hashtables *) + let ct = Hashtbl.create 53 in + let deps = Hashtbl.create 53 in + let rdeps = Hashtbl.create 53 in + let missing = Hashtbl.create 53 in + List.iter (function + | Conflict (l, r, _) -> + add_set ct l (Set.singleton r); + add_set ct r (Set.singleton l) + | Dependency (l, _, rs) -> + add_set deps l (Set.of_list rs); + List.iter (fun r -> add_set rdeps r (Set.singleton l)) rs + | Missing (p, deps) -> + Hashtbl.add missing p deps) + reasons; + (* Get paths from the conflicts to requested or invariant packages *) + let roots = + let add_artefacts set = + Hashtbl.fold (fun p _ acc -> + if is_artefact p then Set.add p acc else acc) + set + in + Set.empty |> add_artefacts deps |> add_artefacts missing |> add_artefacts ct + in + let conflicting = + Hashtbl.fold (fun p _ -> Set.add p) ct Set.empty + in + let all_conflicting = + Hashtbl.fold (fun k _ acc -> Set.add k acc) missing conflicting + in + let ct_chains = + (* get a covering tree from the roots to all reachable packages. + We keep only shortest chains, but all of them *) + (* The chains are stored as lists from packages back to the roots *) + let rec aux pchains seen acc = + if Map.is_empty pchains then acc else + let seen, new_chains = + Map.fold (fun p chains (seen1, new_chains) -> + let append_to_chains pkg acc = + let chain = CS.map (fun c -> pkg :: c) chains in + Map.update pkg (CS.union chain) CS.empty acc + in + let ds = get deps p in + let dsc = ds %% all_conflicting in + if not (Set.is_empty dsc) then + dsc ++ seen1, Set.fold append_to_chains (dsc -- seen1) new_chains + else + Set.fold (fun d (seen1, new_chains) -> + if Set.mem d seen then seen1, new_chains + else Set.add d seen1, append_to_chains d new_chains) + ds (seen1, new_chains)) + pchains (seen, Map.empty) + in + aux new_chains seen @@ + Map.union (fun _ _ -> assert false) pchains acc + in + let init_chains = + Set.fold (fun p -> Map.add p (CS.singleton [p])) roots Map.empty + in + aux init_chains roots Map.empty + in let reasons = - strings_of_reasons packages cudfnv2opam unav_reasons reasons + (* order "reasons" by most interesting first: version conflicts then package + then missing + shortest chains first *) + let clen p = try CS.length (Map.find p ct_chains) with Not_found -> 0 in + let version_conflict = function + | Conflict (l, r, _) -> l.Cudf.package = r.Cudf.package + | _ -> false + in + let cmp a b = match a, b with + | Conflict (l1, r1, _), Conflict (l2, r2, _) -> + let va = version_conflict a and vb = version_conflict b in + if va && not vb then -1 else + if vb && not va then 1 else + (match compare (clen l1 + clen r1) (clen l2 + clen r2) with + | 0 -> (match Package.compare l1 l2 with + | 0 -> Package.compare r1 r2 + | n -> n) + | n -> n) + | _, Conflict _ -> 1 + | Conflict _, _ -> -1 + | Missing (p1, _), Missing (p2, _) -> + (match compare (clen p1) (clen p2) with + | 0 -> Package.compare p1 p2 + | n -> n) + | _, Missing _ -> 1 + | Missing _, _ -> -1 + | Dependency _, Dependency _ -> 0 (* we don't care anymore *) + in + List.sort_uniq cmp reasons + in + + let has_invariant p = + let chain_has_invariant cs = + CS.exists (List.exists is_opam_invariant) cs + in + try chain_has_invariant (Map.find p ct_chains) + with Not_found -> false + in + + let explanations, _remaining_ct_chains = + List.fold_left (fun (explanations, ct_chains) re -> + let cst ?hl_last ct_chains p = + let chains = Map.find p ct_chains in + Map.filter (fun _ c -> not (CS.precludes chains c)) ct_chains, + cs_to_string ?hl_last chains + in + try + match re with + | Conflict (l, r, _) -> + let ct_chains, csl = cst ct_chains l in + let ct_chains, csr = cst ct_chains r in + let msg1 = + if l.Cudf.package = r.Cudf.package then + Some (Package.name_to_string l) + else + None + in + let msg2 = List.sort_uniq compare [csl; csr] in + let msg3 = + (has_invariant l || has_invariant r) && + not (List.exists (function `Conflict (_,_,has_invariant) -> has_invariant | _ -> false) explanations) + in + let msg = `Conflict (msg1, msg2, msg3) in + if List.mem msg explanations then raise Not_found else + msg :: explanations, ct_chains + | Missing (p, deps) -> + let ct_chains, csp = cst ~hl_last:false ct_chains p in + let fdeps = formula_of_vpkgl cudfnv2opam packages deps in + let sdeps = OpamFormula.to_string fdeps in + let msg = `Missing (Some csp, sdeps, fdeps) in + if List.mem msg explanations then raise Not_found else + msg :: explanations, ct_chains + | Dependency _ -> + explanations, ct_chains + with Not_found -> + explanations, ct_chains) + ([], ct_chains) reasons in - OpamStd.List.sort_nodup compare reasons -let strings_of_chains packages cudfnv2opam unav_reasons reasons = - let chains = make_chains packages cudfnv2opam reasons in - let string_of_chain c = - match List.rev c with - | (name, vform) :: r -> - let all_versions = OpamPackage.versions_of_name packages name in - let formula = OpamFormula.simplify_version_set all_versions vform in - arrow_concat - (List.rev_map (fun c -> OpamFormula.to_string (Atom c)) r @ - [OpamConsole.colorise' [`red;`bold] - (OpamFormula.to_string (Atom (name, vform)))]) - ^ (match unav_reasons (name, formula) with "" -> "" | s -> "\n " ^ s) - | [] -> "" + let same_depexts sdeps fdeps = + List.for_all (function + | `Missing (_, sdeps', fdeps') -> sdeps = sdeps' && fdeps = fdeps' + | _ -> false) in - List.map string_of_chain chains + match explanations with + | `Missing (_, sdeps, fdeps) :: rest when same_depexts sdeps fdeps rest -> + [`Missing (None, sdeps, fdeps)] + | _ -> explanations let strings_of_cycles cycles = List.map arrow_concat cycles -let strings_of_conflict packages unav_reasons = function +let string_of_conflict ?(start_column=0) (msg1, msg2, msg3) = + let width = OpamStd.Sys.terminal_columns () - start_column - 2 in + OpamStd.Format.reformat ~start_column ~indent:2 msg1 ^ + OpamStd.List.concat_map ~left:"\n- " ~nil:"" "\n- " + (fun s -> OpamStd.Format.reformat ~indent:2 ~width s) msg2 ^ + OpamStd.List.concat_map ~left:"\n" ~nil:"" "\n" + (fun s -> OpamStd.Format.reformat ~indent:2 ~width s) msg3 + +let conflict_explanations_raw packages = function | univ, version_map, Conflict_dep reasons -> let r = reasons () in let cudfnv2opam = cudfnv2opam ~cudf_universe:univ ~version_map in - strings_of_final_reasons packages cudfnv2opam unav_reasons r, - strings_of_chains packages cudfnv2opam unav_reasons r, + List.rev (extract_explanations packages cudfnv2opam r), [] | _univ, _version_map, Conflict_cycle cycles -> - [], [], strings_of_cycles cycles + [], cycles -let conflict_chains packages = function - | cudf_universe, version_map, Conflict_dep r -> - make_chains packages (cudfnv2opam ~cudf_universe ~version_map) (r ()) - | _ -> [] - -let string_of_conflict packages unav_reasons conflict = - let final, chains, cycles = - strings_of_conflict packages unav_reasons conflict - in +let string_of_explanation unav_reasons = function + | `Conflict (kind, packages, has_invariant) -> + let msg1 = + let format_package_name p = + Printf.sprintf "No agreement on the version of %s:" + (OpamConsole.colorise `bold p) + in + OpamStd.Option.map_default format_package_name + "Incompatible packages:" kind + and msg3 = + if has_invariant then + ["You can temporarily relax the switch invariant with \ + `--update-invariant'"] + else + [] + in + (msg1, packages, msg3) + | `Missing (csp, sdeps, fdeps) -> + let sdeps = OpamConsole.colorise' [`red;`bold] sdeps in + let msg1 = "Missing dependency:" + and msg2 = + OpamStd.Option.map_default (fun csp -> arrow_concat [csp; sdeps]) sdeps csp + and msg3 = OpamFormula.fold_right (fun a x -> unav_reasons x::a) [] fdeps + in + (msg1, [msg2], msg3) + +let conflict_explanations packages unav_reasons = function + | univ, version_map, Conflict_dep reasons -> + let r = reasons () in + let cudfnv2opam = cudfnv2opam ~cudf_universe:univ ~version_map in + let explanations = extract_explanations packages cudfnv2opam r in + List.rev_map (string_of_explanation unav_reasons) explanations, [] + | _univ, _version_map, Conflict_cycle cycles -> + [], strings_of_cycles cycles + +let string_of_explanations unav_reasons (cflts, cycles) = + let cflts = List.map (string_of_explanation unav_reasons) cflts in + let cycles = strings_of_cycles cycles in let b = Buffer.create 1024 in let pr_items b l = - Buffer.add_string b (OpamStd.Format.itemize (fun s -> s) l) + Buffer.add_string b + (OpamStd.Format.itemize (fun s -> s) l) in if cycles <> [] then Printf.bprintf b "The actions to process have cyclic dependencies:\n%a" pr_items cycles; - if chains <> [] then - Printf.bprintf b - "The following dependencies couldn't be met:\n%a" - pr_items chains; - if final <> [] then - Printf.bprintf b - "Your request can't be satisfied:\n%a" - pr_items final; - if final = [] && chains = [] && cycles = [] then (* No explanation found *) + if cflts <> [] then + Buffer.add_string b + (OpamStd.Format.itemize ~bullet:(OpamConsole.colorise `red " * ") + (string_of_conflict ~start_column:4) cflts); + if cflts = [] && cycles = [] then (* No explanation found *) Printf.bprintf b "Sorry, no solution found: \ there seems to be a problem with your request.\n"; Buffer.add_string b "\n"; Buffer.contents b +let string_of_conflicts packages unav_reasons conflict = + string_of_explanations unav_reasons + (conflict_explanations_raw packages conflict) + let check flag p = try Cudf.lookup_typed_package_property p flag = `Bool true with Not_found -> false @@ -623,6 +1152,161 @@ | Some f -> f | None -> assert false +let preprocess_cudf_request (props, univ, creq) criteria = + let chrono = OpamConsole.timer () in + let univ0 = univ in + let do_trimming = + match OpamSolverConfig.(!r.cudf_trim) with + | Some "simple" -> Some false + | b -> + match OpamStd.Option.Op.(b >>= OpamStd.Config.bool_of_string) with + | Some false -> None + | Some true -> Some true + | None -> + (* Full trimming is only correct when there is no maximisation criteria, + so automatically set it to true in this case *) + let neg_crit_re = + Re.(seq [char '-'; + rep1 (diff any (set ",[(")); + opt (seq [set "[("; rep1 (diff any (set ")]")); set ")]"])]) + in + let all_neg_re = + Re.(whole_string (seq [rep (seq [neg_crit_re; char ',']); + neg_crit_re])) + in + Some (Re.execp (Re.compile all_neg_re) criteria) + in + let univ = + let open Set.Op in + let vpkg2set vp = Set.of_list (Common.CudfAdd.resolve_deps univ vp) in + let to_install = + vpkg2set creq.Cudf.install + ++ Set.of_list (Cudf.lookup_packages univ opam_invariant_package_name) + in + let to_install_formula = + List.map (fun x -> [x]) @@ + (opam_invariant_package_name, None) :: + creq.Cudf.install @ creq.Cudf.upgrade + in + let to_map set = + Set.fold (fun p -> + OpamStd.String.Map.update p.Cudf.package (Set.add p) Set.empty) + set OpamStd.String.Map.empty + in + let packages = + match do_trimming with + | None -> + Set.of_list (Cudf.get_packages univ) + | Some false -> (* "simple" trimming *) + let strong_deps_cone = + rec_strong_dependency_map univ to_install_formula + in + (* only limit visible versions of packages appearing in + strong_deps_cone *) + let filter p = + p.Cudf.installed || + match OpamStd.String.Map.find_opt p.Cudf.package strong_deps_cone + with + | Some ps -> Set.mem p ps + | None -> true + in + Set.of_list (Cudf.get_packages ~filter univ) + | Some true -> (* "full" trimming *) + let strong_deps_cone = + rec_strong_dependency_map univ to_install_formula + in + (* limit visibility to only "possibly interesting" packages; this + includes all installed packages, including their other versions (the + changes to installed packages may need up/downgrades). *) + let interesting_set = + List.fold_left (fun acc p -> + let name = p.Cudf.package in + if OpamStd.String.Map.mem name strong_deps_cone then acc + else acc ++ Set.of_list (Cudf.lookup_packages univ name)) + (OpamStd.String.Map.fold (fun _ -> Set.union) + strong_deps_cone Set.empty) + (Cudf.get_packages ~filter:(fun p -> p.Cudf.installed) univ) + in + (* we will also need all the weak dependencies of all these packages *) + Set.fixpoint (fun p -> + Set.filter (fun d -> + not (OpamStd.String.Map.mem d.Cudf.package strong_deps_cone)) + (dependency_set univ p.Cudf.depends)) + interesting_set + in + let direct_conflicts p = + let base_conflicts = + Set.filter (fun q -> q.Cudf.package <> p.Cudf.package) + (vpkg2set p.Cudf.conflicts) + in + (* Dependencies not matching constraints are also conflicts *) + List.fold_left (fun acc -> function + | (n, c) :: disj when List.for_all (fun (m, _) -> m = n) disj -> + let coset = function + | Some (op, v) -> + let filter = Some (OpamFormula.neg_relop op, v) in + Set.of_list (Cudf.lookup_packages ~filter univ n) + | None -> Set.empty + in + acc ++ + List.fold_left (fun acc (_, c) -> acc %% coset c) (coset c) disj + | _ -> acc) + base_conflicts p.Cudf.depends + in + let cache = Hashtbl.create 513 in + let cache_direct = Hashtbl.create 513 in + (* Don't explore deeper than that for transitive conflicts *) + let max_dig_depth = OpamSolverConfig.(!r.dig_depth) in + let rec transitive_conflicts seen p = + try Hashtbl.find cache p with Not_found -> + let direct = + try Hashtbl.find cache_direct p with Not_found -> + let conflicts = direct_conflicts p in + Hashtbl.add cache_direct p conflicts; + conflicts + in + if Set.mem p seen || Set.cardinal seen >= max_dig_depth - 1 then direct + else + let seen = Set.add p seen in + let conflicts = + direct ++ + List.fold_left (fun acc disj -> + acc ++ + Set.map_reduce ~default:Set.empty + (transitive_conflicts seen) + Set.inter + (vpkg2set disj)) + Set.empty + p.Cudf.depends + in + Hashtbl.add cache p conflicts; + conflicts + in + let conflicts = + OpamStd.String.Map.fold (fun _ ps acc -> + acc ++ + Set.map_reduce ~default:Set.empty + (transitive_conflicts Set.empty) + Set.inter + ps) + (to_map to_install) + Set.empty + in + log "Conflicts: %a (%a) pkgs to remove" + (slog OpamStd.Op.(string_of_int @* Set.cardinal)) conflicts + (slog OpamStd.Op.(string_of_int @* Set.cardinal)) (conflicts %% packages); + Cudf.load_universe (Set.elements (packages -- conflicts)) + in + log "Preprocess cudf request (trimming: %s): from %d to %d packages in %.2fs" + (match do_trimming with + None -> "none" | Some false -> "simple" | Some true -> "full") + (Cudf.universe_size univ0) + (Cudf.universe_size univ) + (chrono ()); + props, univ, creq + +exception Timeout of Algo.Depsolver.solver_result option + let call_external_solver ~version_map univ req = let cudf_request = to_cudf univ req in if Cudf.universe_size univ > 0 then @@ -630,20 +1314,46 @@ let chrono = OpamConsole.timer () in ignore (dump_cudf_request ~version_map cudf_request criteria OpamSolverConfig.(!r.cudf_file)); + (* Wrap a return of exn Timeout through Depsolver *) + let check_request_using ~call_solver ~criteria ~explain req = + let timed_out = ref false in + let call_solver args = + try call_solver args with + | OpamCudfSolver.Timeout (Some s) -> timed_out := true; s + | OpamCudfSolver.Timeout None -> raise (Timeout None) + in + let r = + Algo.Depsolver.check_request_using ~call_solver ~criteria ~explain req + in + if !timed_out then raise (Timeout (Some r)) else r + in try + let cudf_request = + if not OpamSolverConfig.(!r.preprocess) then cudf_request + else preprocess_cudf_request cudf_request criteria + in let r = - Algo.Depsolver.check_request_using + check_request_using ~call_solver:(OpamSolverConfig.call_solver ~criteria) ~criteria ~explain:true cudf_request in - log "Solver call done in %.3f" (chrono ()); + log "Solver call done in %.3fs" (chrono ()); r with - | OpamCudfSolver.Timeout -> + | Timeout (Some sol) -> + log "Solver call TIMED OUT with solution after %.3fs" (chrono ()); + OpamConsole.warning + "Resolution of the installation set timed out, so the following \ + solution might not be optimal.\n\ + You may want to make your request more precise, increase the value \ + of OPAMSOLVERTIMEOUT (currently %.1fs), or try a different solver." + OpamSolverConfig.(OpamStd.Option.default 0. !r.solver_timeout); + sol + | Timeout None -> let msg = Printf.sprintf "Sorry, resolution of the request timed out.\n\ - Try to specify a simpler request, use a different solver, or \ + Try to specify a more precise request, use a different solver, or \ increase the allowed time by setting OPAMSOLVERTIMEOUT to a bigger \ value (currently, it is set to %.1f seconds)." OpamSolverConfig.(OpamStd.Option.default 0. !r.solver_timeout) @@ -700,14 +1410,22 @@ Success (Cudf.load_universe []) | Algo.Depsolver.Error str -> fail str | Algo.Depsolver.Unsat r -> + let msg = + Printf.sprintf + "The solver (%s) pretends there is no solution while that's apparently \ + false.\n\ + This is likely an issue with the solver interface, please try a \ + different solver and report if you were using a supported one." + (let module Solver = (val OpamSolverConfig.(Lazy.force !r.solver)) in + Solver.name) + in match r with | Some ({Algo.Diagnostic.result = Algo.Diagnostic.Failure _; _} as r) -> + OpamConsole.error "%s" msg; make_conflicts ~version_map univ r - | Some {Algo.Diagnostic.result = Algo.Diagnostic.Success _; _}(* -> *) - (* fail "inconsistent return value." *) + | Some {Algo.Diagnostic.result = Algo.Diagnostic.Success _; _} | None -> - (* External solver did not provide explanations, hopefully this will *) - check_request ~version_map univ req + raise (Solver_failure msg) let diff univ sol = let before = @@ -735,8 +1453,19 @@ let resolve ~extern ~version_map universe request = log "resolve request=%a" (slog string_of_request) request; - if extern then get_final_universe ~version_map universe request - else check_request ~version_map universe request + let resp = + match check_request ~version_map universe request with + | Success _ when extern -> get_final_universe ~version_map universe request + | resp -> resp + in + let cleanup univ = + Cudf.remove_package univ opam_invariant_package + in + let () = match resp with + | Success univ -> cleanup univ + | Conflicts (univ, _, _) -> cleanup univ + in + resp let to_actions f universe result = let aux u1 u2 = @@ -847,10 +1576,11 @@ (* Nothing can cause these actions after itself *) Unknown | (`Install _ | `Reinstall _), `Before, _ -> - (* An install or reinstall doesn't cause any oter actions on its + (* An install or reinstall doesn't cause any other actions on its dependendants *) Unknown | `Build _, _, _ | _, _, `Build _ -> assert false + | `Fetch _, _, _ | _, _, `Fetch _ -> assert false (* XXX CHECK *) in let get_causes acc roots = let rec aux seen depth pkgname causes = @@ -923,7 +1653,7 @@ required reinstallations and computing the graph of dependency of required actions *) let atomic_actions ~simple_universe ~complete_universe root_actions = - log "graph_of_actions root_actions=%a" + log ~level:2 "graph_of_actions root_actions=%a" (slog string_of_actions) root_actions; let to_remove, to_install = diff -Nru opam-2.0.10/src/solver/opamCudf.mli opam-2.1.2/src/solver/opamCudf.mli --- opam-2.0.10/src/solver/opamCudf.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamCudf.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -14,11 +14,19 @@ open OpamTypes +module Package : sig + type t = Cudf.package + val equal : t -> t -> bool + val compare : t -> t -> int + val to_json : t -> OpamJson.t + val of_json : OpamJson.t -> t option +end + (** Cudf sets *) -module Set: OpamStd.SET with type elt = Cudf.package +module Set: OpamStd.SET with type elt = Package.t (** Cudf maps *) -module Map: OpamStd.MAP with type key = Cudf.package +module Map: OpamStd.MAP with type key = Package.t (** Cudf graph *) module Graph: sig @@ -34,31 +42,31 @@ (** Return the transitive closure of [g] *) val transitive_closure: t -> t - (** Return the transitive closure of dependencies of [set], - sorted in topological order. *) - val close_and_linearize: t -> Set.t -> Cudf.package list - (** Reverse the direction of all edges *) val mirror: t -> t end + (** Computation of differences between universe. Returns the sets of packages to install and remove respectively. *) val diff: Cudf.universe -> Cudf.universe -> (Set.t * Set.t) (** Cudf action graph *) -module ActionGraph: OpamActionGraph.SIG with type package = Cudf.package +module Action: OpamActionGraph.ACTION with type package = Package.t +module ActionGraph: OpamActionGraph.SIG with type package = Package.t (** Abstract type that may be returned in case of conflicts *) type conflict -(** Return the transitive closure of dependencies of [set], - sorted in topological order *) -val dependencies: Cudf.universe -> Cudf.package list -> Cudf.package list - -(** Return the transitive closure of dependencies of [set], - sorted in topological order *) -val reverse_dependencies: Cudf.universe -> Cudf.package list -> Cudf.package list +(** Return the transitive closure of dependencies of [set] *) +val dependencies: Cudf.universe -> Set.t -> Set.t + +(** Return the transitive closure of reverse dependencies of [set] *) +val reverse_dependencies: Cudf.universe -> Set.t -> Set.t + +(** Sorts the given packages topolgically (be careful if there are cycles, e.g. + if the universe was loaded with [post] dependencies enabled) *) +val dependency_sort: Cudf.universe -> Set.t -> Cudf.package list (** Check if a request is satisfiable and return the reasons why not unless [explain] is set to [false] *) @@ -173,6 +181,16 @@ (** the number of versions of the package since this one, cubed *) val s_version_lag: string +(** valid cudf name for the dummy package used for enforcing opam's switch + invariants *) +val opam_invariant_package_name: string + +(** valid cudf name and version for the dummy package used for enforcing opam's + switch invariants *) +val opam_invariant_package: string * int + +val is_opam_invariant: Cudf.package -> bool + (** {2 Pretty-printing} *) (** Convert a package constraint to something readable. *) @@ -185,24 +203,44 @@ version_map:int package_map -> Cudf.universe -> string list list -> ('a, conflict) result +type explanation = + [ `Conflict of string option * string list * bool + | `Missing of string option * string * + (OpamPackage.Name.t * OpamFormula.version_formula) + OpamFormula.formula + ] + (** Convert a conflict to something readable by the user. The second argument should return a string explaining the unavailability, or the empty string, when called on an unavailable package (the reason can't be known this deep in the solver) *) -val string_of_conflict: +val string_of_conflicts: package_set -> (name * OpamFormula.version_formula -> string) -> conflict -> string -(** Returns three lists of strings: - - the final reasons why the request can't be satisfied - - the dependency chains explaining it - - the cycles in the actions to process (exclusive with the other two) *) -val strings_of_conflict: +val string_of_explanations: + (name * OpamFormula.version_formula -> string) -> + explanation list * string list list -> + string + +(** Returns two lists: + - the reasons why the request can't be satisfied with conflict explanations + - the cycles in the actions to process (exclusive with the first) *) +val conflict_explanations: package_set -> (name * OpamFormula.version_formula -> string) -> conflict -> - string list * string list * string list + (string * string list * string list) list * string list + +val string_of_explanation: + (name * OpamFormula.version_formula -> string) -> explanation -> + string * string list * string list + +val conflict_explanations_raw: + package_set -> conflict -> explanation list * string list list -val conflict_chains: - package_set -> conflict -> (name * OpamFormula.version_formula) list list +(** Properly concat a single conflict as returned by [conflict_explanations] for + display *) +val string_of_conflict: + ?start_column:int -> string * string list * string list -> string (** Dumps the given cudf universe to the given channel *) val dump_universe: out_channel -> Cudf.universe -> unit @@ -228,3 +266,44 @@ (** Converts an OPAM request to a Cudf request *) val to_cudf: Cudf.universe -> Cudf_types.vpkg request -> Cudf.preamble * Cudf.universe * Cudf.request + + +module Json: sig + open Cudf_types + + val version_to_json : version OpamJson.encoder + val version_of_json : version OpamJson.decoder + + val relop_to_json : relop OpamJson.encoder + val relop_of_json : relop OpamJson.decoder + + val enum_keep_to_json : enum_keep OpamJson.encoder + val enum_keep_of_json : enum_keep OpamJson.decoder + + val constr_to_json : constr OpamJson.encoder + val constr_of_json : constr OpamJson.decoder + + val vpkg_to_json : vpkg OpamJson.encoder + val vpkg_of_json : vpkg OpamJson.decoder + val vpkglist_to_json : vpkglist OpamJson.encoder + val vpkglist_of_json : vpkglist OpamJson.decoder + + val veqpkg_to_json : veqpkg OpamJson.encoder + val veqpkg_of_json : veqpkg OpamJson.decoder + val veqpkglist_to_json : veqpkglist OpamJson.encoder + val veqpkglist_of_json : veqpkglist OpamJson.decoder + + val vpkgformula_to_json : vpkgformula OpamJson.encoder + val vpkgformula_of_json : vpkgformula OpamJson.decoder + + val typedecl1_to_json : typedecl1 OpamJson.encoder + val typedecl1_of_json : typedecl1 OpamJson.decoder + val typedecl_to_json : typedecl OpamJson.encoder + val typedecl_of_json : typedecl OpamJson.decoder + + val typed_value_to_json : typed_value OpamJson.encoder + val typed_value_of_json : typed_value OpamJson.decoder + + val package_to_json : Cudf.package OpamJson.encoder + val package_of_json : Cudf.package OpamJson.decoder +end diff -Nru opam-2.0.10/src/solver/opamCudfSolver.ml opam-2.1.2/src/solver/opamCudfSolver.ml --- opam-2.0.10/src/solver/opamCudfSolver.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamCudfSolver.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -52,7 +52,7 @@ command in OpamSystem.command - ~verbose:(OpamCoreConfig.(!r.debug_level >= 2)) + ~verbose:(OpamCoreConfig.(abs !r.debug_level >= 2)) cmd in OpamFilename.remove solver_in; @@ -125,16 +125,23 @@ let default_criteria = { crit_default = "-count(removed),\ + -sum(solution,avoid-version),\ -sum(request,version-lag),\ -count(down),\ -sum(solution,version-lag),\ - -count(changed)"; + -count(changed),\ + -sum(solution,missing-depexts)"; crit_upgrade = "-count(down),\ -count(removed),\ + -sum(solution,avoid-version),\ -sum(solution,version-lag),\ + -sum(solution,missing-depexts),\ -count(new)"; crit_fixup = "-count(changed),\ - -notuptodate(solution),-sum(solution,version-lag)"; + -count[avoid-version:,true],\ + -notuptodate(solution),\ + -sum(solution,version-lag),\ + -count[missing-depexts:,true]"; crit_best_effort_prefix = Some "+sum(solution,opam-query),"; } end @@ -170,14 +177,21 @@ let default_criteria = { crit_default = "-removed,\ + -count[avoid-version:,true],\ -count[version-lag:,true],\ -changed,\ -count[version-lag:,false],\ + -count[missing-depexts:,true],\ -new"; crit_upgrade = "-removed,\ + -count[avoid-version:,true],\ -count[version-lag:,false],\ + -count[missing-depexts:,true],\ -new"; - crit_fixup = "-changed,-count[version-lag:,false]"; + crit_fixup = "-changed,\ + -count[avoid-version:,true],\ + -count[version-lag:,false],\ + -count[missing-depexts:,true]"; crit_best_effort_prefix = Some "+count[opam-query:,false],"; } end @@ -215,6 +229,8 @@ let default_solver_selection = OpamBuiltinMccs.all_backends @ [ + (module OpamBuiltinZ3: S); + (module OpamBuiltin0install: S); (module Aspcud: S); (module Mccs: S); (module Aspcud_old: S); diff -Nru opam-2.0.10/src/solver/opamCudfSolver.mli opam-2.1.2/src/solver/opamCudfSolver.mli --- opam-2.0.10/src/solver/opamCudfSolver.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamCudfSolver.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) diff -Nru opam-2.0.10/src/solver/opamCudfSolverSig.ml opam-2.1.2/src/solver/opamCudfSolverSig.ml --- opam-2.0.10/src/solver/opamCudfSolverSig.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamCudfSolverSig.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -15,7 +15,8 @@ crit_best_effort_prefix: string option; } -exception Timeout +(** Timeout might still return a non-optimal solution *) +exception Timeout of (Cudf.preamble option * Cudf.universe) option module type S = sig diff -Nru opam-2.0.10/src/solver/opamSolverConfig.ml opam-2.1.2/src/solver/opamSolverConfig.ml --- opam-2.0.10/src/solver/opamSolverConfig.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamSolverConfig.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -8,6 +8,46 @@ (* *) (**************************************************************************) +module E = struct + + type OpamStd.Config.E.t += + | BESTEFFORT of bool option + | BESTEFFORTPREFIXCRITERIA of string option + | CRITERIA of string option + | CUDFFILE of string option + | CUDFTRIM of string option + | DIGDEPTH of int option + | EXTERNALSOLVER of string option + | FIXUPCRITERIA of string option + | NOASPCUD of bool option + | PREPRO of bool option + | SOLVERALLOWSUBOPTIMAL of bool option + | SOLVERTIMEOUT of float option + | UPGRADECRITERIA of string option + | USEINTERNALSOLVER of bool option + | VERSIONLAGPOWER of int option + + open OpamStd.Config.E + let besteffort = value (function BESTEFFORT b -> b | _ -> None) + let besteffortprefixcriteria = + value (function BESTEFFORTPREFIXCRITERIA s -> s | _ -> None) + let criteria = value (function CRITERIA s -> s | _ -> None) + let cudffile = value (function CUDFFILE s -> s | _ -> None) + let cudftrim = value (function CUDFTRIM s -> s | _ -> None) + let digdepth = value (function DIGDEPTH i -> i | _ -> None) + let externalsolver = value (function EXTERNALSOLVER s -> s | _ -> None) + let fixupcriteria = value (function FIXUPCRITERIA s -> s | _ -> None) + let noaspcud = value (function NOASPCUD b -> b | _ -> None) + let prepro = value (function PREPRO b -> b | _ -> None) + let solverallowsuboptimal = + value (function SOLVERALLOWSUBOPTIMAL b -> b | _ -> None) + let solvertimeout = value (function SOLVERTIMEOUT f -> f | _ -> None) + let useinternalsolver = value (function USEINTERNALSOLVER b -> b | _ -> None) + let upgradecriteria = value (function UPGRADECRITERIA s -> s | _ -> None) + let versionlagpower = value (function VERSIONLAGPOWER i -> i | _ -> None) + +end + type t = { cudf_file: string option; solver: (module OpamCudfSolver.S) Lazy.t; @@ -19,6 +59,11 @@ solver_preferences_fixup: string option Lazy.t; solver_preferences_best_effort_prefix: string option Lazy.t; solver_timeout: float option; + solver_allow_suboptimal: bool; + cudf_trim: string option; + dig_depth: int; + preprocess: bool; + version_lag_power: int; } type 'a options_fun = @@ -30,6 +75,11 @@ ?solver_preferences_fixup:string option Lazy.t -> ?solver_preferences_best_effort_prefix:string option Lazy.t -> ?solver_timeout:float option -> + ?solver_allow_suboptimal:bool -> + ?cudf_trim:string option -> + ?dig_depth:int -> + ?preprocess:bool -> + ?version_lag_power:int -> 'a let default = @@ -45,6 +95,11 @@ solver_preferences_fixup = lazy None; solver_preferences_best_effort_prefix = lazy None; solver_timeout = Some 60.; + solver_allow_suboptimal = true; + cudf_trim = None; + dig_depth = 2; + preprocess = true; + version_lag_power = 1; } let setk k t @@ -56,6 +111,11 @@ ?solver_preferences_fixup ?solver_preferences_best_effort_prefix ?solver_timeout + ?solver_allow_suboptimal + ?cudf_trim + ?dig_depth + ?preprocess + ?version_lag_power = let (+) x opt = match opt with Some x -> x | None -> x in k { @@ -73,6 +133,12 @@ solver_preferences_best_effort_prefix; solver_timeout = t.solver_timeout + solver_timeout; + solver_allow_suboptimal = + t.solver_allow_suboptimal + solver_allow_suboptimal; + cudf_trim = t.cudf_trim + cudf_trim; + dig_depth = t.dig_depth + dig_depth; + preprocess = t.preprocess + preprocess; + version_lag_power = t.version_lag_power + version_lag_power; } let set t = setk (fun x () -> x) t @@ -107,38 +173,40 @@ () let initk k = - let open OpamStd.Config in let open OpamStd.Option.Op in let solver = let open OpamCudfSolver in - match env_string "EXTERNALSOLVER" with + match E.externalsolver () with | Some "" -> lazy (get_solver ~internal:true default_solver_selection) | Some s -> lazy (solver_of_string s) | None -> - let internal = env_bool "USEINTERNALSOLVER" ++ env_bool "NOASPCUD" in + let internal = E.useinternalsolver () ++ E.noaspcud () in lazy (get_solver ?internal default_solver_selection) in - let best_effort = - env_bool "BESTEFFORT" in let criteria = - env_string "CRITERIA" >>| fun c -> lazy (Some c) in + E.criteria () >>| fun c -> lazy (Some c) in let upgrade_criteria = - (env_string "UPGRADECRITERIA" >>| fun c -> lazy (Some c)) ++ criteria in + (E.upgradecriteria () >>| fun c -> lazy (Some c)) ++ criteria in let fixup_criteria = - env_string "FIXUPCRITERIA" >>| fun c -> (lazy (Some c)) in + E.fixupcriteria () >>| fun c -> (lazy (Some c)) in let best_effort_prefix_criteria = - env_string "BESTEFFORTPREFIXCRITERIA" >>| fun c -> (lazy (Some c)) in + E.besteffortprefixcriteria () >>| fun c -> (lazy (Some c)) in let solver_timeout = - env_float "SOLVERTIMEOUT" >>| fun f -> if f <= 0. then None else Some f in + E.solvertimeout () >>| fun f -> if f <= 0. then None else Some f in setk (setk (fun c -> r := with_auto_criteria c; k)) !r - ~cudf_file:(env_string "CUDFFILE") + ~cudf_file:(E.cudffile ()) ~solver - ?best_effort + ?best_effort:(E.besteffort ()) ?solver_preferences_default:criteria ?solver_preferences_upgrade:upgrade_criteria ?solver_preferences_fixup:fixup_criteria ?solver_preferences_best_effort_prefix:best_effort_prefix_criteria ?solver_timeout + ?solver_allow_suboptimal:(E.solverallowsuboptimal ()) + ~cudf_trim:(E.cudftrim ()) + ?dig_depth:(E.digdepth ()) + ?preprocess:(E.prepro ()) + ?version_lag_power:(E.versionlagpower ()) let init ?noop:_ = initk (fun () -> ()) diff -Nru opam-2.0.10/src/solver/opamSolverConfig.mli opam-2.1.2/src/solver/opamSolverConfig.mli --- opam-2.0.10/src/solver/opamSolverConfig.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamSolverConfig.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2017 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -11,6 +11,26 @@ (** Configuration options for the solver lib (record, global reference, setter, initialisation) *) +module E : sig + type OpamStd.Config.E.t += + | BESTEFFORT of bool option + | BESTEFFORTPREFIXCRITERIA of string option + | CRITERIA of string option + | CUDFFILE of string option + | CUDFTRIM of string option + | DIGDEPTH of int option + | EXTERNALSOLVER of string option + | FIXUPCRITERIA of string option + | NOASPCUD of bool option + | PREPRO of bool option + | SOLVERALLOWSUBOPTIMAL of bool option + | SOLVERTIMEOUT of float option + | UPGRADECRITERIA of string option + | USEINTERNALSOLVER of bool option + | VERSIONLAGPOWER of int option + val externalsolver: unit -> string option +end + type t = private { cudf_file: string option; solver: (module OpamCudfSolver.S) Lazy.t; @@ -20,6 +40,11 @@ solver_preferences_fixup: string option Lazy.t; solver_preferences_best_effort_prefix: string option Lazy.t; solver_timeout: float option; + solver_allow_suboptimal: bool; + cudf_trim: string option; + dig_depth: int; + preprocess: bool; + version_lag_power: int; } type 'a options_fun = @@ -31,6 +56,11 @@ ?solver_preferences_fixup:string option Lazy.t -> ?solver_preferences_best_effort_prefix:string option Lazy.t -> ?solver_timeout:float option -> + ?solver_allow_suboptimal:bool -> + ?cudf_trim:string option -> + ?dig_depth:int -> + ?preprocess:bool -> + ?version_lag_power:int -> 'a include OpamStd.Config.Sig diff -Nru opam-2.0.10/src/solver/opamSolver.ml opam-2.1.2/src/solver/opamSolver.ml --- opam-2.0.10/src/solver/opamSolver.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamSolver.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -32,6 +32,7 @@ u_installed_roots = OpamPackage.Set.empty; u_pinned = OpamPackage.Set.empty; u_base = OpamPackage.Set.empty; + u_invariant = OpamFormula.Empty; u_reinstall = OpamPackage.Set.empty; u_attrs = []; } @@ -55,6 +56,11 @@ && List.for_all (fun (n, _) -> n <> name) wish_remove +let solution_to_json solution = + OpamCudf.ActionGraph.to_json solution +let solution_of_json json = + OpamCudf.ActionGraph.of_json json + let cudf_versions_map universe packages = log ~level:3 "cudf_versions_map"; let add_referred_to_packages filt acc refmap = @@ -96,6 +102,8 @@ (this shouldn't happen for any constraint in the universe, now that we compute a full version map, but may still happen for user-provided constraints) *) + log "Warn: fallback constraint for %s" + (OpamFormula.string_of_atom (name, Some (op,v))); let all_versions = OpamPackage.Map.filter (fun nv _ -> nv.name = name) version_map in @@ -125,12 +133,32 @@ name_to_cudf name, OpamStd.Option.Op.(cstr >>= constraint_to_cudf version_map name) +let opam_invariant_package version_map invariant = + let depends = + OpamFormula.to_atom_formula invariant + |> OpamFormula.map (fun at -> Atom (atom2cudf () version_map at)) + |> OpamFormula.cnf_of_formula + |> OpamFormula.ands_to_list + |> List.map (OpamFormula.fold_right (fun acc x -> x::acc) []) + in { + Cudf. + package = OpamCudf.opam_invariant_package_name; + version = snd OpamCudf.opam_invariant_package; + depends; + conflicts = []; + provides = []; + installed = true; + was_installed = true; + keep = `Keep_version; + pkg_extra = [ + OpamCudf.s_source, `String "SWITCH_INVARIANT"; + OpamCudf.s_source_number, `String "NULL"; + ]; + } + let lag_function = - let exp = - OpamStd.Option.default 1 (OpamStd.Config.env_int "VERSIONLAGPOWER") - in let rec power n x = if n <= 0 then 1 else x * power (n-1) x in - power exp + power OpamSolverConfig.(!r.version_lag_power) let opam2cudf universe version_map packages = let set_to_bool_map set = @@ -155,25 +183,32 @@ base_map m in let installed_map = set_to_bool_map universe.u_installed in - let keep_map = - OpamPackage.Set.fold (fun nv acc -> - if OpamPackage.Set.mem nv universe.u_available - then OpamPackage.Map.add nv `Keep_version acc - else if OpamPackage.has_name universe.u_available nv.name - then OpamPackage.Map.add nv `Keep_package acc - else acc) - (packages %% universe.u_base) OpamPackage.Map.empty - in let reinstall_map = set_to_bool_map universe.u_reinstall in let installed_root_map = set_to_bool_map universe.u_installed_roots in let pinned_to_current_version_map = set_to_bool_map universe.u_pinned in + let avoid_versions = + OpamStd.Option.default OpamPackage.Set.empty @@ + OpamStd.List.assoc_opt "avoid-version" universe.u_attrs + in let version_lag_map = OpamPackage.Name.Map.fold (fun name version_set acc -> let nvers, vs = OpamPackage.Version.Set.fold (fun v (i,acc) -> - i+1, OpamPackage.Version.Map.add v i acc) + if OpamPackage.Set.mem (OpamPackage.create name v) avoid_versions + then i, acc + else i+1, OpamPackage.Version.Map.add v i acc) version_set (0, OpamPackage.Version.Map.empty) in + let nvers, vs = + (* Place all avoid-versions after normal versions *) + (* Not strictly necessary, but gives a better fallback in case the + specific criteria for avoided versions are not set *) + OpamPackage.Version.Set.fold (fun v (i,acc) -> + if OpamPackage.Set.mem (OpamPackage.create name v) avoid_versions + then i+1, OpamPackage.Version.Map.add v i acc + else i, acc) + version_set (nvers, vs) + in OpamPackage.Version.Map.fold (fun v i -> let lag = lag_function (nvers - i - 1) in if lag > 0 then @@ -202,7 +237,6 @@ base_map |> add version_map (fun _ version cp -> {cp with Cudf.version}) |> add installed_map (fun _ installed cp -> {cp with Cudf.installed}) - |> add keep_map (fun _ keep cp -> {cp with Cudf.keep}) |> add reinstall_map (fun _ x cp -> {cp with Cudf.pkg_extra = (OpamCudf.s_reinstall, `Bool x) :: cp.Cudf.pkg_extra}) @@ -279,25 +313,12 @@ (* load a cudf universe from an opam one *) let load_cudf_universe - opam_universe ?version_map opam_packages = + opam_universe ?version_map ?(add_invariant=false) opam_packages = let chrono = OpamConsole.timer () in let version_map = match version_map with | Some vm -> vm | None -> cudf_versions_map opam_universe opam_packages in log ~level:3 "Load cudf universe: opam2cudf"; - let opam_packages = - if OpamPackage.Set.subset opam_universe.u_base opam_universe.u_available - then - (* Filter out extra compiler versions, they add too much cost to the - solver and are not needed *) - opam_packages -- - (OpamPackage.packages_of_names opam_packages - OpamPackage.Name.Set.Op.( - OpamPackage.names_of_packages opam_universe.u_base - -- OpamPackage.names_of_packages opam_universe.u_pinned) - -- opam_universe.u_base) - else opam_packages - in let univ_gen = opam2cudf opam_universe version_map opam_packages in @@ -309,8 +330,12 @@ post; let chrono = OpamConsole.timer () in let cudf_universe = + let cudf_packages = univ_gen ~depopts ~build ~post in let cudf_packages = - univ_gen ~depopts ~build ~post + if add_invariant then + opam_invariant_package version_map opam_universe.u_invariant + :: cudf_packages + else cudf_packages in log ~level:3 "opam2cudf: done in %.3fs" (chrono ()); try Cudf.load_universe cudf_packages @@ -419,7 +444,13 @@ let cudf_request = map_request (atom2cudf universe version_map) request in let resolve u req = try + let invariant_pkg = + opam_invariant_package version_map universe.u_invariant + in + Cudf.add_package u invariant_pkg; let resp = OpamCudf.resolve ~extern:true ~version_map u req in + Cudf.remove_package u + (invariant_pkg.Cudf.package, invariant_pkg.Cudf.version); OpamCudf.to_actions add_orphan_packages u resp with OpamCudf.Solver_failure msg -> OpamConsole.error_and_exit `Solver_failure "%s" msg @@ -443,7 +474,8 @@ let installable universe = log "trim"; let simple_universe = - load_cudf_universe universe universe.u_available ~build:true ~post:true () + load_cudf_universe universe ~add_invariant:true + universe.u_available ~build:true ~post:true () in let trimmed_universe = (* Algo.Depsolver.trim simple_universe => this can explode memory, we need @@ -461,7 +493,9 @@ Cudf.load_universe !trimmed_pkgs in Cudf.fold_packages - (fun universe pkg -> OpamPackage.Set.add (OpamCudf.cudf2opam pkg) universe) + (fun universe pkg -> + if pkg.package = OpamCudf.opam_invariant_package_name then universe + else OpamPackage.Set.add (OpamCudf.cudf2opam pkg) universe) OpamPackage.Set.empty trimmed_universe @@ -469,12 +503,15 @@ log "trim-subset"; let version_map = cudf_versions_map universe universe.u_available in let simple_universe = - load_cudf_universe ~build:true ~post:true universe ~version_map + load_cudf_universe ~build:true ~post:true universe + ~version_map ~add_invariant:true universe.u_available () in let cudf_packages = Cudf.get_packages - ~filter:(fun p -> OpamPackage.Set.mem (OpamCudf.cudf2opam p) packages) + ~filter:(fun p -> + p.package <> OpamCudf.opam_invariant_package_name && + OpamPackage.Set.mem (OpamCudf.cudf2opam p) packages) simple_universe in let trimmed_universe = @@ -491,15 +528,46 @@ ignore (listcheck ~callback ~explain:false simple_universe cudf_packages); Cudf.load_universe !trimmed_pkgs in + Cudf.remove_package trimmed_universe OpamCudf.opam_invariant_package; Cudf.fold_packages (fun universe pkg -> OpamPackage.Set.add (OpamCudf.cudf2opam pkg) universe) OpamPackage.Set.empty trimmed_universe +let coinstallable_subset universe set packages = + let u_invariant = + OpamPackage.Set.fold (fun p acc -> + OpamFormula.ands [acc; Atom (p.name, Atom (`Eq, p.version))]) + set OpamFormula.Empty + in + installable_subset {universe with u_invariant} packages + +module PkgGraph = Graph.Imperative.Digraph.ConcreteBidirectional(OpamPackage) + +let dependency_graph + ~depopts ~build ~post ~installed ?(unavailable=false) + universe = + let u_packages = + if installed then universe.u_installed else + if unavailable then universe.u_packages else + universe.u_available in + let cudf_graph = + load_cudf_universe ~depopts ~build ~post universe u_packages () |> + OpamCudf.Graph.of_universe + in + let g = PkgGraph.create ~size:(OpamCudf.Graph.nb_vertex cudf_graph) () in + OpamCudf.Graph.iter_vertex (fun v -> + PkgGraph.add_vertex g (OpamCudf.cudf2opam v)) + cudf_graph; + OpamCudf.Graph.iter_edges (fun v1 v2 -> + PkgGraph.add_edge g (OpamCudf.cudf2opam v1) (OpamCudf.cudf2opam v2)) + cudf_graph; + g + let filter_dependencies f_direction ~depopts ~build ~post ~installed ?(unavailable=false) universe packages = - if OpamPackage.Set.is_empty packages then [] else + if OpamPackage.Set.is_empty packages then OpamPackage.Set.empty else let u_packages = packages ++ if installed then universe.u_installed else @@ -512,23 +580,39 @@ load_cudf_universe ~depopts ~build ~post universe ~version_map u_packages () in let cudf_packages = - opam2cudf universe ~depopts ~build ~post version_map packages + OpamCudf.Set.of_list + (opam2cudf universe ~depopts ~build ~post version_map packages) in log ~level:3 "filter_dependencies: dependency"; - let topo_packages = f_direction cudf_universe cudf_packages in - let result = List.rev_map OpamCudf.cudf2opam topo_packages in + let clos_packages = f_direction cudf_universe cudf_packages in + let result = + OpamCudf.Set.fold (fun cp -> OpamPackage.Set.add (OpamCudf.cudf2opam cp)) + clos_packages OpamPackage.Set.empty + in log "filter_dependencies result=%a" - (slog (OpamStd.List.to_string OpamPackage.to_string)) result; + (slog OpamPackage.Set.to_string) result; result let dependencies = filter_dependencies OpamCudf.dependencies let reverse_dependencies = filter_dependencies OpamCudf.reverse_dependencies +let dependency_sort ~depopts ~build ~post universe packages = + let version_map = cudf_versions_map universe universe.u_packages in + let cudf_universe = + load_cudf_universe ~depopts ~build ~post universe ~version_map + universe.u_packages () in + let cudf_packages = + OpamCudf.Set.of_list + (opam2cudf universe ~depopts ~build ~post version_map packages) + in + List.map OpamCudf.cudf2opam + (OpamCudf.dependency_sort cudf_universe cudf_packages) + let coinstallability_check universe packages = let version_map = cudf_versions_map universe universe.u_packages in let cudf_universe = - load_cudf_universe ~build:true ~post:true ~version_map + load_cudf_universe ~build:true ~post:true ~version_map ~add_invariant:true universe universe.u_packages () in let cudf_packages = @@ -547,36 +631,28 @@ coinstallability_check universe universe.u_installed let atom_coinstallability_check universe atoms = - let packages = OpamFormula.packages_of_atoms universe.u_available atoms in - let map = OpamPackage.to_map packages in - List.for_all (fun (n, _) -> OpamPackage.Name.Map.mem n map) atoms && let version_map = cudf_versions_map universe universe.u_packages in + let check_pkg = { + Cudf.default_package with + package = "=check_coinstallability"; + depends = List.map (fun at -> [atom2cudf () version_map at]) atoms; + } in let cudf_universe = - load_cudf_universe ~build:true ~post:true ~version_map - universe universe.u_packages () - in - let cudf_ll = - OpamPackage.Name.Map.fold (fun n versions acc -> - let packages = - OpamPackage.Version.Set.fold - (fun v -> OpamPackage.(Set.add (create n v))) - versions OpamPackage.Set.empty - in - opam2cudf - universe ~depopts:false ~build:true ~post:true version_map - packages - :: acc) - map [] + Cudf.load_universe + (check_pkg :: + opam_invariant_package version_map universe.u_invariant :: + opam2cudf universe version_map universe.u_available + ~depopts:false ~build:true ~post:true) in - let result = Algo.Depsolver.edos_coinstall_prod cudf_universe cudf_ll in - List.exists Algo.Diagnostic.is_solution result + Algo.Depsolver.edos_install cudf_universe check_pkg + |> Algo.Diagnostic.is_solution let new_packages sol = OpamCudf.ActionGraph.fold_vertex (fun action packages -> match action with | `Install p | `Change (_,_,p) -> OpamPackage.Set.add (OpamCudf.cudf2opam p) packages - | `Reinstall _ | `Remove _ | `Build _ -> packages + | `Reinstall _ | `Remove _ | `Build _ | `Fetch _ -> packages ) sol OpamPackage.Set.empty let all_packages sol = @@ -594,7 +670,7 @@ | `Change (`Down,_,_) -> {stats with s_downgrade = stats.s_downgrade+1} | `Reinstall _ -> {stats with s_reinstall = stats.s_reinstall+1} | `Remove _ -> {stats with s_remove = stats.s_remove+1} - | `Build _ -> stats) + | `Build _ | `Fetch _ -> stats) (OpamCudf.ActionGraph.reduce sol) { s_install=0; s_reinstall=0; s_upgrade=0; s_downgrade=0; s_remove=0 } @@ -660,7 +736,7 @@ match a with | `Install p | `Change (_,_,p) | `Reinstall p -> messages (OpamCudf.cudf2opam p) - | `Remove _ | `Build _ -> [] + | `Remove _ | `Build _ | `Fetch _ -> [] in action :: actions, (cause, messages) :: details ) t ([],[]) diff -Nru opam-2.0.10/src/solver/opamSolver.mli opam-2.1.2/src/solver/opamSolver.mli --- opam-2.0.10/src/solver/opamSolver.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/solver/opamSolver.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -48,14 +48,24 @@ requested:name_set -> reinstall:package_set -> solution -> unit +(** Serialize a solution *) +val solution_to_json : solution OpamJson.encoder +val solution_of_json : solution OpamJson.decoder + (** Computes an opam->cudf version map from a set of package *) val cudf_versions_map: universe -> package_set -> int OpamPackage.Map.t (** Creates a CUDF universe from an OPAM universe, including the given packages. - Evaluation of the first 3 arguments is staged. Warning: when [depopts] is - [true], the optional dependencies may become strong dependencies. *) + Evaluation of the first 4 arguments is staged. Warning: when [depopts] is + [true], the optional dependencies may become strong dependencies. + + Use [add_invariant] if you expect to call the solver and need the switch + invariants to be respected; remember in that case to call + [Cudf.remove_package universe OpamCudf.opam_invariant_package] + before exporting the results *) val load_cudf_universe: - universe -> ?version_map:int package_map -> package_set -> + universe -> ?version_map:int package_map -> ?add_invariant:bool -> + package_set -> ?depopts:bool -> build:bool -> post:bool -> unit -> Cudf.universe @@ -82,7 +92,7 @@ (** Like [installable], but within a subset and potentially much faster *) val installable_subset: universe -> package_set -> package_set -(** Return the topological sort of the transitive dependency closures +(** Return the transitive dependency closures of a collection of packages.*) val dependencies : depopts:bool -> build:bool -> post:bool -> @@ -90,7 +100,7 @@ ?unavailable:bool -> universe -> package_set -> - package list + package_set (** Same as [dependencies] but for reverse dependencies *) val reverse_dependencies : @@ -99,8 +109,24 @@ ?unavailable:bool -> universe -> package_set -> + package_set + +(** Sorts the given package set in topological order (as much as possible, + beware of cycles in particular if [post] is [true]) *) +val dependency_sort : + depopts:bool -> build:bool -> post:bool -> + universe -> + package_set -> package list +module PkgGraph: Graph.Sig.I + with type V.t = OpamPackage.t +val dependency_graph : + depopts:bool -> build:bool -> post:bool -> + installed:bool -> + ?unavailable:bool -> + universe -> PkgGraph.t + (** Check the current set of installed packages in a universe for inconsistencies *) val check_for_conflicts : universe -> OpamCudf.conflict option @@ -113,6 +139,12 @@ universe *) val atom_coinstallability_check : universe -> atom list -> bool +(** [coinstallable_subset univ set packages] returns the subset of [packages] + which are individually co-installable with [set], i.e. that can be installed + if [set] while [set] remains installed. This returns the empty set if [set] + is already not coinstallable. *) +val coinstallable_subset : universe -> package_set -> package_set -> package_set + (** Dumps a cudf file containing all available packages in the given universe, plus version bindings (as '#v2v' comments) for the other ones. *) val dump_universe: universe -> out_channel -> unit diff -Nru opam-2.0.10/src/state/dune opam-2.1.2/src/state/dune --- opam-2.0.10/src/state/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/dune 2021-12-07 16:09:27.000000000 +0000 @@ -4,12 +4,53 @@ (libraries opam-repository) (synopsis "OCaml Package Manager instance management library") (modules_without_implementation OpamStateTypes) + (modules :standard \ flags) (flags (:standard (:include ../ocaml-flags-standard.sexp) - (:include ../ocaml-context-flags.sexp))) + (:include ../ocaml-flags-configure.sexp) + (:include ../ocaml-context-flags.sexp) + (:include flags.sexp))) (wrapped false)) (rule (targets opamScript.ml) (deps ../../shell/crunch.ml (glob_files shellscripts/*.*sh)) (action (with-stdout-to %{targets} (run ocaml %{deps})))) + + +;; These rules are here to document and for easy (re-)generation of the module, +;; but relying on the checked-in file should be OK (we don't want to rely on +;; either `jq` or a network download). Hence the `(mode fallback)`. +(rule + (targets opamSpdxList.ml) + (mode fallback) + (action (with-stdout-to %{targets} + (progn + (echo "(* THIS FILE IS GENERATED. See dune file *)\n\n") + (echo "open OpamCompat\n") + (echo "let licenses = OpamStd.String.Set.of_list @@ List.map String.lowercase_ascii @@ [\n") + (cat spdx-license-list) + (echo "]\n") + (echo "let exceptions = OpamStd.String.Set.of_list @@ List.map String.lowercase_ascii @@ [\n") + (cat spdx-license-exn-list) + (echo "]\n"))))) +(rule + (targets spdx-license-list) + (action (with-stdout-to %{targets} + (system + "curl https://raw.githubusercontent.com/spdx/license-list-data/master/json/licenses.json | + jq -rc '.licenses | map(\" \\\"\" + .licenseId + \"\\\";\") | join(\"\\n\")'")))) +(rule + (targets spdx-license-exn-list) + (action (with-stdout-to %{targets} + (system + "curl https://raw.githubusercontent.com/spdx/license-list-data/master/json/exceptions.json | + jq -rc '.exceptions | map(\" \\\"\" + .licenseExceptionId + \"\\\";\") | join(\"\\n\")'")))) + +(rule + (with-stdout-to flags.ml + (echo "print_string (if String.sub Sys.ocaml_version 0 5 = \"4.02.\" then \"(-w -50)\" else \"()\")"))) + +(rule + (with-stdout-to flags.sexp + (run ocaml %{dep:flags.ml}))) diff -Nru opam-2.0.10/src/state/opamEnv.ml opam-2.1.2/src/state/opamEnv.ml --- opam-2.0.10/src/state/opamEnv.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamEnv.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,6 +9,7 @@ (* *) (**************************************************************************) +open OpamParserTypes open OpamTypes open OpamStateTypes open OpamTypesBase @@ -261,7 +262,8 @@ let get_opam ~set_opamroot ~set_opamswitch ~force_path st = add [] (updates ~set_opamroot ~set_opamswitch ~force_path st) -let get_opam_raw ~set_opamroot ~set_opamswitch ~force_path +let get_opam_raw ~set_opamroot ~set_opamswitch ?(base=[]) + ~force_path root switch = let env_file = OpamPath.Switch.environment root switch in let upd = OpamFile.Environment.safe_read env_file in @@ -285,19 +287,30 @@ upd in - add [] + add base (updates_common ~set_opamroot ~set_opamswitch root switch @ upd) let get_full - ~set_opamroot ~set_opamswitch ~force_path ?updates:(u=[]) + ~set_opamroot ~set_opamswitch ~force_path ?updates:(u=[]) ?(scrub=[]) st = - let env0 = List.map (fun (v,va) -> v,va,None) (OpamStd.Env.list ()) in + let env = + let env = OpamStd.Env.list () in + let map = + if Sys.win32 then + String.uppercase_ascii + else + (fun x -> x) + in + let scrub = List.rev_map map scrub |> OpamStd.String.Set.of_list in + List.filter (fun (name, _) -> not (OpamStd.String.Set.mem (map name) scrub)) env + in + let env0 = List.map (fun (v,va) -> v,va,None) env in let updates = u @ updates ~set_opamroot ~set_opamswitch ~force_path st in add env0 updates -let is_up_to_date_raw updates = - OpamStateConfig.(!r.no_env_notice) || +let is_up_to_date_raw ?(skip=OpamStateConfig.(!r.no_env_notice)) updates = + skip || let not_utd = List.fold_left (fun notutd (var, op, arg, _doc as upd) -> match OpamStd.Env.getopt var with @@ -345,47 +358,60 @@ let env0 = List.map (fun (v,va) -> v,va,None) (OpamStd.Env.list ()) in add env0 (switch_path_update ~force_path root switch @ updates) -let is_up_to_date st = - is_up_to_date_raw +let is_up_to_date ?skip st = + is_up_to_date_raw ?skip (updates ~set_opamroot:false ~set_opamswitch:false ~force_path:false st) +(** Returns shell-appropriate statement to evaluate [cmd]. *) +let shell_eval_invocation shell cmd = + match shell with + | SH_fish -> + Printf.sprintf "eval (%s)" cmd + | SH_csh -> + Printf.sprintf "eval `%s`" cmd + | _ -> + Printf.sprintf "eval $(%s)" cmd + +(** Returns "opam env" invocation string together with optional root and switch + overrides *) +let opam_env_invocation ?root ?switch ?(set_opamswitch=false) () = + let root = OpamStd.Option.map_default (Printf.sprintf " --root=%s") "" root in + let switch = OpamStd.Option.map_default (Printf.sprintf " --switch=%s") "" switch in + let setswitch = if set_opamswitch then " --set-switch" else "" in + Printf.sprintf "opam env%s%s%s" root switch setswitch + let eval_string gt ?(set_opamswitch=false) switch = let root = let opamroot_cur = OpamFilename.Dir.to_string gt.root in let opamroot_env = OpamStd.Option.Op.( - OpamStd.Env.getopt "OPAMROOT" +! + OpamStateConfig.E.root () +! OpamFilename.Dir.to_string OpamStateConfig.(default.root_dir) ) in if opamroot_cur <> opamroot_env then - Printf.sprintf " --root=%s" opamroot_cur + Some opamroot_cur else - "" in + None + in let switch = - match switch with - | None -> "" - | Some sw -> + (* Returns the switch only if it is different from the one determined by the + environment *) + let f sw = let sw_cur = OpamSwitch.to_string sw in let sw_env = OpamStd.Option.Op.( - OpamStd.Env.getopt "OPAMSWITCH" ++ + OpamStateConfig.E.switch () ++ (OpamStateConfig.get_current_switch_from_cwd gt.root >>| - OpamSwitch.to_string) ++ + OpamSwitch.to_string) ++ (OpamFile.Config.switch gt.config >>| OpamSwitch.to_string) ) in - if Some sw_cur <> sw_env then Printf.sprintf " --switch=%s" sw_cur - else "" + if Some sw_cur <> sw_env then Some sw_cur else None + in + OpamStd.Option.replace f switch in - let setswitch = if set_opamswitch then " --set-switch" else "" in - match OpamStd.Sys.guess_shell_compat () with - | SH_fish -> - Printf.sprintf "eval (opam env%s%s%s)" root switch setswitch - | SH_csh -> - Printf.sprintf "eval `opam env%s%s%s`" root switch setswitch - | _ -> - Printf.sprintf "eval $(opam env%s%s%s)" root switch setswitch - + let shell = OpamStd.Sys.guess_shell_compat () in + shell_eval_invocation shell (opam_env_invocation ?root ?switch ~set_opamswitch ()) (* -- Shell and init scripts handling -- *) @@ -473,15 +499,18 @@ (env_hook_script_base shell) let source root shell f = - let file f = OpamFilename.to_string (OpamPath.init root // f) in + let fname = OpamFilename.to_string (OpamPath.init root // f) in match shell with | SH_csh -> - Printf.sprintf "source %s >& /dev/null || true\n" (file f) + Printf.sprintf "if ( -f %s ) source %s >& /dev/null\n" fname fname | SH_fish -> - Printf.sprintf "source %s > /dev/null 2> /dev/null; or true\n" (file f) - | SH_sh | SH_bash | SH_zsh -> + Printf.sprintf "source %s > /dev/null 2> /dev/null; or true\n" fname + | SH_sh | SH_bash -> Printf.sprintf "test -r %s && . %s > /dev/null 2> /dev/null || true\n" - (file f) (file f) + fname fname + | SH_zsh -> + Printf.sprintf "[[ ! -r %s ]] || source %s > /dev/null 2> /dev/null\n" + fname fname let if_interactive_script shell t e = let ielse else_opt = match else_opt with @@ -489,8 +518,10 @@ | Some e -> Printf.sprintf "else\n %s" e in match shell with - | SH_sh | SH_zsh | SH_bash -> + | SH_sh| SH_bash -> Printf.sprintf "if [ -t 0 ]; then\n %s%sfi\n" t @@ ielse e + | SH_zsh -> + Printf.sprintf "if [[ -o interactive ]]; then\n %s%sfi\n" t @@ ielse e | SH_csh -> Printf.sprintf "if ( $?prompt ) then\n %s%sendif\n" t @@ ielse e | SH_fish -> @@ -538,7 +569,7 @@ in List.iter (write_script (OpamPath.init root)) scripts -let write_static_init_scripts root ?completion ?env_hook () = +let write_static_init_scripts root ?completion ?env_hook ?(inplace=false) () = write_init_shell_scripts root; let update_scripts filef scriptf enable = let scripts = @@ -548,13 +579,18 @@ | _ -> None) shells_list in - match enable with - | Some true -> + match enable, inplace with + | Some true, _ -> List.iter (write_script (OpamPath.init root)) scripts - | Some false -> - List.iter (fun (f,_) -> OpamFilename.remove (OpamPath.init root // f)) + | _, true -> + List.iter (fun ((f,_) as fs) -> + if OpamFilename.exists (OpamPath.init root // f) then + write_script (OpamPath.init root) fs) scripts - | None -> () + | Some false, _ -> + List.iter (fun (f,_) -> + OpamFilename.remove (OpamPath.init root // f)) scripts + | None, _ -> () in update_scripts complete_file complete_script completion; update_scripts env_hook_file env_hook_script env_hook @@ -626,7 +662,8 @@ let update_dot_profile root dot_profile shell = let pretty_dot_profile = OpamFilename.prettify dot_profile in let bash_src () = - if shell = SH_bash || shell = SH_sh then + if (shell = SH_bash || shell = SH_sh) + && OpamFilename.(Base.to_string (basename dot_profile)) <> ".bashrc" then OpamConsole.note "Make sure that %s is well %s in your ~/.bashrc.\n" pretty_dot_profile (OpamConsole.colorise `underline "sourced") @@ -662,6 +699,9 @@ ) let check_and_print_env_warning st = + (* if you are trying to silence this warning, + set the ~no_env_notice:true flag from OpamStateConfig, + which is checked by (is_up_to_date st). *) if not (is_up_to_date st) && (OpamFile.Config.switch st.switch_global.config = Some st.switch || OpamStateConfig.(!r.switch_from <> `Command_line)) @@ -673,7 +713,7 @@ let setup root ~interactive ?dot_profile ?update_config ?env_hook ?completion - shell = + ?inplace shell = let update_dot_profile = match update_config, dot_profile, interactive with | Some false, _, _ -> None @@ -685,7 +725,7 @@ OpamConsole.msg "\n\ - \ In normal operation, opam only alters files within ~/.opam.\n\ + \ In normal operation, opam only alters files within ~%s.opam.\n\ \n\ \ However, to best integrate with your system, some environment variables\n\ \ should be set. If you allow it to, this initialisation step will update\n\ @@ -699,38 +739,50 @@ \ %s\n\ \n\ \ You can always re-run this setup with 'opam init' later.\n\n" + Filename.dir_sep (OpamConsole.colorise `bold @@ string_of_shell shell) (OpamConsole.colorise `cyan @@ OpamFilename.prettify dot_profile) (OpamConsole.colorise `bold @@ source root shell (init_file shell)) - (OpamConsole.colorise `bold @@ "eval $(opam env)"); - match - OpamConsole.read - "Do you want opam to modify %s? [N/y/f]\n\ - (default is 'no', use 'f' to choose a different file)" - (OpamFilename.prettify dot_profile) - with - | Some ("y" | "Y" | "yes" | "YES" ) -> Some dot_profile - | Some ("f" | "F" | "file" | "FILE") -> - begin - match OpamConsole.read " Enter the name of the file to update:" - with - | None -> - OpamConsole.msg "Alright, assuming you changed your mind, not \ - performing any changes.\n"; - None - | Some f -> Some (OpamFilename.of_string f) - end - | _ -> None + (OpamConsole.colorise `bold @@ shell_eval_invocation shell (opam_env_invocation ())); + if OpamCoreConfig.answer_is_yes () then begin + OpamConsole.warning "Shell not updated in non-interactive mode: use --shell-setup"; + None + end else + match + OpamConsole.read + "Do you want opam to modify %s? [N/y/f]\n\ + (default is 'no', use 'f' to choose a different file)" + (OpamFilename.prettify dot_profile) + with + | Some ("y" | "Y" | "yes" | "YES" ) -> Some dot_profile + | Some ("f" | "F" | "file" | "FILE") -> + begin + match OpamConsole.read " Enter the name of the file to update:" + with + | None -> + OpamConsole.msg "Alright, assuming you changed your mind, not \ + performing any changes.\n"; + None + | Some f -> Some (OpamFilename.of_string f) + end + | _ -> None in let env_hook = match env_hook, interactive with | Some b, _ -> Some b | None, false -> None | None, true -> - Some - (OpamConsole.confirm ~default:false - "A hook can be added to opam's init scripts to ensure that the \ - shell remains in sync with the opam environment when they are \ - loaded. Set that up?") + (* not just interactive mode *) + if update_config <> None || completion <> None then None else + Some + (OpamConsole.confirm ~default:false + "A hook can be added to opam's init scripts to ensure that the \ + shell remains in sync with the opam environment when they are \ + loaded. Set that up?") in update_user_setup root ?dot_profile:update_dot_profile shell; - write_static_init_scripts root ?completion ?env_hook () + write_static_init_scripts root ?completion ?env_hook ?inplace () + +let hook_env root = + let hook_vnam = OpamVariable.of_string "hooks" in + let hook_vval = Some (OpamVariable.dirname (OpamPath.hooks_dir root)) in + OpamVariable.Map.singleton hook_vnam hook_vval diff -Nru opam-2.0.10/src/state/opamEnv.mli opam-2.1.2/src/state/opamEnv.mli --- opam-2.0.10/src/state/opamEnv.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamEnv.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -19,22 +19,28 @@ (** Get the current environment with OPAM specific additions. If [force_path], the PATH is modified to ensure opam dirs are leading. [set_opamroot] and [set_opamswitch] can be additionally used to set the [OPAMROOT] and - [OPAMSWITCH] variables. *) + [OPAMSWITCH] variables. [scrub] is a list of environment variables to + remove from the environment. *) val get_full: set_opamroot:bool -> set_opamswitch:bool -> force_path:bool -> - ?updates:env_update list -> 'a switch_state -> env + ?updates:env_update list -> ?scrub:string list -> 'a switch_state -> env (** Get only environment modified by OPAM. If [force_path], the PATH is modified to ensure opam dirs are leading. [set_opamroot] and [set_opamswitch] can be - additionally used to set the [OPAMROOT] and [OPAMSWITCH] variables. *) + additionally used to set the [OPAMROOT] and [OPAMSWITCH] variables. + + With [base], apply the modifications to the specified base environment *) val get_opam: set_opamroot:bool -> set_opamswitch:bool -> force_path:bool -> 'a switch_state -> env (** Like [get_opam], but reads the cache file from the given opam root and - switch instead of computing the environment from a switch state *) + switch instead of computing the environment from a switch state. + + With [base], apply the modifications to the specified base environment *) val get_opam_raw: - set_opamroot:bool -> set_opamswitch:bool -> force_path:bool -> + set_opamroot:bool -> set_opamswitch:bool -> ?base:env -> + force_path:bool -> dirname -> switch -> env (** Returns the running environment, with any opam modifications cleaned out, @@ -52,10 +58,9 @@ set_opamroot:bool -> set_opamswitch:bool -> ?force_path:bool -> 'a switch_state -> env_update list -(** Check if the shell environment is in sync with the current OPAM switch (or - if OPAMNOENVNOTICE has been set, in which case we just assume it's up to - date) *) -val is_up_to_date: 'a switch_state -> bool +(** Check if the shell environment is in sync with the current OPAM switch, + unless [skip] is true (it's default value is OPAMNOENVNOTICE *) +val is_up_to_date: ?skip:bool -> 'a switch_state -> bool (** Check if the shell environment is in sync with the given opam root and switch (or if OPAMNOENVNOTICE has been set, in which case we just assume @@ -66,6 +71,15 @@ its set of installed packages *) val compute_updates: ?force_path:bool -> 'a switch_state -> env_update list +(** Returns shell-appropriate statement to evaluate [cmd]. *) +val shell_eval_invocation: + OpamTypes.shell -> string -> string + +(** Returns "opam env" invocation string together with optional root and switch + overrides *) +val opam_env_invocation: + ?root:string -> ?switch:string -> ?set_opamswitch:bool -> unit -> string + (** The shell command to run by the user to set his OPAM environment, adapted to the current shell (as returned by [eval `opam config env`]) *) val eval_string: @@ -86,7 +100,7 @@ and asking the user if either [update_config] or [shell_hook] are unset *) val setup: dirname -> interactive:bool -> ?dot_profile:filename -> - ?update_config:bool -> ?env_hook:bool -> ?completion:bool -> + ?update_config:bool -> ?env_hook:bool -> ?completion:bool -> ?inplace:bool -> shell -> unit (* (\** Display the global and user configuration for OPAM. *\) @@ -98,9 +112,10 @@ (** Write the generic scripts in ~/.opam/opam-init needed to import state for various shells. If specified, completion and env_hook files can also be - written or removed (the default is to keep them as they are) *) + written or removed (the default is to keep them as they are). If [inplace] + is true, they are updated if they exist. *) val write_static_init_scripts: - dirname -> ?completion:bool -> ?env_hook:bool -> unit -> unit + dirname -> ?completion:bool -> ?env_hook:bool -> ?inplace:bool -> unit -> unit (** Write into [OpamPath.hooks_dir] the given custom scripts (listed as (filename, content)), normally provided by opamrc ([OpamFile.InitConfig]) *) @@ -121,3 +136,7 @@ (** Print a warning if the environment is not set-up properly. (General message) *) val check_and_print_env_warning: 'a switch_state -> unit + +(** Hook directory environment *) +val hook_env: + OpamPath.t -> OpamVariable.variable_contents option OpamVariable.Map.t diff -Nru opam-2.0.10/src/state/opamFileTools.ml opam-2.1.2/src/state/opamFileTools.ml --- opam-2.0.10/src/state/opamFileTools.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamFileTools.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -9,13 +9,25 @@ (* *) (**************************************************************************) +open OpamParserTypes.FullPos open OpamTypes open OpamTypesBase -let log fmt = OpamConsole.log "opam-file" fmt +let log ?level fmt = OpamConsole.log "opam-file" ?level fmt open OpamFile.OPAM +let is_valid_license_id s = + let memplus s = + let s = OpamStd.String.remove_suffix ~suffix:"+" s in + OpamStd.String.Set.mem s OpamSpdxList.licenses + in + match OpamStd.String.split (String.lowercase_ascii s) ' ' with + | [s] -> memplus s + | [s; "with"; e] -> + memplus s && OpamStd.String.Set.mem e OpamSpdxList.exceptions + | _ -> false + (** manipulation utilities *) let names_of_formula flag f = @@ -33,7 +45,8 @@ let urlf_urls uf = OpamFile.URL.url uf :: OpamFile.URL.mirrors uf in (match t.url with Some uf -> urlf_urls uf | None -> []) @ (match t.dev_repo with Some u -> [u] | None -> []) @ - List.fold_left (fun acc (_, uf) -> urlf_urls uf @ acc) [] t.extra_sources + List.fold_left (fun acc (_, uf) -> urlf_urls uf @ acc) [] t.extra_sources @ + List.map snd t.pin_depends let filters_of_formula f = OpamFormula.fold_left (fun acc (_, f) -> @@ -256,25 +269,26 @@ |> with_bug_reports [""] |> with_synopsis "" -let lint ?check_extra_files ?(check_upstream=false) t = +let t_lint ?check_extra_files ?(check_upstream=false) ?(all=false) t = let format_errors = List.map (fun (field, (pos, msg)) -> 3, `Error, Printf.sprintf "File format error in '%s'%s: %s" field (match pos with - | Some (_,li,col) when li >= 0 && col >= 0 -> + | Some {start=li,col; _} when li >= 0 && col >= 0 -> Printf.sprintf " at line %d, column %d" li col | _ -> "") msg) (OpamFile.OPAM.format_errors t) in let cond num level msg ?detail cd = - if cd then + if all then Some (num, level, msg) + else if cd then let msg = match detail with + | None | Some [] -> msg | Some d -> Printf.sprintf "%s: \"%s\"" msg (String.concat "\", \"" d) - | None -> msg in Some (num, level, msg) else None @@ -282,6 +296,19 @@ let all_commands = all_commands t in let all_expanded_strings = all_expanded_strings t in let all_depends = all_depends t in + (* Upstream is checked only if it is an archive and non vcs backend *) + let url_is_archive = + let open OpamStd.Option.Op in + t.url >>| OpamFile.URL.url >>| (fun u -> + match u.OpamUrl.backend with + | #OpamUrl.version_control -> false + | _ -> OpamSystem.is_archive (OpamUrl.base_url u)) + in + let check_upstream = + check_upstream && + not (OpamFile.OPAM.has_flag Pkgflag_Conf t) && + url_is_archive = Some true + in let warnings = [ cond 20 `Warning "Field 'opam-version' refers to the patch version of opam, it \ @@ -292,7 +319,7 @@ "Field 'opam-version' doesn't match the current version, \ validation may not be accurate" ~detail:[OpamVersion.to_string t.opam_version] - (OpamVersion.compare t.opam_version OpamVersion.current_nopatch <> 0); + (OpamVersion.compare t.opam_version OpamFile.OPAM.format_version <> 0); (* cond (t.name = None) "Missing field 'name' or directory in the form 'name.version'"; @@ -485,7 +512,7 @@ match t.OpamFile.OPAM.name with | None -> false | Some name -> - not (OpamStd.String.starts_with ~prefix:"opam-" + not (OpamStd.String.starts_with ~prefix:OpamPath.plugin_prefix (OpamPackage.Name.to_string name))); (let unclosed = List.fold_left (fun acc s -> @@ -519,7 +546,7 @@ (t.deprecated_build_test <> [] || t.deprecated_build_doc <> []); (let suspicious_urls = List.filter (fun u -> - OpamUrl.parse ~handle_suffix:true (OpamUrl.to_string u) <> u) + OpamUrl.parse_opt ~handle_suffix:true (OpamUrl.to_string u) <> Some u) (all_urls t) in cond 49 `Warning @@ -587,9 +614,16 @@ "Mismatching 'extra-files:' field" ~detail:(List.map OpamFilename.Base.to_string mismatching_extra_files) (mismatching_extra_files <> [])); - (let spaced_depexts = List.concat (List.map (fun (dl,_) -> - List.filter (fun d -> String.contains d ' ' || String.length d = 0) dl) - t.depexts) in + (let spaced_depexts = + List.concat (List.map (fun (dl,_) -> + OpamStd.List.filter_map + (fun s -> + let d = OpamSysPkg.to_string s in + if String.contains d ' ' || String.length d = 0 then + Some d + else None) + (OpamSysPkg.Set.elements dl)) + t.depexts) in cond 54 `Warning "External dependencies should not contain spaces nor empty string" ~detail:spaced_depexts @@ -641,44 +675,68 @@ (rem_test || rem_doc)); cond 59 `Warning "url doesn't contain a checksum" (check_upstream && - not (OpamFile.OPAM.has_flag Pkgflag_Conf t) && OpamStd.Option.map OpamFile.URL.checksum t.url = Some []); (let upstream_error = - if not check_upstream || OpamFile.OPAM.has_flag Pkgflag_Conf t then None - else + if not check_upstream then None else match t.url with | None -> Some "No url defined" - | Some url -> + | Some urlf -> let open OpamProcess.Job.Op in + let check_checksum f = + match OpamFile.URL.checksum urlf with + | [] -> None + | chks -> + let not_corresponding = + OpamStd.List.filter_map (fun chk -> + match OpamHash.mismatch (OpamFilename.to_string f) chk with + | Some m -> Some (m, chk) + | None -> None) + chks + in + if not_corresponding = [] then None + else + let msg = + let is_singular = function [_] -> true | _ -> false in + Printf.sprintf "The archive doesn't match checksum%s:\n%s." + (if is_singular not_corresponding then "" else "s") + (OpamStd.Format.itemize (fun (good, bad) -> + Printf.sprintf "archive: %s, in opam file: %s" + (OpamHash.to_string good) (OpamHash.to_string bad)) + not_corresponding) + in + Some msg + in + let url = OpamFile.URL.url urlf in OpamProcess.Job.run @@ OpamFilename.with_tmp_dir_job @@ fun dir -> - OpamProcess.Job.catch (function - Failure msg -> Done (Some msg) - | OpamDownload.Download_fail (s,l) -> - Done (Some (OpamStd.Option.default l s)) - | e -> Done (Some (Printexc.to_string e))) - @@ fun () -> - OpamDownload.download ~overwrite:false (OpamFile.URL.url url) dir - @@| fun f -> - (match OpamFile.URL.checksum url with - | [] -> None - | chks -> - let not_corresponding = - List.filter (fun chk -> - not (OpamHash.check_file (OpamFilename.to_string f) chk)) - chks - in - if not_corresponding = [] then None - else - let msg = - Printf.sprintf "Cheksum%s %s don't verify archive" - (if List.length chks = 1 then "" else "s") - (OpamStd.List.to_string OpamHash.to_string not_corresponding) - in - Some msg) + match url.backend with + | #OpamUrl.version_control -> Done None (* shouldn't happen *) + | `http -> + OpamProcess.Job.catch (function + | Failure msg -> Done (Some msg) + | OpamDownload.Download_fail (s,l) -> + Done (Some (OpamStd.Option.default l s)) + | e -> Done (Some (Printexc.to_string e))) + @@ fun () -> + OpamDownload.download ~overwrite:false url dir + @@| check_checksum + | `rsync -> + let filename = + let open OpamStd.Option.Op in + (OpamFile.OPAM.name_opt t + >>| OpamPackage.Name.to_string) + +! "lint-check-upstream" + |> OpamFilename.Base.of_string + |> OpamFilename.create dir + in + OpamLocal.rsync_file url filename + @@| function + | Up_to_date f | Result f -> check_checksum f + | Not_available (_,src) -> + Some ("Source not found: "^src) in cond 60 `Error "Upstream check failed" - ~detail:[OpamStd.Option.default "" upstream_error] + ~detail:(OpamStd.Option.to_list upstream_error) (upstream_error <> None)); (let with_test = List.exists ((=) (OpamVariable.Full.of_string "with-test")) @@ -687,11 +745,137 @@ cond 61 `Warning "`with-test` variable in `run-test` is out of scope, it will be ignored" with_test); + (let bad_licenses = + List.filter (fun s -> not (is_valid_license_id s)) t.license + in + cond 62 `Warning + "License doesn't adhere to the SPDX standard, see https://spdx.org/licenses/" + ~detail:bad_licenses + (bad_licenses <> [])); +(* + (let subpath = + match OpamStd.String.Map.find_opt "x-subpath" (extensions t) with + | Some {pelem = String _; _} -> true + | _ -> false + in + let opam_restriction = + OpamFilter.fold_down_left (fun acc filter -> + acc || + match filter with + | FOp (FIdent (_, var, _), op, FString version) + when OpamVariable.to_string var = "opam-version" -> + OpamFormula.simplify_version_formula + (OpamFormula.ands + [ Atom (`Lt, OpamPackage.Version.of_string "2.1"); + Atom (op, OpamPackage.Version.of_string version) ]) + = None + | _ -> false) false t.available + in + cond 63 `Error + "`subpath` field need `opam-version = 2.1` restriction" + (subpath && not opam_restriction)); + (let subpath_string = + match OpamStd.String.Map.find_opt "x-subpath" (extensions t) with + | Some {pelem = String _; _} | None -> false + | _ -> true + in + cond 64 `Warning + "`x-subpath` must be a simple string to be considered as a subpath`" + subpath_string); +*) + (let relative = + let open OpamUrl in + List.filter (fun u -> + (* OpamUrl.local_dir is not used because it checks the existence of + the directory *) + (match u.backend, u.transport with + | (#version_control | `rsync), + ("file" | "path" | "local" | "rsync") -> true + | _, _ -> false) + && (Filename.is_relative u.path + || OpamStd.String.contains ~sub:".." u.path)) + (all_urls t) + in + cond 65 `Error + "URLs must be absolute" + ~detail:(List.map (fun u -> u.OpamUrl.path) relative) + (relative <> [])); + (let maybe_bool = + (* Regexp from [OpamFilter.string_interp_regexp] *) + let re = + let open Re in + let notclose = + rep @@ alt [ + diff notnl @@ set "}"; + seq [char '}'; alt [diff notnl @@ set "%"; stop] ] + ] + in + compile @@ seq [ + bos; alt [ + str "true"; str "false"; str "%%"; + seq [str "%{"; greedy notclose; opt @@ str "}%"]; + ]; eos] + in + fun s -> + try let _ = Re.exec re s in true with Not_found -> false + in + let check_strings = + let rec aux acc oped = function + | FString s -> if oped || maybe_bool s then acc else s::acc + | FIdent _ | FBool _ -> acc + | FOp (fl,_,fr) -> (aux acc true fl) @ aux acc true fr + | FAnd (fl, fr) | FOr (fl, fr) -> + (aux acc false fl) @ aux acc false fr + | FNot f | FDefined f | FUndef f -> aux acc false f + in + aux [] false + in + let check_formula = + OpamFormula.fold_left (fun acc (_, form as ff) -> + match + OpamFormula.fold_left (fun acc fc -> + match fc with + | Filter f -> check_strings f @ acc + | Constraint _ -> acc) [] form + with + | [] -> acc + | strs -> (ff, List.rev strs)::acc + ) + in + let not_bool_strings = + List.fold_left check_formula [] + (t.depends :: t.depopts :: t.conflicts + :: List.map (fun (_,f,_) -> f) t.features) + in + cond 66 `Warning + "String that can't be resolved to bool in filtered package formula" + ~detail:(List.map (fun (f, strs) -> + Printf.sprintf "%s in '%s'" + (OpamStd.Format.pretty_list (List.map (Printf.sprintf "%S") strs)) + (OpamFilter.string_of_filtered_formula (Atom f))) + not_bool_strings) + (not_bool_strings <> [])); + cond 67 `Error + "Checksum specified with a non archive url" + ?detail:(OpamStd.Option.map (fun url -> + [Printf.sprintf "%s - %s" + (OpamFile.URL.url url |> OpamUrl.to_string) + (OpamFile.URL.checksum url + |> List.map OpamHash.to_string + |> OpamStd.Format.pretty_list)]) + t.url) + (match t.url with + | None -> false + | Some urlf -> + (OpamFile.URL.checksum urlf <> []) + && url_is_archive <> Some true); ] in format_errors @ OpamStd.List.filter_map (fun x -> x) warnings +let lint = t_lint ~all:false + let extra_files_default filename = let dir = OpamFilename.Op.(OpamFilename.dirname @@ -703,12 +887,13 @@ OpamHash.check_file (OpamFilename.to_string f)) (OpamFilename.rec_files dir) -let lint_gen ?check_extra_files ?check_upstream reader filename = +let lint_gen ?check_extra_files ?check_upstream ?(handle_dirname=false) + reader filename = let warnings, t = let warn_of_bad_format (pos, msg) = 2, `Error, Printf.sprintf "File format error%s: %s" (match pos with - | Some (_,li,col) when li >= 0 && col >= 0 -> + | Some {start=li,col; _} when li >= 0 && col >= 0 -> Printf.sprintf " at line %d, column %d" li col | _ -> "") msg @@ -721,6 +906,7 @@ (OpamFormat.I.map_file OpamFile.OPAM.pp_raw_fields) f in let t, warnings = + if handle_dirname = false then t, [] else match OpamPackage.of_filename (OpamFile.filename filename) with | None -> t, [] | Some nv -> @@ -757,7 +943,10 @@ in warnings, Some (OpamFile.OPAM.with_metadata_dir - (Some (OpamFilename.dirname (OpamFile.filename filename))) t) + (Some (None, + OpamFilename.Dir.to_string + (OpamFilename.dirname + (OpamFile.filename filename)))) t) with | OpamSystem.File_not_found _ -> OpamConsole.error "%s not found" (OpamFile.to_string filename); @@ -774,7 +963,7 @@ warnings @ (match t with Some t -> lint ~check_extra_files ?check_upstream t | None -> []), t -let lint_file ?check_extra_files ?check_upstream filename = +let lint_file ?check_extra_files ?check_upstream ?handle_dirname filename = let reader filename = try let ic = OpamFilename.open_in (OpamFile.filename filename) in @@ -786,15 +975,20 @@ OpamConsole.error_and_exit `Bad_arguments "File %s not found" (OpamFile.to_string filename) in - lint_gen ?check_extra_files ?check_upstream reader filename + lint_gen ?check_extra_files ?check_upstream ?handle_dirname reader filename -let lint_channel ?check_extra_files ?check_upstream filename ic = +let lint_channel ?check_extra_files ?check_upstream ?handle_dirname + filename ic = let reader filename = OpamFile.Syntax.of_channel filename ic in - lint_gen ?check_extra_files ?check_upstream reader filename + lint_gen ?check_extra_files ?check_upstream ?handle_dirname reader filename -let lint_string ?check_extra_files ?check_upstream filename string = +let lint_string ?check_extra_files ?check_upstream ?handle_dirname + filename string = let reader filename = OpamFile.Syntax.of_string filename string in - lint_gen ?check_extra_files ?check_upstream reader filename + lint_gen ?check_extra_files ?check_upstream ?handle_dirname reader filename + +let all_lint_warnings () = + t_lint ~all:true OpamFile.OPAM.empty let warns_to_string ws = OpamStd.List.concat_map "\n" @@ -807,6 +1001,35 @@ (Printf.sprintf " %16s %2d: %s" ws n s)) ws +let warns_to_json ?filename ws = + let filename = + match filename with + | Some f -> f + | None -> "stdout" + in + let warn, err = + List.fold_left (fun (w,e) (n,we,s) -> + let arr = + `O [ "id", `Float (float_of_int n); + "message", `String s] + in + match we with + | `Warning -> arr::w, e + | `Error -> w, arr::e) ([],[]) ws + in + let result = + match warn,err with + | [],[] -> "passed" + | _, _::_ -> "error" + | _::_, [] -> "warning" + in + `O [ + "file", `String filename; + "result", `String result; + "warnings", `A warn; + "errors", `A err + ] + (* Package definition loading *) open OpamFilename.Op @@ -835,7 +1058,11 @@ let add_aux_files ?dir ~files_subdir_hashes opam = let dir = match dir with - | None -> OpamFile.OPAM.metadata_dir opam + | None -> + OpamFile.OPAM.get_metadata_dir ~repos_roots:(fun r -> + failwith ("Repository "^OpamRepositoryName.to_string r^ + " not registered for add_aux_files!")) + opam | some -> some in match dir with @@ -883,15 +1110,25 @@ if not files_subdir_hashes then opam else let extra_files = OpamFilename.opt_dir files_dir >>| fun dir -> - List.map - (fun f -> - OpamFilename.Base.of_string (OpamFilename.remove_prefix dir f), - OpamHash.compute (OpamFilename.to_string f)) - (OpamFilename.rec_files dir) + OpamFilename.rec_files dir + |> List.map (fun file -> + file, + OpamFilename.Base.of_string (OpamFilename.remove_prefix dir file)) in match OpamFile.OPAM.extra_files opam, extra_files with | None, None -> opam - | None, Some ef -> OpamFile.OPAM.with_extra_files ef opam + | None, Some ef -> + log ~level:2 "Missing extra-files field for %s, adding them." + (OpamStd.List.concat_map ", " + (fun (_,f) -> OpamFilename.Base.to_string f) ef); + let ef = + List.map + (fun (file, basename) -> + basename, + OpamHash.compute (OpamFilename.to_string file)) + ef + in + OpamFile.OPAM.with_extra_files ef opam | Some ef, None -> log "Missing expected extra files %s at %s/files" (OpamStd.List.concat_map ", " @@ -899,10 +1136,35 @@ (OpamFilename.Dir.to_string dir); opam | Some oef, Some ef -> - let sort = List.sort (fun (b, _) (b', _) -> compare b b') in - if sort oef <> sort ef then - log "Mismatching extra-files at %s" - (OpamFilename.Dir.to_string dir); + let wr_check, nf_opam, rest = + List.fold_left (fun (wr_check, nf_opam, rest) (file, basename) -> + match OpamStd.List.pick_assoc basename rest with + | None, rest -> + wr_check, (basename::nf_opam), rest + | Some ohash, rest -> + (if OpamHash.check_file (OpamFilename.to_string file) ohash then + wr_check + else + basename::wr_check), + nf_opam, rest + ) ([], [], oef) ef + in + let nf_file = List.map fst rest in + if nf_file <> [] || wr_check <> [] || nf_opam <> [] then + log "Mismatching extra-files at %s: %s" + (OpamFilename.Dir.to_string dir) + ((if nf_file = [] then None else + Some (Printf.sprintf "missing from 'files' directory (%d)" + (List.length nf_file))) + :: (if nf_opam = [] then None else + Some (Printf.sprintf "missing from opam file (%d)" + (List.length nf_opam))) + :: (if wr_check = [] then None else + Some (Printf.sprintf "wrong checksum (%d)" + (List.length wr_check))) + :: [] + |> OpamStd.List.filter_some + |> OpamStd.Format.pretty_list); opam in opam @@ -920,3 +1182,51 @@ (OpamPp.string_of_bad_format (OpamPp.Bad_format (snd err))); None | None, None -> None + +let read_repo_opam ~repo_name ~repo_root dir = + let open OpamStd.Option.Op in + read_opam dir >>| + OpamFile.OPAM.with_metadata_dir + (Some (Some repo_name, OpamFilename.remove_prefix_dir repo_root dir)) + +let dep_formula_to_string f = + let pp = + OpamFormat.V.(package_formula `Conj (constraints version)) + in + OpamPrinter.FullPos.value (OpamPp.print pp f) + +let sort_opam opam = + log "sorting %s" (OpamPackage.to_string (package opam)); + let sort_ff = + let compare_filters filter filter' = + let get_vars = function + | Constraint _ -> [] + | Filter filter -> + List.sort compare (OpamFilter.variables filter) + in + match get_vars filter, get_vars filter' with + | v::_, v'::_ -> compare v v' + | [], _::_ -> 1 + | _::_, [] -> -1 + | [],[] -> 0 + in + OpamFilter.sort_filtered_formula + (fun (n,filter) (n',filter') -> + let cmp = OpamFormula.compare_formula compare_filters filter filter' in + if cmp <> 0 then cmp else + OpamPackage.Name.compare n n') + in + let fst_sort ?comp = + let comp = OpamStd.Option.default compare comp in + fun l -> List.sort (fun (e,_) (e',_) -> comp e e') l + in + opam + |> with_author @@ List.sort compare opam.author + |> with_tags @@ List.sort compare opam.tags + |> with_depends @@ sort_ff opam.depends + |> with_depopts @@ sort_ff opam.depopts + |> with_depexts @@ fst_sort opam.depexts + |> with_conflicts @@ sort_ff opam.conflicts + |> with_pin_depends @@ fst_sort ~comp:OpamPackage.compare opam.pin_depends + |> with_extra_files_opt @@ OpamStd.Option.map fst_sort opam.extra_files + |> with_extra_sources @@ fst_sort opam.extra_sources diff -Nru opam-2.0.10/src/state/opamFileTools.mli opam-2.1.2/src/state/opamFileTools.mli --- opam-2.0.10/src/state/opamFileTools.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamFileTools.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2016 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -19,20 +19,22 @@ (** Runs several sanity checks on the opam file; returns a list of warnings. [`Error] level should be considered unfit for publication, while [`Warning] are advisory but may be accepted. The int is an identifier for this specific - warning/error. If [check_extra_files] is unspecified, warning 53 won't be - checked. *) + warning/error. If [check_extra_files] is unspecified or false, warning 53 + won't be checked. *) val lint: ?check_extra_files:(basename * (OpamHash.t -> bool)) list -> - ?check_upstream: bool -> + ?check_upstream:bool -> OpamFile.OPAM.t -> (int * [`Warning|`Error] * string) list (** Same as [lint], but operates on a file, which allows catching parse errors - too. You can specify an expected name and version. [check_extra_files] - defaults to a function that will look for a [files/] directory besides - [filename] *) + too. [check_extra_files] defaults to a function that will look for a [files/] + directory besides [filename]. [handle_dirname] is used for warning 4, and + should be set when reading packages from a repository, so that package name + and version are inferred from the filename. *) val lint_file: ?check_extra_files:(basename * (OpamHash.t -> bool)) list -> - ?check_upstream: bool -> + ?check_upstream:bool -> + ?handle_dirname:bool -> OpamFile.OPAM.t OpamFile.typed_file -> (int * [`Warning|`Error] * string) list * OpamFile.OPAM.t option @@ -42,6 +44,7 @@ val lint_channel: ?check_extra_files:(basename * (OpamHash.t -> bool)) list -> ?check_upstream: bool -> + ?handle_dirname:bool -> OpamFile.OPAM.t OpamFile.typed_file -> in_channel -> (int * [`Warning|`Error] * string) list * OpamFile.OPAM.t option @@ -51,17 +54,47 @@ val lint_string: ?check_extra_files:(basename * (OpamHash.t -> bool)) list -> ?check_upstream: bool -> + ?handle_dirname:bool -> OpamFile.OPAM.t OpamFile.typed_file -> string -> (int * [`Warning|`Error] * string) list * OpamFile.OPAM.t option +val all_lint_warnings: unit -> (int * [`Warning|`Error] * string) list + (** Utility function to print validation results *) val warns_to_string: (int * [`Warning|`Error] * string) list -> string +(** Utility function to construct a json of validation results. + The format is as follow: + { "file" : string , + "result" : string (passed | error | warning), + "warnings" : + [ { "id" : int, + "message" : string }, + ... + ], + "errors" : + [ { "id" : int, + "message" : string }, + ... + ] + } +*) +val warns_to_json: + ?filename:string -> (int * [`Warning|`Error] * string) list -> OpamJson.t + (** Read the opam metadata from a given directory (opam file, with possible overrides from url and descr files). Also includes the names and hashes - of files below files/ *) + of files below files/ + Warning: use [read_repo_opam] instead for correctly reading files from + repositories!*) val read_opam: dirname -> OpamFile.OPAM.t option +(** Like [read_opam], but additionally fills in the [metadata_dir] info + correctly for the given repository. *) +val read_repo_opam: + repo_name:repository_name -> repo_root:dirname -> + dirname -> OpamFile.OPAM.t option + (** Adds data from 'url' and 'descr' files found in the specified dir or the opam file's metadata dir, if not already present in the opam file. if [files_subdir_hashes] is [true], also adds the names and hashes of files @@ -75,3 +108,11 @@ val map_all_filters: (filter -> filter) -> OpamFile.OPAM.t -> OpamFile.OPAM.t + +(** Converts a dependency formula to the same format as used in opam package + definition files. *) +val dep_formula_to_string: formula -> string + +(** Sort opam fields: author, tags, depexts, depends, depopts, conflicts, + pin_depends, extra_files, extra_sources *) +val sort_opam: OpamFile.OPAM.t -> OpamFile.OPAM.t diff -Nru opam-2.0.10/src/state/opamFormatUpgrade.ml opam-2.1.2/src/state/opamFormatUpgrade.ml --- opam-2.0.10/src/state/opamFormatUpgrade.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamFormatUpgrade.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -17,7 +17,7 @@ let log fmt = OpamConsole.log "FMT_UPG" fmt let slog = OpamConsole.slog -exception Upgrade_done of OpamFile.Config.t +exception Upgrade_done of OpamFile.Config.t * (OpamFile.Config.t -> unit) option (* - Package and aux functions - *) @@ -42,7 +42,7 @@ | "nixpkgs" -> eq distro "nixos" | "arch" -> eq distro "archlinux" | "homebrew" | "macports" | "debian" | "ubuntu" | "centos" | "fedora" - | "rhel" | "opensuse" | "oraclelinux" | "mageia" | "alpine" + | "rhel" | "opensuse" | "oraclelinux" | "ol" | "mageia" | "alpine" | "archlinux" | "gentoo" | "nixos" as d -> eq distro d | "bsd" -> eq os_family "bsd" @@ -65,6 +65,8 @@ None) depexts +let v2_0 = OpamVersion.of_string "2.0" + let opam_file_from_1_2_to_2_0 ?filename opam = let ocaml_pkgname = OpamPackage.Name.of_string "ocaml" in @@ -78,7 +80,11 @@ let filename = match filename with | Some f -> OpamFile.to_string f | None -> match OpamFile.OPAM.metadata_dir opam with - | Some d -> OpamFilename.to_string (d // "opam") + | Some (Some r, rel_d) -> + Printf.sprintf "<%s>/%s/opam" (OpamRepositoryName.to_string r) rel_d + | Some (None, abs_d) -> + let d = OpamFilename.Dir.of_string abs_d in + OpamFilename.to_string (d // "opam") | None -> "opam file" in let available = @@ -156,7 +162,8 @@ aux available in let pkg_deps = - if NMap.mem ocaml_wrapper_pkgname pkg_deps || + if OpamVersion.compare (OpamFile.OPAM.opam_version opam) v2_0 >= 0 || + NMap.mem ocaml_wrapper_pkgname pkg_deps || OpamFile.OPAM.has_flag Pkgflag_Conf opam then pkg_deps else NMap.add ocaml_wrapper_pkgname Empty pkg_deps @@ -325,7 +332,7 @@ | ft -> ft) in opam |> - OpamFile.OPAM.with_opam_version (OpamVersion.of_string "2.0") |> + OpamFile.OPAM.with_opam_version v2_0 |> OpamFile.OPAM.with_depends depends |> OpamFile.OPAM.with_depopts depopts |> OpamFile.OPAM.with_conflicts conflicts |> @@ -764,6 +771,7 @@ in let repositories_list = List.map (fun (_, r, _) -> r) prio_repositories in OpamFile.Config.with_repositories repositories_list conf + |> OpamFile.Config.with_opam_version v2_0 let v2_0_alpha2 = OpamVersion.of_string "2.0~alpha2" @@ -930,6 +938,8 @@ repos = None; opam_root; paths; variables; wrappers = OpamFile.Wrappers.empty; env = []; + invariant = None; + depext_bypass = OpamSysPkg.Set.empty; } in OpamFile.Switch_config.write (OpamFile.make new_config_file) new_config; @@ -1035,72 +1045,254 @@ (OpamFile.Config.eval_variables conf)) conf -let v2_0 = OpamVersion.of_string "2.0" - let from_2_0_beta5_to_2_0 _ conf = conf -let latest_version = v2_0 +(* swiitch config with opam-version 2.1 *) +let v2_1_alpha = OpamVersion.of_string "2.1~alpha" +(* config with opam-version 2.1 *) +let v2_1_alpha2 = OpamVersion.of_string "2.1~alpha2" +(* config & sw config downgrade opam-version to 2.0 and add opam root version *) +let v2_1_rc = OpamVersion.of_string "2.1~rc" -let as_necessary global_lock root config = - let config_version = OpamFile.Config.opam_version config in - let cmp = OpamVersion.(compare current_nopatch config_version) in - if cmp = 0 then () else - if OpamVersion.compare config_version latest_version >= 0 then () else - let is_dev = OpamVersion.git () <> None in - OpamConsole.formatted_msg - "This %sversion of opam requires an update to the layout of %s \ - from version %s to version %s, which can't be reverted.\n\ - You may want to back it up before going further.\n" - (if is_dev then "development " else "") - (OpamFilename.Dir.to_string root) - (OpamVersion.to_string config_version) +let v2_1 = OpamVersion.of_string "2.1" + +let from_2_0_to_2_1_alpha _ conf = conf + +let downgrade_2_1_switches root conf = + List.iter (fun switch -> + let f = OpamPath.Switch.switch_config root switch in + OpamStd.Option.iter (OpamFile.Switch_config.write f) + (OpamStateConfig.downgrade_2_1_switch f)) + (OpamFile.Config.installed_switches conf); + conf + +let from_2_1_alpha_to_2_1_alpha2 root conf = + downgrade_2_1_switches root conf + +let from_2_1_alpha2_to_v2_1_rc root conf = + downgrade_2_1_switches root conf + +let from_2_1_rc_to_v2_1 _ conf = conf + +let from_2_0_to_v2_1 _ conf = conf + +let latest_version = OpamFile.Config.root_version + +let latest_hard_upgrade = (* to *) v2_0_beta5 + +(* intermediates roots that need an hard upgrade *) +let intermediate_roots = [ + v2_1_alpha; v2_1_alpha2; v2_1_rc +] + +let remove_missing_switches root conf = + let exists, missing = + List.partition (fun switch -> + OpamFilename.exists (OpamFile.filename + (OpamPath.Switch.switch_config root switch))) + (OpamFile.Config.installed_switches conf) + in + OpamFile.Config.with_installed_switches exists conf, missing + +let as_necessary ?reinit requested_lock global_lock root config = + let root_version = + match OpamFile.Config.opam_root_version_opt config with + | Some v -> v + | None -> + let v = OpamFile.Config.opam_version config in + if OpamVersion.compare v v2_0 <> 0 then v else + try + List.iter (fun switch -> + ignore @@ + OpamFile.Switch_config.read_opt + (OpamPath.Switch.switch_config root switch)) + (OpamFile.Config.installed_switches config); + v + with Sys_error _ | OpamPp.Bad_version _ -> v2_1_alpha + in + let cmp = OpamVersion.(compare OpamFile.Config.root_version root_version) in + if cmp <= 0 then config (* newer or same *) else + let is_intermdiate_root = List.mem root_version intermediate_roots in + let keep_needed_upgrades = + List.filter (fun (v,_) -> OpamVersion.compare root_version v < 0) + in + (* to generalise *) + let intermediates = + let hard = [ + v2_1_alpha, from_2_0_to_2_1_alpha; + v2_1_alpha2, from_2_1_alpha_to_2_1_alpha2; + v2_1_rc, from_2_1_alpha2_to_v2_1_rc; + ] in + let light = [ + v2_1, from_2_1_rc_to_v2_1; + ] in + keep_needed_upgrades hard, + light + in + let hard_upg, light_upg = + if is_intermdiate_root then intermediates else + [ + v1_1, from_1_0_to_1_1; + v1_2, from_1_1_to_1_2; + v1_3_dev2, from_1_2_to_1_3_dev2; + v1_3_dev5, from_1_3_dev2_to_1_3_dev5; + v1_3_dev6, from_1_3_dev5_to_1_3_dev6; + v1_3_dev7, from_1_3_dev6_to_1_3_dev7; + v2_0_alpha, from_1_3_dev7_to_2_0_alpha; + v2_0_alpha2, from_2_0_alpha_to_2_0_alpha2; + v2_0_alpha3, from_2_0_alpha2_to_2_0_alpha3; + v2_0_beta, from_2_0_alpha3_to_2_0_beta; + v2_0_beta5, from_2_0_beta_to_2_0_beta5; + v2_0, from_2_0_beta5_to_2_0; + v2_1, from_2_0_to_v2_1; + ] + |> keep_needed_upgrades + |> List.partition (fun (v,_) -> + OpamVersion.compare v latest_hard_upgrade <= 0) + in + let need_hard_upg = hard_upg <> [] in + let on_the_fly, global_lock_kind = + if not need_hard_upg && requested_lock <> `Lock_write then + true, `Lock_read + else + false, `Lock_write + in + let erase_plugin_links root = + let plugins_bin = OpamPath.plugins_bin root in + if OpamFilename.exists_dir plugins_bin then begin + List.iter OpamFilename.remove @@ OpamFilename.files_and_links plugins_bin + end + in + let light config = + let config = + List.fold_left (fun config (v, from) -> + from root config |> OpamFile.Config.with_opam_root_version v) + config light_upg + in + if not on_the_fly then begin + OpamFile.Config.write (OpamPath.config root) config; + erase_plugin_links root; + end; + config + in + let hard config = + List.fold_left (fun config (v, from) -> + let config = from root config |> OpamFile.Config.with_opam_root_version v in + (* save the current version to mitigate damage is the upgrade goes + wrong afterwards *) + OpamFile.Config.write (OpamPath.config root) config; + erase_plugin_links root; + config) + config hard_upg + in + let config = + let config, missing_switches = remove_missing_switches root config in + let global = List.filter (OpamSwitch.is_external @> not) missing_switches in + if not on_the_fly && global <> [] then + OpamConsole.warning "Removing global switch%s %s as %s" + (match global with | [_] -> "" | _ -> "es") + (OpamStd.Format.pretty_list + (List.map (OpamSwitch.to_string + @> OpamConsole.colorise `bold + @> Printf.sprintf "'%s'") + global)) + (match global with + | [_] -> "it no longer exists" + | _ -> "they no longer exist"); + config + in + if hard_upg = [] && light_upg = [] then config (* no upgrade to do *) else + let is_dev = OpamVersion.is_dev_version () in + log "%s config upgrade, from %s to %s" + (if on_the_fly then "On-the-fly" else + if need_hard_upg then "Hard" else "Light") + (OpamVersion.to_string root_version) (OpamVersion.to_string latest_version); + if not on_the_fly then + OpamConsole.errmsg "%s\n" @@ + OpamStd.Format.reformat @@ + Printf.sprintf + "This %sversion of opam requires an update to the layout of %s \ + from version %s to version %s, which can't be reverted.\n\ + You may want to back it up before going further.\n" + (if is_dev then "development " else "") + (OpamFilename.Dir.to_string root) + (OpamVersion.to_string root_version) + (OpamVersion.to_string latest_version); let dontblock = (* Deadlock until one is killed in interactive mode, but abort in batch *) if OpamStd.Sys.tty_out then None else Some true in try - OpamFilename.with_flock_upgrade `Lock_write ?dontblock global_lock + OpamFilename.with_flock_upgrade global_lock_kind ?dontblock global_lock @@ fun _ -> - if is_dev && - Some "yes" = - OpamConsole.read "Type \"yes\" to perform the update and continue:" || - not is_dev && - OpamConsole.confirm "Perform the update and continue?" - then - let update_to v f config = - if OpamVersion.compare config_version v < 0 then - let config = f root config |> OpamFile.Config.with_opam_version v in - (* save the current version to mitigate damage is the upgrade goes - wrong afterwards *) - OpamFile.Config.write (OpamPath.config root) - (OpamFile.Config.with_opam_version v config); - config - else config - in - let config = - config |> - update_to v1_1 from_1_0_to_1_1 |> - update_to v1_2 from_1_1_to_1_2 |> - update_to v1_3_dev2 from_1_2_to_1_3_dev2 |> - update_to v1_3_dev5 from_1_3_dev2_to_1_3_dev5 |> - update_to v1_3_dev6 from_1_3_dev5_to_1_3_dev6 |> - update_to v1_3_dev7 from_1_3_dev6_to_1_3_dev7 |> - update_to v2_0_alpha from_1_3_dev7_to_2_0_alpha |> - update_to v2_0_alpha2 from_2_0_alpha_to_2_0_alpha2 |> - update_to v2_0_alpha3 from_2_0_alpha2_to_2_0_alpha3 |> - update_to v2_0_beta from_2_0_alpha3_to_2_0_beta |> - update_to v2_0_beta5 from_2_0_beta_to_2_0_beta5 |> - update_to v2_0 from_2_0_beta5_to_2_0 - in - OpamConsole.msg "Format upgrade done.\n"; - raise (Upgrade_done config) + if not on_the_fly then + if need_hard_upg then + if is_dev && + Some "yes" = + OpamConsole.read "Type \"yes\" to perform the update and continue:" + || not is_dev && + OpamConsole.confirm "Perform the update and continue?" + then + let config = hard config |> light in + OpamConsole.msg "Format upgrade done.\n"; + (* We need to re run init in case of hard upgrade *) + raise (Upgrade_done (config, reinit)) + else + OpamStd.Sys.exit_because `Aborted + else + if OpamConsole.confirm "Continue?" then + (let config = light config in + OpamConsole.msg "Format upgrade done.\n"; + config) + else + OpamStd.Sys.exit_because `Aborted else - OpamStd.Sys.exit_because `Aborted + (let config = light config in + log "Format upgrade done"; + config) with OpamSystem.Locked -> OpamConsole.error_and_exit `Locked "Could not acquire lock for performing format upgrade." +let hard_upgrade_from_2_1_intermediates ?reinit ?global_lock root = + let config_f = OpamPath.config root in + let opam_root_version = OpamFile.Config.raw_root_version config_f in + match opam_root_version with + | Some v when OpamVersion.compare v v2_0 <= 0 + || OpamVersion.compare v2_1 v <= 0 -> + () (* do nothing, need to reraise parsing exception *) + | _ -> + log "Intermediate opam root detected%s, launch hard upgrade" + (match opam_root_version with + None -> "" + | Some v -> "("^(OpamVersion.to_string v)^")"); + let filename = OpamFile.filename config_f in + let opamfile = OpamParser.FullPos.file (OpamFilename.to_string filename) in + let opamfile' = + let open OpamParserTypes.FullPos in + { opamfile with + file_contents = + List.map (fun item -> + match item.pelem with + | Variable (({pelem = "opam-version"; _} as opam_v), + ({pelem = String "2.1"; _} as v)) -> + { item with + pelem = Variable ({opam_v with pelem = "opam-version"}, + {v with pelem = String "2.0"})} + | _ -> item) opamfile.file_contents} + in + log "Downgrade config opam-version to fix up"; + OpamFilename.write filename (OpamPrinter.FullPos.opamfile opamfile'); + let config = OpamFile.Config.read config_f in + let global_lock = match global_lock with + | Some g -> g + | None -> OpamFilename.flock `Lock_read (OpamPath.lock root) + in + (* it will trigger only hard upgrades that won't get back *) + ignore @@ as_necessary `Lock_write global_lock root ?reinit + (OpamFile.Config.with_opam_root_version v2_1_alpha2 config) + let opam_file ?(quiet=false) ?filename opam = let v = OpamFile.OPAM.opam_version opam in if OpamVersion.compare v v2_0_alpha3 < 0 diff -Nru opam-2.0.10/src/state/opamFormatUpgrade.mli opam-2.1.2/src/state/opamFormatUpgrade.mli --- opam-2.0.10/src/state/opamFormatUpgrade.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamFormatUpgrade.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -13,21 +13,40 @@ versions to the current one *) open OpamTypes +open OpamStateTypes (** Raised when the opam root has been updated to a newer format, and further - action (opam init/update) is needed. *) -exception Upgrade_done of OpamFile.Config.t + action (opam init/update) is needed. + [Upgrade_done conf reinit] specifies the new config file and a reinit + function to call instead of default (see [OpamCliMain.main_catch_all]). *) +exception Upgrade_done of OpamFile.Config.t * (OpamFile.Config.t -> unit) option (** The latest version of the opam root format, that normal operation of this instance of opam requires *) val latest_version: OpamVersion.t -(** Runs the upgrade from its current format to the latest version for the opam - root at the given directory. A global write lock must be supplied. If an - upgrade has been done, raises [Upgrade_done updated_config]. *) -val as_necessary: OpamSystem.lock -> dirname -> OpamFile.Config.t -> unit +(** [as_necessary requested_lock global_lock root config] + Runs the upgrade from its current format to the latest compatible version + for the opam root at [root] directory. Performs an on-the-fly upgrade + (loaded state, not written) if possible: no hard upgrade needed, and no + write lock required ([requested_lock]). If upgrade need to be written (hard + upgrade), a write lock on the global state ([global_lock]) is taken and + when it's done raises [Upgrade_done updated_config]. Otherwise, it returns + the upgraded or unchanged config file.*) +val as_necessary: + ?reinit:(OpamFile.Config.t -> unit) -> 'a lock -> OpamSystem.lock -> dirname -> + OpamFile.Config.t -> OpamFile.Config.t + +(* Try to launch a hard upgrade from 2;1 alpha's & beta's root + to 2.1~rc one. Raises [Upgrade_done] (catched by main + function) if an upgrade is done, otherwise do nothing. + It is intend to be called after a config file that error with + [OpamPp.Bad_version] *) +val hard_upgrade_from_2_1_intermediates: + ?reinit:(OpamFile.Config.t -> unit) -> ?global_lock: OpamSystem.lock -> + dirname -> unit -(** Converts the opam file format, including rewriting availabillity conditions +(** Converts the opam file format, including rewriting availability conditions based on OCaml-related variables into dependencies. The filename is used to report errors *) val opam_file_from_1_2_to_2_0: diff -Nru opam-2.0.10/src/state/opamGlobalState.ml opam-2.1.2/src/state/opamGlobalState.ml --- opam-2.0.10/src/state/opamGlobalState.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamGlobalState.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -22,6 +22,9 @@ let config = match OpamStateConfig.load ~lock_kind root with | Some c -> c + | exception (OpamPp.Bad_version _ as e) -> + OpamFormatUpgrade.hard_upgrade_from_2_1_intermediates ~global_lock root; + raise e | None -> if OpamFilename.exists (root // "aliases") then OpamFile.Config.(with_opam_version (OpamVersion.of_string "1.1") empty) @@ -32,14 +35,16 @@ argument" (OpamFilename.Dir.to_string root) in - OpamFormatUpgrade.as_necessary global_lock root config; + let config = + OpamFormatUpgrade.as_necessary lock_kind global_lock root config + in config let inferred_from_system = "Inferred from system" let load lock_kind = let root = OpamStateConfig.(!r.root_dir) in - log "LOAD-GLOBAL-STATE @ %a" (slog OpamFilename.Dir.to_string) root; + log "LOAD-GLOBAL-STATE %@ %a" (slog OpamFilename.Dir.to_string) root; (* Always take a global read lock, this is only used to prevent concurrent ~/.opam format changes *) let has_root = OpamFilename.exists_dir root in @@ -55,12 +60,16 @@ OpamConsole.error_and_exit `Configuration_error "Opam has not been initialised, please run `opam init'"; let config_lock = OpamFilename.flock lock_kind (OpamPath.config_lock root) in - let config = load_config lock_kind global_lock root in + let config = + try load_config lock_kind global_lock root + with OpamFormatUpgrade.Upgrade_done _ as e -> + OpamSystem.funlock config_lock; + raise e + in if OpamStateConfig.is_newer config && lock_kind <> `Lock_write then log "root version (%s) is greater than running binary's (%s); \ load with best-effort (read-only)" - (OpamStd.Option.to_string OpamVersion.to_string - (OpamFile.Config.opam_root_version config)) + (OpamVersion.to_string (OpamFile.Config.opam_root_version config)) (OpamVersion.to_string (OpamFile.Config.root_version)); let switches = List.filter @@ -156,6 +165,9 @@ OpamSystem.funlock gt.global_lock; (gt :> unlocked global_state) +let drop gt = + let _ = unlock gt in () + let with_write_lock ?dontblock gt f = if OpamStateConfig.is_newer_than_self gt then OpamConsole.error_and_exit `Locked @@ -171,8 +183,8 @@ let with_ lock f = let gt = load lock in - try let r = f gt in ignore (unlock gt); r - with e -> OpamStd.Exn.finalise e (fun () -> ignore (unlock gt)) + try let r = f gt in drop gt; r + with e -> OpamStd.Exn.finalise e (fun () -> drop gt) let write gt = OpamFile.Config.write (OpamPath.config gt.root) gt.config @@ -193,7 +205,8 @@ OpamFile.Config.with_installed_switches known_switches gt.config in let gt = { gt with config } in - if not OpamCoreConfig.(!r.safe_mode) then + if not OpamCoreConfig.(!r.safe_mode) + && OpamSystem.get_lock_flag gt.global_lock = `Lock_write then try snd @@ with_write_lock ~dontblock:true gt @@ fun gt -> write gt, gt diff -Nru opam-2.0.10/src/state/opamGlobalState.mli opam-2.1.2/src/state/opamGlobalState.mli --- opam-2.0.10/src/state/opamGlobalState.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamGlobalState.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -49,6 +49,15 @@ (** Releases any locks on the given global_state *) val unlock: 'a global_state -> unlocked global_state +(** Releases any locks on the given global state and then ignores it. + + Using [drop gt] is equivalent to [ignore (unlock gt)], + and safer than other uses of [ignore] + where it is not enforced by the type-system + that the value is unlocked before it is lost. +*) +val drop: 'a global_state -> unit + (** Calls the provided function, ensuring a temporary write lock on the given global state *) val with_write_lock: diff -Nru opam-2.0.10/src/state/opamPackageVar.ml opam-2.1.2/src/state/opamPackageVar.ml --- opam-2.0.10/src/state/opamPackageVar.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamPackageVar.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -23,6 +23,7 @@ configuration"; "root", "The current opam root directory"; "make", "The 'make' command to use"; + "exe", "Suffix needed for executable filenames (Windows)"; ] let package_variable_names = [ @@ -45,6 +46,7 @@ "dev", "True if this is a development package"; "build-id", "A hash identifying the precise package version with all its \ dependencies"; + "opamfile", "Path of the curent opam file"; ] let predefined_depends_variables = @@ -67,6 +69,9 @@ | "jobs" -> Some (V.int (OpamStateConfig.(Lazy.force !r.jobs))) | "root" -> Some (V.string (OpamFilename.Dir.to_string gt.root)) | "make" -> Some (V.string OpamStateConfig.(Lazy.force !r.makecmd)) + | "exe" -> Some (V.string (OpamStd.Sys.executable_name "")) + | "switch" -> OpamStd.Option.map (OpamSwitch.to_string @> V.string) + (OpamStateConfig.get_switch_opt ()) | _ -> None (** Resolve switch-global variables only, as allowed by the 'available:' @@ -103,6 +108,7 @@ | Some _ as c -> c | None -> match V.to_string var with + (* we keep it in case no global switch is defined *) | "switch" -> Some (V.string (OpamSwitch.to_string switch)) | _ -> None @@ -153,20 +159,20 @@ let all_installed_deps st opam = let deps = OpamFormula.atoms (all_depends ~post:false st opam) in - OpamStd.List.filter_map - (fun (n,cstr) -> + List.fold_left + (fun deps (n,cstr) -> try let nv = - OpamPackage.Set.find (fun nv -> nv.name = n) - st.installed + OpamPackage.Set.find (fun nv -> nv.name = n) st.installed in let version = nv.version in match cstr with - | None -> Some nv - | Some (op,v) when OpamFormula.eval_relop op version v -> Some nv - | Some _ -> None - with Not_found -> None) - deps + | None -> OpamPackage.Set.add nv deps + | Some (op,v) when OpamFormula.eval_relop op version v -> + OpamPackage.Set.add nv deps + | Some _ -> deps + with Not_found -> deps) + OpamPackage.Set.empty deps let build_id st opam = let kind = `SHA256 in @@ -178,14 +184,12 @@ raise Exit | _ -> let hash_map, deps_hashes = - List.fold_left (fun (hash_map, hashes) nv -> + OpamPackage.Set.fold (fun nv (hash_map, hashes) -> let hash_map, hash = aux hash_map nv (OpamPackage.Map.find nv st.opams) in hash_map, hash::hashes) - (hash_map, []) - (List.sort (fun a b -> - OpamPackage.compare a b) - (all_installed_deps st opam)) + (all_installed_deps st opam) (hash_map, []) in let opam_hash = OpamHash.compute_from_string ~kind @@ -212,11 +216,7 @@ let read_package_var v = let get name = try - let cfg = - OpamPackage.Map.find - (OpamPackage.package_of_name st.installed name) - st.conf_files - in + let cfg = OpamPackage.Name.Map.find name st.conf_files in OpamFile.Dot_config.variable cfg (OpamVariable.Full.variable v) with Not_found -> None in @@ -264,8 +264,11 @@ Some (bool false) | "pinned", _ -> Some (bool (OpamPackage.has_name st.pinned name)) - | "name", _ -> - if OpamPackage.has_name st.packages name + | "name", opam -> + (* On reinstall, orphan packages are not present in the state, and we + need to resolve their internal name variable *) + if OpamStd.Option.map OpamFile.OPAM.name opam = Some name + || OpamPackage.has_name st.packages name then Some (string (OpamPackage.Name.to_string name)) else None | _, None -> None @@ -288,23 +291,36 @@ | "version", Some opam -> Some (string (OpamPackage.Version.to_string (OpamFile.OPAM.version opam))) | "depends", Some opam -> - let installed_deps = all_installed_deps st opam in let str_deps = - OpamStd.List.concat_map " " OpamPackage.to_string installed_deps + all_installed_deps st opam + |> OpamPackage.Set.elements + |> OpamStd.List.concat_map " " OpamPackage.to_string in Some (string str_deps) | "hash", Some opam -> - (try - let nv = get_nv opam in - let f = OpamPath.archive root nv in - if OpamFilename.exists f then - Some (string (OpamHash.to_string - (OpamHash.compute ~kind:`MD5 - (OpamFilename.to_string f)))) - else Some (string "") - with Not_found -> Some (string "")) + OpamStd.Option.Op.( + OpamFile.OPAM.url opam + >>| OpamFile.URL.checksum + (* on download, the cache is populated with the first checksum found *) + >>= (function [] -> None + | h::_ -> Some (string (OpamHash.to_string h)))) | "dev", Some opam -> Some (bool (is_dev_package st opam)) | "build-id", Some opam -> OpamStd.Option.map string (build_id st opam) + | "opamfile", Some opam -> + (* Opamfile path is retrieved from overlay directory for pinned packages, + or from temporary repository in /tmp *) + let repos_roots reponame = + match Hashtbl.find st.switch_repos.repos_tmp reponame with + | lazy repo_root -> repo_root + | exception Not_found -> + OpamRepositoryPath.root st.switch_global.root reponame + in + OpamFile.OPAM.get_metadata_dir ~repos_roots opam + |> OpamStd.Option.map (fun d -> + OpamFilename.Op.(d//"opam") + |> OpamFilename.to_string + |> string + ) | _, _ -> None in let make_package_local v = diff -Nru opam-2.0.10/src/state/opamPackageVar.mli opam-2.1.2/src/state/opamPackageVar.mli --- opam-2.0.10/src/state/opamPackageVar.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamPackageVar.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2017 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/state/opamPinned.ml opam-2.1.2/src/state/opamPinned.ml --- opam-2.0.10/src/state/opamPinned.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamPinned.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -13,6 +13,8 @@ open OpamStateTypes open OpamFilename.Op +let log fmt = OpamConsole.log "PIN" fmt + let package st name = OpamPackage.package_of_name st.pinned name let package_opt st name = try Some (package st name) with Not_found -> None @@ -30,14 +32,22 @@ dir // "opam" ] -let check_locked default = +let check_locked ?subpath default = + (* we keep the check, but this function shouldn't be called if the package is + not asked as locked *) match OpamStateConfig.(!r.locked) with | None -> default | Some ext -> - let fl = OpamFilename.add_extension default ext in - if OpamFilename.exists fl then - (let base_depends = - OpamFile.make default + let flo = + match subpath with + | Some s -> OpamFilename.(Op.(Dir.of_string s // to_string default)) + | None -> default + in + let fl = OpamFilename.add_extension flo ext in + if not (OpamFilename.exists fl) then default else + (log "Lock file found %s" (OpamFilename.to_string flo); + let base_depends = + OpamFile.make flo |> OpamFile.OPAM.read |> OpamFile.OPAM.depends in @@ -52,12 +62,12 @@ OpamPackage.Name.Set.empty lock_depends in let base_formula = - OpamFilter.filter_deps ~build:true ~post:true ~test:true ~doc:true - ~dev:true base_depends + OpamFilter.filter_deps ~build:true ~post:true ~test:false ~doc:false + ~dev:false base_depends in let lock_formula = - OpamFilter.filter_deps ~build:true ~post:true ~test:true ~doc:true - ~dev:true lock_depends + OpamFilter.filter_deps ~build:true ~post:true ~test:false ~doc:false + ~dev:false lock_depends in let lpkg_f = lock_formula @@ -123,24 +133,19 @@ (OpamFormula.string_of_formula (fun (op, vc) -> Printf.sprintf "%s %s" - (OpamPrinter.relop op) (OpamPackage.Version.to_string vc)) + (OpamPrinter.FullPos.relop_kind op) (OpamPackage.Version.to_string vc)) bv)) consistent) else ""))); - fl) - else - (OpamConsole.warning "Lock-file %s doesn't exist, using %s instead." - (OpamConsole.colorise `underline (OpamFilename.to_string fl)) - (OpamFilename.to_string default); - default) + OpamFilename.add_extension default ext) -let find_opam_file_in_source name dir = +let find_opam_file_in_source ?(locked=false) name dir = let opt = OpamStd.List.find_opt OpamFilename.exists (possible_definition_filenames dir name) in (match opt with - | Some base -> Some (check_locked base) + | Some base when locked -> Some (check_locked base) | _ -> opt) |> OpamStd.Option.map OpamFile.make @@ -167,37 +172,73 @@ try Some (OpamPackage.Name.of_string name) with Failure _ -> None -let files_in_source d = +let files_in_source ?(recurse=false) ?subpath d = let baseopam = OpamFilename.Base.of_string "opam" in - let files d = - List.filter (fun f -> - OpamFilename.basename f = baseopam || - OpamFilename.check_suffix f ".opam") - (OpamFilename.files d) @ - OpamStd.List.filter_map (fun d -> - if OpamFilename.(basename_dir d = Base.of_string "opam") || - OpamStd.String.ends_with ~suffix:".opam" - (OpamFilename.Dir.to_string d) - then OpamFilename.opt_file OpamFilename.Op.(d//"opam") - else None) - (OpamFilename.dirs d) + let files = + let rec files_aux acc base d = + let acc = + OpamStd.List.filter_map (fun f -> + if OpamFilename.basename f = baseopam || + OpamFilename.check_suffix f ".opam" then + let base = + match base, subpath with + | Some b, Some sp -> Some (Filename.concat sp b) + | Some b, _ | _, Some b -> Some b + | None, None -> None + in + Some (f, base) + else + None) + (OpamFilename.files d) @ acc + in + List.fold_left + (fun acc d -> + if OpamFilename.(basename_dir d = Base.of_string "opam") || + OpamStd.String.ends_with ~suffix:".opam" + (OpamFilename.Dir.to_string d) + then + match OpamFilename.opt_file OpamFilename.Op.(d//"opam") with + | None -> acc + | Some f -> (f, base) :: acc + else + let base_dir = OpamFilename.basename_dir d in + let basename = OpamFilename.Base.to_string base_dir in + if recurse && + not (base_dir = OpamFilename.Base.of_string OpamSwitch.external_dirname || + base_dir = OpamFilename.Base.of_string "_build" || + OpamStd.String.starts_with ~prefix:"." basename) + then + let base = match base with + | None -> Some basename + | Some base -> Some (Filename.concat base basename) in + files_aux acc base d + else + acc) + acc (OpamFilename.dirs d) + in + files_aux [] None + in + let d = + (OpamStd.Option.map_default (fun sp -> OpamFilename.Op.(d / sp)) d subpath) in files d @ files (d / "opam") |> - List.map check_locked |> + List.map (fun (f,s) -> (check_locked ?subpath:s f), s) |> OpamStd.List.filter_map - (fun f -> + (fun (f, subpath) -> try (* Ignore empty files *) if (Unix.stat (OpamFilename.to_string f)).Unix.st_size = 0 then None - else Some (name_of_opam_filename d f, OpamFile.make f) + else Some (name_of_opam_filename d f, OpamFile.make f, subpath) with Unix.Unix_error _ -> OpamConsole.error "Can not read %s, ignored." (OpamFilename.to_string f); None) -let orig_opam_file name opam = +let orig_opam_file st name opam = let open OpamStd.Option.Op in - OpamFile.OPAM.metadata_dir opam >>= fun dir -> + OpamFile.OPAM.get_metadata_dir + ~repos_roots:(OpamRepositoryState.get_root st.switch_repos) + opam >>= fun dir -> OpamStd.List.find_opt OpamFilename.exists [ dir // (OpamPackage.Name.to_string name ^ ".opam"); dir // "opam" diff -Nru opam-2.0.10/src/state/opamPinned.mli opam-2.1.2/src/state/opamPinned.mli --- opam-2.0.10/src/state/opamPinned.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamPinned.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -30,14 +30,17 @@ val packages: 'a switch_state -> package_set (** Looks up an 'opam' file for the given named package in a source directory. - This is affected by [OpamStateConfig.(!r.locked)]. *) -val find_opam_file_in_source: name -> dirname -> OpamFile.OPAM.t OpamFile.t option + if [locked] is true, look for a locked file. *) +val find_opam_file_in_source: + ?locked:bool -> name -> dirname -> OpamFile.OPAM.t OpamFile.t option (** Finds all package definition files in a given source dir [opam], [pkgname.opam/opam], etc. This is affected by [OpamStateConfig.(!r.locked)] *) val files_in_source: - dirname -> (name option * OpamFile.OPAM.t OpamFile.t) list + ?recurse:bool -> + ?subpath:string -> + dirname -> (name option * OpamFile.OPAM.t OpamFile.t * string option) list (** From an opam file location, sitting below the given project directory, find the corresponding package name if specified ([.opam] or @@ -48,4 +51,5 @@ (** Finds back the location of the opam file this package definition was loaded from *) val orig_opam_file: - OpamPackage.Name.t -> OpamFile.OPAM.t -> OpamFile.OPAM.t OpamFile.t option + 'a switch_state -> OpamPackage.Name.t -> OpamFile.OPAM.t -> + OpamFile.OPAM.t OpamFile.t option diff -Nru opam-2.0.10/src/state/opamRepositoryState.ml opam-2.1.2/src/state/opamRepositoryState.ml --- opam-2.0.10/src/state/opamRepositoryState.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamRepositoryState.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -10,7 +10,6 @@ (**************************************************************************) open OpamTypes -open OpamTypesBase open OpamStd.Op open OpamStateTypes @@ -20,82 +19,25 @@ module Cache = struct type t = { cached_repofiles: (repository_name * OpamFile.Repo.t) list; - cached_opams: (repository_name * (package * OpamFile.OPAM.t) list) list; + cached_opams: (repository_name * OpamFile.OPAM.t OpamPackage.Map.t) list; } - let check_marshaled_file fd = - try - let ic = Unix.in_channel_of_descr fd in - let this_magic = OpamVersion.magic () in - let magic_len = String.length this_magic in - let file_magic = - let b = Bytes.create magic_len in - really_input ic b 0 magic_len; - Bytes.to_string b in - if not OpamCoreConfig.developer && - file_magic <> this_magic then ( - log "Bad cache: incompatible magic string %S (expected %S)." - file_magic this_magic; - None - ) else - let header = Bytes.create Marshal.header_size in - really_input ic header 0 Marshal.header_size; - let expected_size = magic_len + Marshal.total_size header 0 in - let current_size = in_channel_length ic in - if expected_size <> current_size then ( - log "Bad cache: wrong length %d (advertised %d)." - current_size expected_size; - None - ) else ( - seek_in ic magic_len; - Some ic - ) - with e -> - OpamStd.Exn.fatal e; - log "Bad cache: %s" (Printexc.to_string e); - None - - let marshal_from_file file fd = - let chrono = OpamConsole.timer () in - let f ic = - let (cache: t) = Marshal.from_channel ic in - log "Loaded %a in %.3fs" (slog OpamFilename.to_string) file (chrono ()); - let repofiles_map = - OpamRepositoryName.Map.of_list cache.cached_repofiles - in - let repo_opams_map = - OpamRepositoryName.Map.map OpamPackage.Map.of_list - (OpamRepositoryName.Map.of_list cache.cached_opams) - in - (repofiles_map, repo_opams_map) - in - OpamStd.Option.map f (check_marshaled_file fd) + module C = OpamCached.Make (struct + type nonrec t = t + let name = "repository" + end) - let load root = - match OpamFilename.opt_file (OpamPath.state_cache root) with - | Some file -> - let r = - OpamFilename.with_flock `Lock_read file @@ fun fd -> - marshal_from_file file fd - in - if r = None then begin - log "Invalid cache, removing"; - OpamFilename.remove file - end; - r - | None -> None + let remove () = + let root = OpamStateConfig.(!r.root_dir) in + let cache_dir = OpamPath.state_cache_dir root in + let remove_cache_file file = + if OpamFilename.check_suffix file ".cache" then + OpamFilename.remove file + in + List.iter remove_cache_file (OpamFilename.files cache_dir) let save rt = - if OpamCoreConfig.(!r.safe_mode) then - log "Running in safe mode, not upgrading the repository cache" - else - let chrono = OpamConsole.timer () in let file = OpamPath.state_cache rt.repos_global.root in - OpamFilename.with_flock `Lock_write file @@ fun fd -> - log "Writing the cache of repository metadata to %s ...\n" - (OpamFilename.prettify file); - let oc = Unix.out_channel_of_descr fd in - output_string oc (OpamVersion.magic ()); (* Repository without remote are not cached, they are intended to be manually edited *) let filter_out_nourl repos_map = @@ -107,33 +49,36 @@ with Not_found -> false) repos_map in - Marshal.to_channel oc + let t = { cached_repofiles = OpamRepositoryName.Map.bindings (filter_out_nourl rt.repos_definitions); cached_opams = OpamRepositoryName.Map.bindings - (OpamRepositoryName.Map.map OpamPackage.Map.bindings - (filter_out_nourl rt.repo_opams)); + (filter_out_nourl rt.repo_opams); } - [Marshal.No_sharing]; - flush oc; - log "%a written in %.3fs" (slog OpamFilename.prettify) file (chrono ()) + in + remove (); + C.save file t - let remove () = - let root = OpamStateConfig.(!r.root_dir) in + let load root = let file = OpamPath.state_cache root in - OpamFilename.remove file + match C.load file with + | Some cache -> + Some + (OpamRepositoryName.Map.of_list cache.cached_repofiles, + OpamRepositoryName.Map.of_list cache.cached_opams) + | None -> None end -let load_repo_opams repo = - let t = OpamConsole.timer () in +let load_opams_from_dir repo_name repo_root = + (* FIXME: why is this different from OpamPackage.list ? *) let rec aux r dir = if OpamFilename.exists_dir dir then let fnames = Sys.readdir (OpamFilename.Dir.to_string dir) in if Array.fold_left (fun a f -> a || f = "opam") false fnames then - match OpamFileTools.read_opam dir with + match OpamFileTools.read_repo_opam ~repo_name ~repo_root dir with | Some opam -> (try let nv = @@ -154,30 +99,64 @@ r fnames else r in - let r = - aux OpamPackage.Map.empty - (OpamRepositoryPath.packages_dir repo.repo_root) + aux OpamPackage.Map.empty (OpamRepositoryPath.packages_dir repo_root) + +let load_repo repo repo_root = + let t = OpamConsole.timer () in + let repo_def = + OpamFile.Repo.safe_read (OpamRepositoryPath.repo repo_root) + |> OpamFile.Repo.with_root_url repo.repo_url in + let opams = load_opams_from_dir repo.repo_name repo_root in log "loaded opam files from repo %s in %.3fs" (OpamRepositoryName.to_string repo.repo_name) (t ()); - r + repo_def, opams + +(* Cleaning directories follows the repo path pattern: + TMPDIR/opam-tmp-dir/repo-dir, defined in [load]. *) +let clean_repo_tmp tmp_dir = + if Lazy.is_val tmp_dir then + (let dir = Lazy.force tmp_dir in + OpamFilename.rmdir dir; + let parent = OpamFilename.dirname_dir dir in + if OpamFilename.dir_is_empty parent then + OpamFilename.rmdir parent) + +let remove_from_repos_tmp rt name = + try + clean_repo_tmp (Hashtbl.find rt.repos_tmp name); + Hashtbl.remove rt.repos_tmp name + with Not_found -> () + +let cleanup rt = + Hashtbl.iter (fun _ tmp_dir -> clean_repo_tmp tmp_dir) rt.repos_tmp; + Hashtbl.clear rt.repos_tmp + +let get_root_raw root repos_tmp name = + match Hashtbl.find repos_tmp name with + | lazy repo_root -> repo_root + | exception Not_found -> OpamRepositoryPath.root root name + +let get_root rt name = + get_root_raw rt.repos_global.root rt.repos_tmp name + +let get_repo_root rt repo = + get_root_raw rt.repos_global.root rt.repos_tmp repo.repo_name let load lock_kind gt = - log "LOAD-REPOSITORY-STATE @ %a" (slog OpamFilename.Dir.to_string) gt.root; + log "LOAD-REPOSITORY-STATE %@ %a" (slog OpamFilename.Dir.to_string) gt.root; let lock = OpamFilename.flock lock_kind (OpamPath.repos_lock gt.root) in let repos_map = OpamStateConfig.Repos.safe_read ~lock_kind gt in if OpamStateConfig.is_newer_than_self gt then log "root version (%s) is greater than running binary's (%s); \ load with best-effort (read-only)" - (OpamStd.Option.to_string OpamVersion.to_string - (OpamFile.Config.opam_root_version gt.config)) + (OpamVersion.to_string (OpamFile.Config.opam_root_version gt.config)) (OpamVersion.to_string (OpamFile.Config.root_version)); let mk_repo name url_opt = { - repo_root = OpamRepositoryPath.create gt.root name; repo_name = name; repo_url = OpamStd.Option.Op.((url_opt >>| fst) +! OpamUrl.empty); - repo_trust = OpamStd.Option.Op.((url_opt >>= snd)); + repo_trust = OpamStd.Option.Op.(url_opt >>= snd); } in let uncached = (* Don't cache repositories without remote, as they should be editable @@ -185,19 +164,40 @@ OpamRepositoryName.Map.filter (fun _ url -> url = None) repos_map in let repositories = OpamRepositoryName.Map.mapi mk_repo repos_map in - let load_repos_definitions repositories = - OpamRepositoryName.Map.map (fun r -> - OpamFile.Repo.safe_read - OpamRepositoryPath.(repo (create gt.root r.repo_name)) |> - OpamFile.Repo.with_root_url r.repo_url) - repositories - in + let repos_tmp_root = lazy (OpamFilename.mk_tmp_dir ()) in + let repos_tmp = Hashtbl.create 23 in + OpamRepositoryName.Map.iter (fun name repo -> + let uncompressed_root = OpamRepositoryPath.root gt.root repo.repo_name in + let tar = OpamRepositoryPath.tar gt.root repo.repo_name in + if not (OpamFilename.exists_dir uncompressed_root) && + OpamFilename.exists tar + then + let tmp = lazy ( + let tmp_root = Lazy.force repos_tmp_root in + try + (* We rely on this path pattern to clean the repo. + cf. [clean_repo_tmp] *) + OpamFilename.extract_in tar tmp_root; + OpamFilename.Op.(tmp_root / OpamRepositoryName.to_string name) + with Failure s -> + OpamFilename.remove tar; + OpamConsole.error_and_exit `Aborted + "%s.\nRun `opam update --repositories %s` to fix the issue" + s (OpamRepositoryName.to_string name); + ) in + Hashtbl.add repos_tmp name tmp + ) repositories; let make_rt repos_definitions opams = - { repos_global = (gt :> unlocked global_state); + let rt = { + repos_global = (gt :> unlocked global_state); repos_lock = lock; + repos_tmp; repositories; repos_definitions; - repo_opams = opams; } + repo_opams = opams; + } in + OpamStd.Sys.at_exit (fun () -> cleanup rt); + rt in match Cache.load gt.root with | Some (repofiles, opams) when OpamRepositoryName.Map.is_empty uncached -> @@ -206,23 +206,31 @@ | Some (repofiles, opams) -> log "Cache found, loading repositories without remote only"; OpamFilename.with_flock_upgrade `Lock_read lock @@ fun _ -> - let uncached_repos = OpamRepositoryName.Map.mapi mk_repo uncached in - let uncached_repofiles = load_repos_definitions uncached_repos in - let uncached_opams = - OpamRepositoryName.Map.map load_repo_opams uncached_repos + let repofiles, opams = + OpamRepositoryName.Map.fold (fun name url (defs, opams) -> + let repo = mk_repo name url in + let repo_def, repo_opams = + load_repo repo (get_root_raw gt.root repos_tmp name) + in + OpamRepositoryName.Map.add name repo_def defs, + OpamRepositoryName.Map.add name repo_opams opams) + uncached (repofiles, opams) in - make_rt - (OpamRepositoryName.Map.union (fun _ x -> x) repofiles uncached_repofiles) - (OpamRepositoryName.Map.union (fun _ x -> x) opams uncached_opams) + make_rt repofiles opams | None -> log "No cache found"; OpamFilename.with_flock_upgrade `Lock_read lock @@ fun _ -> - let repos = OpamRepositoryName.Map.mapi mk_repo repos_map in - let rt = - make_rt - (load_repos_definitions repos) - (OpamRepositoryName.Map.map load_repo_opams repos) + let repofiles, opams = + OpamRepositoryName.Map.fold (fun name url (defs, opams) -> + let repo = mk_repo name url in + let repo_def, repo_opams = + load_repo repo (get_root_raw gt.root repos_tmp name) + in + OpamRepositoryName.Map.add name repo_def defs, + OpamRepositoryName.Map.add name repo_opams opams) + repos_map (OpamRepositoryName.Map.empty, OpamRepositoryName.Map.empty) in + let rt = make_rt repofiles opams in Cache.save rt; rt @@ -251,10 +259,14 @@ let get_repo rt name = OpamRepositoryName.Map.find name rt.repositories -let unlock rt = +let unlock ?cleanup:(cln=true) rt = + if cln then cleanup rt; OpamSystem.funlock rt.repos_lock; (rt :> unlocked repos_state) +let drop ?cleanup rt = + let _ = unlock ?cleanup rt in () + let with_write_lock ?dontblock rt f = if OpamStateConfig.is_newer_than_self rt.repos_global then OpamConsole.error_and_exit `Locked @@ -270,8 +282,7 @@ let with_ lock gt f = let rt = load lock gt in - try let r = f rt in ignore (unlock rt); r - with e -> OpamStd.Exn.finalise e (fun () -> ignore (unlock rt)) + OpamStd.Exn.finally (fun () -> drop rt) (fun () -> f rt) let write_config rt = OpamFile.Repos_config.write (OpamPath.repos_config rt.repos_global.root) @@ -279,3 +290,15 @@ if r.repo_url = OpamUrl.empty then None else Some (r.repo_url, r.repo_trust)) rt.repositories) + +let check_last_update () = + if OpamCoreConfig.(!r.debug_level) < 0 then () else + let last_update = + OpamFilename.written_since + (OpamPath.state_cache (OpamStateConfig.(!r.root_dir))) + in + if last_update > float_of_int (3600*24*21) then + OpamConsole.note "It seems you have not updated your repositories \ + for a while. Consider updating them with:\n%s\n" + (OpamConsole.colorise `bold "opam update"); + diff -Nru opam-2.0.10/src/state/opamRepositoryState.mli opam-2.1.2/src/state/opamRepositoryState.mli --- opam-2.0.10/src/state/opamRepositoryState.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamRepositoryState.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -49,18 +49,60 @@ ROOT/repos/) *) val get_repo: 'a repos_state -> repository_name -> repository +val load_opams_from_dir: repository_name -> dirname -> OpamFile.OPAM.t OpamPackage.Map.t + (** Load all the metadata within the local mirror of the given repository, without cache *) -val load_repo_opams: repository -> OpamFile.OPAM.t OpamPackage.Map.t - -(** Releases any locks on the given repos_state *) -val unlock: 'a repos_state -> unlocked repos_state +val load_repo: + repository -> OpamFilename.Dir.t -> + OpamFile.Repo.t * OpamFile.OPAM.t OpamPackage.Map.t + +(** Get the (lazily extracted) repository root for the given repository *) +val get_root: 'a repos_state -> repository_name -> OpamFilename.Dir.t + +(** Same as [get_root], but with a repository rather than just a name as argument *) +val get_repo_root: 'a repos_state -> repository -> OpamFilename.Dir.t + +(* (\** Runs the given function with access to a (possibly temporary) directory + * containing the extracted structure of the given repository, and cleans it up + * afterwards if temporary. The basename of the directory is guaranteed to + * match the repository name (this is important for e.g. [tar]) *\) + * val with_repo_root: + * 'a global_state -> repository -> (OpamFilename.Dir.t -> 'b) -> 'b + * + * (\** As [with_repo_root], but on jobs *\) + * val with_repo_root_job: + * 'a global_state -> repository -> + * (OpamFilename.Dir.t -> 'b OpamProcess.job) -> 'b OpamProcess.job *) + +(** Releases any locks on the given repos_state, and cleans the tmp extracted + tree if any unless [cleanup=false] *) +val unlock: ?cleanup:bool -> 'a repos_state -> unlocked repos_state + +(** Releases any locks on the given repos_state and then ignores it. + + Using [drop ?cleanup rt] is equivalent to [ignore (unlock ?cleanup rt)], + and safer than other uses of [ignore] + where it is not enforced by the type-system + that the value is unlocked before it is lost. +*) +val drop: ?cleanup:bool -> 'a repos_state -> unit + +(** Cleanup before removing the repository from temporary table *) +val remove_from_repos_tmp: 'a repos_state -> repository_name -> unit + +(** Clears tmp files corresponding to a repo state (uncompressed repository + contents) *) +val cleanup: 'a repos_state -> unit (** Calls the provided function, ensuring a temporary write lock on the given repository state*) val with_write_lock: - ?dontblock:bool -> 'a repos_state -> (rw repos_state -> 'b * rw repos_state) -> - 'b * 'a repos_state + ?dontblock:bool -> 'a repos_state -> (rw repos_state -> 'b * rw repos_state) + -> 'b * 'a repos_state (** Writes the repositories config file back to disk *) val write_config: rw repos_state -> unit + +(** Display a warning if repository has not been updated since 3 weeks *) +val check_last_update: unit -> unit diff -Nru opam-2.0.10/src/state/opamScript.mli opam-2.1.2/src/state/opamScript.mli --- opam-2.0.10/src/state/opamScript.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamScript.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2018 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) diff -Nru opam-2.0.10/src/state/opamSpdxList.ml opam-2.1.2/src/state/opamSpdxList.ml --- opam-2.0.10/src/state/opamSpdxList.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/state/opamSpdxList.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,439 @@ +(* THIS FILE IS GENERATED. See dune file *) + +open OpamCompat +let licenses = OpamStd.String.Set.of_list @@ List.map String.lowercase_ascii @@ [ + "0BSD"; + "AAL"; + "ADSL"; + "AFL-1.1"; + "AFL-1.2"; + "AFL-2.0"; + "AFL-2.1"; + "AFL-3.0"; + "AGPL-1.0"; + "AGPL-1.0-only"; + "AGPL-1.0-or-later"; + "AGPL-3.0"; + "AGPL-3.0-only"; + "AGPL-3.0-or-later"; + "AMDPLPA"; + "AML"; + "AMPAS"; + "ANTLR-PD"; + "APAFML"; + "APL-1.0"; + "APSL-1.0"; + "APSL-1.1"; + "APSL-1.2"; + "APSL-2.0"; + "Abstyles"; + "Adobe-2006"; + "Adobe-Glyph"; + "Afmparse"; + "Aladdin"; + "Apache-1.0"; + "Apache-1.1"; + "Apache-2.0"; + "Artistic-1.0"; + "Artistic-1.0-Perl"; + "Artistic-1.0-cl8"; + "Artistic-2.0"; + "BSD-1-Clause"; + "BSD-2-Clause"; + "BSD-2-Clause-FreeBSD"; + "BSD-2-Clause-NetBSD"; + "BSD-2-Clause-Patent"; + "BSD-3-Clause"; + "BSD-3-Clause-Attribution"; + "BSD-3-Clause-Clear"; + "BSD-3-Clause-LBNL"; + "BSD-3-Clause-No-Nuclear-License"; + "BSD-3-Clause-No-Nuclear-License-2014"; + "BSD-3-Clause-No-Nuclear-Warranty"; + "BSD-3-Clause-Open-MPI"; + "BSD-4-Clause"; + "BSD-4-Clause-UC"; + "BSD-Protection"; + "BSD-Source-Code"; + "BSL-1.0"; + "Bahyph"; + "Barr"; + "Beerware"; + "BitTorrent-1.0"; + "BitTorrent-1.1"; + "BlueOak-1.0.0"; + "Borceux"; + "CATOSL-1.1"; + "CC-BY-1.0"; + "CC-BY-2.0"; + "CC-BY-2.5"; + "CC-BY-3.0"; + "CC-BY-4.0"; + "CC-BY-NC-1.0"; + "CC-BY-NC-2.0"; + "CC-BY-NC-2.5"; + "CC-BY-NC-3.0"; + "CC-BY-NC-4.0"; + "CC-BY-NC-ND-1.0"; + "CC-BY-NC-ND-2.0"; + "CC-BY-NC-ND-2.5"; + "CC-BY-NC-ND-3.0"; + "CC-BY-NC-ND-4.0"; + "CC-BY-NC-SA-1.0"; + "CC-BY-NC-SA-2.0"; + "CC-BY-NC-SA-2.5"; + "CC-BY-NC-SA-3.0"; + "CC-BY-NC-SA-4.0"; + "CC-BY-ND-1.0"; + "CC-BY-ND-2.0"; + "CC-BY-ND-2.5"; + "CC-BY-ND-3.0"; + "CC-BY-ND-4.0"; + "CC-BY-SA-1.0"; + "CC-BY-SA-2.0"; + "CC-BY-SA-2.5"; + "CC-BY-SA-3.0"; + "CC-BY-SA-4.0"; + "CC-PDDC"; + "CC0-1.0"; + "CDDL-1.0"; + "CDDL-1.1"; + "CDLA-Permissive-1.0"; + "CDLA-Sharing-1.0"; + "CECILL-1.0"; + "CECILL-1.1"; + "CECILL-2.0"; + "CECILL-2.1"; + "CECILL-B"; + "CECILL-C"; + "CERN-OHL-1.1"; + "CERN-OHL-1.2"; + "CNRI-Jython"; + "CNRI-Python"; + "CNRI-Python-GPL-Compatible"; + "CPAL-1.0"; + "CPL-1.0"; + "CPOL-1.02"; + "CUA-OPL-1.0"; + "Caldera"; + "ClArtistic"; + "Condor-1.1"; + "Crossword"; + "CrystalStacker"; + "Cube"; + "D-FSL-1.0"; + "DOC"; + "DSDP"; + "Dotseqn"; + "ECL-1.0"; + "ECL-2.0"; + "EFL-1.0"; + "EFL-2.0"; + "EPL-1.0"; + "EPL-2.0"; + "EUDatagrid"; + "EUPL-1.0"; + "EUPL-1.1"; + "EUPL-1.2"; + "Entessa"; + "ErlPL-1.1"; + "Eurosym"; + "FSFAP"; + "FSFUL"; + "FSFULLR"; + "FTL"; + "Fair"; + "Frameworx-1.0"; + "FreeImage"; + "GFDL-1.1"; + "GFDL-1.1-only"; + "GFDL-1.1-or-later"; + "GFDL-1.2"; + "GFDL-1.2-only"; + "GFDL-1.2-or-later"; + "GFDL-1.3"; + "GFDL-1.3-only"; + "GFDL-1.3-or-later"; + "GL2PS"; + "GPL-1.0"; + "GPL-1.0+"; + "GPL-1.0-only"; + "GPL-1.0-or-later"; + "GPL-2.0"; + "GPL-2.0+"; + "GPL-2.0-only"; + "GPL-2.0-or-later"; + "GPL-2.0-with-GCC-exception"; + "GPL-2.0-with-autoconf-exception"; + "GPL-2.0-with-bison-exception"; + "GPL-2.0-with-classpath-exception"; + "GPL-2.0-with-font-exception"; + "GPL-3.0"; + "GPL-3.0+"; + "GPL-3.0-only"; + "GPL-3.0-or-later"; + "GPL-3.0-with-GCC-exception"; + "GPL-3.0-with-autoconf-exception"; + "Giftware"; + "Glide"; + "Glulxe"; + "HPND"; + "HPND-sell-variant"; + "HaskellReport"; + "IBM-pibs"; + "ICU"; + "IJG"; + "IPA"; + "IPL-1.0"; + "ISC"; + "ImageMagick"; + "Imlib2"; + "Info-ZIP"; + "Intel"; + "Intel-ACPI"; + "Interbase-1.0"; + "JPNIC"; + "JSON"; + "JasPer-2.0"; + "LAL-1.2"; + "LAL-1.3"; + "LGPL-2.0"; + "LGPL-2.0+"; + "LGPL-2.0-only"; + "LGPL-2.0-or-later"; + "LGPL-2.1"; + "LGPL-2.1+"; + "LGPL-2.1-only"; + "LGPL-2.1-or-later"; + "LGPL-3.0"; + "LGPL-3.0+"; + "LGPL-3.0-only"; + "LGPL-3.0-or-later"; + "LGPLLR"; + "LPL-1.0"; + "LPL-1.02"; + "LPPL-1.0"; + "LPPL-1.1"; + "LPPL-1.2"; + "LPPL-1.3a"; + "LPPL-1.3c"; + "Latex2e"; + "Leptonica"; + "LiLiQ-P-1.1"; + "LiLiQ-R-1.1"; + "LiLiQ-Rplus-1.1"; + "Libpng"; + "Linux-OpenIB"; + "MIT"; + "MIT-0"; + "MIT-CMU"; + "MIT-advertising"; + "MIT-enna"; + "MIT-feh"; + "MITNFA"; + "MPL-1.0"; + "MPL-1.1"; + "MPL-2.0"; + "MPL-2.0-no-copyleft-exception"; + "MS-PL"; + "MS-RL"; + "MTLL"; + "MakeIndex"; + "MirOS"; + "Motosoto"; + "Multics"; + "Mup"; + "NASA-1.3"; + "NBPL-1.0"; + "NCSA"; + "NGPL"; + "NLOD-1.0"; + "NLPL"; + "NOSL"; + "NPL-1.0"; + "NPL-1.1"; + "NPOSL-3.0"; + "NRL"; + "NTP"; + "Naumen"; + "Net-SNMP"; + "NetCDF"; + "Newsletr"; + "Nokia"; + "Noweb"; + "Nunit"; + "OCCT-PL"; + "OCLC-2.0"; + "ODC-By-1.0"; + "ODbL-1.0"; + "OFL-1.0"; + "OFL-1.1"; + "OGL-UK-1.0"; + "OGL-UK-2.0"; + "OGL-UK-3.0"; + "OGTSL"; + "OLDAP-1.1"; + "OLDAP-1.2"; + "OLDAP-1.3"; + "OLDAP-1.4"; + "OLDAP-2.0"; + "OLDAP-2.0.1"; + "OLDAP-2.1"; + "OLDAP-2.2"; + "OLDAP-2.2.1"; + "OLDAP-2.2.2"; + "OLDAP-2.3"; + "OLDAP-2.4"; + "OLDAP-2.5"; + "OLDAP-2.6"; + "OLDAP-2.7"; + "OLDAP-2.8"; + "OML"; + "OPL-1.0"; + "OSET-PL-2.1"; + "OSL-1.0"; + "OSL-1.1"; + "OSL-2.0"; + "OSL-2.1"; + "OSL-3.0"; + "OpenSSL"; + "PDDL-1.0"; + "PHP-3.0"; + "PHP-3.01"; + "Parity-6.0.0"; + "Plexus"; + "PostgreSQL"; + "Python-2.0"; + "QPL-1.0"; + "Qhull"; + "RHeCos-1.1"; + "RPL-1.1"; + "RPL-1.5"; + "RPSL-1.0"; + "RSA-MD"; + "RSCPL"; + "Rdisc"; + "Ruby"; + "SAX-PD"; + "SCEA"; + "SGI-B-1.0"; + "SGI-B-1.1"; + "SGI-B-2.0"; + "SHL-0.5"; + "SHL-0.51"; + "SISSL"; + "SISSL-1.2"; + "SMLNJ"; + "SMPPL"; + "SNIA"; + "SPL-1.0"; + "SSPL-1.0"; + "SWL"; + "Saxpath"; + "Sendmail"; + "Sendmail-8.23"; + "SimPL-2.0"; + "Sleepycat"; + "Spencer-86"; + "Spencer-94"; + "Spencer-99"; + "StandardML-NJ"; + "SugarCRM-1.1.3"; + "TAPR-OHL-1.0"; + "TCL"; + "TCP-wrappers"; + "TMate"; + "TORQUE-1.1"; + "TOSL"; + "TU-Berlin-1.0"; + "TU-Berlin-2.0"; + "UPL-1.0"; + "Unicode-DFS-2015"; + "Unicode-DFS-2016"; + "Unicode-TOU"; + "Unlicense"; + "VOSTROM"; + "VSL-1.0"; + "Vim"; + "W3C"; + "W3C-19980720"; + "W3C-20150513"; + "WTFPL"; + "Watcom-1.0"; + "Wsuipa"; + "X11"; + "XFree86-1.1"; + "XSkat"; + "Xerox"; + "Xnet"; + "YPL-1.0"; + "YPL-1.1"; + "ZPL-1.1"; + "ZPL-2.0"; + "ZPL-2.1"; + "Zed"; + "Zend-2.0"; + "Zimbra-1.3"; + "Zimbra-1.4"; + "Zlib"; + "blessing"; + "bzip2-1.0.5"; + "bzip2-1.0.6"; + "copyleft-next-0.3.0"; + "copyleft-next-0.3.1"; + "curl"; + "diffmark"; + "dvipdfm"; + "eCos-2.0"; + "eGenix"; + "gSOAP-1.3b"; + "gnuplot"; + "iMatix"; + "libpng-2.0"; + "libtiff"; + "mpich2"; + "psfrag"; + "psutils"; + "wxWindows"; + "xinetd"; + "xpp"; + "zlib-acknowledgement"; +] +let exceptions = OpamStd.String.Set.of_list @@ List.map String.lowercase_ascii @@ [ + "Libtool-exception"; + "Linux-syscall-note"; + "Autoconf-exception-3.0"; + "OCCT-exception-1.0"; + "openvpn-openssl-exception"; + "gnu-javamail-exception"; + "OpenJDK-assembly-exception-1.0"; + "Bison-exception-2.2"; + "i2p-gpl-java-exception"; + "Universal-FOSS-exception-1.0"; + "Qt-LGPL-exception-1.1"; + "389-exception"; + "Classpath-exception-2.0"; + "Fawkes-Runtime-exception"; + "PS-or-PDF-font-exception-20170817"; + "Qt-GPL-exception-1.0"; + "LZMA-exception"; + "freertos-exception-2.0"; + "Qwt-exception-1.0"; + "CLISP-exception-2.0"; + "FLTK-exception"; + "Bootloader-exception"; + "Nokia-Qt-exception-1.1"; + "LLVM-exception"; + "WxWindows-exception-3.1"; + "DigiRule-FOSS-exception"; + "Swift-exception"; + "GCC-exception-3.1"; + "eCos-exception-2.0"; + "Autoconf-exception-2.0"; + "GPL-CC-1.0"; + "Font-exception-2.0"; + "u-boot-exception-2.0"; + "GCC-exception-2.0"; + "mif-exception"; + "OCaml-LGPL-linking-exception"; +] diff -Nru opam-2.0.10/src/state/opamSpdxList.mli opam-2.1.2/src/state/opamSpdxList.mli --- opam-2.0.10/src/state/opamSpdxList.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/state/opamSpdxList.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,17 @@ +(**************************************************************************) +(* *) +(* Copyright 2019 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(** Implementation generated from the SPDX lists at https://spdx.org/licenses/ + See the dune file for instructions. *) + +(** The sets only contain the short IDs of all the licenses *) + +val licenses: OpamStd.String.Set.t +val exceptions: OpamStd.String.Set.t diff -Nru opam-2.0.10/src/state/opamStateConfig.ml opam-2.1.2/src/state/opamStateConfig.ml --- opam-2.0.10/src/state/opamStateConfig.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamStateConfig.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2020 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -11,10 +11,48 @@ open OpamTypes open OpamStateTypes +module E = struct + + type OpamStd.Config.E.t += + | BUILDDOC of bool option + | BUILDTEST of bool option + | DOWNLOADJOBS of int option + | DRYRUN of bool option + | IGNORECONSTRAINTS of string option + | JOBS of int option + | LOCKED of string option + | MAKECMD of string option + | NODEPEXTS of bool option + | NOENVNOTICE of bool option + | ROOT of string option + | SWITCH of string option + | UNLOCKBASE of bool option + | WITHDOC of bool option + | WITHTEST of bool option + + open OpamStd.Config.E + let builddoc = value (function BUILDDOC b -> b | _ -> None) + let buildtest = value (function BUILDTEST b -> b | _ -> None) + let downloadjobs = value (function DOWNLOADJOBS i -> i | _ -> None) + let dryrun = value (function DRYRUN b -> b | _ -> None) + let ignoreconstraints = value (function IGNORECONSTRAINTS s -> s | _ -> None) + let jobs = value (function JOBS i -> i | _ -> None) + let locked = value (function LOCKED s -> s | _ -> None) + let makecmd = value (function MAKECMD s -> s | _ -> None) + let nodepexts = value (function NODEPEXTS b -> b | _ -> None) + let noenvnotice = value (function NOENVNOTICE b -> b | _ -> None) + let root = value (function ROOT s -> s | _ -> None) + let switch = value (function SWITCH s -> s | _ -> None) + let unlockbase = value (function UNLOCKBASE b -> b | _ -> None) + let withdoc = value (function WITHDOC b -> b | _ -> None) + let withtest = value (function WITHTEST b -> b | _ -> None) + +end + type t = { root_dir: OpamFilename.Dir.t; current_switch: OpamSwitch.t option; - switch_from: [ `Env | `Command_line | `Default ]; + switch_from: provenance; jobs: int Lazy.t; dl_jobs: int; build_test: bool; @@ -25,6 +63,7 @@ unlock_base: bool; no_env_notice: bool; locked: string option; + no_depexts: bool; } let default = { @@ -33,7 +72,7 @@ ); current_switch = None; switch_from = `Default; - jobs = lazy (max 1 (OpamSystem.cpu_count () - 1)); + jobs = lazy (max 1 (OpamSysPoll.cores () - 1)); dl_jobs = 3; build_test = false; build_doc = false; @@ -47,12 +86,13 @@ unlock_base = false; no_env_notice = false; locked = None; + no_depexts = false; } type 'a options_fun = ?root_dir:OpamFilename.Dir.t -> ?current_switch:OpamSwitch.t -> - ?switch_from:[ `Env | `Command_line | `Default ] -> + ?switch_from:provenance -> ?jobs:(int Lazy.t) -> ?dl_jobs:int -> ?build_test:bool -> @@ -63,6 +103,7 @@ ?unlock_base:bool -> ?no_env_notice:bool -> ?locked:string option -> + ?no_depexts: bool -> 'a let setk k t @@ -79,6 +120,7 @@ ?unlock_base ?no_env_notice ?locked + ?no_depexts = let (+) x opt = match opt with Some x -> x | None -> x in k { @@ -96,6 +138,7 @@ unlock_base = t.unlock_base + unlock_base; no_env_notice = t.no_env_notice + no_env_notice; locked = t.locked + locked; + no_depexts = t.no_depexts + no_depexts; } let set t = setk (fun x () -> x) t @@ -105,31 +148,31 @@ let update ?noop:_ = setk (fun cfg () -> r := cfg) !r let initk k = - let open OpamStd.Config in let open OpamStd.Option.Op in let current_switch, switch_from = - match env_string "SWITCH" with + match E.switch () with | Some "" | None -> None, None | Some s -> Some (OpamSwitch.of_string s), Some `Env in setk (setk (fun c -> r := c; k)) !r - ?root_dir:(env_string "ROOT" >>| OpamFilename.Dir.of_string) + ?root_dir:(E.root () >>| OpamFilename.Dir.of_string) ?current_switch ?switch_from - ?jobs:(env_int "JOBS" >>| fun s -> lazy s) - ?dl_jobs:(env_int "DOWNLOADJOBS") - ?build_test:(env_bool "WITHTEST" ++ env_bool "BUILDTEST") - ?build_doc:(env_bool "WITHDOC" ++ env_bool "BUILDDOC") - ?dryrun:(env_bool "DRYRUN") - ?makecmd:(env_string "MAKECMD" >>| fun s -> lazy s) + ?jobs:(E.jobs () >>| fun s -> lazy s) + ?dl_jobs:(E.downloadjobs ()) + ?build_test:(E.withtest () ++ E.buildtest ()) + ?build_doc:(E.withdoc () ++ E.builddoc ()) + ?dryrun:(E.dryrun ()) + ?makecmd:(E.makecmd () >>| fun s -> lazy s) ?ignore_constraints_on: - (env_string "IGNORECONSTRAINTS" >>| fun s -> + (E.ignoreconstraints () >>| fun s -> OpamStd.String.split s ',' |> List.map OpamPackage.Name.of_string |> OpamPackage.Name.Set.of_list) - ?unlock_base:(env_bool "UNLOCKBASE") - ?no_env_notice:(env_bool "NOENVNOTICE") - ?locked:(env_string "LOCKED" >>| function "" -> None | s -> Some s) + ?unlock_base:(E.unlockbase ()) + ?no_env_notice:(E.noenvnotice ()) + ?locked:(E.locked () >>| function "" -> None | s -> Some s) + ?no_depexts:(E.nodepexts ()) let init ?noop:_ = initk (fun () -> ()) @@ -145,7 +188,7 @@ | None -> false let is_newer config = - is_newer_raw (OpamFile.Config.opam_root_version config) + is_newer_raw (Some (OpamFile.Config.opam_root_version config)) (** none -> shouldn't load (write attempt in readonly) Some true -> everything is fine normal read @@ -158,23 +201,12 @@ let is_readonly_opamroot_t ?lock_kind gt = is_readonly_opamroot_raw ?lock_kind - (OpamFile.Config.opam_root_version gt.config) + (Some (OpamFile.Config.opam_root_version gt.config)) let is_newer_than_self ?lock_kind gt = is_readonly_opamroot_t ?lock_kind gt <> Some false let load_if_possible_raw ?lock_kind root version (read,read_wo_err) f = - (* Wrap possible error due to an opam 2.1 intermediate root *) - let wrap readf = - try readf f with (OpamPp.Bad_version _ as e) -> - (let root_version, opam_version = OpamFile.Config.raw_root_version f in - if root_version = Some (OpamVersion.of_string "2.1~rc") - || (root_version = None - && opam_version = Some (OpamVersion.of_string "2.1")) then - OpamConsole.error_and_exit `Aborted - "Please upgrade your opam root with opam 2.1" - else raise e) - in match is_readonly_opamroot_raw ?lock_kind version with | None -> OpamConsole.error_and_exit `Locked @@ -183,24 +215,30 @@ (OpamFilename.Dir.to_string root) (OpamStd.Option.to_string OpamVersion.to_string version) OpamVersion.(to_string current_nopatch) - | Some true -> wrap read_wo_err - | Some false -> wrap read + | Some true -> read_wo_err f + | Some false -> read f let load_if_possible_t ?lock_kind opamroot config readf f = load_if_possible_raw ?lock_kind - opamroot (OpamFile.Config.opam_root_version config) readf f + opamroot (Some (OpamFile.Config.opam_root_version config)) readf f let load_if_possible ?lock_kind gt = load_if_possible_t ?lock_kind gt.root gt.config let load_config_root ?lock_kind readf opamroot = let f = OpamPath.config opamroot in - let root_version = fst @@ OpamFile.Config.raw_root_version f in - load_if_possible_raw ?lock_kind opamroot root_version readf f + load_if_possible_raw ?lock_kind + opamroot + (OpamFile.Config.raw_root_version f) + readf f + +let safe read read' default = + let safe r f = OpamStd.Option.default default @@ r f in + safe read, safe read' let safe_load ?lock_kind opamroot = load_config_root ?lock_kind - OpamFile.Config.(safe_read, BestEffort.safe_read) opamroot + OpamFile.Config.(safe read_opt BestEffort.read_opt empty) opamroot let load ?lock_kind opamroot = load_config_root ?lock_kind @@ -210,31 +248,21 @@ module Switch = struct let load_raw ?lock_kind root config readf switch = - let f = OpamPath.Switch.switch_config root switch in - match OpamFile.Switch_config.raw_opam_version f with - | Some v when OpamVersion.compare v OpamVersion.current_nopatch > 0 -> - load_if_possible_raw ?lock_kind root (Some v) readf f - | _ -> - load_if_possible_t ?lock_kind root config readf f + load_if_possible_t ?lock_kind root config readf + (OpamPath.Switch.switch_config root switch) let safe_load_t ?lock_kind root switch = let config = safe_load ~lock_kind:`Lock_read root in load_raw ?lock_kind root config - OpamFile.Switch_config.(safe_read, BestEffort.safe_read) + OpamFile.Switch_config.(safe read_opt BestEffort.read_opt empty) switch - let safe_read_selections_t ?lock_kind root switch = - let config = safe_load ~lock_kind:`Lock_read root in - load_if_possible_t ?lock_kind root config - OpamFile.SwitchSelections.(safe_read, BestEffort.safe_read) - (OpamPath.Switch.selections root switch) - let load ?lock_kind gt readf switch = load_raw ?lock_kind gt.root gt.config readf switch let safe_load ?lock_kind gt switch = load ?lock_kind gt - OpamFile.Switch_config.(safe_read, BestEffort.safe_read) + OpamFile.Switch_config.(safe read_opt BestEffort.read_opt empty) switch let read_opt ?lock_kind gt switch = @@ -244,7 +272,7 @@ let safe_read_selections ?lock_kind gt switch = load_if_possible ?lock_kind gt - OpamFile.SwitchSelections.(safe_read, BestEffort.safe_read) + OpamFile.SwitchSelections.(safe read_opt BestEffort.read_opt empty) (OpamPath.Switch.selections gt.root switch) end @@ -253,16 +281,49 @@ module Repos = struct let safe_read ?lock_kind gt = load_if_possible ?lock_kind gt - OpamFile.Repos_config.(safe_read, BestEffort.safe_read) + OpamFile.Repos_config.(safe read_opt BestEffort.read_opt empty) (OpamPath.repos_config gt.root) end +let downgrade_2_1_switch f = + let filename = OpamFile.filename f in + let str_f = OpamFilename.to_string filename in + let opamfile = OpamParser.FullPos.file str_f in + let opamfile' = + let open OpamParserTypes.FullPos in + { opamfile with + file_contents = + List.map (fun item -> + match item.pelem with + | Variable (({pelem = "opam-version"; _} as opam_v), + ({pelem = String "2.1"; _} as v)) -> + { item with + pelem = Variable ({opam_v with pelem = "opam-version"}, + {v with pelem = String "2.0"})} + | _ -> item) + opamfile.file_contents} + in + if opamfile' = opamfile then None else + Some (opamfile' + |> OpamPrinter.FullPos.opamfile + |> OpamFile.Switch_config.read_from_string) + let local_switch_exists root switch = (* we don't use safe loading function to avoid errors displaying *) - OpamPath.Switch.switch_config root switch |> - OpamFile.Switch_config.BestEffort.read_opt |> function + let f = OpamPath.Switch.switch_config root switch in + match OpamFile.Switch_config.BestEffort.read_opt f with | None -> false | Some conf -> conf.OpamFile.Switch_config.opam_root = Some root + | exception (OpamPp.Bad_version _ as e) -> + match OpamFile.Config.raw_root_version (OpamPath.config root) with + | None -> raise e + | Some _ -> + match downgrade_2_1_switch f with + | None -> raise e + | Some conf -> + if conf.OpamFile.Switch_config.opam_root = Some root then + (OpamFile.Switch_config.write f conf; true) + else false let resolve_local_switch root s = let switch_root = OpamSwitch.get_root root s in @@ -271,18 +332,20 @@ else s let get_current_switch_from_cwd root = - let open OpamStd.Option.Op in - OpamFilename.find_in_parents (fun dir -> - OpamSwitch.of_string (OpamFilename.Dir.to_string dir) |> - local_switch_exists root) - (OpamFilename.cwd ()) - >>| OpamSwitch.of_dirname - >>| resolve_local_switch root + try + let open OpamStd.Option.Op in + OpamFilename.find_in_parents (fun dir -> + OpamSwitch.of_string (OpamFilename.Dir.to_string dir) |> + local_switch_exists root) + (OpamFilename.cwd ()) + >>| OpamSwitch.of_dirname + >>| resolve_local_switch root + with OpamPp.Bad_version _ -> None (* do we want `load_defaults` to fail / run a format upgrade ? *) let load_defaults ?lock_kind root_dir = let current_switch = - match OpamStd.Config.env_string "SWITCH" with + match E.switch () with | Some "" | None -> get_current_switch_from_cwd root_dir | _ -> (* OPAMSWITCH is set, no need to lookup *) None in @@ -302,7 +365,7 @@ update ?current_switch:(OpamFile.Config.switch conf) ~switch_from:`Default - ~jobs:(lazy (OpamFile.Config.jobs conf)) + ?jobs:(OpamFile.Config.jobs conf >>| fun s -> lazy s) ~dl_jobs:(OpamFile.Config.dl_jobs conf) (); update ?current_switch (); diff -Nru opam-2.0.10/src/state/opamStateConfig.mli opam-2.1.2/src/state/opamStateConfig.mli --- opam-2.0.10/src/state/opamStateConfig.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamStateConfig.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2015 OCamlPro *) +(* Copyright 2015-2020 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -14,10 +14,31 @@ open OpamTypes open OpamStateTypes +module E : sig + type OpamStd.Config.E.t += + | BUILDDOC of bool option + | BUILDTEST of bool option + | DOWNLOADJOBS of int option + | DRYRUN of bool option + | IGNORECONSTRAINTS of string option + | JOBS of int option + | LOCKED of string option + | MAKECMD of string option + | NODEPEXTS of bool option + | NOENVNOTICE of bool option + | ROOT of string option + | SWITCH of string option + | UNLOCKBASE of bool option + | WITHDOC of bool option + | WITHTEST of bool option + val root: unit -> string option + val switch: unit -> string option +end + type t = private { root_dir: OpamFilename.Dir.t; current_switch: OpamSwitch.t option; - switch_from: [ `Env | `Command_line | `Default ]; + switch_from: provenance; jobs: int Lazy.t; dl_jobs: int; build_test: bool; @@ -28,12 +49,13 @@ unlock_base: bool; no_env_notice: bool; locked: string option; + no_depexts : bool; } type 'a options_fun = ?root_dir:OpamFilename.Dir.t -> ?current_switch:OpamSwitch.t -> - ?switch_from:[ `Env | `Command_line | `Default ] -> + ?switch_from:provenance -> ?jobs:(int Lazy.t) -> ?dl_jobs:int -> ?build_test:bool -> @@ -44,6 +66,7 @@ ?unlock_base:bool -> ?no_env_notice:bool -> ?locked:string option -> + ?no_depexts: bool -> 'a include OpamStd.Config.Sig @@ -105,8 +128,6 @@ ?lock_kind: 'a lock -> 'b global_state -> switch -> OpamFile.Switch_config.t val safe_read_selections: ?lock_kind: 'a lock -> 'b global_state -> switch -> switch_selections - val safe_read_selections_t: - ?lock_kind: 'a lock -> dirname -> switch -> switch_selections val read_opt: ?lock_kind: 'a lock -> 'b global_state -> switch -> OpamFile.Switch_config.t option @@ -116,3 +137,12 @@ val safe_read: ?lock_kind: 'a lock -> 'b global_state -> OpamFile.Repos_config.t end + +(* Raw read an switch config to downgrade its [opam-version] from 2.1 to 2.0. + It is necessary to handle opam root and switch upgrade from 2.1 + intermediates roots to 2.1: this allows a workaround for a bug in versions + 2.1~alpha which wrongly updated the declared switch versions, requiring that + we fix it during [OpamFormatUpgrade] from these specific intermediate + versions, and at switch loading for that specific case. *) +val downgrade_2_1_switch: + OpamFile.Switch_config.t OpamFile.t -> OpamFile.Switch_config.t option diff -Nru opam-2.0.10/src/state/opamStateTypes.mli opam-2.1.2/src/state/opamStateTypes.mli --- opam-2.0.10/src/state/opamStateTypes.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamStateTypes.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -79,8 +79,13 @@ repo_opams: OpamFile.OPAM.t package_map repository_name_map; (** All opam files that can be found in the configured repositories *) + + repos_tmp: (OpamRepositoryName.t, OpamFilename.Dir.t Lazy.t) Hashtbl.t; + (** Temporary directories containing the uncompressed contents of the + repositories *) } constraint 'lock = 'lock lock + (** State of a given switch: options, available and installed packages, etc.*) type +'lock switch_state = { switch_lock: OpamSystem.lock; @@ -92,8 +97,13 @@ switch: switch; (** The current active switch *) + switch_invariant: formula; + (** Defines the "base" of the switch, e.g. what compiler is desired *) + compiler_packages: package_set; - (** The packages that form the base of the current compiler *) + (** The packages that form the base of the current compiler. Normally equal to + the subset of installed packages matching the invariant defined in + switch_config *) switch_config: OpamFile.Switch_config.t; (** The configuration file for this switch *) @@ -108,13 +118,17 @@ in separate files), as well as the original metadata directory (that can be used to retrieve the files/ subdir) *) - conf_files: OpamFile.Dot_config.t package_map; + conf_files: OpamFile.Dot_config.t name_map; (** The opam-config of installed packages (from ".opam-switch/config/pkgname.config") *) packages: package_set; (** The set of all known packages *) + sys_packages: sys_pkg_status package_map Lazy.t; + (** Map of package and their system dependencies packages status. Only + initialised for otherwise available packages *) + available_packages: package_set Lazy.t; (** The set of available packages, filtered by their [available:] field *) @@ -134,10 +148,21 @@ happen not to be installed at some point, but this indicates that the user would like them installed. *) - reinstall: package_set; - (** The set of packages which needs to be reinstalled *) + reinstall: package_set Lazy.t; + (** The set of packages which need to be reinstalled *) + + invalidated: package_set Lazy.t; + (** The set of packages which are installed but no longer valid, e.g. because + of removed system dependencies. Only packages which are unavailable end up + in this set, they are otherwise put in [reinstall]. *) (* Missing: a cache for - switch-global and package variables - the solver universe? *) } constraint 'lock = 'lock lock + +(** Command-line setting provenance *) +type provenance = [ `Env (** Environment variable *) + | `Command_line (** Command line *) + | `Default (** Default value *) + ] diff -Nru opam-2.0.10/src/state/opamSwitchAction.ml opam-2.1.2/src/state/opamSwitchAction.ml --- opam-2.0.10/src/state/opamSwitchAction.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamSwitchAction.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -16,7 +16,8 @@ let log fmt = OpamConsole.log "SWACT" fmt let slog = OpamConsole.slog -let gen_switch_config root ?(synopsis="") ?repos _switch = +let gen_switch_config + root ?(synopsis="") ?repos ?invariant _switch = let vars = List.map (fun (s,p) -> OpamVariable.of_string s, S p) [ ("user" , @@ -28,14 +29,17 @@ ] in { OpamFile.Switch_config. - opam_version = OpamVersion.current_nopatch; + opam_version = OpamFile.Switch_config.file_format_version; synopsis; variables = vars; paths = []; opam_root = Some root; repos; wrappers = OpamFile.Wrappers.empty; - env = []; } + env = []; + invariant; + depext_bypass = OpamSysPkg.Set.empty; + } let install_switch_config root switch config = log "install_switch_config switch=%a" (slog OpamSwitch.to_string) switch; @@ -43,7 +47,7 @@ (OpamPath.Switch.switch_config root switch) config -let create_empty_switch gt ?synopsis ?repos switch = +let create_empty_switch gt ?synopsis ?repos ?invariant switch = log "create_empty_switch at %a" (slog OpamSwitch.to_string) switch; let root = gt.root in let switch_dir = OpamPath.Switch.root root switch in @@ -57,7 +61,7 @@ (* Create base directories *) OpamFilename.mkdir switch_dir; - let config = gen_switch_config root ?synopsis ?repos switch in + let config = gen_switch_config root ?synopsis ?repos ?invariant switch in OpamFilename.mkdir (OpamPath.Switch.lib_dir root switch config); OpamFilename.mkdir (OpamPath.Switch.stublibs root switch config); @@ -120,27 +124,26 @@ OpamFilename.remove (OpamFile.filename reinstall_file) else OpamFile.PkgList.write reinstall_file reinstall; - { st with reinstall = st.reinstall ++ add_reinst_packages } + { st with reinstall = lazy (Lazy.force st.reinstall ++ add_reinst_packages) } -let set_current_switch lock gt ?rt switch = - if OpamSwitch.is_external switch then +let set_current_switch gt st = + if OpamSwitch.is_external st.switch then OpamConsole.error_and_exit `Bad_arguments "Can not set external switch '%s' globally. To set it in the current \ shell use:\n %s" - (OpamSwitch.to_string switch) - (OpamEnv.eval_string gt ~set_opamswitch:true (Some switch)); - let config = OpamFile.Config.with_switch switch gt.config in + (OpamSwitch.to_string st.switch) + (OpamEnv.eval_string gt ~set_opamswitch:true (Some st.switch)); + let config = OpamFile.Config.with_switch st.switch gt.config in let gt = { gt with config } in OpamGlobalState.write gt; - let rt = match rt with - | Some rt -> { rt with repos_global = gt } - | None -> OpamRepositoryState.load `Lock_none gt - in - let st = OpamSwitchState.load lock gt rt switch in + let rt = { st.switch_repos with repos_global = gt } in + let st = { st with switch_global = gt; switch_repos = rt } in OpamEnv.write_dynamic_init_scripts st; st let install_metadata st nv = + OpamSwitchState.Installed_cache.remove + (OpamPath.Switch.installed_opams_cache st.switch_global.root st.switch); let opam = OpamSwitchState.opam st nv in OpamFile.OPAM.write (OpamPath.Switch.installed_opam st.switch_global.root st.switch nv) @@ -154,9 +157,13 @@ in OpamFilename.mkdir (OpamFilename.dirname dst); OpamFilename.copy ~src:f ~dst) - (OpamFile.OPAM.get_extra_files opam) + (OpamFile.OPAM.get_extra_files + ~repos_roots:(OpamRepositoryState.get_root st.switch_repos) + opam) let remove_metadata st packages = + OpamSwitchState.Installed_cache.remove + (OpamPath.Switch.installed_opams_cache st.switch_global.root st.switch); OpamPackage.Set.iter (fun nv -> OpamFilename.rmdir (OpamPath.Switch.installed_package_dir @@ -167,26 +174,17 @@ let open OpamStd.Option.Op in let open OpamPackage.Set.Op in let installed = installed +! st.installed in - let reinstall0 = st.reinstall in + let reinstall0 = Lazy.force st.reinstall in let reinstall = (reinstall +! reinstall0) %% installed in let compiler_packages = - if OpamPackage.Set.is_empty (st.compiler_packages -- installed) then - st.compiler_packages - else (* adjust version of installed compiler packages *) - let names = OpamPackage.names_of_packages st.compiler_packages in - let installed_base = OpamPackage.packages_of_names installed names in - installed_base ++ - (* keep version of uninstalled compiler packages *) - OpamPackage.packages_of_names st.compiler_packages - (OpamPackage.Name.Set.diff names - (OpamPackage.names_of_packages installed_base)) + OpamPackage.Set.filter (OpamFormula.verifies st.switch_invariant) installed in let old_selections = OpamSwitchState.selections st in let st = { st with installed; installed_roots = installed_roots +! st.installed_roots; - reinstall; + reinstall = lazy reinstall; pinned = pinned +! st.pinned; compiler_packages; } in @@ -203,7 +201,7 @@ let st = update_switch_state st ~installed:(OpamPackage.Set.add nv st.installed) - ~reinstall:(OpamPackage.Set.remove nv st.reinstall) + ~reinstall:(OpamPackage.Set.remove nv (Lazy.force st.reinstall)) ~installed_roots: (let roots = OpamPackage.Set.filter (fun nv1 -> nv1.name <> nv.name) @@ -216,7 +214,7 @@ OpamFile.Dot_config.safe_read (OpamPath.Switch.config st.switch_global.root st.switch nv.name) in - let st = { st with conf_files = OpamPackage.Map.add nv conf st.conf_files } in + let st = { st with conf_files = OpamPackage.Name.Map.add nv.name conf st.conf_files } in if not OpamStateConfig.(!r.dryrun) then ( install_metadata st nv; if OpamFile.OPAM.env opam <> [] && @@ -233,7 +231,7 @@ ~installed:(rm st.installed) ?installed_roots:(if keep_as_root then None else Some (rm st.installed_roots)) - ~reinstall:(rm st.reinstall) + ~reinstall:(rm (Lazy.force st.reinstall)) in let has_setenv = match OpamStd.Option.map OpamFile.OPAM.env (OpamSwitchState.opam_opt st nv) @@ -244,4 +242,4 @@ then (* note: don't remove_metadata just yet *) OpamEnv.write_dynamic_init_scripts st; - { st with conf_files = OpamPackage.Map.remove nv st.conf_files } + { st with conf_files = OpamPackage.Name.Map.remove nv.name st.conf_files } diff -Nru opam-2.0.10/src/state/opamSwitchAction.mli opam-2.1.2/src/state/opamSwitchAction.mli --- opam-2.0.10/src/state/opamSwitchAction.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamSwitchAction.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -17,22 +17,23 @@ (** Initialises a new switch with the given name in the given opam root, registers it in the global config and returns the updated global state *) val create_empty_switch: - rw global_state -> ?synopsis:string -> ?repos:repository_name list -> + rw global_state -> + ?synopsis:string -> ?repos:repository_name list -> ?invariant:formula -> switch -> rw global_state (** Writes the current state file to disk (installed, pinned, root packages etc.). Unless [OpamStateConfig.(!r.dryrun)] *) val write_selections: rw switch_state -> unit -(** Updates the defined default switch and loads its state; fails and exits with - a message if the switch is external *) -val set_current_switch: - 'a lock -> rw global_state -> ?rt:'b repos_state -> switch -> 'a switch_state +(** Updates the global default switch to the one corresponding to the given + state; fails and exits with a message if the switch is external *) +val set_current_switch: rw global_state -> 'a switch_state -> 'a switch_state (** Create the default global_config structure for a switch, including default prefix *) val gen_switch_config: - dirname -> ?synopsis:string -> ?repos:repository_name list -> + dirname -> + ?synopsis:string -> ?repos:repository_name list -> ?invariant:formula -> switch -> OpamFile.Switch_config.t (** (Re-)install the configuration for a given root and switch *) diff -Nru opam-2.0.10/src/state/opamSwitchState.ml opam-2.1.2/src/state/opamSwitchState.ml --- opam-2.0.10/src/state/opamSwitchState.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamSwitchState.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -25,10 +25,8 @@ match OpamStateConfig.Switch.read_opt ?lock_kind gt switch with | Some c -> c | exception (OpamPp.Bad_version _ as e) -> -(* OpamFormatUpgrade.hard_upgrade_from_2_1_intermediates ~global_lock:gt.global_lock gt.root; -*) raise e | None -> (OpamConsole.error @@ -77,9 +75,156 @@ let repos_list st = repos_list_raw st.switch_repos st.switch_config +let infer_switch_invariant_raw + gt switch switch_config opams + packages compiler_packages installed_roots available_packages + = + let compiler = compiler_packages %% installed_roots in + let compiler = + if OpamPackage.Set.is_empty compiler then compiler_packages + else compiler + in + let env nv v = + if List.mem v OpamPackageVar.predefined_depends_variables then + match OpamVariable.Full.to_string v with + | "dev" | "with-test" | "with-doc" -> Some (B false) + | _ -> None + else + OpamPackageVar.resolve_switch_raw ~package:nv gt switch switch_config v + in + let resolve_deps nv = + let opam = OpamPackage.Map.find nv opams in + OpamPackageVar.filter_depends_formula + ~build:true ~post:true ~default:true ~env:(env nv) + (OpamFormula.ands [ + OpamFile.OPAM.depends opam; + OpamFile.OPAM.depopts opam + ]) + |> OpamFormula.packages packages + in + let dmap = + OpamPackage.Set.fold (fun nv dmap -> + let deps = resolve_deps nv in + let dmap = + OpamPackage.Map.update nv ((++) deps) OpamPackage.Set.empty dmap + in + let dmap = + OpamPackage.Set.fold (fun d dmap -> + OpamPackage.Map.update d (OpamPackage.Set.add nv) + OpamPackage.Set.empty dmap) + deps dmap + in + dmap) + (OpamPackage.packages_of_names (Lazy.force available_packages) @@ + OpamPackage.names_of_packages @@ + compiler) + OpamPackage.Map.empty + in + let counts = + OpamPackage.Set.fold (fun nv counts -> + let count = + try OpamPackage.Set.cardinal (OpamPackage.Map.find nv dmap) + with Not_found -> 0 + in + (nv, count) :: counts + ) + compiler [] + in + match List.sort (fun (_, c1) (_, c2) -> compare c1 c2) counts with + | (nv, _) :: _ -> + let versions = + OpamPackage.packages_of_name (Lazy.force available_packages) nv.name + in + let n = OpamPackage.Set.cardinal versions in + if n <= 1 then + OpamFormula.Atom (nv.name, Empty) + else if nv = OpamPackage.Set.max_elt versions then + OpamFormula.Atom (nv.name, Atom (`Geq, nv.version)) + else + OpamFormula.Atom (nv.name, Atom (`Eq, nv.version)) + | [] -> OpamFormula.Empty + +let infer_switch_invariant st = + let compiler_packages = + if OpamPackage.Set.is_empty st.compiler_packages then + OpamPackage.Set.filter (fun nv -> + OpamFile.OPAM.has_flag Pkgflag_Compiler + (OpamPackage.Map.find nv st.opams)) + st.installed + else st.compiler_packages + in + infer_switch_invariant_raw + st.switch_global st.switch st.switch_config st.opams + st.packages compiler_packages st.installed_roots st.available_packages + +let depexts_raw ~env nv opams = + try + let opam = OpamPackage.Map.find nv opams in + List.fold_left (fun depexts (names, filter) -> + if OpamFilter.eval_to_bool ~default:false env filter then + OpamSysPkg.Set.Op.(names ++ depexts) + else depexts) + OpamSysPkg.Set.empty + (OpamFile.OPAM.depexts opam) + with Not_found -> OpamSysPkg.Set.empty + +module Installed_cache = OpamCached.Make(struct + type t = OpamFile.OPAM.t OpamPackage.Map.t + let name = "installed" + end) + +let depexts_status_of_packages_raw ~depexts global_config switch_config packages = + let open OpamSysPkg.Set.Op in + let syspkg_set, syspkg_map = + OpamPackage.Set.fold (fun nv (set, map) -> + let s = depexts nv in + s ++ set, + if OpamSysPkg.Set.is_empty s then map + else OpamPackage.Map.add nv s map) + packages (OpamSysPkg.Set.empty, OpamPackage.Map.empty) + in + let chronos = OpamConsole.timer () in + let bypass = + OpamFile.Config.depext_bypass global_config ++ + switch_config.OpamFile.Switch_config.depext_bypass + in + let syspkg_set = syspkg_set -- bypass in + let ret = + match OpamSysInteract.packages_status syspkg_set with + | avail, not_found -> + let avail, not_found = + if OpamStateConfig.(!r.no_depexts) then + (* Mark all as available. This is necessary to store the exceptions + afterwards *) + avail ++ not_found, OpamSysPkg.Set.empty + else if OpamFile.Config.depext_cannot_install global_config then + OpamSysPkg.Set.empty, avail ++ not_found + else + avail, not_found + in + OpamPackage.Map.map (fun set -> + { OpamSysPkg.s_available = set %% avail; + OpamSysPkg.s_not_found = set %% not_found} + ) syspkg_map + | exception (Failure msg) -> + OpamConsole.note "%s\nYou can disable this check using 'opam \ + option --global depext=false'" + msg; + OpamPackage.Map.empty + in + log "depexts loaded in %.3fs" (chronos()); + ret + +let depexts_unavailable_raw sys_packages nv = + match OpamPackage.Map.find_opt nv sys_packages with + | Some { OpamSysPkg.s_not_found; _} + when not (OpamSysPkg.Set.is_empty s_not_found) -> + Some s_not_found + | _ -> None + let load lock_kind gt rt switch = let chrono = OpamConsole.timer () in - log "LOAD-SWITCH-STATE @ %a" (slog OpamSwitch.to_string) switch; + log "LOAD-SWITCH-STATE %@ %a" (slog OpamSwitch.to_string) switch; if not (OpamGlobalState.switch_exists gt switch) then (log "The switch %a does not appear to be installed according to %a" (slog OpamSwitch.to_string) switch @@ -107,9 +252,19 @@ if OpamStateConfig.is_newer_than_self gt then log "root version (%s) is greater than running binary's (%s); \ load with best-effort (read-only)" - (OpamStd.Option.to_string OpamVersion.to_string - (OpamFile.Config.opam_root_version gt.config)) + (OpamVersion.to_string (OpamFile.Config.opam_root_version gt.config)) (OpamVersion.to_string (OpamFile.Config.root_version)); + if OpamVersion.compare + switch_config.opam_version + OpamFile.Switch_config.oldest_compatible_format_version + < 0 then + OpamConsole.error_and_exit `Configuration_error + "Could not load opam switch %s: it reports version %s while >= %s was \ + expected" + (OpamSwitch.to_string switch) + (OpamVersion.to_string (switch_config.opam_version)) + (OpamVersion.to_string + OpamFile.Switch_config.oldest_compatible_format_version); let { sel_installed = installed; sel_roots = installed_roots; sel_pinned = pinned; sel_compiler = compiler_packages; } = load_selections ~lock_kind gt switch @@ -143,13 +298,30 @@ pinned (OpamPackage.Set.empty, OpamPackage.Map.empty) in let installed_opams = - OpamPackage.Set.fold (fun nv opams -> - OpamStd.Option.Op.( - (OpamFile.OPAM.read_opt - (OpamPath.Switch.installed_opam gt.root switch nv) - >>| fun opam -> OpamPackage.Map.add nv opam opams) - +! opams)) - installed OpamPackage.Map.empty + let cache_file = OpamPath.Switch.installed_opams_cache gt.root switch in + match Installed_cache.load cache_file with + | Some opams -> + OpamPackage.Map.mapi (fun nv opam -> + let metadata_dir = + OpamPath.Switch.installed_opam gt.root switch nv + |> OpamFile.filename + |> OpamFilename.dirname + |> OpamFilename.Dir.to_string + in + OpamFile.OPAM.with_metadata_dir (Some (None, metadata_dir)) opam) + opams + | None -> + let opams = + OpamPackage.Set.fold (fun nv opams -> + OpamStd.Option.Op.( + (OpamFile.OPAM.read_opt + (OpamPath.Switch.installed_opam gt.root switch nv) + >>| fun opam -> OpamPackage.Map.add nv opam opams) + +! opams)) + installed OpamPackage.Map.empty + in + Installed_cache.save cache_file opams; + opams in let repos_package_index = OpamRepositoryState.build_index rt (repos_list_raw rt switch_config) @@ -157,7 +329,6 @@ let opams = OpamPackage.Map.union (fun _ x -> x) repos_package_index pinned_opams in - let packages = OpamPackage.keys opams in let available_packages = lazy (compute_available_packages gt switch switch_config ~pinned ~opams) @@ -167,6 +338,7 @@ computing availability *) OpamPackage.Map.union (fun _ x -> x) installed_opams opams in + let packages = OpamPackage.keys opams in let installed_without_def = OpamPackage.Set.fold (fun nv nodef -> if OpamPackage.Map.mem nv installed_opams then nodef else @@ -187,30 +359,27 @@ "No definition found for the following installed packages: %s\n\ This switch may need to be reinstalled" (OpamPackage.Set.to_string installed_without_def); - let changed = + let changed = lazy ( (* Note: This doesn't detect changed _dev_ packages, since it's based on the metadata or the archive hash changing and they don't have an archive hash. Therefore, dev package update needs to add to the reinstall file *) - OpamPackage.Map.merge (fun _ opam_new opam_installed -> - match opam_new, opam_installed with - | Some r, Some i when not (OpamFile.OPAM.effectively_equal i r) -> - Some () - | _ -> None) - opams installed_opams - |> OpamPackage.keys - in - let changed = - changed -- - OpamPackage.Set.filter - (fun nv -> not (OpamPackage.has_name pinned nv.name)) - compiler_packages - in - log "Detected changed packages (marked for reinstall): %a" - (slog OpamPackage.Set.to_string) changed; + let changed = + OpamPackage.Map.merge (fun _ opam_new opam_installed -> + match opam_new, opam_installed with + | Some r, Some i when not (OpamFile.OPAM.effectively_equal i r) -> + Some () + | _ -> None) + opams installed_opams + |> OpamPackage.keys + in + log "Detected changed packages (marked for reinstall): %a" + (slog OpamPackage.Set.to_string) changed; + changed + ) in (* Detect and initialise missing switch description *) let switch_config = if switch_config <> OpamFile.Switch_config.empty && - switch_config.OpamFile.Switch_config.synopsis = "" then + switch_config.synopsis = "" then let synopsis = match OpamPackage.Set.elements (compiler_packages %% installed_roots) with @@ -221,7 +390,7 @@ OpamPackage.to_string nv | pkgs -> OpamStd.List.concat_map " " OpamPackage.to_string pkgs in - let conf = { switch_config with OpamFile.Switch_config.synopsis } in + let conf = { switch_config with synopsis } in if lock_kind = `Lock_write then (* auto-repair *) OpamFile.Switch_config.write (OpamPath.Switch.switch_config gt.root switch) @@ -229,16 +398,59 @@ conf else switch_config in + let switch_config, switch_invariant = + match switch_config.invariant with + | Some invariant -> switch_config, invariant + | None -> + let invariant = + infer_switch_invariant_raw + gt switch switch_config opams + packages compiler_packages installed_roots available_packages + in + log "Inferred invariant: from base packages %a, (roots %a) => %a" + (slog OpamPackage.Set.to_string) compiler_packages + (slog @@ fun () -> + OpamPackage.Set.to_string (compiler_packages %% installed_roots)) () + (slog OpamFileTools.dep_formula_to_string) invariant; + let min_opam_version = OpamVersion.of_string "2.0" in + let opam_version = + if OpamVersion.compare switch_config.opam_version min_opam_version < 0 + then min_opam_version + else switch_config.opam_version + in + let switch_config = + {switch_config with invariant = Some invariant; opam_version} + in + if lock_kind = `Lock_write then + OpamFile.Switch_config.write + (OpamPath.Switch.switch_config gt.root switch) + switch_config; + switch_config, invariant + in let conf_files = - OpamPackage.Set.fold (fun nv acc -> - OpamPackage.Map.add nv - (OpamFile.Dot_config.safe_read - (OpamPath.Switch.config gt.root switch nv.name)) - acc) - installed OpamPackage.Map.empty + let conf_files = + OpamFilename.files (OpamPath.Switch.config_dir gt.root switch) + in + List.fold_left (fun acc f -> + if OpamFilename.check_suffix f ".config" then + match + OpamPackage.Name.of_string + OpamFilename.(Base.to_string (basename (chop_extension f))) + with + | name when OpamPackage.has_name installed name -> + OpamPackage.Name.Map.add name + (OpamFile.Dot_config.safe_read + (OpamPath.Switch.config gt.root switch name)) + acc + | exception (Failure _) -> acc + | _ -> acc + else acc) + OpamPackage.Name.Map.empty + conf_files in - let ext_files_changed = - OpamPackage.Map.fold (fun nv conf acc -> + let ext_files_changed = lazy ( + OpamPackage.Name.Map.fold (fun name conf acc -> + let nv = OpamPackage.package_of_name installed name in if List.exists (fun (file, hash) -> let exists = OpamFilename.exists file in @@ -252,52 +464,127 @@ exists <> should_exist || exists && not (OpamHash.check_file (OpamFilename.to_string file) hash) in - (* /!\ fixme: the package removal instructions won't actually ever - be called in this case *) if not exists && should_exist then - OpamConsole.error - "System file %s, which package %s depends upon, \ - no longer exists.\n\ - The package has been marked as removed, and opam will \ - try to reinstall it if necessary, but you should reinstall \ - its system dependencies first." + OpamConsole.warning + "System file %s, which package %s depends upon, no longer \ + exists.\n\ + The package will need to either be removed, or reinstalled. \ + You may need to restore its system dependencies for the \ + latter." (OpamFilename.to_string file) (OpamPackage.to_string nv) else if changed then OpamConsole.warning - "File %s, which package %s depends upon, \ - was changed on your system. \ - %s has been marked as removed, and will be reinstalled if \ - necessary." - (OpamFilename.to_string file) (OpamPackage.to_string nv) - (OpamPackage.name_to_string nv); + "File %s, which package %s depends upon, was changed on your \ + system.\n\ + The package will need to be reinstalled." + (OpamFilename.to_string file) (OpamPackage.to_string nv); changed) (OpamFile.Dot_config.file_depends conf) then OpamPackage.Set.add nv acc else acc) conf_files OpamPackage.Set.empty + ) in + (* depext check *) + let sys_packages = + if not (OpamFile.Config.depext gt.config) + || OpamStateConfig.(!r.no_depexts) then + lazy OpamPackage.Map.empty + else lazy ( + depexts_status_of_packages_raw gt.config switch_config + (Lazy.force available_packages) + ~depexts:(fun package -> + let env = + OpamPackageVar.resolve_switch_raw ~package gt switch switch_config + in + depexts_raw ~env package opams) + ) in - let installed = - installed -- ext_files_changed - in - let reinstall = - OpamFile.PkgList.safe_read (OpamPath.Switch.reinstall gt.root switch) ++ + let available_packages = + if not (OpamFile.Config.depext gt.config) then available_packages + else lazy ( + let sys_packages = Lazy.force sys_packages in + OpamPackage.Set.filter (fun nv -> + depexts_unavailable_raw sys_packages nv = None) + (Lazy.force available_packages) + ) + in + let sys_packages_changed = lazy ( + let sys_packages = + OpamPackage.Map.filter (fun pkg spkg -> + OpamPackage.Set.mem pkg installed + && not (OpamSysPkg.Set.is_empty spkg.OpamSysPkg.s_available + && OpamSysPkg.Set.is_empty spkg.OpamSysPkg.s_not_found)) + (Lazy.force sys_packages) + in + if OpamPackage.Map.is_empty sys_packages then + OpamPackage.Set.empty + else + let lchanged = OpamPackage.Map.keys sys_packages in + let changed = OpamPackage.Set.of_list lchanged in + let sgl_pkg = OpamPackage.Set.is_singleton changed in + let open OpamSysPkg.Set.Op in + let missing_map = + OpamPackage.Map.map (fun sys -> + sys.OpamSysPkg.s_available ++ sys.OpamSysPkg.s_not_found) + sys_packages + in + let missing_set = + OpamPackage.Map.fold (fun _ -> OpamSysPkg.Set.union) + missing_map + OpamSysPkg.Set.empty + in + let sgl_spkg = OpamSysPkg.Set.is_singleton missing_set in + if sgl_pkg then + OpamConsole.warning + "Opam package %s depends on the following system package%s that can \ + no longer be found: %s" + (OpamPackage.to_string (OpamPackage.Set.choose_one changed)) + (if sgl_spkg then "" else "s") + (OpamStd.List.concat_map " " OpamSysPkg.to_string + (OpamSysPkg.Set.elements missing_set)) + else + (OpamConsole.warning + "Opam packages %s depend on the following system package%s that are \ + no longer installed: %s" + (OpamStd.Format.pretty_list (List.map OpamPackage.to_string lchanged)) + (if sgl_spkg then "" else "s") + (OpamStd.List.concat_map " " OpamSysPkg.to_string + (OpamSysPkg.Set.elements missing_set)); + if OpamConsole.verbose () then + OpamConsole.errmsg "%s" + (OpamStd.Format.itemize (fun (pkg, spkg) -> + Printf.sprintf "%s: depends on %s" + (OpamPackage.to_string pkg) + (OpamStd.List.concat_map ", " OpamSysPkg.to_string + (OpamSysPkg.Set.elements spkg))) + (OpamPackage.Map.bindings missing_map))); changed - in + ) in + let reinstall = lazy ( + OpamFile.PkgList.safe_read (OpamPath.Switch.reinstall gt.root switch) ++ + Lazy.force changed ++ + (Lazy.force ext_files_changed %% Lazy.force available_packages) ++ + Lazy.force sys_packages_changed + ) in + let invalidated = lazy ( + Lazy.force ext_files_changed ++ Lazy.force sys_packages_changed + -- Lazy.force available_packages + ) in let st = { switch_global = (gt :> unlocked global_state); switch_repos = (rt :> unlocked repos_state); switch_lock = lock; - switch; compiler_packages; switch_config; + switch; switch_invariant; compiler_packages; switch_config; repos_package_index; installed_opams; installed; pinned; installed_roots; opams; conf_files; - packages; available_packages; reinstall; + packages; available_packages; sys_packages; reinstall; invalidated; } in log "Switch state loaded in %.3fs" (chrono ()); st -let load_virtual ?repos_list gt rt = +let load_virtual ?repos_list ?(avail_default=true) gt rt = let repos_list = match repos_list with | Some rl -> rl | None -> OpamGlobalState.repos_list gt @@ -306,11 +593,20 @@ OpamRepositoryState.build_index rt repos_list in let packages = OpamPackage.keys opams in + let available_packages = lazy ( + OpamPackage.Map.filter (fun _ opam -> + OpamFilter.eval_to_bool ~default:avail_default + (OpamPackageVar.resolve_global gt) + (OpamFile.OPAM.available opam)) + opams + |> OpamPackage.keys + ) in { switch_global = (gt :> unlocked global_state); switch_repos = (rt :> unlocked repos_state); switch_lock = OpamSystem.lock_none; switch = OpamSwitch.unset; + switch_invariant = OpamFormula.Empty; compiler_packages = OpamPackage.Set.empty; switch_config = { OpamFile.Switch_config.empty @@ -322,10 +618,12 @@ installed_roots = OpamPackage.Set.empty; repos_package_index = opams; opams; - conf_files = OpamPackage.Map.empty; + conf_files = OpamPackage.Name.Map.empty; packages; - available_packages = lazy packages; - reinstall = OpamPackage.Set.empty; + sys_packages = lazy OpamPackage.Map.empty; + available_packages; + reinstall = lazy OpamPackage.Set.empty; + invalidated = lazy (OpamPackage.Set.empty); } let selections st = @@ -338,6 +636,9 @@ OpamSystem.funlock st.switch_lock; (st :> unlocked switch_state) +let drop st = + let _ = unlock st in () + let with_write_lock ?dontblock st f = if OpamStateConfig.is_newer_than_self st.switch_global then OpamConsole.error_and_exit `Locked @@ -367,17 +668,26 @@ let primary_url st nv = OpamStd.Option.Op.(url st nv >>| OpamFile.URL.url) +let primary_url_with_subpath st nv = + match url st nv with + | None -> None + | Some urlf -> + let url = OpamFile.URL.url urlf in + match OpamFile.URL.subpath urlf with + | None -> Some url + | Some subpath -> Some (OpamUrl.Op.(url / subpath)) + let files st nv = match opam_opt st nv with | None -> [] | Some opam -> List.map (fun (file,_base,_hash) -> file) - (OpamFile.OPAM.get_extra_files opam) + (OpamFile.OPAM.get_extra_files + ~repos_roots:(OpamRepositoryState.get_root st.switch_repos) + opam) let package_config st name = - OpamPackage.Map.find - (OpamPackage.package_of_name st.installed name) - st.conf_files + OpamPackage.Name.Map.find name st.conf_files let is_name_installed st name = OpamPackage.has_name st.installed name @@ -395,7 +705,13 @@ OpamPackage.max_version st.packages name let is_dev_package st nv = - match opam_opt st nv with + let opam_opt = + if OpamPackage.Set.mem nv st.pinned then + opam_opt st nv + else + OpamPackage.Map.find_opt nv st.repos_package_index + in + match opam_opt with | Some opam -> OpamPackageVar.is_dev_package st opam | None -> false @@ -418,16 +734,14 @@ let depexts st nv = let env v = OpamPackageVar.resolve_switch ~package:nv st v in - match opam_opt st nv with - | None -> OpamStd.String.Set.empty - | Some opam -> - List.fold_left (fun depexts (names, filter) -> - if OpamFilter.eval_to_bool ~default:false env filter then - List.fold_left (fun depexts n -> OpamStd.String.Set.add n depexts) - depexts names - else depexts) - OpamStd.String.Set.empty - (OpamFile.OPAM.depexts opam) + depexts_raw ~env nv st.opams + +let depexts_status_of_packages st set = + depexts_status_of_packages_raw st.switch_global.config st.switch_config set + ~depexts:(depexts st) + +let depexts_unavailable st nv = + depexts_unavailable_raw (Lazy.force st.sys_packages) nv let dev_packages st = OpamPackage.Set.filter (is_dev_package st) @@ -453,22 +767,24 @@ (fun nv -> not (OpamPackage.has_name subset nv.name) && (OpamFormula.verifies forward_conflicts nv || - let opam = OpamPackage.Map.find nv st.opams in - List.exists (fun cl -> OpamPackage.Name.Set.mem cl conflict_classes) - (OpamFile.OPAM.conflict_class opam) - || - let backwards_conflicts = - OpamFilter.filter_formula ~default:false - (OpamPackageVar.resolve_switch ~package:nv st) - (OpamFile.OPAM.conflicts opam) - in - OpamPackage.Set.exists - (OpamFormula.verifies backwards_conflicts) subset)) + try + let opam = OpamPackage.Map.find nv st.opams in + List.exists (fun cl -> OpamPackage.Name.Set.mem cl conflict_classes) + (OpamFile.OPAM.conflict_class opam) + || + let backwards_conflicts = + OpamFilter.filter_formula ~default:false + (OpamPackageVar.resolve_switch ~package:nv st) + (OpamFile.OPAM.conflicts opam) + in + OpamPackage.Set.exists + (OpamFormula.verifies backwards_conflicts) subset + with Not_found -> false)) let remove_conflicts st subset pkgs = pkgs -- conflicts_with st subset pkgs -let get_conflicts st packages opams_map = +let get_conflicts_t env packages opams_map = let conflict_classes = OpamPackage.Map.fold (fun nv opam acc -> List.fold_left (fun acc cc -> @@ -495,7 +811,7 @@ OpamPackage.Map.fold (fun nv opam acc -> let conflicts = OpamFilter.filter_formula ~default:false - (OpamPackageVar.resolve_switch ~package:nv st) + (env nv) (OpamFile.OPAM.conflicts opam) in let conflicts = @@ -515,6 +831,17 @@ opams_map OpamPackage.Map.empty +let get_conflicts st packages opams_map = + get_conflicts_t + (fun package -> OpamPackageVar.resolve_switch ~package st) + packages opams_map + +let can_upgrade_to_avoid_version name st = + OpamPackage.Set.exists (fun pkg -> + OpamPackage.Name.equal (OpamPackage.name pkg) name && + OpamFile.OPAM.has_flag Pkgflag_AvoidVersion (OpamPackage.Map.find pkg st.opams) + ) st.installed + let universe st ?(test=OpamStateConfig.(!r.build_test)) ?(doc=OpamStateConfig.(!r.build_doc)) @@ -570,20 +897,54 @@ in get_deps depend st.opams in + let u_depopts = get_deps OpamFile.OPAM.depopts st.opams in let u_conflicts = get_conflicts st st.packages st.opams in - let base = - if OpamStateConfig.(!r.unlock_base) then OpamPackage.Set.empty - else st.compiler_packages + let base = st.compiler_packages in + let u_invariant = + if OpamStateConfig.(!r.unlock_base) then OpamFormula.Empty + else st.switch_invariant in let u_available = - remove_conflicts st base (Lazy.force st.available_packages) - in - let u_reinstall = match reinstall with + (* TODO: removing what conflicts with the base is no longer correct now that + we use invariants instead. Removing what conflicts with the invariant + would be much more involved, but some solvers might struggle without any + cleanup at this point *) + (* remove_conflicts st base *) + (Lazy.force st.available_packages) + in + let u_reinstall = + (* Ignore reinstalls outside of the dependency cone of + [requested_allpkgs] *) + let resolve_deps nv = + OpamPackageVar.filter_depends_formula + ~build:true ~post:true ~default:true ~env:(env nv) + (OpamFormula.ands [ OpamPackage.Map.find nv u_depends; + OpamPackage.Map.find nv u_depopts ]) + |> OpamFormula.packages st.packages + in + let requested_deps = + OpamPackage.Set.fixpoint resolve_deps requested_allpkgs + in + requested_deps %% Lazy.force st.reinstall ++ + match reinstall with | Some set -> set - | None -> - OpamPackage.Set.filter - (fun nv -> OpamPackage.Name.Set.mem nv.name requested) - st.reinstall + | None -> OpamPackage.Set.empty + in + let missing_depexts = + OpamPackage.Map.fold (fun nv status acc -> + if OpamSysPkg.Set.is_empty status.OpamSysPkg.s_available + then acc + else OpamPackage.Set.add nv acc) + (Lazy.force st.sys_packages) + OpamPackage.Set.empty + in + let avoid_versions = + OpamPackage.Map.fold (fun nv opam acc -> + if OpamFile.OPAM.has_flag Pkgflag_AvoidVersion opam && + not (can_upgrade_to_avoid_version (OpamFile.OPAM.name opam) st) + then OpamPackage.Set.add nv acc else acc) + st.opams + OpamPackage.Set.empty in let u = { @@ -592,13 +953,16 @@ u_installed = st.installed; u_available; u_depends; - u_depopts = get_deps OpamFile.OPAM.depopts st.opams; + u_depopts; u_conflicts; u_installed_roots = st.installed_roots; u_pinned = OpamPinned.packages st; u_base = base; + u_invariant; u_reinstall; - u_attrs = ["opam-query", requested_allpkgs]; + u_attrs = ["opam-query", requested_allpkgs; + "missing-depexts", missing_depexts; + "avoid-version", avoid_versions]; } in u @@ -613,7 +977,7 @@ let base = OpamPackage.Set.mem nv st.compiler_packages in let pinned = OpamPackage.Set.mem nv st.pinned in let available = OpamPackage.Set.mem nv (Lazy.force st.available_packages) in - let reinstall = OpamPackage.Set.mem nv st.reinstall in + let reinstall = OpamPackage.Set.mem nv (Lazy.force st.reinstall) in let dev = OpamPackageVar.is_dev_package st opam in (* current state *) Printf.fprintf oc "available: %b\n" available; @@ -665,14 +1029,14 @@ | Some (relop,v) when OpamPackage.has_name st.packages name -> Printf.sprintf "Package %s has no version %s%s." (OpamPackage.Name.to_string name) - (match relop with `Eq -> "" | r -> OpamPrinter.relop r) + (match relop with `Eq -> "" | r -> OpamPrinter.FullPos.relop_kind r) (OpamPackage.Version.to_string v) | _ -> Printf.sprintf "No package named %s found." (OpamPackage.Name.to_string name) (* Display a meaningful error for an unavailable package *) -let unavailable_reason st ?(default="") (name, vformula) = +let unavailable_reason_raw st (name, vformula) = let candidates = OpamPackage.packages_of_name st.packages name in let candidates = OpamPackage.Set.filter @@ -680,46 +1044,88 @@ candidates in if OpamPackage.Set.is_empty candidates then - (if OpamPackage.has_name st.packages name then "no matching version" - else "unknown package") + (if OpamPackage.has_name st.packages name then `UnknownVersion + else `UnknownPackage) else let nv = try OpamPinned.package st name with Not_found -> - match vformula with - | Atom (_, v) when - OpamPackage.Set.mem (OpamPackage.create name v) candidates -> - OpamPackage.create name v - | _ -> OpamPackage.max_version candidates name + match vformula with + | Atom (_, v) when + OpamPackage.Set.mem (OpamPackage.create name v) candidates -> + OpamPackage.create name v + | _ -> OpamPackage.max_version candidates name in match opam_opt st nv with - | None -> "no package definition found" + | None -> `NoDefinition | Some opam -> let avail = OpamFile.OPAM.available opam in if not (OpamPackage.Set.mem nv candidates) then - Printf.sprintf - "not available because the package is pinned to version %s" - (OpamPackage.version_to_string nv) + `Pinned nv else if not (OpamFilter.eval_to_bool ~default:false (OpamPackageVar.resolve_switch ~package:nv st) - avail) - then - Printf.sprintf "unmet availability conditions%s%s" - (if OpamPackage.Set.cardinal candidates = 1 then ": " - else ", e.g. ") - (OpamFilter.to_string avail) + avail) then + `Unavailable + (Printf.sprintf "%s'%s'" + (if OpamPackage.Set.cardinal candidates = 1 then ": " + else ", e.g. ") + (OpamFilter.to_string avail)) else if OpamPackage.has_name (Lazy.force st.available_packages -- remove_conflicts st st.compiler_packages (Lazy.force st.available_packages)) - name - then - "conflict with the base packages of this switch" + name then + `ConflictsBase else if OpamPackage.has_name st.compiler_packages name && not OpamStateConfig.(!r.unlock_base) then - "base of this switch (use `--unlock-base' to force)" + `ConflictsInvariant + (OpamFileTools.dep_formula_to_string st.switch_invariant) else - default + match depexts_unavailable st (OpamPackage.Set.max_elt candidates) with + | Some missing -> + let missing = + List.rev_map OpamSysPkg.to_string (OpamSysPkg.Set.elements missing) + in + `MissingDepexts missing + | None -> `Default + +(* Display a meaningful error for an unavailable package *) +let unavailable_reason st ?(default="") atom = + match unavailable_reason_raw st atom with + | `UnknownVersion -> + "no matching version" + | `UnknownPackage -> + "unknown package" + | `NoDefinition -> + "no package definition found" + | `Pinned nv -> + Printf.sprintf + "not available because the package is pinned to version %s" + (OpamPackage.version_to_string nv) + | `Unavailable msg -> + Printf.sprintf "unmet availability conditions%s" msg + | `ConflictsBase -> + "conflict with the base packages of this switch" + | `ConflictsInvariant invariant -> + Printf.sprintf + "incompatible with the switch invariant %s (use `--update-invariant' \ + to force)" + (OpamConsole.colorise `bold invariant) + | `MissingDepexts missing -> + let msg = + match missing with + | [pkg] -> " '" ^ pkg ^ "'" + | pkgs -> + "s " ^ (OpamStd.Format.pretty_list + (List.rev_map (Printf.sprintf "'%s'") pkgs)) + in + Printf.sprintf + "depends on the unavailable system package%s. Use \ + `--no-depexts' to attempt installation anyway, or it is \ + possible that a depext package name in the opam file \ + is incorrect." msg + | `Default -> + default let update_package_metadata nv opam st = { st with @@ -733,13 +1139,13 @@ then OpamPackage.Set.add nv (Lazy.force st.available_packages) else OpamPackage.Set.remove nv (Lazy.force st.available_packages) ); - reinstall = + reinstall = lazy (match OpamPackage.Map.find_opt nv st.installed_opams with | Some inst -> if OpamFile.OPAM.effectively_equal inst opam - then OpamPackage.Set.remove nv (st.reinstall) - else OpamPackage.Set.add nv (st.reinstall) - | _ -> st.reinstall); + then OpamPackage.Set.remove nv (Lazy.force st.reinstall) + else OpamPackage.Set.add nv (Lazy.force st.reinstall) + | _ -> Lazy.force st.reinstall); } let remove_package_metadata nv st = @@ -785,14 +1191,14 @@ (Printf.sprintf "\nThe former state can be restored with:\n\ \ %s switch import %S\n" - Sys.argv.(0) (OpamFile.to_string file) ^ + Sys.executable_name (OpamFile.to_string file) ^ if OpamPackage.Set.is_empty (new_selections.sel_roots -- new_selections.sel_installed) then "" else Printf.sprintf "Or you can retry to install your package selection with:\n\ \ %s install --restore\n" - Sys.argv.(0)))) + Sys.executable_name))) | _ -> fun _ -> () let with_ lock ?rt ?(switch=OpamStateConfig.get_switch ()) gt f = @@ -802,10 +1208,10 @@ @@ fun rt -> let st = load lock gt rt switch in let cleanup_backup = do_backup lock st in - try let r = f st in ignore (unlock st); cleanup_backup true; r + try let r = f st in drop st; cleanup_backup true; r with e -> OpamStd.Exn.finalise e @@ fun () -> - ignore (unlock st); + drop st; if not OpamCoreConfig.(!r.keep_log_dir) then cleanup_backup false let update_repositories gt update_fun switch = diff -Nru opam-2.0.10/src/state/opamSwitchState.mli opam-2.1.2/src/state/opamSwitchState.mli --- opam-2.0.10/src/state/opamSwitchState.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamSwitchState.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2020 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -32,11 +32,14 @@ [< unlocked ] global_state -> ('a switch_state -> 'b) -> 'b -(** Creates a virtual state with all package available and nothing installed. +(** Creates a virtual state with nothing installed. + Availability is computed just from the global state, and [avail_default] + (default [true]) controls the result when the availability can't be computed + due to undefined variables. Useful for querying and simulating actions when no switch is yet configured, or querying packages directly from the repos *) val load_virtual: - ?repos_list: repository_name list -> + ?repos_list: repository_name list -> ?avail_default: bool -> 'a global_state -> 'b repos_state -> unlocked switch_state (** Load the switch's state file, without constructing the package maps: much @@ -52,9 +55,30 @@ pinned:package_set -> opams:OpamFile.OPAM.t package_map -> package_set +(** Raw function to compute the conflicts for all packages, given + the set of available packages and the corresponding opam files. + This is useful to populate the `u_conflicts` field when building + a universe manually. *) +val get_conflicts_t: + (package -> OpamFilter.env) -> package_set -> + OpamFile.OPAM.t package_map -> formula package_map + +(** Infer a switch invariant from a switch state with compiler_packages and + roots set, using some heuristics. Useful for migration from pre-2.1 opam *) +val infer_switch_invariant: 'a switch_state -> OpamFormula.t + (** Releases any locks on the given switch_state *) val unlock: 'a switch_state -> unlocked switch_state +(** Releases any locks on the given switch state and then ignores it. + + Using [drop st] is equivalent to [ignore (unlock st)], + and safer than other uses of [ignore] + where it is not enforced by the type-system + that the value is unlocked before it is lost. +*) +val drop: 'a switch_state -> unit + (** Calls the provided function, ensuring a temporary write lock on the given switch state *) val with_write_lock: @@ -78,16 +102,18 @@ any *) val opam_opt: 'a switch_state -> package -> OpamFile.OPAM.t option -(** Return the URL file for the given package *) +(** Return the URL field for the given package *) val url: 'a switch_state -> package -> OpamFile.URL.t option -(** Returns the primary URL from the URL file of the given package *) +(** Returns the primary URL from the URL field of the given package *) val primary_url: 'a switch_state -> package -> url option -(** Return the Descr file for the given package (or an empty descr if none) *) +val primary_url_with_subpath: 'a switch_state -> package -> url option + +(** Return the descr field for the given package (or an empty descr if none) *) val descr: 'a switch_state -> package -> OpamFile.Descr.t -(** Return the Descr file for the given package *) +(** Return the descr field for the given package *) val descr_opt: 'a switch_state -> package -> OpamFile.Descr.t option (** Returns the full paths of overlay files under the files/ directory *) @@ -134,7 +160,15 @@ (** Returns the set of active external dependencies for the package, computed from the values of the system-specific variables *) -val depexts: 'a switch_state -> package -> OpamStd.String.Set.t +val depexts: 'a switch_state -> package -> OpamSysPkg.Set.t + +(** Returns required system packages of each of the given packages (elements are + not added to the map if they don't have system dependencies) *) +val depexts_status_of_packages: + 'a switch_state -> package_set -> OpamSysPkg.status package_map + +(** Returns not found depexts for the package *) +val depexts_unavailable: 'a switch_state -> package -> OpamSysPkg.Set.t option (** [conflicts_with st subset pkgs] returns all packages declared in conflict with at least one element of [subset] within [pkgs], through forward or @@ -198,6 +232,19 @@ (** Returns a message about a package or version that couldn't be found *) val not_found_message: 'a switch_state -> atom -> string +val unavailable_reason_raw: + 'a switch_state -> name * OpamFormula.version_formula -> + [ `UnknownVersion + | `UnknownPackage + | `NoDefinition + | `Pinned of OpamPackage.t + | `Unavailable of string + | `ConflictsBase + | `ConflictsInvariant of string + | `MissingDepexts of string list + | `Default + ] + (** Returns a printable explanation why a package is not currently available (pinned to an incompatible version, unmet [available:] constraints...). [default] is returned if no reason why it wouldn't be available was found @@ -205,3 +252,13 @@ val unavailable_reason: 'a switch_state -> ?default:string -> name * OpamFormula.version_formula -> string + +(** Returns whether or not the package can be upgraded to a version tagged with avoid-version *) +val can_upgrade_to_avoid_version : OpamPackage.Name.t -> 'a switch_state -> bool + +(** Handle a cache of the opam files of installed packages *) +module Installed_cache: sig + type t = OpamFile.OPAM.t OpamPackage.Map.t + val save: OpamFilename.t -> t -> unit + val remove: OpamFilename.t -> unit +end diff -Nru opam-2.0.10/src/state/opamSysInteract.ml opam-2.1.2/src/state/opamSysInteract.ml --- opam-2.0.10/src/state/opamSysInteract.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/state/opamSysInteract.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,745 @@ +(**************************************************************************) +(* *) +(* Copyright 2019-2020 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +let log fmt = OpamConsole.log "XSYS" fmt + +(* Run commands *) +(* Always call this function to run a command, as it handles `dryrun` option *) + +let run_command + ?vars ?(discard_err=false) ?allow_stdin ?verbose ?(dryrun=false) cmd args = + let clean_output = + if not discard_err then + fun k -> k None + else + fun k -> OpamFilename.with_tmp_dir_job @@ fun dir -> + let f = OpamFilename.Op.(dir // "out") in + OpamFilename.touch f; + k (Some (OpamFilename.to_string f)) + in + let verbose = + OpamStd.Option.default OpamCoreConfig.(!r.verbose_level >= 3) verbose + in + let env = + match vars with + | None -> None + | Some vars -> + let env = OpamStd.Env.list () in + let set_vars, kept_vars, env = + List.fold_left (fun (n,p,e) (op, (name, content as var)) -> + match OpamStd.List.assoc_opt name env, op with + | Some c, `add when String.compare c content = 0 -> n, p, e + | Some _, `set -> var::n, p, (List.remove_assoc name env) + | Some _, _ -> n, var::p, e + | None, _ -> var::n, p, e + ) + ([],[], env) vars + in + let str_var (v,c) = Printf.sprintf "%s=%s" v c in + if set_vars = [] then + ((if kept_vars <> [] then + log "Won't override %s" + (OpamStd.List.to_string str_var kept_vars)); + None) + else + (log "Adding to env %s" + (OpamStd.List.to_string str_var set_vars); + Some (set_vars @ env + |> List.rev_map str_var + |> Array.of_list)) + in + let run = + if dryrun then OpamProcess.Job.dry_run else OpamProcess.Job.run + in + let open OpamProcess.Job.Op in + run @@ clean_output @@ fun stdout -> + OpamSystem.make_command + ?env ?stdout ?allow_stdin ~verbose cmd args + @@> fun r -> + let code = r.r_code in + let out = r.r_stdout in + OpamProcess.cleanup r; + Done (code, out) + +let run_query_command ?vars cmd args = + let vars = (`set, ("LC_ALL","C"))::OpamStd.Option.to_list vars in + let code,out = run_command ~vars cmd args in + if code = 0 then out + else [] + +let run_command_exit_code ?vars ?allow_stdin ?verbose cmd args = + let code,_ = + run_command ?vars ?allow_stdin ?verbose ~dryrun:OpamStateConfig.(!r.dryrun) + cmd args + in + code + +(* Please keep this alphabetically ordered, in the type definition, and in + below pattern matching *) +type families = + | Alpine + | Arch + | Centos + | Debian + | Freebsd + | Gentoo + | Homebrew + | Macports + | Netbsd + | Openbsd + | Suse + +(* System status *) +let family = + let family = lazy ( + match OpamSysPoll.os_family () with + | None -> + Printf.ksprintf failwith + "External dependency unusable, OS family not detected." + | Some family -> + match family with + | "alpine" -> Alpine + | "amzn" | "centos" | "fedora" | "mageia" | "oraclelinux" | "ol" + | "rhel" -> Centos + | "archlinux" | "arch" -> Arch + | "bsd" -> + begin match OpamSysPoll.os_distribution () with + | Some ("freebsd" | "dragonfly") -> Freebsd + | Some "netbsd" -> Netbsd + | Some "openbsd" -> Openbsd + | _ -> + Printf.ksprintf failwith + "External dependency handling not supported for OS family 'bsd'." + end + | "debian" | "ubuntu" -> Debian + | "gentoo" -> Gentoo + | "homebrew" -> Homebrew + | "macports" -> Macports + | "suse" | "opensuse" -> Suse + | family -> + Printf.ksprintf failwith + "External dependency handling not supported for OS family '%s'." + family + ) in + fun () -> Lazy.force family + +let yum_cmd = lazy begin + if OpamSystem.resolve_command "yum" <> None then + "yum" + else if OpamSystem.resolve_command "dnf" <> None then + "dnf" + else + raise (OpamSystem.Command_not_found "yum or dnf") +end + +let packages_status packages = + let (+++) pkg set = OpamSysPkg.Set.add (OpamSysPkg.of_string pkg) set in + (* Some package managers don't permit to request on available packages. In + this case, we consider all non installed packages as [available]. *) + let open OpamSysPkg.Set.Op in + let compute_sets ?sys_available sys_installed = + let installed = packages %% sys_installed in + let available, not_found = + match sys_available with + | Some sys_available -> + let available = (packages -- installed) %% sys_available in + let not_found = packages -- installed -- available in + available, not_found + | None -> + let available = packages -- installed in + available, OpamSysPkg.Set.empty + in + available, not_found + in + let to_string_list pkgs = + OpamSysPkg.(Set.fold (fun p acc -> to_string p :: acc) pkgs []) + in + let names_re ?str_pkgs () = + let str_pkgs = + OpamStd.Option.default (to_string_list packages) str_pkgs + in + let need_escape = Re.(compile (group (set "+."))) in + Printf.sprintf "^(%s)$" + (OpamStd.List.concat_map "|" + (Re.replace ~all:true need_escape ~f:(fun g -> "\\"^Re.Group.get g 1)) + str_pkgs) + in + let with_regexp_sgl re_pkg = + List.fold_left (fun pkgs l -> + try + Re.(Group.get (exec re_pkg l) 1) +++ pkgs + with Not_found -> pkgs) OpamSysPkg.Set.empty + in + let with_regexp_dbl ~re_installed ~re_pkg = + List.fold_left (fun (inst,avail) l -> + try + let pkg = Re.(Group.get (exec re_pkg l) 1) in + if Re.execp re_installed l then + pkg +++ inst, avail + else + inst, pkg +++ avail + with Not_found -> inst, avail) + OpamSysPkg.Set.(empty, empty) + in + let package_set_of_pkgpath l = + List.fold_left (fun set pkg -> + let short_name = + match String.rindex pkg '/' with + | exception Not_found -> pkg + | idx -> String.sub pkg idx (String.length pkg - idx) + in + set + |> OpamSysPkg.Set.add (OpamSysPkg.of_string pkg) + |> OpamSysPkg.Set.add (OpamSysPkg.of_string short_name) + ) OpamSysPkg.Set.empty l + in + let compute_sets_with_virtual get_avail_w_virtuals get_installed = + let sys_available, sys_provides = get_avail_w_virtuals () in + let need_inst_check = + OpamSysPkg.Map.fold (fun cp vps set -> + if OpamSysPkg.Set.(is_empty (inter vps packages)) then set else + OpamSysPkg.Set.add cp set) + sys_provides packages + in + let str_need_inst_check = to_string_list need_inst_check in + let sys_installed = get_installed str_need_inst_check in + let sys_installed = + (* Resolve installed "provides" packages; + assumes provides are not recursive *) + OpamSysPkg.Set.fold (fun p acc -> + match OpamSysPkg.Map.find_opt p sys_provides with + | None -> acc + | Some ps -> OpamSysPkg.Set.union acc ps) + sys_installed sys_installed + in + compute_sets sys_installed ~sys_available + in + match family () with + | Alpine -> + (* Output format + >capnproto policy: + > 0.8.0-r1: + > lib/apk/db/installed + > @edgecommunity https://dl-cdn.alpinelinux.org/alpine/edge/community + >at policy: + > 3.2.1-r1: + > https://dl-cdn.alpinelinux.org/alpine/v3.13/community + >vim policy: + > 8.2.2320-r0: + > lib/apk/db/installed + > https://dl-cdn.alpinelinux.org/alpine/v3.13/main + > 8.2.2852-r0: + > @edge https://dl-cdn.alpinelinux.org/alpine/edge/main + >hwids-udev policy: + > 20201207-r0: + > https://dl-cdn.alpinelinux.org/alpine/v3.13/main + > @edge https://dl-cdn.alpinelinux.org/alpine/v3.13/main + > https://dl-cdn.alpinelinux.org/alpine/edge/main + > @edge https://dl-cdn.alpinelinux.org/alpine/edge/main + *) + let sys_installed, sys_available = + let pkg_name = + Re.(compile @@ seq + [ bol; + group @@ rep1 @@ alt [ alnum; punct ]; + space; + str "policy:"; + eol + ]) + in + let repo_name = + Re.(compile @@ seq + [ bol; + repn space 4 (Some 4); + char '@'; + group @@ rep1 @@ alt [ alnum; punct ]; + space + ]) + in + let add_pkg pkg repo installed (inst,avail) = + let pkg = match repo with Some r -> pkg^"@"^r | None -> pkg in + if installed then pkg +++ inst, avail else inst, pkg +++ avail + in + to_string_list packages + |> List.map (fun s -> + match OpamStd.String.cut_at s '@' with + | Some (pkg, _repo) -> pkg + | None -> s) + |> (fun l -> run_query_command "apk" ("policy"::l)) + |> List.fold_left (fun (pkg, installed, instavail) l -> + try (* package name *) + Re.(Group.get (exec pkg_name l) 1), false, instavail + with Not_found -> + if l.[2] != ' ' then (* only version field is after two spaces *) + pkg, false, instavail + else if l = " lib/apk/db/installed" then + (* from https://git.alpinelinux.org/apk-tools/tree/src/database.c#n58 *) + pkg, true, instavail + else (* repo (tagged and non-tagged) *) + let repo = + try Some Re.(Group.get (exec repo_name l) 1) + with Not_found -> None + in + pkg, installed, add_pkg pkg repo installed instavail) + ("", false, OpamSysPkg.Set.(empty, empty)) + |> (fun (_,_, instavail) -> instavail) + in + compute_sets sys_installed ~sys_available + | Arch -> + let get_avail_w_virtuals () = + let package_provided str = + OpamSysPkg.of_string + (match OpamStd.String.cut_at str '=' with + | None -> str + | Some (p, _vc) -> p) + in + (* Output format: + >Repository : core + >Name : python + >Version : 3.9.6-1 + >Description : Next generation of the python high-level scripting language + >Architecture : x86_64 + >URL : https://www.python.org/ + >Licenses : custom + >Groups : None + >Provides : python3 + >Depends On : bzip2 expat gdbm libffi libnsl libxcrypt openssl + >Optional Deps : python-setuptools + > python-pip + >[...] + + Format partially described in https://archlinux.org/pacman/PKGBUILD.5.html + *) + (* Discard stderr to not have it pollute output. Plus, exit code is the + number of packages not found. *) + run_command ~discard_err:true "pacman" ["-Si"] + |> snd + |> List.fold_left (fun (avail, provides, latest) l -> + match OpamStd.String.split l ' ' with + | "Name"::":"::p::_ -> + p +++ avail, provides, Some (OpamSysPkg.of_string p) + | "Provides"::":"::"None"::[] -> avail, provides, latest + | "Provides"::":"::pkgs -> + let ps = OpamSysPkg.Set.of_list (List.map package_provided pkgs) in + let provides = + match latest with + | Some p -> OpamSysPkg.Map.add p ps provides + | None -> provides (* Bad pacman output ?? *) + in + ps ++ avail, provides, None + | _ -> avail, provides, latest) + (OpamSysPkg.Set.empty, OpamSysPkg.Map.empty, None) + |> (fun (a,p,_) -> a,p) + in + let get_installed str_pkgs = + (* output: + >extra/cmake 3.17.1-1 [installed] + > A cross-platform open-source make system + >extra/cmark 0.29.0-1 + > CommonMark parsing and rendering library and program in C + *) + let re_pkg = + Re.(compile @@ seq + [ bol; + rep1 @@ alt [alnum; punct]; + char '/'; + group @@ rep1 @@ alt [alnum; punct]; + space; + ]) + in + run_query_command "pacman" ["-Qs" ; names_re ~str_pkgs ()] + |> with_regexp_sgl re_pkg + in + compute_sets_with_virtual get_avail_w_virtuals get_installed + | Centos -> + (* Output format: + >crypto-policies + >python3-pip-wheel + *) + let sys_installed = + run_query_command "rpm" ["-qa"; "--qf"; "%{NAME}\\n"] + |> List.map OpamSysPkg.of_string + |> OpamSysPkg.Set.of_list + in + compute_sets sys_installed + | Debian -> + let get_avail_w_virtuals () = + let provides_sep = Re.(compile @@ str ", ") in + let package_provided str = + OpamSysPkg.of_string + (match OpamStd.String.cut_at str ' ' with + | None -> str + | Some (p, _vc) -> p) + in + (* Output format: + >Package: apt + >Version: 2.1.7 + >Installed-Size: 4136 + >Maintainer: APT Development Team + >Architecture: amd64 + >Replaces: apt-transport-https (<< 1.5~alpha4~), apt-utils (<< 1.3~exp2~) + >Provides: apt-transport-https (= 2.1.7) + > [...] + > + The `Provides' field contains provided virtual package(s) by current + `Package:'. + * manpages.debian.org/buster/apt/apt-cache.8.en.html + * www.debian.org/doc/debian-policy/ch-relationships.html#s-virtual + *) + run_query_command "apt-cache" + ["search"; names_re (); "--names-only"; "--full"] + |> List.fold_left (fun (avail, provides, latest) l -> + if OpamStd.String.starts_with ~prefix:"Package: " l then + let p = String.sub l 9 (String.length l - 9) in + p +++ avail, provides, Some (OpamSysPkg.of_string p) + else if OpamStd.String.starts_with ~prefix:"Provides: " l then + let ps = + List.map package_provided (Re.split ~pos:10 provides_sep l) + |> OpamSysPkg.Set.of_list + in + avail ++ ps, + (match latest with + | Some p -> OpamSysPkg.Map.add p ps provides + | None -> provides (* Bad apt-cache output ?? *)), + None + else avail, provides, latest) + (OpamSysPkg.Set.empty, OpamSysPkg.Map.empty, None) + |> (fun (a,p,_) -> a,p) + in + let get_installed str_pkgs = + (* ouput: + >ii uim-gtk3 1:1.8.8-6.1 amd64 Universal ... + >ii uim-gtk3-immodule:amd64 1:1.8.8-6.1 amd64 Universal ... + *) + let re_pkg = + Re.(compile @@ seq + [ bol; + str "ii"; + rep1 @@ space; + group @@ rep1 @@ diff (alt [alnum; punct]) (char ':'); + (* pkg:arch convention *) + ]) + in + (* discard stderr as just nagging *) + run_command ~discard_err:true "dpkg-query" ("-l" :: str_pkgs) + |> snd + |> with_regexp_sgl re_pkg + in + compute_sets_with_virtual get_avail_w_virtuals get_installed + | Freebsd -> + let sys_installed = + run_query_command "pkg" ["query"; "%n\n%o"] + |> List.map OpamSysPkg.of_string + |> OpamSysPkg.Set.of_list + in + compute_sets sys_installed + | Gentoo -> + let sys_installed = + let re_pkg = + Re.(compile @@ seq + [ group @@ rep1 @@ alt [alnum; punct]; + char '-'; + rep @@ seq [rep1 digit; char '.']; + rep1 digit; + rep any; + eol ]) + in + List.fold_left (fun inst dir -> + List.fold_left (fun inst pkg -> + let to_string d = + OpamFilename.basename_dir d + |> OpamFilename.Base.to_string + in + let pkg = Filename.concat (to_string dir) (to_string pkg) in + try Re.(Group.get (exec re_pkg pkg) 1) :: inst + with Not_found -> inst + ) inst (OpamFilename.dirs dir)) + [] + (OpamFilename.dirs (OpamFilename.Dir.of_string "/var/db/pkg")) + |> package_set_of_pkgpath + in + compute_sets sys_installed + | Homebrew -> + (* accept 'pkgname' and 'pkgname@version' + exampe output + >openssl@1.1 + >bmake + >koekeishiya/formulae/skhd + *) + let sys_installed = + run_query_command "brew" ["list"; "--full-name"] + |> List.fold_left (fun res s -> + List.fold_left (fun res spkg -> + let parse_fullname pkg = + match List.rev (OpamStd.String.split pkg '/') with + | [] -> [] + | [pkg] -> [pkg] + | simple_name::_ -> [pkg; simple_name] + in + match OpamStd.String.cut_at spkg '@' with + | Some (n,_v) -> + parse_fullname n + @ parse_fullname spkg + @ res + | None -> parse_fullname spkg @ res) + res (OpamStd.String.split s ' ')) [] + |> List.map OpamSysPkg.of_string + |> OpamSysPkg.Set.of_list + in + compute_sets sys_installed + | Macports -> + let variants_map, packages = + OpamSysPkg.(Set.fold (fun spkg (map, set) -> + match OpamStd.String.cut_at (to_string spkg) ' ' with + | Some (pkg, variant) -> + OpamStd.String.Map.add pkg variant map, + pkg +++ set + | None -> map, Set.add spkg set) + packages (OpamStd.String.Map.empty, Set.empty)) + in + let str_pkgs = to_string_list packages in + let sys_installed = + (* output: + > zlib @1.2.11_0 (active) + > gtk3 @3.24.21_0+quartz (active) + *) + let re_pkg = + Re.(compile @@ seq + [ bol; + rep space; + group @@ rep1 @@ alt [alnum; punct]; + rep1 space; + char '@'; + rep1 @@ diff any (char '+'); + opt @@ group @@ rep1 @@ alt [alnum; punct]; + rep1 space; + str "(active)"; + eol + ]) + in + run_query_command "port" ("installed" :: str_pkgs) + |> (function _::lines -> lines | _ -> []) + |> List.fold_left (fun pkgs l -> + try + let pkg = Re.(Group.get (exec re_pkg l) 1) in + (* variant handling *) + match OpamStd.String.Map.find_opt pkg variants_map with + | Some variant -> + (try + if Re.(Group.get (exec re_pkg l) 2) = variant then + (pkg ^ " " ^ variant) +++ pkgs + else pkgs + with Not_found -> pkgs) + | None -> pkg +++ pkgs + with Not_found -> pkgs) + OpamSysPkg.Set.empty + in + let sys_available = + (* example output + >diffutils 3.7 sysutils textproc devel GNU diff utilities + >-- + >No match for gcc found + *) + let re_pkg = + Re.(compile @@ seq + [ bol; + group @@ rep1 @@ alt [alnum; punct]; + rep1 space; + rep1 @@ alt [digit; punct]; + ]) + in + let avail = + run_query_command "port" + [ "search"; "--line"; "--regex"; names_re ~str_pkgs () ] + |> with_regexp_sgl re_pkg + in + (* variants handling *) + let variants = + OpamStd.String.Map.filter + (fun p _ -> OpamSysPkg.Set.mem (OpamSysPkg.of_string p) avail) + variants_map + |> OpamStd.String.Map.keys + in + run_query_command "port" ([ "info"; "--name"; "--variants" ] @ variants) + |> List.fold_left (fun (prec, avail) l -> + match prec, OpamStd.String.split l ' ' with + | _, "name:"::pkg::[] -> Some pkg, avail + | Some pkg, "variants:"::variants -> + None, + List.fold_left (fun avail v -> + (pkg ^ " +" ^ (OpamStd.String.remove_suffix ~suffix:"," v)) + +++ avail) avail variants + | _ -> None, avail + ) (None, avail) + |> snd + in + compute_sets sys_installed ~sys_available + | Netbsd -> + let sys_installed = + run_query_command "pkg_info" ["-Q"; "PKGPATH"; "-a"] + |> package_set_of_pkgpath + in + compute_sets sys_installed + | Openbsd -> + let sys_installed = + run_query_command "pkg_info" ["-mqP"] + |> package_set_of_pkgpath + in + compute_sets sys_installed + | Suse -> + (* get the second column of the table: + zypper --quiet se -i -t package|grep '^i '|awk -F'|' '{print $2}'|xargs echo + output: + >S | Name | Summary + >--+-----------------------------+------------- + > | go-gosqlite | Trivial SQLi + >i | libqt4-sql-sqlite-32bit | Qt 4 sqlite + *) + let re_pkg = + Re.(compile @@ seq + [ bol; + rep1 any; + char '|'; + rep1 space; + group @@ rep1 @@ alt [alnum; punct]; + rep1 space; + char '|'; + ]) + in + let re_installed = Re.(compile @@ seq [bol ; char 'i']) in + let sys_installed, sys_available = + run_query_command "zypper" ["--quiet"; "se"; "-t"; "package"] + |> with_regexp_dbl ~re_installed ~re_pkg + in + compute_sets sys_installed ~sys_available + +(* Install *) + +let install_packages_commands_t sys_packages = + let unsafe_yes = OpamCoreConfig.answer_is `unsafe_yes in + let yes ?(no=[]) yes r = + if unsafe_yes then + yes @ r else no @ r + in + let packages = + List.map OpamSysPkg.to_string (OpamSysPkg.Set.elements sys_packages) + in + match family () with + | Alpine -> ["apk", "add"::yes ~no:["-i"] [] packages], None + | Arch -> ["pacman", "-Su"::yes ["--noconfirm"] packages], None + | Centos -> + (* TODO: check if they all declare "rhel" as primary family *) + (* Kate's answer: no they don't :( (e.g. Fedora, Oraclelinux define Nothing and "fedora" respectively) *) + (* When opam-packages specify the epel-release package, usually it + means that other dependencies require the EPEL repository to be + already setup when yum-install is called. Cf. opam-depext/#70,#76. *) + let epel_release = "epel-release" in + let install_epel rest = + if List.mem epel_release packages then + [Lazy.force yum_cmd, "install"::yes ["-y"] [epel_release]] @ rest + else rest + in + install_epel + [Lazy.force yum_cmd, "install"::yes ["-y"] + (OpamStd.String.Set.of_list packages + |> OpamStd.String.Set.remove epel_release + |> OpamStd.String.Set.elements); + "rpm", "-q"::"--whatprovides"::packages], None + | Debian -> ["apt-get", "install"::yes ["-qq"; "-yy"] packages], + (if unsafe_yes then Some ["DEBIAN_FRONTEND", "noninteractive"] else None) + | Freebsd -> ["pkg", "install"::yes ["-y"] packages], None + | Gentoo -> ["emerge", yes ~no:["-a"] [] packages], None + | Homebrew -> + ["brew", "install"::packages], (* NOTE: Does not have any interactive mode *) + Some (["HOMEBREW_NO_AUTO_UPDATE","yes"]) + | Macports -> + let packages = (* Separate variants from their packages *) + List.map (fun p -> OpamStd.String.split p ' ') packages + |> List.flatten + in + ["port", yes ["-N"] ("install"::packages)], + None + | Netbsd -> ["pkgin", yes ["-y"] ("install" :: packages)], None + | Openbsd -> ["pkg_add", yes ~no:["-i"] ["-I"] packages], None + | Suse -> ["zypper", yes ["--non-interactive"] ("install"::packages)], None + +let install_packages_commands sys_packages = + fst (install_packages_commands_t sys_packages) + +let sudo_run_command ?vars cmd args = + let cmd, args = + let not_root = Unix.getuid () <> 0 in + match OpamSysPoll.os (), OpamSysPoll.os_distribution () with + | Some "openbsd", _ when not_root -> + "doas", cmd::args + | Some ("linux" | "unix" | "freebsd" | "netbsd" | "dragonfly"), _ + | Some "macos", Some "macports" when not_root -> + if OpamSystem.resolve_command "sudo" = None then + "su", + ["root"; "-c"; Printf.sprintf "%S" (String.concat " " (cmd::args))] + else + "sudo", cmd::args + | _ -> cmd, args + in + match run_command_exit_code ?vars ~allow_stdin:true ~verbose:true cmd args with + | 0 -> () + | code -> + Printf.ksprintf failwith + "failed with exit code %d at command:\n %s" + code (String.concat " " (cmd::args)) + +let install packages = + if OpamSysPkg.Set.is_empty packages then + log "Nothing to install" + else + let commands, vars = install_packages_commands_t packages in + let vars = OpamStd.Option.map (List.map (fun x -> `add, x)) vars in + List.iter + (fun (cmd, args) -> + try sudo_run_command ?vars cmd args + with Failure msg -> failwith ("System package install " ^ msg)) + commands + +let update () = + let cmd = + match family () with + | Alpine -> Some ("apk", ["update"]) + | Arch -> Some ("pacman", ["-Sy"]) + | Centos -> Some (Lazy.force yum_cmd, ["makecache"]) + | Debian -> Some ("apt-get", ["update"]) + | Gentoo -> Some ("emerge", ["--sync"]) + | Homebrew -> Some ("brew", ["update"]) + | Macports -> Some ("port", ["sync"]) + | Suse -> Some ("zypper", ["--non-interactive"; "update"]) + | Freebsd | Netbsd | Openbsd -> + None + in + match cmd with + | None -> + OpamConsole.warning + "Unknown update command for %s, skipping system update" + OpamStd.Option.Op.(OpamSysPoll.os_family () +! "unknown") + | Some (cmd, args) -> + try sudo_run_command cmd args + with Failure msg -> failwith ("System package update " ^ msg) + +let repo_enablers () = + if family () <> Centos then None else + let (needed, _) = + packages_status (OpamSysPkg.raw_set + (OpamStd.String.Set.singleton "epel-release")) + in + if OpamSysPkg.Set.is_empty needed then None + else + Some + "On CentOS/RHEL, many packages may assume that the Extra Packages for \ + Enterprise Linux (EPEL) repository has been enabled. \ + This is typically done by installing the 'epel-release' package. \ + Please see https://fedoraproject.org/wiki/EPEL for more information" diff -Nru opam-2.0.10/src/state/opamSysInteract.mli opam-2.1.2/src/state/opamSysInteract.mli --- opam-2.0.10/src/state/opamSysInteract.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/state/opamSysInteract.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,31 @@ +(**************************************************************************) +(* *) +(* Copyright 2019-2020 OCamlPro *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(* Given a list of system packages, retrieve their installation status from the + system and returns a pair of [sys_package] set: + * first one is available set: package that exist on the default + repositories, but not installed) + * second one, not found set: packages not found on the defined repositories +*) +val packages_status: + OpamSysPkg.Set.t -> OpamSysPkg.Set.t * OpamSysPkg.Set.t + +(* Return the commands to run to install given system packages *) +val install_packages_commands: OpamSysPkg.Set.t -> (string * string list) list + +(* Install given system packages, by calling local system package manager *) +val install: OpamSysPkg.Set.t -> unit + +val update: unit -> unit + +(* Determine if special packages may need installing to enable other + repositories. + Presently used to check for epel-release on CentOS and RHEL. *) +val repo_enablers : unit -> string option diff -Nru opam-2.0.10/src/state/opamSysPoll.ml opam-2.1.2/src/state/opamSysPoll.ml --- opam-2.0.10/src/state/opamSysPoll.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamSysPoll.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -158,3 +158,14 @@ "os-version", os_version_lazy; "os-family", os_family_lazy; ] + +let cores_lazy = lazy (OpamSystem.cpu_count ()) +let cores () = Lazy.force cores_lazy + +let to_string () = + let open OpamStd.Option.Op in + Printf.sprintf "arch=%s os=%s os-distribution=%s os-version=%s" + (arch () +! "unknown") + (os () +! "unknown") + (os_distribution () +! "unknown") + (os_version () +! "unknown") diff -Nru opam-2.0.10/src/state/opamSysPoll.mli opam-2.1.2/src/state/opamSysPoll.mli --- opam-2.0.10/src/state/opamSysPoll.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamSysPoll.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2017 OCamlPro *) +(* Copyright 2017-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -28,3 +28,10 @@ its input lowercased if not a recognised OS). This is typically called on the output of [uname -s] *) val normalise_os: string -> string + +(* Number of cores *) +val cores: unit -> int + +(** Returns a string containing arch, os, os-distribution & os-version values, + unknown if they are not available *) +val to_string: unit -> string diff -Nru opam-2.0.10/src/state/opamUpdate.ml opam-2.1.2/src/state/opamUpdate.ml --- opam-2.0.10/src/state/opamUpdate.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamUpdate.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -18,11 +18,10 @@ let log fmt = OpamConsole.log "UPDATE" fmt let slog = OpamConsole.slog -let eval_redirect gt repo = +let eval_redirect gt repo repo_root = if repo.repo_url.OpamUrl.backend <> `http then None else let redirect = - repo.repo_root - |> OpamRepositoryPath.repo + OpamRepositoryPath.repo repo_root |> OpamFile.Repo.safe_read |> OpamFile.Repo.redirect in @@ -36,15 +35,23 @@ | (redirect, f) :: _ -> let redirect_url = if OpamStd.String.contains ~sub:"://" redirect - then OpamUrl.of_string redirect - else OpamUrl.Op.(repo.repo_url / redirect) + then + let red = OpamUrl.parse_opt ~handle_suffix:false redirect in + if red = None then + OpamConsole.error "Ignoring malformed redirection url %s" redirect; + red + else Some OpamUrl.Op.(repo.repo_url / redirect) in - if redirect_url = repo.repo_url then None - else Some (redirect_url, f) + match redirect_url with + | Some ru when ru = repo.repo_url -> None + | Some ru -> Some (ru, f) + | None -> None -let repository gt repo = +let repository rt repo = let max_loop = 10 in + let gt = rt.repos_global in if repo.repo_url = OpamUrl.empty then Done (fun rt -> rt) else + let repo_root = OpamRepositoryState.get_repo_root rt repo in (* Recursively traverse redirection links, but stop after 10 steps or if we cycle back to the initial repo. *) let rec job r n = @@ -59,15 +66,15 @@ OpamUrl.(string_of_backend repo.repo_url.backend) in OpamProcess.Job.with_text text @@ - OpamRepository.update r @@+ fun () -> + OpamRepository.update r repo_root @@+ fun () -> if n <> max_loop && r = repo then (OpamConsole.warning "%s: Cyclic redirections, stopping." (OpamRepositoryName.to_string repo.repo_name); Done r) - else match eval_redirect gt r with + else match eval_redirect gt r repo_root with | None -> Done r | Some (new_url, f) -> - OpamFilename.cleandir repo.repo_root; + OpamFilename.cleandir repo_root; let reason = match f with | None -> "" | Some f -> Printf.sprintf " (%s)" (OpamFilter.to_string f) in @@ -80,7 +87,7 @@ job { r with repo_url = new_url } (n-1) in job repo max_loop @@+ fun repo -> - let repo_file_path = OpamRepositoryPath.repo repo.repo_root in + let repo_file_path = OpamRepositoryPath.repo repo_root in if not (OpamFile.exists repo_file_path) then OpamConsole.warning "The repository '%s' at %s doesn't have a 'repo' file, and might not be \ @@ -90,8 +97,9 @@ let repo_file = OpamFile.Repo.safe_read repo_file_path in let repo_file = OpamFile.Repo.with_root_url repo.repo_url repo_file in let repo_vers = - OpamStd.Option.default OpamVersion.current_nopatch @@ - OpamFile.Repo.opam_version repo_file in + OpamStd.Option.default OpamFile.Repo.format_version @@ + OpamFile.Repo.opam_version repo_file + in if not OpamFormatConfig.(!r.skip_version_checks) && OpamVersion.compare repo_vers OpamVersion.current > 0 then Printf.ksprintf failwith @@ -107,20 +115,37 @@ (OpamConsole.colorise `bold (OpamUrl.to_string repo.repo_url)) msg) (OpamFile.Repo.announce repo_file); - let opams = OpamRepositoryState.load_repo_opams repo in - Done ( - (* Return an update function to make parallel execution possible *) - fun rt -> - { rt with - repositories = - OpamRepositoryName.Map.add repo.repo_name repo rt.repositories; - repos_definitions = - OpamRepositoryName.Map.add repo.repo_name repo_file - rt.repos_definitions; - repo_opams = - OpamRepositoryName.Map.add repo.repo_name opams rt.repo_opams; - } - ) + OpamFilename.make_tar_gz_job + (OpamRepositoryPath.tar gt.root repo.repo_name) + repo_root + @@+ function + | Some e -> + OpamStd.Exn.fatal e; + Printf.ksprintf failwith + "Failed to regenerate local repository archive: %s" + (Printexc.to_string e) + | None -> + let opams = + OpamRepositoryState.load_opams_from_dir repo.repo_name repo_root + in + let local_dir = OpamRepositoryPath.root gt.root repo.repo_name in + if OpamFilename.exists_dir local_dir then + (* Mark the obsolete local directory for deletion once we complete: it's + no longer needed once we have a tar.gz *) + Hashtbl.add rt.repos_tmp repo.repo_name (lazy local_dir); + Done ( + (* Return an update function to make parallel execution possible *) + fun rt -> + { rt with + repositories = + OpamRepositoryName.Map.add repo.repo_name repo rt.repositories; + repos_definitions = + OpamRepositoryName.Map.add repo.repo_name repo_file + rt.repos_definitions; + repo_opams = + OpamRepositoryName.Map.add repo.repo_name opams rt.repo_opams; + } + ) let repositories rt repos = let command repo = @@ -131,7 +156,7 @@ (OpamRepositoryName.to_string repo.repo_name) (match ex with Failure s -> s | ex -> Printexc.to_string ex); Done ([repo], fun t -> t)) @@ - fun () -> repository rt.repos_global repo @@| + fun () -> repository rt repo @@| fun f -> [], f in let failed, rt_update = @@ -148,14 +173,16 @@ OpamRepositoryState.Cache.save rt; failed, rt -let fetch_dev_package url srcdir ?(working_dir=false) nv = +let fetch_dev_package url srcdir ?(working_dir=false) ?subpath nv = let remote_url = OpamFile.URL.url url in let mirrors = remote_url :: OpamFile.URL.mirrors url in let checksum = OpamFile.URL.checksum url in - log "updating %a" (slog OpamUrl.to_string) remote_url; + log "updating %a%a" (slog OpamUrl.to_string) remote_url + (slog (OpamStd.Option.map_default (fun s -> " ("^s^")") "")) subpath; OpamRepository.pull_tree ~cache_dir:(OpamRepositoryPath.download_cache OpamStateConfig.(!r.root_dir)) - (OpamPackage.to_string nv) srcdir checksum ~working_dir mirrors + (OpamPackage.to_string nv) srcdir checksum ~working_dir ?subpath mirrors + @@| OpamRepository.report_fetch_result nv let pinned_package st ?version ?(working_dir=false) name = log "update-pinned-package %s%a" (OpamPackage.Name.to_string name) @@ -168,6 +195,7 @@ | None | Some (_, None) -> Done ((fun st -> st), false) | Some (opam, Some urlf) -> let url = OpamFile.URL.url urlf in + let subpath = OpamFile.URL.subpath urlf in let version = OpamFile.OPAM.version_opt opam ++ version +! @@ -183,8 +211,13 @@ then OpamFileTools.add_aux_files ~files_subdir_hashes:true opam else opam in + (* append subpath to source dir to retrieve opam files *) + let srcdir_find = + OpamStd.Option.map_default + (fun x -> OpamFilename.Op.(srcdir / x)) srcdir subpath + in let old_source_opam_hash, old_source_opam = - match OpamPinned.find_opam_file_in_source name srcdir with + match OpamPinned.find_opam_file_in_source name srcdir_find with | None -> None, None | Some f -> Some (OpamHash.compute (OpamFile.to_string f)), @@ -212,16 +245,17 @@ | Some h -> OpamRepository.current_branch url @@| fun branch -> branch = Some h) @@+ function false -> Done () | true -> - OpamRepository.is_dirty url + OpamRepository.is_dirty ?subpath url @@| function false -> () | true -> OpamConsole.note - "Ignoring uncommitted changes in %s (`--working-dir' not active)." - url.OpamUrl.path) + "Ignoring uncommitted changes in %s%s (`--working-dir' not active)." + url.OpamUrl.path + (match subpath with None -> "" | Some s -> "/" ^ s)) @@+ fun () -> (* Do the update *) - fetch_dev_package urlf srcdir ~working_dir nv @@+ fun result -> + fetch_dev_package urlf srcdir ~working_dir ?subpath nv @@+ fun result -> let new_source_opam = - OpamPinned.find_opam_file_in_source name srcdir >>= fun f -> + OpamPinned.find_opam_file_in_source name srcdir_find >>= fun f -> let warns, opam_opt = OpamFileTools.lint_file f in let warns, opam_opt = match opam_opt with | Some opam0 -> @@ -293,13 +327,15 @@ else OpamConsole.warning "Ignoring file %s with invalid hash" (OpamFilename.to_string file)) - (OpamFile.OPAM.get_extra_files opam); + (OpamFile.OPAM.get_extra_files + ~repos_roots:(OpamRepositoryState.get_root st.switch_repos) + opam); OpamFile.OPAM.write opam_file (OpamFile.OPAM.with_extra_files_opt None opam); opam in match result, new_source_opam with - | Result (), Some new_opam + | Result _, Some new_opam when changed_opam old_source_opam new_source_opam && changed_opam overlay_opam new_source_opam -> log "Metadata from the package source of %s changed" @@ -339,7 +375,7 @@ Done (interactive_part, true) | (Up_to_date _ | Not_available _), _ -> Done ((fun st -> st), false) - | Result (), Some new_opam + | Result _, Some new_opam when not (changed_opam old_source_opam overlay_opam) -> (* The new opam is not _effectively_ different from the old, so no need to confirm, but use it still (e.g. descr may have changed) *) @@ -347,7 +383,7 @@ Done ((fun st -> {st with opams = OpamPackage.Map.add nv opam st.opams}), true) - | Result (), _ -> + | Result _, _ -> Done ((fun st -> st), true) let dev_package st ?working_dir nv = @@ -423,7 +459,7 @@ in let st_update, updates = OpamParallel.reduce - ~jobs:(OpamFile.Config.jobs st.switch_global.config) + ~jobs:(Lazy.force OpamStateConfig.(!r.jobs)) ~command ~merge ~nil:((fun st -> st), OpamPackage.Name.Set.empty) @@ -452,10 +488,14 @@ | None -> OpamSystem.internal_error "repo file of unknown origin" | Some u -> u in - List.map (fun rel -> + OpamStd.List.filter_map (fun rel -> if OpamStd.String.contains ~sub:"://" rel - then OpamUrl.of_string rel - else OpamUrl.Op.(root_url / rel)) + then + let r = OpamUrl.parse_opt ~handle_suffix:false rel in + if r = None then + OpamConsole.warning "Invalid cache url %s, skipping" rel; + r + else Some OpamUrl.Op.(root_url / rel)) (OpamFile.Repo.dl_cache repo_def) in repo_cache @ global_cache @@ -467,7 +507,16 @@ { u with OpamUrl.hash = None } in let url_remote opam = OpamFile.OPAM.url opam >>| base_url in - if url_remote new_opam <> (old_opam_opt >>= url_remote) then + let new_opam_o = url_remote new_opam in + let old_opam_o = old_opam_opt >>= url_remote in + let backend u = u.OpamUrl.backend in + let clean = + match new_opam_o >>| backend, old_opam_o >>| backend with + | Some #OpamUrl.version_control, (Some #OpamUrl.version_control | None) -> + false + | _ -> new_opam_o <> old_opam_o + in + if clean then OpamFilename.rmdir (OpamSwitchState.source_dir st (OpamFile.OPAM.package new_opam)) @@ -480,26 +529,28 @@ match OpamFile.OPAM.url opam with | None -> Done None | Some u -> - OpamRepository.pull_tree (OpamPackage.to_string nv) - ~cache_dir ~cache_urls + (OpamRepository.pull_tree (OpamPackage.to_string nv) + ~cache_dir ~cache_urls ?subpath:(OpamFile.URL.subpath u) dirname (OpamFile.URL.checksum u) - (OpamFile.URL.url u :: OpamFile.URL.mirrors u) - @@| OpamStd.Option.some + (OpamFile.URL.url u :: OpamFile.URL.mirrors u)) + @@| fun r -> Some r in let fetch_extra_source_job (name, u) = function - | Some (Not_available _) as err -> Done err + | (_, Not_available _) :: _ as err -> Done err | ret -> - OpamRepository.pull_file_to_cache - (OpamPackage.to_string nv ^"/"^ OpamFilename.Base.to_string name) - ~cache_dir ~cache_urls - (OpamFile.URL.checksum u) - (OpamFile.URL.url u :: OpamFile.URL.mirrors u) - @@| function - | Not_available _ as na -> Some na - | _ -> ret - in - fetch_source_job @@+ - OpamProcess.Job.seq - (List.map fetch_extra_source_job - (OpamFile.OPAM.extra_sources opam)) + (OpamRepository.pull_file_to_cache + (OpamPackage.to_string nv ^"/"^ OpamFilename.Base.to_string name) + ~cache_dir ~cache_urls + (OpamFile.URL.checksum u) + (OpamFile.URL.url u :: OpamFile.URL.mirrors u)) + @@| fun r -> (OpamFilename.Base.to_string name, r) :: ret + in + fetch_source_job @@+ function + | Some (Not_available _) as r -> Done (r, []) + | r -> + OpamProcess.Job.seq + (List.map fetch_extra_source_job + (OpamFile.OPAM.extra_sources opam)) + [] + @@| fun r1 -> r, r1 diff -Nru opam-2.0.10/src/state/opamUpdate.mli opam-2.1.2/src/state/opamUpdate.mli --- opam-2.0.10/src/state/opamUpdate.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/opamUpdate.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -72,10 +72,15 @@ (** Download or synchronise the upstream source for the given package into the given directory. Also places all of the package extra files (that have a known hash) into the cache. For non-VC remotes, verifies the checksum if - any *) + any. + + Stops on first error. The extra downloads list is reverted, so that the + error is always first if any. + + Does not print the results as it used to. *) val download_package_source: 'a switch_state -> package -> dirname -> - unit download option OpamProcess.job + (string download option * (string * string download) list) OpamProcess.job (** [cleanup_source old_opam_option new_opam] checks if the remote URL has changed between [old_opam_option] and [new_opam], and, depending on that, @@ -86,5 +91,5 @@ (** Low-level function to retrieve the package source into its local cache *) val fetch_dev_package: - OpamFile.URL.t -> dirname -> ?working_dir:bool -> package -> + OpamFile.URL.t -> dirname -> ?working_dir:bool -> ?subpath:string -> package -> unit download OpamProcess.job diff -Nru opam-2.0.10/src/state/shellscripts/complete.sh opam-2.1.2/src/state/shellscripts/complete.sh --- opam-2.0.10/src/state/shellscripts/complete.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/shellscripts/complete.sh 2021-12-07 16:09:27.000000000 +0000 @@ -33,7 +33,7 @@ _opam_vars() { - opam config list --safe 2>/dev/null | \ + opam var --safe 2>/dev/null | \ sed -n \ -e '/^PKG:/d' \ -e 's%^\([^#= ][^ ]*\).*%\1%p' diff -Nru opam-2.0.10/src/state/shellscripts/complete.zsh opam-2.1.2/src/state/shellscripts/complete.zsh --- opam-2.0.10/src/state/shellscripts/complete.zsh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/shellscripts/complete.zsh 2021-12-07 16:09:27.000000000 +0000 @@ -35,7 +35,7 @@ _opam_vars() { - opam config list --safe 2>/dev/null | \ + opam var -safe 2>/dev/null | \ sed -n \ -e '/^PKG:/d' \ -e 's%^\([^#= ][^ ]*\).*%\1%p' diff -Nru opam-2.0.10/src/state/shellscripts/env_hook.sh opam-2.1.2/src/state/shellscripts/env_hook.sh --- opam-2.0.10/src/state/shellscripts/env_hook.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/shellscripts/env_hook.sh 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ _opam_env_hook() { local previous_exit_status=$?; - eval $(opam env --shell=bash --readonly 2> /dev/null); + eval $(opam env --shell=bash --readonly 2> /dev/null <&- ); return $previous_exit_status; }; if ! [[ "$PROMPT_COMMAND" =~ _opam_env_hook ]]; then diff -Nru opam-2.0.10/src/state/shellscripts/env_hook.zsh opam-2.1.2/src/state/shellscripts/env_hook.zsh --- opam-2.0.10/src/state/shellscripts/env_hook.zsh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/state/shellscripts/env_hook.zsh 2021-12-07 16:09:27.000000000 +0000 @@ -1,5 +1,5 @@ _opam_env_hook() { - eval $(opam env --shell=zsh --readonly 2> /dev/null); + eval $(opam env --shell=zsh --readonly 2> /dev/null <&-); } typeset -ag precmd_functions; if [[ -z ${precmd_functions[(r)_opam_env_hook]} ]]; then diff -Nru opam-2.0.10/src/stubs/dune opam-2.1.2/src/stubs/dune --- opam-2.0.10/src/stubs/dune 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/dune 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,4 @@ +(rule + (targets c-flags.sexp) + (mode fallback) + (action (with-stdout-to %{targets} (echo "()")))) diff -Nru opam-2.0.10/src/stubs/dune-win32 opam-2.1.2/src/stubs/dune-win32 --- opam-2.0.10/src/stubs/dune-win32 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/stubs/dune-win32 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -(library - (name opam_stubs) - (public_name opam-core.stubs) - (synopsis "OCaml Package Manager C stubs") - (libraries unix) - (flags (:standard - (:include ../ocaml-flags-standard.sexp) - (:include ../ocaml-context-flags.sexp))) - (c_names opamInject opamWindows) - (c_flags (:standard - (:include c-flags.sexp))) - (c_library_flags (:standard - (:include c-libraries.sexp))) - (wrapped false)) - -(rule - (with-stdout-to c-flags.sexp (run ocaml %{dep:../../shell/subst_var.ml} CONF_CFLAGS "" %{dep:c-flags.sexp.in}))) - -(rule - (with-stdout-to c-libraries.sexp (run ocaml %{dep:../../shell/context_flags.ml} clibs))) diff -Nru opam-2.0.10/src/stubs/libacl/c-libraries.sexp.in opam-2.1.2/src/stubs/libacl/c-libraries.sexp.in --- opam-2.0.10/src/stubs/libacl/c-libraries.sexp.in 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/libacl/c-libraries.sexp.in 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +(@CONF_LIBACL_LINK@) diff -Nru opam-2.0.10/src/stubs/libacl/dune-libacl opam-2.1.2/src/stubs/libacl/dune-libacl --- opam-2.0.10/src/stubs/libacl/dune-libacl 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/libacl/dune-libacl 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,19 @@ +(library + (name opam_stubs_libacl) + (public_name opam-core.libacl) + (synopsis "OCaml Package Manager libacl C stubs") + (flags (:standard + (:include ../../ocaml-flags-standard.sexp) + (:include ../../ocaml-flags-configure.sexp) + (:include ../../ocaml-context-flags.sexp))) + (modules opamlibACL) + (c_names opamACL) + (c_flags (:standard + (:include ../c-flags.sexp))) + (c_library_flags (:include c-libraries.sexp)) + (wrapped false)) + +(rule + (targets c-libraries.sexp) + (mode fallback) + (action (with-stdout-to %{targets} (echo "()")))) diff -Nru opam-2.0.10/src/stubs/libacl/opamACL.c opam-2.1.2/src/stubs/libacl/opamACL.c --- opam-2.0.10/src/stubs/libacl/opamACL.c 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/libacl/opamACL.c 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,128 @@ +/**************************************************************************/ +/* */ +/* Copyright 2020 David Allsopp Ltd. */ +/* */ +/* All rights reserved. This file is distributed under the terms of the */ +/* GNU Lesser General Public License version 2.1, with the special */ +/* exception on linking described in the file LICENSE. */ +/* */ +/**************************************************************************/ + +#include +#include +#include + +#define CAML_NAME_SPACE +#include +#include +#include + +#ifndef Val_none +#define Val_none Val_int(0) +#endif + +/* OPAM_get_acl_executable_info(file, owner) takes a filename and the uid of + * the file's owner (this saves a call to stat on both the OCaml and C sides). + * The result is: + * None - the process cannot execute file + * Some [] - the process can execute file + * Some gids - the process can execute file if it is any of these gids + */ +CAMLprim value OPAM_get_acl_executable_info(value file, value owner) +{ + CAMLparam2(file, owner); + CAMLlocal2(result, cell); + acl_t acl = acl_get_file(String_val(file), ACL_TYPE_ACCESS); + uid_t owner_uid = Int_val(owner); + uid_t uid = geteuid(); + + result = Val_none; + + if (acl) + { + acl_entry_t entry; + size_t siz; + + if (acl_get_entry(acl, ACL_FIRST_ENTRY, &entry) == 1) + { + int mask = 1; + int user = 0; + do + { + acl_tag_t tag; + acl_permset_t perms; + if (acl_get_tag_type(entry, &tag) == 0 && + acl_get_permset(entry, &perms) == 0) + { + void *qualifier = NULL; + int executable = acl_get_perm(perms, ACL_EXECUTE); + + switch(tag) + { + case ACL_USER: + if (executable && (qualifier = acl_get_qualifier(entry))) + { + uid_t entry_uid = *((uid_t *)qualifier); + /* NB ACL_USER entries do not override ACL_USER_OBJ */ + if (entry_uid != owner_uid && entry_uid == uid) + { + /* result = Some [] */ + if (!Is_block(result)) + { + result = caml_alloc_small(1, 0); + Field(result, 0) = Val_int(0); + } else { + caml_modify(&Field(result, 0), Val_int(0)); + } + user = 1; + } + } + break; + case ACL_GROUP: + if (!user && executable && (qualifier = acl_get_qualifier(entry))) + { + gid_t entry_gid = *((gid_t *)qualifier); + /* Construct a cons cell */ + cell = caml_alloc_small(2, 0); + Field(cell, 0) = Val_int(entry_gid); + if (Is_block(result)) + { + /* Put cons cell at head of existing list */ + Field(cell, 1) = Field(result, 0); + caml_modify(&Field(result, 0), cell); + } + else + { + /* result = Some [cell] */ + Field(cell, 1) = Val_int(0); + result = caml_alloc_small(1, 0); + Field(result, 0) = cell; + } + } + break; + case ACL_USER_OBJ: + case ACL_GROUP_OBJ: + /* These have already been done by the stat check */ + break; + case ACL_MASK: + if (!(mask = executable)) + { + result = Val_none; + } + break; + default: + /* ACL_UNDEFINED_TAG or ACL_OTHER */ + break; + } + + if (qualifier) + acl_free(qualifier); + } + } while (mask && acl_get_entry(acl, ACL_NEXT_ENTRY, &entry) == 1); + } + + acl_free((void *)acl); + } + + CAMLreturn(result); +} diff -Nru opam-2.0.10/src/stubs/libacl/opamlibACL.ml opam-2.1.2/src/stubs/libacl/opamlibACL.ml --- opam-2.0.10/src/stubs/libacl/opamlibACL.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/libacl/opamlibACL.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,11 @@ +(**************************************************************************) +(* *) +(* Copyright 2020 David Allsopp Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +external get_acl_executable_info : string -> int -> int list option = "OPAM_get_acl_executable_info" diff -Nru opam-2.0.10/src/stubs/opamInject.c opam-2.1.2/src/stubs/opamInject.c --- opam-2.0.10/src/stubs/opamInject.c 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/stubs/opamInject.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,164 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright 2015, 2016, 2017, 2018 MetaStack Solutions Ltd. */ -/* */ -/* All rights reserved. This file is distributed under the terms of the */ -/* GNU Lesser General Public License version 2.1, with the special */ -/* exception on linking described in the file LICENSE. */ -/* */ -/**************************************************************************/ - -#include - -/* SetEnvironmentVariable function pointer type */ -typedef LRESULT (WINAPI *SETENVIRONMENTVARIABLE)(LPCTSTR,LPCTSTR); - -/* - * Data structure to pass to the remote thread - */ -typedef struct { - SETENVIRONMENTVARIABLE SetEnvironmentVariable; - TCHAR lpName[4096]; - TCHAR lpValue[4096]; - BOOL result; -} INJDATA, *PINJDATA; - -/* - * Code to inject into the parent process - */ -static DWORD WINAPI ThreadFunc (INJDATA *pData) -{ - /* - * Call the provided function pointer with its two arguments and return the - * result. - */ - pData->result = pData->SetEnvironmentVariable(pData->lpName, pData->lpValue); - - return 0; -} - -/* - * This is a dummy function used to calculate the code size of ThreadFunc. - * This assumes that the linker does not re-order the functions. - * If it's a worry, could make the symbols public and use /ORDER - * (http://msdn.microsoft.com/en-us/library/00kh39zz.aspx) - * Presumably there's a gcc equivalent for mingw. - */ -static void AfterThreadFunc (void) -{ - return; -} - -char* InjectSetEnvironmentVariable(DWORD pid, char* key, char* val) -{ - /* - * Open the parent process for code injection - */ - HANDLE hProcess = - OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION - | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, - FALSE, pid); - INJDATA payload = {NULL, "", "", FALSE}; - INJDATA* pData; - DWORD* pCode; - const int codeSize = ((LPBYTE)AfterThreadFunc - (LPBYTE)ThreadFunc); - HANDLE hThread; - - if (!hProcess) - return "OPAMW_process_putenv: could not open parent process"; - - payload.SetEnvironmentVariable = - (SETENVIRONMENTVARIABLE)GetProcAddress(GetModuleHandle("kernel32"), - "SetEnvironmentVariableA"); - - /* - * Set-up the instruction - */ - strcpy(payload.lpName, key); - strcpy(payload.lpValue, val); - - /* - * Allocate a page in the parent process to hold the instruction and copy the - * payload to it. - */ - pData = - (INJDATA*)VirtualAllocEx(hProcess, - 0, - sizeof(INJDATA), - MEM_COMMIT, - PAGE_READWRITE); - if (!pData) - { - CloseHandle(hProcess); - return "OPAMW_process_putenv: VirtualAllocEx (data) in parent failed"; - } - if (!WriteProcessMemory(hProcess, pData, &payload, sizeof(INJDATA), NULL)) - { - VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); - CloseHandle(hProcess); - return "OPAMW_process_putenv: could not copy data to parent process"; - } - - /* - * Allocate a page in the parent process to hold ThreadFunc and copy the code - * there. - */ - pCode = - (PDWORD)VirtualAllocEx(hProcess, - 0, - codeSize, - MEM_COMMIT, - PAGE_EXECUTE_READWRITE); - if (!pCode) - { - VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); - CloseHandle(hProcess); - return "OPAMW_process_putenv: VirtualAllocEx (exec) in parent failed"; - } - if (!WriteProcessMemory(hProcess, pCode, &ThreadFunc, codeSize, NULL)) - { - VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); - VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); - CloseHandle(hProcess); - return "OPAMW_process_putenv: could not copy code to parent process"; - } - - /* - * Start the remote thread - */ - hThread = - CreateRemoteThread(hProcess, - NULL, - 0, - (LPTHREAD_START_ROUTINE)pCode, - pData, - 0, - NULL); - if (!hThread) - { - VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); - VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); - CloseHandle(hProcess); - return "OPAMW_process_putenv: could not start remote thread in parent"; - } - - /* - * Wait for the thread to terminate. - */ - WaitForSingleObject(hThread, INFINITE); - CloseHandle(hThread); - - /* - * Get the result back - */ - ReadProcessMemory(hProcess, pData, &payload, sizeof(INJDATA), NULL); - - /* - * Release the memory - */ - VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); - VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); - CloseHandle(hProcess); - - return (payload.result ? NULL : ""); -} diff -Nru opam-2.0.10/src/stubs/opamWin32Stubs.ml opam-2.1.2/src/stubs/opamWin32Stubs.ml --- opam-2.0.10/src/stubs/opamWin32Stubs.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/stubs/opamWin32Stubs.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,36 +0,0 @@ -(**************************************************************************) -(* *) -(* Copyright 2018 MetaStack Solutions Ltd. *) -(* *) -(* All rights reserved. This file is distributed under the terms of the *) -(* GNU Lesser General Public License version 2.1, with the special *) -(* exception on linking described in the file LICENSE. *) -(* *) -(**************************************************************************) - -external getCurrentProcessID : unit -> int32 = "OPAMW_GetCurrentProcessID" -(* Polymorphic parameters below are used as placeholders for types in - * OpamStubsTypes - it's not worth the effort of propagating the types here, - * even if it does result in some ugly-looking primitives! - *) -external getStdHandle : 'a -> 'b = "OPAMW_GetStdHandle" -external getConsoleScreenBufferInfo : 'a -> 'b = "OPAMW_GetConsoleScreenBufferInfo" -external setConsoleTextAttribute : 'a -> int -> unit = "OPAMW_SetConsoleTextAttribute" -external fillConsoleOutputCharacter : 'a -> char -> int -> int * int -> bool = "OPAMW_FillConsoleOutputCharacter" -external getConsoleMode : 'a -> int = "OPAMW_GetConsoleMode" -external setConsoleMode : 'a -> int -> bool = "OPAMW_SetConsoleMode" -external getWindowsVersion : unit -> int * int * int * int = "OPAMW_GetWindowsVersion" -external isWoW64 : unit -> bool = "OPAMW_IsWoW64" -external waitpids : int list -> int -> int * Unix.process_status = "OPAMW_waitpids" -external writeRegistry : 'a -> string -> string -> 'b -> 'c -> unit = "OPAMW_WriteRegistry" -external getConsoleOutputCP : unit -> int = "OPAMW_GetConsoleOutputCP" -external getCurrentConsoleFontEx : 'a -> bool -> 'b = "OPAMW_GetCurrentConsoleFontEx" -external create_glyph_checker : string -> 'a * 'a = "OPAMW_CreateGlyphChecker" -external delete_glyph_checker : 'a * 'a -> unit = "OPAMW_DeleteGlyphChecker" -external has_glyph : 'a * 'a -> Uchar.t -> bool = "OPAMW_HasGlyph" -external isWoW64Process : int32 -> bool = "OPAMW_IsWoW64Process" -external process_putenv : int32 -> string -> string -> bool = "OPAMW_process_putenv" -external shGetFolderPath : int -> 'a -> string = "OPAMW_SHGetFolderPath" -external sendMessageTimeout : nativeint -> int -> int -> 'a -> 'b -> 'c -> int * 'd = "OPAMW_SendMessageTimeout_byte" "OPAMW_SendMessageTimeout" -external getParentProcessID : int32 -> int32 = "OPAMW_GetParentProcessID" -external getConsoleAlias : string -> string -> string = "OPAMW_GetConsoleAlias" diff -Nru opam-2.0.10/src/stubs/opamWindows.c opam-2.1.2/src/stubs/opamWindows.c --- opam-2.0.10/src/stubs/opamWindows.c 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/stubs/opamWindows.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,754 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright 2015, 2016, 2017, 2018 MetaStack Solutions Ltd. */ -/* */ -/* All rights reserved. This file is distributed under the terms of the */ -/* GNU Lesser General Public License version 2.1, with the special */ -/* exception on linking described in the file LICENSE. */ -/* */ -/**************************************************************************/ - -#define CAML_NAME_SPACE -/* We need the UTF16 conversion functions */ -#define CAML_INTERNALS -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -/* In a previous incarnation, dummy C stubs were generated for non-Windows - * builds. Although this is no longer used, the C sources retain the ability to - * be compiled this way. */ -#ifdef _WIN32 - -#include -#include -#include - -static struct custom_operations HandleOps = -{ - "org.ocaml.opam.Win32.Handle/1", - custom_finalize_default, - custom_compare_default, - custom_hash_default, - custom_serialize_default, - custom_deserialize_default -}; - -#define HANDLE_val(v) (*((HANDLE*)Data_custom_val(v))) - -typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); - -static LPFN_ISWOW64PROCESS IsWoW64Process = NULL; - -static inline BOOL has_IsWoW64Process(void) -{ - return (IsWoW64Process - || (IsWoW64Process = - (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), - "IsWow64Process"))); -} - -/* - * Taken from otherlibs/win32unix/winwait.c (sadly declared static) - * Altered only for CAML_NAME_SPACE - */ -static value alloc_process_status(HANDLE pid, int status) -{ - value res, st; - - st = caml_alloc(1, 0); - Field(st, 0) = Val_int(status); - Begin_root (st); - res = caml_alloc_small(2, 0); - Field(res, 0) = Val_long((intnat) pid); - Field(res, 1) = st; - End_roots(); - return res; -} - -/* Order must match OpamStubsTypes.registry_root */ -static HKEY roots[] = - {HKEY_CLASSES_ROOT, - HKEY_CURRENT_CONFIG, - HKEY_CURRENT_USER, - HKEY_LOCAL_MACHINE, - HKEY_USERS}; - -/* - * OPAMW_process_putenv is implemented using Process Injection. - * Idea inspired by Bill Stewart's editvar - * (see http://www.westmesatech.com/editv.html) - * Full technical details at http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces#section_3 - */ - -static char* getProcessInfo(HANDLE hProcessSnapshot, - DWORD processId, - PROCESSENTRY32 *entry) -{ - entry->dwSize = sizeof(PROCESSENTRY32); - - if (hProcessSnapshot == INVALID_HANDLE_VALUE) - return "getProcessInfo: could not create snapshot"; - - /* - * Locate our process - */ - if (!Process32First(hProcessSnapshot, entry)) - { - CloseHandle(hProcessSnapshot); - return "getProcessInfo: could not walk process tree"; - } - else - { - while (entry->th32ProcessID != processId) - { - if (!Process32Next(hProcessSnapshot, entry)) - { - CloseHandle(hProcessSnapshot); - return "getProcessInfo: could not find process!"; - } - } - } - - return NULL; -} - -char* InjectSetEnvironmentVariable(DWORD pid, char* key, char* val); - -#define OPAMreturn CAMLreturn - -#else - -#define OPAMreturn(v) CAMLreturn(Val_unit) - -#endif - -/* Actual primitives from here */ -CAMLprim value OPAMW_GetCurrentProcessID(value unit) -{ - CAMLparam1(unit); - - OPAMreturn(caml_copy_int32(GetCurrentProcessId())); -} - -CAMLprim value OPAMW_GetStdHandle(value nStdHandle) -{ - CAMLparam1(nStdHandle); -#ifdef _WIN32 - CAMLlocal1(result); - - HANDLE hResult; - - if ((hResult = GetStdHandle(-Int_val(nStdHandle) - 10)) == NULL) - caml_raise_not_found(); - - result = caml_alloc_custom(&HandleOps, sizeof(HANDLE), 0, 1); - HANDLE_val(result) = hResult; -#endif - - OPAMreturn(result); -} - -CAMLprim value OPAMW_GetConsoleScreenBufferInfo(value hConsoleOutput) -{ - CAMLparam1(hConsoleOutput); -#ifdef _WIN32 - CAMLlocal2(result, coord); - - CONSOLE_SCREEN_BUFFER_INFO buffer; - - if (!GetConsoleScreenBufferInfo(HANDLE_val(hConsoleOutput), &buffer)) - caml_raise_not_found(); - - result = caml_alloc(5, 0); - coord = caml_alloc(2, 0); - Store_field(coord, 0, Val_int(buffer.dwSize.X)); - Store_field(coord, 1, Val_int(buffer.dwSize.Y)); - Store_field(result, 0, coord); - coord = caml_alloc(2, 0); - Store_field(coord, 0, Val_int(buffer.dwCursorPosition.X)); - Store_field(coord, 1, Val_int(buffer.dwCursorPosition.Y)); - Store_field(result, 1, coord); - Store_field(result, 2, Val_int(buffer.wAttributes)); - coord = caml_alloc(4, 0); - Store_field(coord, 0, Val_int(buffer.srWindow.Left)); - Store_field(coord, 1, Val_int(buffer.srWindow.Top)); - Store_field(coord, 2, Val_int(buffer.srWindow.Right)); - Store_field(coord, 3, Val_int(buffer.srWindow.Bottom)); - Store_field(result, 3, coord); - coord = caml_alloc(2, 0); - Store_field(coord, 0, Val_int(buffer.dwMaximumWindowSize.X)); - Store_field(coord, 1, Val_int(buffer.dwMaximumWindowSize.Y)); - Store_field(result, 4, coord); -#endif - - OPAMreturn(result); -} - -CAMLprim value OPAMW_SetConsoleTextAttribute(value hConsoleOutput, - value wAttributes) -{ - CAMLparam2(hConsoleOutput, wAttributes); - -#ifdef _WIN32 - if (!SetConsoleTextAttribute(HANDLE_val(hConsoleOutput), - Int_val(wAttributes))) - caml_failwith("setConsoleTextAttribute"); -#endif - - OPAMreturn(Val_unit); -} - -CAMLprim value OPAMW_FillConsoleOutputCharacter(value vhConsoleOutput, - value character, - value vnLength, - value vdwWriteCoord) -{ - CAMLparam4(vhConsoleOutput, character, vnLength, vdwWriteCoord); - -#ifdef _WIN32 - HANDLE hConsoleOutput = HANDLE_val(vhConsoleOutput); - CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; - WCHAR cCharacter = Int_val(character) & 0xFF; - DWORD nLength = Int_val(vnLength); - COORD dwWriteCoord = - {Int_val(Field(vdwWriteCoord, 0)), Int_val(Field(vdwWriteCoord, 1))}; - DWORD dwNumberOfCharsWritten; - BOOL result = FALSE; - - if (GetConsoleScreenBufferInfo(hConsoleOutput, &ConsoleScreenBufferInfo)) - { - while ((result = FillConsoleOutputCharacter(hConsoleOutput, - cCharacter, - nLength, - dwWriteCoord, - &dwNumberOfCharsWritten)) - && dwNumberOfCharsWritten != nLength) - { - nLength -= dwNumberOfCharsWritten; - dwWriteCoord.X += dwNumberOfCharsWritten; - dwWriteCoord.Y += dwWriteCoord.X / ConsoleScreenBufferInfo.dwSize.X; - dwWriteCoord.X %= ConsoleScreenBufferInfo.dwSize.X; - } - } -#endif - - OPAMreturn(Val_bool(result)); -} - -CAMLprim value OPAMW_GetConsoleMode(value hConsoleHandle) -{ - CAMLparam1(hConsoleHandle); - -#ifdef _WIN32 - DWORD dwMode; - if (!GetConsoleMode(HANDLE_val(hConsoleHandle), &dwMode)) -#endif - caml_raise_not_found(); - - OPAMreturn(Val_int(dwMode)); -} - -CAMLprim value OPAMW_SetConsoleMode(value hConsoleMode, value dwMode) -{ - CAMLparam2(hConsoleMode, dwMode); - -#ifdef _WIN32 - BOOL result = SetConsoleMode(HANDLE_val(hConsoleMode), Int_val(dwMode)); -#endif - - OPAMreturn(Val_bool(result)); -} - -CAMLprim value OPAMW_GetWindowsVersion(value unit) -{ - CAMLparam1(unit); - -#ifdef _WIN32 - CAMLlocal1(result); - result = caml_alloc_tuple(4); -#if OCAML_VERSION >= 40600 - Store_field(result, 0, Val_int(caml_win32_major)); - Store_field(result, 1, Val_int(caml_win32_minor)); - Store_field(result, 2, Val_int(caml_win32_build)); - Store_field(result, 3, Val_int(caml_win32_revision)); -#else - Store_field(result, 0, Val_int(0)); - Store_field(result, 1, Val_int(0)); - Store_field(result, 2, Val_int(0)); - Store_field(result, 3, Val_int(0)); -#endif -#endif - - OPAMreturn(result); -} - -CAMLprim value OPAMW_IsWoW64(value unit) -{ - CAMLparam1(unit); - -#ifdef _WIN32 - BOOL result = FALSE; - /* - * 32-bit versions may or may not have IsWow64Process (depends on age). - * Recommended way is to use GetProcAddress to obtain IsWow64Process, rather - * than relying on Windows.h. - * See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms684139.aspx - */ - if (has_IsWoW64Process() && !IsWoW64Process(GetCurrentProcess(), &result)) - result = FALSE; -#endif - - OPAMreturn(Val_bool(result)); -} - -/* - * Adapted from otherlibs/win32unix/winwait.c win_waitpid - */ -CAMLprim value OPAMW_waitpids(value vpid_reqs, value vpid_len) -{ -#ifdef _WIN32 - int i; - DWORD status, retcode; - HANDLE pid_req; - DWORD err = 0; - int len = Int_val(vpid_len); - HANDLE *lpHandles = (HANDLE*)malloc(sizeof(HANDLE) * len); - value ptr = vpid_reqs; - - if (lpHandles == NULL) - caml_raise_out_of_memory(); - - for (i = 0; i < len; i++) { - lpHandles[i] = (HANDLE)Long_val(Field(ptr, 0)); - ptr = Field(ptr, 1); - } - - caml_enter_blocking_section(); - retcode = WaitForMultipleObjects(len, lpHandles, FALSE, INFINITE); - if (retcode == WAIT_FAILED) err = GetLastError(); - caml_leave_blocking_section(); - if (err) { - win32_maperr(err); - uerror("waitpids", Nothing); - } - pid_req = lpHandles[retcode - WAIT_OBJECT_0]; - free(lpHandles); - if (! GetExitCodeProcess(pid_req, &status)) { - win32_maperr(GetLastError()); - uerror("waitpids", Nothing); - } - - /* - * NB Unlike in win_waitpid, it's not possible to have status == STILL_ACTIVE - */ - CloseHandle(pid_req); - return alloc_process_status(pid_req, status); -#else - return Val_unit; -#endif -} - -CAMLprim value OPAMW_WriteRegistry(value hKey, - value lpSubKey, - value lpValueName, - value dwType, - value lpData) -{ - CAMLparam5(hKey, lpSubKey, lpValueName, dwType, lpData); - -#ifdef _WIN32 - HKEY key; - void* buf = NULL; - DWORD cbData = 0; - DWORD type = 0; - - switch (RegOpenKeyEx(roots[Int_val(hKey)], - String_val(lpSubKey), - 0, - KEY_WRITE, - &key)) - { - case ERROR_SUCCESS: - { - /* Cases match OpamStubsTypes.registry_value */ - switch (Int_val(dwType)) - { - case 0: - { - buf = String_val(lpData); - cbData = strlen(buf) + 1; - type = REG_SZ; - break; - } - default: - { - caml_failwith("OPAMW_WriteRegistry: value not implemented"); - break; - } - } - if (RegSetValueEx(key, - String_val(lpValueName), - 0, - type, - (LPBYTE)buf, - cbData) != ERROR_SUCCESS) - { - RegCloseKey(key); - caml_failwith("RegSetValueEx"); - } - RegCloseKey(key); - break; - } - case ERROR_FILE_NOT_FOUND: - { - caml_raise_not_found(); - break; - } - default: - { - caml_failwith("RegOpenKeyEx"); - break; - } - } -#endif - - OPAMreturn(Val_unit); -} - -CAMLprim value OPAMW_GetConsoleOutputCP(value unit) -{ - CAMLparam1(unit); - - OPAMreturn(Val_int(GetConsoleOutputCP())); -} - -CAMLprim value OPAMW_GetCurrentConsoleFontEx(value hConsoleOutput, - value bMaximumWindow) -{ - CAMLparam2(hConsoleOutput, bMaximumWindow); -#ifdef _WIN32 - CAMLlocal3(result, coord, name); - - int len; - CONSOLE_FONT_INFOEX fontInfo; - fontInfo.cbSize = sizeof(fontInfo); - - if (GetCurrentConsoleFontEx(HANDLE_val(hConsoleOutput), - Bool_val(bMaximumWindow), - &fontInfo)) - { - result = caml_alloc(5, 0); - Store_field(result, 0, Val_int(fontInfo.nFont)); - coord = caml_alloc(2, 0); - Store_field(coord, 0, Val_int(fontInfo.dwFontSize.X)); - Store_field(coord, 0, Val_int(fontInfo.dwFontSize.Y)); - Store_field(result, 1, coord); - Store_field(result, 2, Val_int(fontInfo.FontFamily)); - Store_field(result, 3, Val_int(fontInfo.FontWeight)); - Store_field(result, 4, caml_copy_string_of_utf16(fontInfo.FaceName)); - } - else - { - caml_raise_not_found(); - } -#endif - - OPAMreturn(result); -} - -CAMLprim value OPAMW_CreateGlyphChecker(value fontName) -{ - CAMLparam1(fontName); -#ifdef _WIN32 - CAMLlocal2(result, handle); - - /* - * Any device context will do to load the font, so use the Screen DC. - */ - HDC hDC = GetDC(NULL); - - if (hDC) - { - wchar_t* lpszFace = caml_stat_strdup_to_utf16(String_val(fontName)); - HFONT hFont = - CreateFontW(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, - OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, - DEFAULT_PITCH, lpszFace); - caml_stat_free(lpszFace); - - if (hFont) - { - if (SelectObject(hDC, hFont)) - { - result = caml_alloc_tuple(2); - handle = caml_alloc_custom(&HandleOps, sizeof(HANDLE), 0, 1); - HANDLE_val(handle) = hDC; - Store_field(result, 0, handle); - handle = caml_alloc_custom(&HandleOps, sizeof(HANDLE), 0, 1); - HANDLE_val(handle) = hFont; - Store_field(result, 1, handle); - } - else - { - caml_failwith("OPAMW_CheckGlyphs: SelectObject"); - } - } - else - { - caml_failwith("OPAMW_CheckGlyphs: CreateFontW"); - } - } - else - { - caml_failwith("OPAMW_CheckGlyphs: GetDC"); - } -#endif - - OPAMreturn(result); -} - -CAMLprim value OPAMW_DeleteGlyphChecker(value checker) -{ - CAMLparam1(checker); - -#ifdef _WIN32 - DeleteObject(HANDLE_val(Field(checker, 1))); - ReleaseDC(NULL, HANDLE_val(Field(checker, 0))); -#endif - - CAMLreturn(Val_unit); -} - -CAMLprim value OPAMW_HasGlyph(value checker, value scalar) -{ - CAMLparam2(checker, scalar); -#ifdef _WIN32 - BOOL result = FALSE; - HDC hDC = HANDLE_val(Field(checker, 0)); - - WCHAR test = (WCHAR)Int_val(scalar); - WORD index = 0; - - switch (GetGlyphIndicesW(hDC, &test, 1, &index, GGI_MARK_NONEXISTING_GLYPHS)) - { - case 1: - break; - case GDI_ERROR: - caml_failwith("OPAMW_CheckGlyphs: GetGlyphIndicesW"); - default: - caml_failwith("OPAMW_CheckGlyphs: GetGlyphIndicesW (unexpected return)"); - } -#endif - - OPAMreturn(Val_bool(test != 0xffff)); -} - -CAMLprim value OPAMW_process_putenv(value pid, value key, value val) -{ - CAMLparam3(pid, key, val); -#ifdef _WIN32 - CAMLlocal1(res); - - char* result; - - /* - * MSDN is all over the place as to what the technical limits are for - * environment variables (looks like 32KiB for both both name and value) - * however there's no need to inject 64KiB data each time - hence 4KiB limit. - */ - if (caml_string_length(key) > 4095 || caml_string_length(val) > 4095) - caml_invalid_argument("Strings too long"); - - result = - InjectSetEnvironmentVariable(Int32_val(pid), - String_val(key), - String_val(val)); - - if (result == NULL) - { - res = Val_true; - } - else if (strlen(result) == 0) - { - res = Val_false; - } - else - { - caml_failwith(result); - } -#endif - - OPAMreturn(res); -} - -CAMLprim value OPAMW_IsWoW64Process(value pid) -{ - CAMLparam1(pid); - -#ifdef _WIN32 - BOOL result = FALSE; - - if (has_IsWoW64Process()) - { - HANDLE hProcess = - OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, Int32_val(pid)); - - if (hProcess) - { - if (!IsWoW64Process(hProcess, &result)) - result = FALSE; - CloseHandle(hProcess); - } - } -#endif - - OPAMreturn(Val_bool(result)); -} - -/* - * Somewhat against my better judgement, wrap SHGetFolderPath rather than - * SHGetKnownFolderPath to maintain XP compatibility. OPAM already requires - * Windows Vista+ because of GetCurrentConsoleFontEx, but there may be a - * workaround for that for XP lusers. - */ -CAMLprim value OPAMW_SHGetFolderPath(value nFolder, value dwFlags) -{ - CAMLparam2(nFolder, dwFlags); -#ifdef _WIN32 - CAMLlocal1(result); - TCHAR szPath[MAX_PATH]; - - if (SUCCEEDED(SHGetFolderPath(NULL, - Int_val(nFolder), - NULL, - Int_val(dwFlags), - szPath))) - result = caml_copy_string(szPath); - else - caml_failwith("OPAMW_SHGetFolderPath"); -#endif - - OPAMreturn(result); -} - -CAMLprim value OPAMW_SendMessageTimeout(value hWnd, - value uTimeout, - value fuFlags, - value vmsg, - value vwParam, - value vlParam) -{ - CAMLparam5(hWnd, vmsg, vwParam, vlParam, fuFlags); - CAMLxparam1(uTimeout); -#ifdef _WIN32 - CAMLlocal1(result); - - DWORD_PTR dwReturnValue; - HRESULT lResult; - WPARAM wParam; - LPARAM lParam; - UINT msg; - - switch (Int_val(vmsg)) - { - case 0: - { - msg = WM_SETTINGCHANGE; - wParam = Int_val(vwParam); - lParam = (LPARAM)String_val(vlParam); - break; - } - default: - { - caml_failwith("OPAMW_SendMessageTimeout: message not implemented"); - break; - } - } - - lResult = - SendMessageTimeout((HWND)Nativeint_val(hWnd), - msg, - wParam, - lParam, - Int_val(fuFlags), - Int_val(uTimeout), - &dwReturnValue); - - switch (Int_val(vmsg)) - { - case 0: - { - result = caml_alloc(2, 0); - Store_field(result, 0, Val_int(lResult)); - Store_field(result, 1, Val_int(dwReturnValue)); - break; - } - } -#endif - - OPAMreturn(result); -} - -CAMLprim value OPAMW_SendMessageTimeout_byte(value * v, int n) -{ - return OPAMW_SendMessageTimeout(v[0], v[1], v[2], v[3], v[4], v[5]); -} - -CAMLprim value OPAMW_GetParentProcessID(value processId) -{ - CAMLparam1(processId); - -#ifdef _WIN32 - PROCESSENTRY32 entry; - char* msg; - /* - * Create a Toolhelp Snapshot of running processes - */ - HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); - - if ((msg = getProcessInfo(hProcessSnapshot, Int32_val(processId), &entry))) - caml_failwith(msg); - - /* - * Finished with the snapshot - */ - CloseHandle(hProcessSnapshot); -#endif - - OPAMreturn(caml_copy_int32(entry.th32ParentProcessID)); -} - -CAMLprim value OPAMW_GetConsoleAlias(value alias, value exeName) -{ - CAMLparam2(alias, exeName); -#ifdef _WIN32 - CAMLlocal1(result); - - DWORD nLength = 8192; - LPTSTR buffer = (LPTSTR)malloc(nLength); - - if (!buffer) - caml_raise_out_of_memory(); - - if (GetConsoleAlias(String_val(alias), buffer, nLength, String_val(exeName))) - { - result = caml_copy_string(buffer); - } - else - { - result = caml_copy_string(""); - } - - free(buffer); -#endif - - OPAMreturn(result); -} diff -Nru opam-2.0.10/src/stubs/win32/build-putenv.ml opam-2.1.2/src/stubs/win32/build-putenv.ml --- opam-2.0.10/src/stubs/win32/build-putenv.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/win32/build-putenv.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,35 @@ +#load "unix.cma" + +let () = + let (prefix, suffix) = + let c = open_in Sys.argv.(3) in + let prefix = input_line c in + let suffix = input_line c in + let rec f () = + match input_line c with + | env -> g env + | exception End_of_file -> close_in c + and g env = + let elt = input_line c in + let elt = if elt.[String.length elt - 1] <> ';' then elt ^ ";" else elt in + let elt = + (* See https://support.microsoft.com/en-us/help/830473 *) + let l = String.length elt in + if l > 8191 then begin + Printf.eprintf "Variable %s has length %d which exceeds the maximum of 8191\n%!" env l; + exit 1 + end else + let current = Unix.getenv env in + if String.length elt + String.length current > 8191 then begin + Printf.eprintf "Warning: replacing, rather than prepending %s\n%!" env; + elt + end else + elt ^ current + in + let () = Unix.putenv env elt in + f () + in + f (); + (prefix, suffix) + in + exit (Sys.command (Printf.sprintf "%s%s%s%s" prefix Sys.argv.(1) suffix Sys.argv.(2))) diff -Nru opam-2.0.10/src/stubs/win32/dune-win32 opam-2.1.2/src/stubs/win32/dune-win32 --- opam-2.0.10/src/stubs/win32/dune-win32 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/win32/dune-win32 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,34 @@ +(library + (name opam_stubs_win32) + (public_name opam-core.stubs) + (synopsis "OCaml Package Manager C stubs") + (libraries unix) + (flags (:standard + (:include ../../ocaml-flags-standard.sexp) + (:include ../../ocaml-flags-configure.sexp) + (:include ../../ocaml-context-flags.sexp))) + (modules opamWin32Stubs) + (c_names opamInject opamWindows) + (c_flags (:standard + (:include ../c-flags.sexp))) + (c_library_flags (:standard + (:include c-libraries.sexp))) + (wrapped false)) + +(rule + (targets opam-putenv.exe) + (deps opamInject.c) + (action (run ocaml %{dep:build-putenv.ml} %{targets} %{dep:opam-putenv.c} %{dep:cc64}))) + +(rule + (targets cc64) + (mode fallback) + (action (with-stdout-to %{targets} (echo "")))) + +(install + (section bin) + (package opam) + (files opam-putenv.exe)) + +(rule + (with-stdout-to c-libraries.sexp (run ocaml %{dep:../../../shell/context_flags.ml} clibs))) diff -Nru opam-2.0.10/src/stubs/win32/opamInject.c opam-2.1.2/src/stubs/win32/opamInject.c --- opam-2.0.10/src/stubs/win32/opamInject.c 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/win32/opamInject.c 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,164 @@ +/**************************************************************************/ +/* */ +/* Copyright 2015, 2016, 2017, 2018 MetaStack Solutions Ltd. */ +/* */ +/* All rights reserved. This file is distributed under the terms of the */ +/* GNU Lesser General Public License version 2.1, with the special */ +/* exception on linking described in the file LICENSE. */ +/* */ +/**************************************************************************/ + +#include + +/* SetEnvironmentVariable function pointer type */ +typedef LRESULT (WINAPI *SETENVIRONMENTVARIABLE)(LPCTSTR,LPCTSTR); + +/* + * Data structure to pass to the remote thread + */ +typedef struct { + SETENVIRONMENTVARIABLE SetEnvironmentVariable; + TCHAR lpName[4096]; + TCHAR lpValue[4096]; + BOOL result; +} INJDATA, *PINJDATA; + +/* + * Code to inject into the parent process + */ +static DWORD WINAPI ThreadFunc (INJDATA *pData) +{ + /* + * Call the provided function pointer with its two arguments and return the + * result. + */ + pData->result = pData->SetEnvironmentVariable(pData->lpName, pData->lpValue); + + return 0; +} + +/* + * This is a dummy function used to calculate the code size of ThreadFunc. + * This assumes that the linker does not re-order the functions. + * If it's a worry, could make the symbols public and use /ORDER + * (http://msdn.microsoft.com/en-us/library/00kh39zz.aspx) + * Presumably there's a gcc equivalent for mingw. + */ +static void AfterThreadFunc (void) +{ + return; +} + +char* InjectSetEnvironmentVariable(DWORD pid, const char* key, const char* val) +{ + /* + * Open the parent process for code injection + */ + HANDLE hProcess = + OpenProcess(PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION + | PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_VM_READ, + FALSE, pid); + INJDATA payload = {NULL, "", "", FALSE}; + INJDATA* pData; + DWORD* pCode; + const int codeSize = ((LPBYTE)AfterThreadFunc - (LPBYTE)ThreadFunc); + HANDLE hThread; + + if (!hProcess) + return "OPAMW_process_putenv: could not open parent process"; + + payload.SetEnvironmentVariable = + (SETENVIRONMENTVARIABLE)GetProcAddress(GetModuleHandle("kernel32"), + "SetEnvironmentVariableA"); + + /* + * Set-up the instruction + */ + strcpy(payload.lpName, key); + strcpy(payload.lpValue, val); + + /* + * Allocate a page in the parent process to hold the instruction and copy the + * payload to it. + */ + pData = + (INJDATA*)VirtualAllocEx(hProcess, + 0, + sizeof(INJDATA), + MEM_COMMIT, + PAGE_READWRITE); + if (!pData) + { + CloseHandle(hProcess); + return "OPAMW_process_putenv: VirtualAllocEx (data) in parent failed"; + } + if (!WriteProcessMemory(hProcess, pData, &payload, sizeof(INJDATA), NULL)) + { + VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); + CloseHandle(hProcess); + return "OPAMW_process_putenv: could not copy data to parent process"; + } + + /* + * Allocate a page in the parent process to hold ThreadFunc and copy the code + * there. + */ + pCode = + (PDWORD)VirtualAllocEx(hProcess, + 0, + codeSize, + MEM_COMMIT, + PAGE_EXECUTE_READWRITE); + if (!pCode) + { + VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); + CloseHandle(hProcess); + return "OPAMW_process_putenv: VirtualAllocEx (exec) in parent failed"; + } + if (!WriteProcessMemory(hProcess, pCode, &ThreadFunc, codeSize, NULL)) + { + VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); + VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); + CloseHandle(hProcess); + return "OPAMW_process_putenv: could not copy code to parent process"; + } + + /* + * Start the remote thread + */ + hThread = + CreateRemoteThread(hProcess, + NULL, + 0, + (LPTHREAD_START_ROUTINE)pCode, + pData, + 0, + NULL); + if (!hThread) + { + VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); + VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); + CloseHandle(hProcess); + return "OPAMW_process_putenv: could not start remote thread in parent"; + } + + /* + * Wait for the thread to terminate. + */ + WaitForSingleObject(hThread, INFINITE); + CloseHandle(hThread); + + /* + * Get the result back + */ + ReadProcessMemory(hProcess, pData, &payload, sizeof(INJDATA), NULL); + + /* + * Release the memory + */ + VirtualFreeEx(hProcess, pCode, 0, MEM_RELEASE); + VirtualFreeEx(hProcess, pData, 0, MEM_RELEASE); + CloseHandle(hProcess); + + return (payload.result ? NULL : ""); +} diff -Nru opam-2.0.10/src/stubs/win32/opam-putenv.c opam-2.1.2/src/stubs/win32/opam-putenv.c --- opam-2.0.10/src/stubs/win32/opam-putenv.c 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/win32/opam-putenv.c 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,67 @@ +/**************************************************************************/ +/* */ +/* Copyright 2015, 2016, 2017, 2018 MetaStack Solutions Ltd. */ +/* */ +/* All rights reserved. This file is distributed under the terms of the */ +/* GNU Lesser General Public License version 2.1, with the special */ +/* exception on linking described in the file LICENSE. */ +/* */ +/**************************************************************************/ + +#include +/* + * This will be being built for a different architecture, so it's easier just to + * #include the code, rather than having to deal with .o(obj) files for + * different architectures. + */ +#include "opamInject.c" + +/* + * This trivially simple utility takes a single PID and then reads CRLF + * terminated lines from STDIN. The line ::QUIT causes the program to terminate + * otherwise a further line is read and the two lines together form the + * key/value pair to be set in the process's environment. + * + * This utility is always compiled x86 if OPAM is compiled x64 and vice versa + * and allows OPAM to manipulate a parent whose architecture differs from its + * own. When the architecture matches, OPAM injects the code itself, but + * injecting from a 64-bit process to a 32-bit parent is quite hard (and + * potentially unstable) and injecting from a 32-bit process to a 64-bit parent + * is phenomenally hard! + */ +int main(int argc, char *argv[], char *envp[]) +{ + if (argc != 2) + { + printf("Invalid command line: this utility is an internal part of OPAM\n"); + } + else + { + DWORD pid = atoi(argv[1]); + BOOL running = TRUE; + char* key = (char*)malloc(4097); + char* value = (char*)malloc(4097); + + while (running) + { + if (fgets(key, 4097, stdin)) + { + if (strcmp(key, "::QUIT\n") && fgets(value, 4097, stdin)) + { + key[strlen(key) - 1] = value[strlen(value) - 1] = '\0'; + InjectSetEnvironmentVariable(pid, key, value); + } + else + { + running = FALSE; + } + } + else + { + running = FALSE; + } + } + free(key); + free(value); + } +} diff -Nru opam-2.0.10/src/stubs/win32/opamWin32Stubs.ml opam-2.1.2/src/stubs/win32/opamWin32Stubs.ml --- opam-2.0.10/src/stubs/win32/opamWin32Stubs.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/win32/opamWin32Stubs.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,36 @@ +(**************************************************************************) +(* *) +(* Copyright 2018 MetaStack Solutions Ltd. *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +external getCurrentProcessID : unit -> int32 = "OPAMW_GetCurrentProcessID" +(* Polymorphic parameters below are used as placeholders for types in + * OpamStubsTypes - it's not worth the effort of propagating the types here, + * even if it does result in some ugly-looking primitives! + *) +external getStdHandle : 'a -> 'b = "OPAMW_GetStdHandle" +external getConsoleScreenBufferInfo : 'a -> 'b = "OPAMW_GetConsoleScreenBufferInfo" +external setConsoleTextAttribute : 'a -> int -> unit = "OPAMW_SetConsoleTextAttribute" +external fillConsoleOutputCharacter : 'a -> char -> int -> int * int -> bool = "OPAMW_FillConsoleOutputCharacter" +external getConsoleMode : 'a -> int = "OPAMW_GetConsoleMode" +external setConsoleMode : 'a -> int -> bool = "OPAMW_SetConsoleMode" +external getWindowsVersion : unit -> int * int * int * int = "OPAMW_GetWindowsVersion" +external isWoW64 : unit -> bool = "OPAMW_IsWoW64" +external waitpids : int list -> int -> int * Unix.process_status = "OPAMW_waitpids" +external writeRegistry : 'a -> string -> string -> 'b -> 'c -> unit = "OPAMW_WriteRegistry" +external getConsoleOutputCP : unit -> int = "OPAMW_GetConsoleOutputCP" +external getCurrentConsoleFontEx : 'a -> bool -> 'b = "OPAMW_GetCurrentConsoleFontEx" +external create_glyph_checker : string -> 'a * 'a = "OPAMW_CreateGlyphChecker" +external delete_glyph_checker : 'a * 'a -> unit = "OPAMW_DeleteGlyphChecker" +external has_glyph : 'a * 'a -> Uchar.t -> bool = "OPAMW_HasGlyph" +external isWoW64Process : int32 -> bool = "OPAMW_IsWoW64Process" +external process_putenv : int32 -> string -> string -> bool = "OPAMW_process_putenv" +external shGetFolderPath : int -> 'a -> string = "OPAMW_SHGetFolderPath" +external sendMessageTimeout : nativeint -> int -> int -> 'a -> 'b -> 'c -> int * 'd = "OPAMW_SendMessageTimeout_byte" "OPAMW_SendMessageTimeout" +external getParentProcessID : int32 -> int32 = "OPAMW_GetParentProcessID" +external getConsoleAlias : string -> string -> string = "OPAMW_GetConsoleAlias" diff -Nru opam-2.0.10/src/stubs/win32/opamWindows.c opam-2.1.2/src/stubs/win32/opamWindows.c --- opam-2.0.10/src/stubs/win32/opamWindows.c 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/src/stubs/win32/opamWindows.c 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,755 @@ +/**************************************************************************/ +/* */ +/* Copyright 2015, 2016, 2017, 2018 MetaStack Solutions Ltd. */ +/* */ +/* All rights reserved. This file is distributed under the terms of the */ +/* GNU Lesser General Public License version 2.1, with the special */ +/* exception on linking described in the file LICENSE. */ +/* */ +/**************************************************************************/ + +#define CAML_NAME_SPACE +/* We need the UTF16 conversion functions */ +#define CAML_INTERNALS +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* In a previous incarnation, dummy C stubs were generated for non-Windows + * builds. Although this is no longer used, the C sources retain the ability to + * be compiled this way. */ +#ifdef _WIN32 + +#include +#include +#include + +static struct custom_operations HandleOps = +{ + "org.ocaml.opam.Win32.Handle/1", + custom_finalize_default, + custom_compare_default, + custom_hash_default, + custom_serialize_default, + custom_deserialize_default +}; + +#define HANDLE_val(v) (*((HANDLE*)Data_custom_val(v))) + +typedef BOOL (WINAPI *LPFN_ISWOW64PROCESS) (HANDLE, PBOOL); + +static LPFN_ISWOW64PROCESS IsWoW64Process = NULL; + +static inline BOOL has_IsWoW64Process(void) +{ + return (IsWoW64Process + || (IsWoW64Process = + (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandle("kernel32"), + "IsWow64Process"))); +} + +/* + * Taken from otherlibs/win32unix/winwait.c (sadly declared static) + * Altered only for CAML_NAME_SPACE + */ +static value alloc_process_status(HANDLE pid, int status) +{ + value res, st; + + st = caml_alloc(1, 0); + Field(st, 0) = Val_int(status); + Begin_root (st); + res = caml_alloc_small(2, 0); + Field(res, 0) = Val_long((intnat) pid); + Field(res, 1) = st; + End_roots(); + return res; +} + +/* Order must match OpamStubsTypes.registry_root */ +static HKEY roots[] = + {HKEY_CLASSES_ROOT, + HKEY_CURRENT_CONFIG, + HKEY_CURRENT_USER, + HKEY_LOCAL_MACHINE, + HKEY_USERS}; + +/* + * OPAMW_process_putenv is implemented using Process Injection. + * Idea inspired by Bill Stewart's editvar + * (see http://www.westmesatech.com/editv.html) + * Full technical details at http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces#section_3 + */ + +static char* getProcessInfo(HANDLE hProcessSnapshot, + DWORD processId, + PROCESSENTRY32 *entry) +{ + entry->dwSize = sizeof(PROCESSENTRY32); + + if (hProcessSnapshot == INVALID_HANDLE_VALUE) + return "getProcessInfo: could not create snapshot"; + + /* + * Locate our process + */ + if (!Process32First(hProcessSnapshot, entry)) + { + CloseHandle(hProcessSnapshot); + return "getProcessInfo: could not walk process tree"; + } + else + { + while (entry->th32ProcessID != processId) + { + if (!Process32Next(hProcessSnapshot, entry)) + { + CloseHandle(hProcessSnapshot); + return "getProcessInfo: could not find process!"; + } + } + } + + return NULL; +} + +char* InjectSetEnvironmentVariable(DWORD pid, const char* key, const char* val); + +#define OPAMreturn CAMLreturn + +#else + +#define OPAMreturn(v) CAMLreturn(Val_unit) + +#endif + +/* Actual primitives from here */ +CAMLprim value OPAMW_GetCurrentProcessID(value unit) +{ + CAMLparam1(unit); + + OPAMreturn(caml_copy_int32(GetCurrentProcessId())); +} + +CAMLprim value OPAMW_GetStdHandle(value nStdHandle) +{ + CAMLparam1(nStdHandle); +#ifdef _WIN32 + CAMLlocal1(result); + + HANDLE hResult; + + if ((hResult = GetStdHandle(-Int_val(nStdHandle) - 10)) == NULL) + caml_raise_not_found(); + + result = caml_alloc_custom(&HandleOps, sizeof(HANDLE), 0, 1); + HANDLE_val(result) = hResult; +#endif + + OPAMreturn(result); +} + +CAMLprim value OPAMW_GetConsoleScreenBufferInfo(value hConsoleOutput) +{ + CAMLparam1(hConsoleOutput); +#ifdef _WIN32 + CAMLlocal2(result, coord); + + CONSOLE_SCREEN_BUFFER_INFO buffer; + + if (!GetConsoleScreenBufferInfo(HANDLE_val(hConsoleOutput), &buffer)) + caml_raise_not_found(); + + result = caml_alloc(5, 0); + coord = caml_alloc(2, 0); + Store_field(coord, 0, Val_int(buffer.dwSize.X)); + Store_field(coord, 1, Val_int(buffer.dwSize.Y)); + Store_field(result, 0, coord); + coord = caml_alloc(2, 0); + Store_field(coord, 0, Val_int(buffer.dwCursorPosition.X)); + Store_field(coord, 1, Val_int(buffer.dwCursorPosition.Y)); + Store_field(result, 1, coord); + Store_field(result, 2, Val_int(buffer.wAttributes)); + coord = caml_alloc(4, 0); + Store_field(coord, 0, Val_int(buffer.srWindow.Left)); + Store_field(coord, 1, Val_int(buffer.srWindow.Top)); + Store_field(coord, 2, Val_int(buffer.srWindow.Right)); + Store_field(coord, 3, Val_int(buffer.srWindow.Bottom)); + Store_field(result, 3, coord); + coord = caml_alloc(2, 0); + Store_field(coord, 0, Val_int(buffer.dwMaximumWindowSize.X)); + Store_field(coord, 1, Val_int(buffer.dwMaximumWindowSize.Y)); + Store_field(result, 4, coord); +#endif + + OPAMreturn(result); +} + +CAMLprim value OPAMW_SetConsoleTextAttribute(value hConsoleOutput, + value wAttributes) +{ + CAMLparam2(hConsoleOutput, wAttributes); + +#ifdef _WIN32 + if (!SetConsoleTextAttribute(HANDLE_val(hConsoleOutput), + Int_val(wAttributes))) + caml_failwith("setConsoleTextAttribute"); +#endif + + OPAMreturn(Val_unit); +} + +CAMLprim value OPAMW_FillConsoleOutputCharacter(value vhConsoleOutput, + value character, + value vnLength, + value vdwWriteCoord) +{ + CAMLparam4(vhConsoleOutput, character, vnLength, vdwWriteCoord); + +#ifdef _WIN32 + HANDLE hConsoleOutput = HANDLE_val(vhConsoleOutput); + CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo; + WCHAR cCharacter = Int_val(character) & 0xFF; + DWORD nLength = Int_val(vnLength); + COORD dwWriteCoord = + {Int_val(Field(vdwWriteCoord, 0)), Int_val(Field(vdwWriteCoord, 1))}; + DWORD dwNumberOfCharsWritten; + BOOL result = FALSE; + + if (GetConsoleScreenBufferInfo(hConsoleOutput, &ConsoleScreenBufferInfo)) + { + while ((result = FillConsoleOutputCharacter(hConsoleOutput, + cCharacter, + nLength, + dwWriteCoord, + &dwNumberOfCharsWritten)) + && dwNumberOfCharsWritten != nLength) + { + nLength -= dwNumberOfCharsWritten; + dwWriteCoord.X += dwNumberOfCharsWritten; + dwWriteCoord.Y += dwWriteCoord.X / ConsoleScreenBufferInfo.dwSize.X; + dwWriteCoord.X %= ConsoleScreenBufferInfo.dwSize.X; + } + } +#endif + + OPAMreturn(Val_bool(result)); +} + +CAMLprim value OPAMW_GetConsoleMode(value hConsoleHandle) +{ + CAMLparam1(hConsoleHandle); + +#ifdef _WIN32 + DWORD dwMode; + if (!GetConsoleMode(HANDLE_val(hConsoleHandle), &dwMode)) +#endif + caml_raise_not_found(); + + OPAMreturn(Val_int(dwMode)); +} + +CAMLprim value OPAMW_SetConsoleMode(value hConsoleMode, value dwMode) +{ + CAMLparam2(hConsoleMode, dwMode); + +#ifdef _WIN32 + BOOL result = SetConsoleMode(HANDLE_val(hConsoleMode), Int_val(dwMode)); +#endif + + OPAMreturn(Val_bool(result)); +} + +CAMLprim value OPAMW_GetWindowsVersion(value unit) +{ + CAMLparam1(unit); + +#ifdef _WIN32 + CAMLlocal1(result); + result = caml_alloc_tuple(4); +#if OCAML_VERSION >= 40600 + Store_field(result, 0, Val_int(caml_win32_major)); + Store_field(result, 1, Val_int(caml_win32_minor)); + Store_field(result, 2, Val_int(caml_win32_build)); + Store_field(result, 3, Val_int(caml_win32_revision)); +#else + Store_field(result, 0, Val_int(0)); + Store_field(result, 1, Val_int(0)); + Store_field(result, 2, Val_int(0)); + Store_field(result, 3, Val_int(0)); +#endif +#endif + + OPAMreturn(result); +} + +CAMLprim value OPAMW_IsWoW64(value unit) +{ + CAMLparam1(unit); + +#ifdef _WIN32 + BOOL result = FALSE; + /* + * 32-bit versions may or may not have IsWow64Process (depends on age). + * Recommended way is to use GetProcAddress to obtain IsWow64Process, rather + * than relying on Windows.h. + * See http://msdn.microsoft.com/en-gb/library/windows/desktop/ms684139.aspx + */ + if (has_IsWoW64Process() && !IsWoW64Process(GetCurrentProcess(), &result)) + result = FALSE; +#endif + + OPAMreturn(Val_bool(result)); +} + +/* + * Adapted from otherlibs/win32unix/winwait.c win_waitpid + */ +CAMLprim value OPAMW_waitpids(value vpid_reqs, value vpid_len) +{ +#ifdef _WIN32 + int i; + DWORD status, retcode; + HANDLE pid_req; + DWORD err = 0; + int len = Int_val(vpid_len); + HANDLE *lpHandles = (HANDLE*)malloc(sizeof(HANDLE) * len); + value ptr = vpid_reqs; + + if (lpHandles == NULL) + caml_raise_out_of_memory(); + + for (i = 0; i < len; i++) { + lpHandles[i] = (HANDLE)Long_val(Field(ptr, 0)); + ptr = Field(ptr, 1); + } + + caml_enter_blocking_section(); + retcode = WaitForMultipleObjects(len, lpHandles, FALSE, INFINITE); + if (retcode == WAIT_FAILED) err = GetLastError(); + caml_leave_blocking_section(); + if (err) { + win32_maperr(err); + uerror("waitpids", Nothing); + } + pid_req = lpHandles[retcode - WAIT_OBJECT_0]; + free(lpHandles); + if (! GetExitCodeProcess(pid_req, &status)) { + win32_maperr(GetLastError()); + uerror("waitpids", Nothing); + } + + /* + * NB Unlike in win_waitpid, it's not possible to have status == STILL_ACTIVE + */ + CloseHandle(pid_req); + return alloc_process_status(pid_req, status); +#else + return Val_unit; +#endif +} + +CAMLprim value OPAMW_WriteRegistry(value hKey, + value lpSubKey, + value lpValueName, + value dwType, + value lpData) +{ + CAMLparam5(hKey, lpSubKey, lpValueName, dwType, lpData); + +#ifdef _WIN32 + HKEY key; + const void* buf = NULL; + DWORD cbData = 0; + DWORD type = 0; + + switch (RegOpenKeyEx(roots[Int_val(hKey)], + String_val(lpSubKey), + 0, + KEY_WRITE, + &key)) + { + case ERROR_SUCCESS: + { + /* Cases match OpamStubsTypes.registry_value */ + switch (Int_val(dwType)) + { + case 0: + { + buf = String_val(lpData); + cbData = strlen(buf) + 1; + type = REG_SZ; + break; + } + default: + { + caml_failwith("OPAMW_WriteRegistry: value not implemented"); + break; + } + } + if (RegSetValueEx(key, + String_val(lpValueName), + 0, + type, + (LPBYTE)buf, + cbData) != ERROR_SUCCESS) + { + RegCloseKey(key); + caml_failwith("RegSetValueEx"); + } + RegCloseKey(key); + break; + } + case ERROR_FILE_NOT_FOUND: + { + caml_raise_not_found(); + break; + } + default: + { + caml_failwith("RegOpenKeyEx"); + break; + } + } +#endif + + OPAMreturn(Val_unit); +} + +CAMLprim value OPAMW_GetConsoleOutputCP(value unit) +{ + CAMLparam1(unit); + + OPAMreturn(Val_int(GetConsoleOutputCP())); +} + +CAMLprim value OPAMW_GetCurrentConsoleFontEx(value hConsoleOutput, + value bMaximumWindow) +{ + CAMLparam2(hConsoleOutput, bMaximumWindow); +#ifdef _WIN32 + CAMLlocal3(result, coord, name); + + int len; + CONSOLE_FONT_INFOEX fontInfo; + fontInfo.cbSize = sizeof(fontInfo); + + if (GetCurrentConsoleFontEx(HANDLE_val(hConsoleOutput), + Bool_val(bMaximumWindow), + &fontInfo)) + { + result = caml_alloc(5, 0); + Store_field(result, 0, Val_int(fontInfo.nFont)); + coord = caml_alloc(2, 0); + Store_field(coord, 0, Val_int(fontInfo.dwFontSize.X)); + Store_field(coord, 0, Val_int(fontInfo.dwFontSize.Y)); + Store_field(result, 1, coord); + Store_field(result, 2, Val_int(fontInfo.FontFamily)); + Store_field(result, 3, Val_int(fontInfo.FontWeight)); + Store_field(result, 4, caml_copy_string_of_utf16(fontInfo.FaceName)); + } + else + { + caml_raise_not_found(); + } +#endif + + OPAMreturn(result); +} + +CAMLprim value OPAMW_CreateGlyphChecker(value fontName) +{ + CAMLparam1(fontName); +#ifdef _WIN32 + CAMLlocal2(result, handle); + + /* + * Any device context will do to load the font, so use the Screen DC. + */ + HDC hDC = GetDC(NULL); + + if (hDC) + { + wchar_t* lpszFace = caml_stat_strdup_to_utf16(String_val(fontName)); + HFONT hFont = + CreateFontW(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, + OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, + DEFAULT_PITCH, lpszFace); + caml_stat_free(lpszFace); + + if (hFont) + { + if (SelectObject(hDC, hFont)) + { + result = caml_alloc_tuple(2); + handle = caml_alloc_custom(&HandleOps, sizeof(HANDLE), 0, 1); + HANDLE_val(handle) = hDC; + Store_field(result, 0, handle); + handle = caml_alloc_custom(&HandleOps, sizeof(HANDLE), 0, 1); + HANDLE_val(handle) = hFont; + Store_field(result, 1, handle); + } + else + { + caml_failwith("OPAMW_CheckGlyphs: SelectObject"); + } + } + else + { + caml_failwith("OPAMW_CheckGlyphs: CreateFontW"); + } + } + else + { + caml_failwith("OPAMW_CheckGlyphs: GetDC"); + } +#endif + + OPAMreturn(result); +} + +CAMLprim value OPAMW_DeleteGlyphChecker(value checker) +{ + CAMLparam1(checker); + +#ifdef _WIN32 + DeleteObject(HANDLE_val(Field(checker, 1))); + ReleaseDC(NULL, HANDLE_val(Field(checker, 0))); +#endif + + CAMLreturn(Val_unit); +} + +CAMLprim value OPAMW_HasGlyph(value checker, value scalar) +{ + CAMLparam2(checker, scalar); +#ifdef _WIN32 + BOOL result = FALSE; + HDC hDC = HANDLE_val(Field(checker, 0)); + + WCHAR test = (WCHAR)Int_val(scalar); + WORD index = 0; + + switch (GetGlyphIndicesW(hDC, &test, 1, &index, GGI_MARK_NONEXISTING_GLYPHS)) + { + case 1: + break; + case GDI_ERROR: + caml_failwith("OPAMW_CheckGlyphs: GetGlyphIndicesW"); + default: + caml_failwith("OPAMW_CheckGlyphs: GetGlyphIndicesW (unexpected return)"); + } +#endif + + OPAMreturn(Val_bool(index != 0xffff)); +} + +CAMLprim value OPAMW_process_putenv(value pid, value key, value val) +{ + CAMLparam3(pid, key, val); +#ifdef _WIN32 + CAMLlocal1(res); + + char* result; + + /* + * MSDN is all over the place as to what the technical limits are for + * environment variables (looks like 32KiB for both both name and value) + * however there's no need to inject 64KiB data each time - hence 4KiB limit. + */ + if (caml_string_length(key) > 4095 || caml_string_length(val) > 4095) + caml_invalid_argument("Strings too long"); + + result = + InjectSetEnvironmentVariable(Int32_val(pid), + String_val(key), + String_val(val)); + + if (result == NULL) + { + res = Val_true; + } + else if (strlen(result) == 0) + { + res = Val_false; + } + else + { + caml_failwith(result); + } +#endif + + OPAMreturn(res); +} + +CAMLprim value OPAMW_IsWoW64Process(value pid) +{ + CAMLparam1(pid); + +#ifdef _WIN32 + BOOL result = FALSE; + + if (has_IsWoW64Process()) + { + HANDLE hProcess = + OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, Int32_val(pid)); + + if (hProcess) + { + if (!IsWoW64Process(hProcess, &result)) + result = FALSE; + CloseHandle(hProcess); + } + } +#endif + + OPAMreturn(Val_bool(result)); +} + +/* + * Somewhat against my better judgement, wrap SHGetFolderPath rather than + * SHGetKnownFolderPath to maintain XP compatibility. OPAM already requires + * Windows Vista+ because of GetCurrentConsoleFontEx, but there may be a + * workaround for that for XP lusers. + */ +CAMLprim value OPAMW_SHGetFolderPath(value nFolder, value dwFlags) +{ + CAMLparam2(nFolder, dwFlags); +#ifdef _WIN32 + CAMLlocal1(result); + TCHAR szPath[MAX_PATH]; + + if (SUCCEEDED(SHGetFolderPath(NULL, + Int_val(nFolder), + NULL, + Int_val(dwFlags), + szPath))) + result = caml_copy_string(szPath); + else + caml_failwith("OPAMW_SHGetFolderPath"); +#endif + + OPAMreturn(result); +} + +CAMLprim value OPAMW_SendMessageTimeout(value hWnd, + value uTimeout, + value fuFlags, + value vmsg, + value vwParam, + value vlParam) +{ + CAMLparam5(hWnd, vmsg, vwParam, vlParam, fuFlags); + CAMLxparam1(uTimeout); +#ifdef _WIN32 + CAMLlocal1(result); + + DWORD_PTR dwReturnValue; + HRESULT lResult; + WPARAM wParam; + LPARAM lParam; + UINT msg; + + switch (Int_val(vmsg)) + { + case 0: + { + msg = WM_SETTINGCHANGE; + wParam = Int_val(vwParam); + lParam = (LPARAM)String_val(vlParam); + break; + } + default: + { + caml_failwith("OPAMW_SendMessageTimeout: message not implemented"); + break; + } + } + + lResult = + SendMessageTimeout((HWND)Nativeint_val(hWnd), + msg, + wParam, + lParam, + Int_val(fuFlags), + Int_val(uTimeout), + &dwReturnValue); + + switch (Int_val(vmsg)) + { + case 0: + { + result = caml_alloc(2, 0); + Store_field(result, 0, Val_int(lResult)); + Store_field(result, 1, Val_int(dwReturnValue)); + break; + } + } +#endif + + OPAMreturn(result); +} + +CAMLprim value OPAMW_SendMessageTimeout_byte(value * v, int n) +{ + return OPAMW_SendMessageTimeout(v[0], v[1], v[2], v[3], v[4], v[5]); +} + +CAMLprim value OPAMW_GetParentProcessID(value processId) +{ + CAMLparam1(processId); + +#ifdef _WIN32 + PROCESSENTRY32 entry; + char* msg; + /* + * Create a Toolhelp Snapshot of running processes + */ + HANDLE hProcessSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if ((msg = getProcessInfo(hProcessSnapshot, Int32_val(processId), &entry))) + caml_failwith(msg); + + /* + * Finished with the snapshot + */ + CloseHandle(hProcessSnapshot); +#endif + + OPAMreturn(caml_copy_int32(entry.th32ParentProcessID)); +} + +CAMLprim value OPAMW_GetConsoleAlias(value alias, value exeName) +{ + CAMLparam2(alias, exeName); +#ifdef _WIN32 + CAMLlocal1(result); + + DWORD nLength = 8192; + LPTSTR buffer = (LPTSTR)malloc(nLength); + + if (!buffer) + caml_raise_out_of_memory(); + + if (GetConsoleAlias((LPTSTR)String_val(alias), buffer, nLength, + (LPTSTR)String_val(exeName))) + { + result = caml_copy_string(buffer); + } + else + { + result = caml_copy_string(""); + } + + free(buffer); +#endif + + OPAMreturn(result); +} diff -Nru opam-2.0.10/src/tools/dune opam-2.1.2/src/tools/dune --- opam-2.0.10/src/tools/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/tools/dune 2021-12-07 16:09:27.000000000 +0000 @@ -1,26 +1,25 @@ -(executable +(library (name opam_admin_top) + (public_name opam-admin.top) + (synopsis "OCaml Package Manager admin toplevel") (modules opam_admin_top) + (libraries opam-client opam-file-format compiler-libs.toplevel re) + (wrapped false)) + +(executable + (name opam_admin_topstart) + (public_name opam-admin.top) + (package opam-admin) + (modes byte) + (modules opam_admin_topstart) + (libraries opam-admin.top) (ocamlc_flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp) - -linkall)) - (libraries opam-client opam-file-format compiler-libs.toplevel re)) - -(install - (section bin) - (package opam-admin) - (files (opam_admin_top.bc as opam-admin.top))) - -(executable - (name opam_check) - (modules opam_check) - (flags (:standard - (:include ../ocaml-flags-standard.sexp) - (:include ../ocaml-context-flags.sexp))) - (libraries opam-state)) + -linkall))) -(include opam-putenv.inc) +(rule (with-stdout-to opam_admin_topstart.ml (echo "include Opam_admin_top\n\nlet _ = Topmain.main ()"))) (executable (name opam_installer) @@ -30,4 +29,5 @@ (libraries opam-format cmdliner) (flags (:standard (:include ../ocaml-flags-standard.sexp) + (:include ../ocaml-flags-configure.sexp) (:include ../ocaml-context-flags.sexp)))) diff -Nru opam-2.0.10/src/tools/opam_admin_top.ml opam-2.1.2/src/tools/opam_admin_top.ml --- opam-2.0.10/src/tools/opam_admin_top.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/tools/opam_admin_top.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2014 OCamlPro *) +(* Copyright 2014-2019 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -11,12 +11,11 @@ (* To be used for quick repo scripts using the toplevel *) open OpamFilename.Op open OpamStd.Op -open OpamTypes let identity _ x = x let true_ _ = true -let repo = OpamRepositoryBackend.local (OpamFilename.cwd ()) +let repo = OpamFilename.cwd () let packages = OpamRepository.packages repo let wopt w f = function @@ -51,15 +50,15 @@ if not quiet then OpamConsole.msg "Processing package %s... " (OpamPackage.to_string package); - let opam_file = OpamRepositoryPath.opam repo.repo_root prefix package in + let opam_file = OpamRepositoryPath.opam repo prefix package in let opam = OpamFile.OPAM.read opam_file in - let descr_file = OpamRepositoryPath.descr repo.repo_root prefix package in + let descr_file = OpamRepositoryPath.descr repo prefix package in let descr = OpamFile.Descr.read_opt descr_file in - let url_file = OpamRepositoryPath.url repo.repo_root prefix package in + let url_file = OpamRepositoryPath.url repo prefix package in let url = OpamFile.URL.read_opt url_file in let dot_install_file : OpamFile.Dot_install.t OpamFile.t = OpamFile.make - (OpamRepositoryPath.files repo.repo_root prefix package + (OpamRepositoryPath.files repo prefix package // (OpamPackage.Name.to_string (OpamPackage.name package) ^ ".install")) in let dot_install = OpamFile.Dot_install.read_opt dot_install_file in @@ -129,5 +128,3 @@ List.exists (fun re -> OpamStd.String.exact_match re str) regexps let filter_packages = filter OpamPackage.to_string - -let _ = Topmain.main () diff -Nru opam-2.0.10/src/tools/opam_admin_top.mli opam-2.1.2/src/tools/opam_admin_top.mli --- opam-2.0.10/src/tools/opam_admin_top.mli 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/tools/opam_admin_top.mli 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2012-2015 OCamlPro *) +(* Copyright 2012-2019 OCamlPro *) (* Copyright 2012 INRIA *) (* *) (* All rights reserved. This file is distributed under the terms of the *) @@ -12,7 +12,7 @@ (** Small lib for writing opam-repo admin scripts *) (** The current repo (taken from CWD!) *) -val repo : OpamTypes.repository +val repo : OpamTypes.dirname (** All defined packages in the current repo *) val packages : OpamPackage.Set.t diff -Nru opam-2.0.10/src/tools/opam_check.ml opam-2.1.2/src/tools/opam_check.ml --- opam-2.0.10/src/tools/opam_check.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/tools/opam_check.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,63 +0,0 @@ -(**************************************************************************) -(* *) -(* Copyright 2012-2015 OCamlPro *) -(* Copyright 2012 INRIA *) -(* *) -(* All rights reserved. This file is distributed under the terms of the *) -(* GNU Lesser General Public License version 2.1, with the special *) -(* exception on linking described in the file LICENSE. *) -(* *) -(**************************************************************************) - -(* Utility helper to check if a given set of packages is installed *) - -let usage = "opam-check [--root root] [-l label] +" - -let label = ref "" -let root_dir_ref = ref "" -let spec = Arg.align [ - ("--root", Arg.Set_string root_dir_ref, " Set opam path"); - ("-l" , Arg.Set_string label , " Set a test label"); - ("--version", Arg.Unit OpamVersion.message , " Display version information"); - ] - -let packages = ref [] -let ano x = packages := x :: !packages - -let () = - Arg.parse spec ano usage; - let root_dir = match !root_dir_ref with - | "" -> None - | d -> Some (OpamFilename.Dir.of_string d) - in - OpamSystem.init(); - OpamStd.Config.init(); - OpamFormatConfig.init(); - OpamRepositoryConfig.init(); - OpamStateConfig.init - ?root_dir - () - - -let packages = OpamPackage.Set.of_list (List.map OpamPackage.of_string !packages) - -let installed () = - let root = OpamStateConfig.(!r.root_dir) in - let config = OpamFile.Config.read (OpamPath.config root) in - let version = match OpamFile.Config.switch config with - | Some sw -> sw - | None -> failwith "No switch set" in - let state = OpamFile.SwitchSelections.safe_read (OpamPath.Switch.selections root version) in - state.OpamTypes.sel_installed - -let () = - let installed = installed () in - let diff1 = OpamPackage.Set.diff packages installed in - let diff2 = OpamPackage.Set.diff installed packages in - let diff = OpamPackage.Set.union diff1 diff2 in - let label = if !label = "" then "" else Printf.sprintf "[%s] " !label in - if not (OpamPackage.Set.is_empty diff) then ( - OpamConsole.error "%swaiting for: %s" label (OpamPackage.Set.to_string diff1); - OpamConsole.error "%sgot: %s" label (OpamPackage.Set.to_string diff2); - exit 1 - ) diff -Nru opam-2.0.10/src/tools/opam_installer.ml opam-2.1.2/src/tools/opam_installer.ml --- opam-2.0.10/src/tools/opam_installer.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/tools/opam_installer.ml 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,6 @@ (**************************************************************************) (* *) -(* Copyright 2013 OCamlPro *) +(* Copyright 2013-2018 OCamlPro *) (* *) (* All rights reserved. This file is distributed under the terms of the *) (* GNU Lesser General Public License version 2.1, with the special *) @@ -156,7 +156,16 @@ where [dest src dst] returns the destination of a file with a ["src" {"dst"}] line in the .install *) let iter_install f instfile o = - let module D = OpamPath.Switch.Default in + let open OpamFilename.Op in + let module D = + struct + include OpamPath.Switch.DefaultF (struct + type ctx = unit + let root d _ = d + let lib_dir t a = root t a / "lib" + end) + end + in let module S = OpamFile.Dot_install in let dest ?fix dir = let dir = OpamStd.Option.default dir fix in @@ -164,18 +173,14 @@ OpamFilename.create dir (OpamStd.Option.default (OpamFilename.basename src) dst) in - let dest_global ?fix instdir_f = - dest ?fix (instdir_f o.prefix (OpamSwitch.of_string "")) - in + let dest_global ?fix instdir_f = dest ?fix (instdir_f o.prefix ()) in let dest_pkg ?fix instdir_f = let fix = - OpamStd.Option.map - OpamFilename.Op.(fun d -> - d / OpamPackage.Name.to_string o.pkgname) + OpamStd.Option.map (fun d -> + d / OpamPackage.Name.to_string o.pkgname) fix in - dest ?fix - (instdir_f o.prefix (OpamSwitch.of_string "") o.pkgname) + dest ?fix (instdir_f o.prefix () o.pkgname) in List.iter f [ dest_global D.bin, S.bin instfile, true; @@ -381,7 +386,7 @@ let () = OpamSystem.init (); - OpamStd.Config.init (); + OpamCoreConfig.init (); try match Term.eval ~catch:false (command,info) diff -Nru opam-2.0.10/src/tools/opam-putenv.c opam-2.1.2/src/tools/opam-putenv.c --- opam-2.0.10/src/tools/opam-putenv.c 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/tools/opam-putenv.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -/**************************************************************************/ -/* */ -/* Copyright 2015, 2016, 2017, 2018 MetaStack Solutions Ltd. */ -/* */ -/* OPAM is distributed in the hope that it will be useful, but WITHOUT */ -/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY */ -/* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public */ -/* License for more details. */ -/* */ -/**************************************************************************/ - -#include -/* - * This will be being built for a different architecture, so it's easier just to - * #include the code, rather than having to deal with .o(obj) files for - * different architectures. - */ -#include "../stubs/opamInject.c" - -/* - * This trivially simple utility takes a single PID and then reads CRLF - * terminated lines from STDIN. The line ::QUIT causes the program to terminate - * otherwise a further line is read and the two lines together form the - * key/value pair to be set in the process's environment. - * - * This utility is always compiled x86 if OPAM is compiled x64 and vice versa - * and allows OPAM to manipulate a parent whose architecture differs from its - * own. When the architecture matches, OPAM injects the code itself, but - * injecting from a 64-bit process to a 32-bit parent is quite hard (and - * potentially unstable) and injecting from a 32-bit process to a 64-bit parent - * is phenomenally hard! - */ -int main(int argc, char *argv[], char *envp[]) -{ - if (argc != 2) - { - printf("Invalid command line: this utility is an internal part of OPAM\n"); - } - else - { - DWORD pid = atoi(argv[1]); - BOOL running = TRUE; - char* key = (char*)malloc(4097); - char* value = (char*)malloc(4097); - - while (running) - { - if (fgets(key, 4097, stdin)) - { - if (strcmp(key, "::QUIT\n") && fgets(value, 4097, stdin)) - { - key[strlen(key) - 1] = value[strlen(value) - 1] = '\0'; - InjectSetEnvironmentVariable(pid, key, value); - } - else - { - running = FALSE; - } - } - else - { - running = FALSE; - } - } - free(key); - free(value); - } -} diff -Nru opam-2.0.10/src/tools/opam-putenv.inc.in opam-2.1.2/src/tools/opam-putenv.inc.in --- opam-2.0.10/src/tools/opam-putenv.inc.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/src/tools/opam-putenv.inc.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -(rule - (targets opam-putenv.exe) - (deps (:source opam-putenv.c) ../stubs/opamInject.c) - (action @CC64_JBUILD@)) - -(install - (section bin) - (package opam) - (files opam-putenv.exe)) diff -Nru opam-2.0.10/tests/dune opam-2.1.2/tests/dune --- opam-2.0.10/tests/dune 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/dune 2021-12-07 16:09:27.000000000 +0000 @@ -1,6 +1,4 @@ -(alias - (name runtest) - (deps (source_tree .) ../src/client/opamMain.exe ../src/tools/opam_check.exe ./bin/ocamlc ./bin/ocamlopt) - (action (run make all))) - -(ignored_subdirs (packages)) +(test + (name patcher) + (modules patcher) + (libraries opam-core)) diff -Nru opam-2.0.10/tests/init-repo.sh opam-2.1.2/tests/init-repo.sh --- opam-2.0.10/tests/init-repo.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/init-repo.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,117 +0,0 @@ -#! /bin/sh - -TEST_DIR=/tmp -OPAM_ROOT=$TEST_DIR/OPAM.ROOT -OPAM_REPO=$TEST_DIR/OPAM.REPO -BIN=$TEST_DIR/OPAM.BIN -REPO=test - -BINARIES=opam - -# opam in the path should not be a requirement -ENV="OCAMLRUNPARAM=b OPAMDEBUG=2 OPAM_ROOT=$OPAM_ROOT PATH=$BIN:$PATH" -ENV="OCAMLRUNPARAM=b OPAM_ROOT=$OPAM_ROOT PATH=$BIN:$PATH" -OPAM="$ENV opam --yes --root $OPAM_ROOT" - -function binaries() { - mkdir -p $BIN - for bin in $BINARIES; do \ - cp ../_obuild/$bin/$bin.asm $BIN/$bin ; \ - done -} - -function opam_clean() { - rm -rf $ARCHIVES - rm -rf $OPAM_ROOT $BIN - rm -rf $OPAM_REPO -} - -function opam_init() { - mkdir -p $OPAM_REPO - binaries - eval $OPAM init -no-base-packages $REPO $OPAM_REPO -kind rsync -} - -function opam_upload_stage1() { - - cd packages - eval $OPAM upload -opam P1-1.opam -descr P1-1/README -archive P1-1.tar.gz -repo $REPO - eval $OPAM upload -opam P2.opam -descr P2/README -archive P2.tar.gz -repo $REPO - eval $OPAM upload -opam P3.opam -descr P3/README -archive P3.tar.gz -repo $REPO - eval $OPAM upload -opam P4-1.opam -descr P4/README -archive P4.tar.gz -repo $REPO - eval $OPAM upload -opam P5.opam -descr P5/README -archive P5.tar.gz -repo $REPO - cd - - - cp compilers/* $OPAM_REPO/compilers/ - # update the list of available packages with the one being updated - eval $OPAM update -} - -function opam_upload_stage2() { - - cd packages - eval $OPAM upload -opam P1-2.opam -descr P1-2/README -archive P1-2.tar.gz -repo $REPO - eval $OPAM upload -opam P4-2.opam -descr P4/README -archive P4.tar.gz -repo $REPO - eval $OPAM upload -opam P4-3.opam -descr P4/README -archive P4.tar.gz -repo $REPO - cd - - - # update the list of available packages with the one being updated - eval $OPAM update -} -function usage() { -DESCRIPTION="Opam unittest init functions" -cat << EOF -usage: $0 options - -$DESCRIPTION - -OPTIONS: - -h Show this message - -v Verbose - -d Debug - -i Init - -c Clean -EOF -} - -VERBOSE= -DEBUG= -INIT= -CLEAN= -STAGE= - -while getopts "vhdcis:" flag -do - case "$flag" in - d) set -x ; DEBUG=true;; - v) VERBOSE=true ;; - i) INIT=true ;; - s) STAGE=$OPTARG ;; - c) CLEAN=true ;; - h) usage ; exit 0 ;; - esac -# echo "$flag" $OPTIND $OPTARG -done - -if [ -n "$INIT" ]; then - opam_clean - opam_init -fi - -if [ -n "$STAGE" ]; then - if [ $STAGE = "1" ]; then - opam_upload_stage1 - fi - - if [ $STAGE = "2" ]; then - opam_upload_stage2 - fi -fi - -if [ -n "$CLEAN" ]; then - opam_clean -fi - -exit 0 - - diff -Nru opam-2.0.10/tests/Makefile opam-2.1.2/tests/Makefile --- opam-2.0.10/tests/Makefile 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/Makefile 1970-01-01 00:00:00.000000000 +0000 @@ -1,316 +0,0 @@ -PATH = $(PWD)/bin:$(shell echo $$PATH) - -TMP_DIR = $(realpath .)/tmp -OPAM_ROOT = $(TMP_DIR)/OPAM.ROOT -OPAM_REPO = $(TMP_DIR)/OPAM.REPO - -# repositoy name -REPO = test -REPOKIND ?= local - -# To test GIT repo -OPAM_GIT = $(TMP_DIR)/OPAM.GIT - -PACKAGES = P1-0 P1-1 P1-2 P2 P3 P4 P5 - -unexport OCAMLLIB - -ifndef OPAM - OPAM = $(firstword $(realpath ../../install/default/bin/opam$(EXE) ../_build/install/default/bin/opamMain.exe)) -endif -ENV = PATH=$(PATH) $(DEBUG) OPAMKEEPBUILDDIR=1 OPAMROOT=$(OPAM_ROOT) OPAMSWITCH= OPAMNOBASEPACKAGES=1 OPAMYES=1 OPAM=$(OPAM) -OPAMBIN = $(ENV) $(OPAM) -ifndef CHECK - CHECK = $(ENV) $(firstword $(realpath ../src/tools/opam_check.exe ../_build/default/src/tools/opam_check.exe)) -endif - -ifeq ($(OPAMTESTQUIET), 1) - DEBUG = -else - DEBUG = OPAMDEBUG=2 OCAMLRUNPARAM=b -endif - -ARCHIVES = $(PACKAGES:%=packages/%.tar.gz) - -.PHONY: all local git - -all: local git - @ - -quiet: - $(MAKE) OPAMTESTQUIET=1 all - -printf = /usr/bin/printf - -define RUN -@COUNT=$$(ls -1 $(REPOKIND)-*.log 2>/dev/null | wc -l); \ -LOG=$$($(printf) "$(REPOKIND)-%02d-$(1).log" $$COUNT); \ -$(printf) " %02d \e[1m%-20s\e[0m ..................................... " \ - $$COUNT $(1); \ -if $(MAKE) $(1) >$$LOG 2>&1; then \ - $(printf) "\e[32m[ OK ]\e[0m\n"; \ -else \ - $(printf) "\e[31m$(1) FAILED\e[0m\n\n" >>$$LOG; \ - $(printf) "\e[31m[FAIL]\e[0m\n"; \ - { $(printf) "\e[31m>> %s FAILED <<\e[0m\n" $(1); cat $$LOG; } \ - >> failed-$(REPOKIND).log; \ -fi; \ -cat $$LOG >> fulltest-$(REPOKIND).log -endef - -run: - @rm -f failed-$(REPOKIND).log fulltest-$(REPOKIND).log - @rm -f $(REPOKIND)-*.log - $(call RUN,init) - $(call RUN,upload) - $(call RUN,install-remove) - $(call RUN,list) - $(call RUN,install-opt) - $(call RUN,list) - $(call RUN,install) - $(call RUN,list) - $(call RUN,reinstall) - $(call RUN,list) - $(call RUN,upload-new) - $(call RUN,list) - $(call RUN,upgrade) - $(call RUN,list) - $(call RUN,downgrade) - $(call RUN,list) - $(call RUN,switch-alias) - $(call RUN,list) - $(call RUN,switch-env-packages) - $(call RUN,repo) - $(call RUN,list) - @if [ -e failed-$(REPOKIND).log ]; \ - then echo "FAILED! Logs in `pwd`/failed-$(REPOKIND).log"; exit 1; \ - else echo "SUCCESS!"; fi - -local: - @$(MAKE) --silent --no-print-directory clean - $(MAKE) --no-print-directory REPOKIND=local run - -git: - @$(MAKE) --silent --no-print-directory clean - $(MAKE) --no-print-directory REPOKIND=git run - -define GIT_INIT - git init && echo '*.sh text eol=lf'> .gitattributes && $(if $1,touch $1 && )\ - git add -A && git config --local user.name 'OPAM test environment' &&\ - git config --local user.email noreply@ocaml.org && git commit -m "Initial commit" -endef - -init: - rm -rf $(OPAM_REPO) - mkdir -p $(OPAM_REPO) -ifeq ($(REPOKIND), git) - cd $(OPAM_REPO) && $(call GIT_INIT,README) -endif - $(OPAMBIN) init --bare --no-setup --disable-sandboxing $(REPO) $(OPAM_REPO) -k $(REPOKIND) - -define mkurl - echo 'src: "http://dev.null" checksum: "'`openssl md5 packages/$(2) |cut -d' ' -f2`'"' \ - > $(OPAM_REPO)/packages/$(1)/url -endef - -upload: $(ARCHIVES) - cp -r packages/ocaml $(OPAM_REPO)/packages - mkdir -p $(OPAM_REPO)/packages/P1.0 - cp packages/P1-0.opam $(OPAM_REPO)/packages/P1.0/opam - $(call mkurl,P1.0,P1-0.tar.gz) - mkdir -p $(OPAM_REPO)/packages/P1.1 - cp packages/P1-1.opam $(OPAM_REPO)/packages/P1.1/opam - cp packages/P1-1/README $(OPAM_REPO)/packages/P1.1/descr - $(call mkurl,P1.1,P1-1.tar.gz) - mkdir -p $(OPAM_REPO)/packages/P2.1 - cp packages/P2/README $(OPAM_REPO)/packages/P2.1/descr - cp packages/P2.opam $(OPAM_REPO)/packages/P2.1/opam - $(call mkurl,P2.1,P2.tar.gz) - mkdir -p $(OPAM_REPO)/packages/P3.1~weird-version.test - cp packages/P3.opam $(OPAM_REPO)/packages/P3.1~weird-version.test/opam - cp packages/P3/README $(OPAM_REPO)/packages/P3.1~weird-version.test/descr - $(call mkurl,P3.1~weird-version.test,P3.tar.gz) - mkdir -p $(OPAM_REPO)/packages/P4.1 - cp packages/P4-1.opam $(OPAM_REPO)/packages/P4.1/opam - cp packages/P4/README $(OPAM_REPO)/packages/P4.1/descr - $(call mkurl,P4.1,P4.tar.gz) - mkdir -p $(OPAM_REPO)/packages/P5.1 - cp packages/P5.opam $(OPAM_REPO)/packages/P5.1/opam - cp packages/P5/README $(OPAM_REPO)/packages/P5.1/descr - $(call mkurl,P5.1,P5.tar.gz) -ifeq ($(REPOKIND), git) - cd $(OPAM_REPO)/packages/ocaml.system && git add * && git commit -m "Adding ocaml.system" - cd $(OPAM_REPO)/packages/ocaml.20 && git add * && git commit -m "Adding ocaml.20" - cd $(OPAM_REPO)/packages/ocaml.10+a+b && git add * && git commit -m "Adding ocaml.10+a+b" - echo 'git: "$(OPAM_GIT)/P1-0"' > $(OPAM_REPO)/packages/P1.0/url - cd $(OPAM_REPO)/packages/P1.0/ && git add * && git commit -m "Adding P0" - echo 'git: "$(OPAM_GIT)/P1-1"' > $(OPAM_REPO)/packages/P1.1/url - cd $(OPAM_REPO)/packages/P1.1/ && git add * && git commit -m "Adding P1" - echo 'git: "$(OPAM_GIT)/P2"' > $(OPAM_REPO)/packages/P2.1/url - cd $(OPAM_REPO)/packages/P2.1/ && git add * && git commit -m "Adding P2" - echo 'git: "$(OPAM_GIT)/P3"' > $(OPAM_REPO)/packages/P3.1~weird-version.test/url - cd $(OPAM_REPO)/packages/P3.1~weird-version.test/ && git add * && git commit -m "Adding P3" - echo 'git: "$(OPAM_GIT)/P4"' > $(OPAM_REPO)/packages/P4.1/url - cd $(OPAM_REPO)/packages/P4.1/ && git add * && git commit -m "Adding P4" - echo 'git: "$(OPAM_GIT)/P5"' > $(OPAM_REPO)/packages/P5.1/url - cd $(OPAM_REPO)/packages/P5.1/ && git add * && git commit -m "Adding P5" - rm -rf $(OPAM_GIT) && mkdir -p $(OPAM_GIT) - mkdir $(OPAM_GIT)/P1-0 && cp packages/P1-0/* $(OPAM_GIT)/P1-0/ - mkdir $(OPAM_GIT)/P1-1 && cp packages/P1-1/* $(OPAM_GIT)/P1-1/ - mkdir $(OPAM_GIT)/P2 && cp packages/P2/* $(OPAM_GIT)/P2/ - mkdir $(OPAM_GIT)/P3 && cp packages/P3/* $(OPAM_GIT)/P3/ - mkdir $(OPAM_GIT)/P4 && cp packages/P4/* $(OPAM_GIT)/P4/ - mkdir $(OPAM_GIT)/P5 && cp packages/P5/* $(OPAM_GIT)/P5/ - cd $(OPAM_GIT)/P1-0 && $(call GIT_INIT) - cd $(OPAM_GIT)/P1-1 && $(call GIT_INIT) - cd $(OPAM_GIT)/P2 && $(call GIT_INIT) - cd $(OPAM_GIT)/P3 && $(call GIT_INIT) - cd $(OPAM_GIT)/P4 && $(call GIT_INIT) - cd $(OPAM_GIT)/P5 && $(call GIT_INIT) -else - mkdir -p $(OPAM_REPO)/cache - for p in P1-0 P1-1 P1-2 P2 P3 P4 P5; do \ - f=packages/$$p.tar.gz; \ - md5=`openssl md5 $$f |cut -d' ' -f2`; \ - dir=$(OPAM_REPO)/cache/md5/`echo $$md5 |head -c2`; \ - mkdir -p $$dir; \ - cp $$f $$dir/$$md5; \ - done - echo 'archive-mirrors: "$(OPAM_REPO)/cache"' >> $(OPAM_REPO)/repo -endif - $(OPAMBIN) update - $(OPAMBIN) switch create system --packages ocaml.system - -list: - $(OPAMBIN) list -A - -install-remove: - $(CHECK) -l install-remove-1 ocaml.system - $(OPAMBIN) install P1 - $(CHECK) -l install-remove-2 ocaml.system P1.1 - $(OPAMBIN) remove P1 - $(CHECK) -l install-remove-3 ocaml.system - -install-opt: - $(CHECK) -l install-opt-1 ocaml.system - $(OPAMBIN) install P5 - test -f $(OPAM_ROOT)/system/lib/p5/p2_absent - $(CHECK) -l install-opt-2 ocaml.system P1.1 P5.1 - $(OPAMBIN) remove P5 - $(CHECK) -l install-opt-3 ocaml.system P1.1 - $(OPAMBIN) install P5 - $(CHECK) -l install-opt-4 ocaml.system P1.1 P5.1 - $(OPAMBIN) remove P5 -a - $(CHECK) -l install-opt-5 ocaml.system - $(OPAMBIN) install P5 - $(CHECK) -l install-opt-6 ocaml.system P1.1 P5.1 - $(OPAMBIN) install P2 - test -f $(OPAM_ROOT)/system/lib/p5/p2_present - $(CHECK) -l install-opt-7 ocaml.system P1.1 P2.1 P5.1 - $(OPAMBIN) remove P5 -a - $(CHECK) -l install-opt-8 ocaml.system P1.1 P2.1 - $(OPAMBIN) remove P2 -a - $(CHECK) -l install-opt-9 ocaml.system - $(OPAMBIN) install P1 P2 P5 - test -f $(OPAM_ROOT)/system/lib/p5/p2_present - $(CHECK) -l install-opt-10 ocaml.system P1.1 P2.1 P5.1 - $(OPAMBIN) remove P2 -a - test -f $(OPAM_ROOT)/system/lib/p5/p2_absent - $(CHECK) -l install-opt-11 ocaml.system P1.1 P5.1 - $(OPAMBIN) remove P1 - $(CHECK) -l install-opt-12 ocaml.system - -install: - $(CHECK) -l install-1 ocaml.system - $(OPAMBIN) install P1 - $(CHECK) -l install-2 ocaml.system P1.1 - $(OPAMBIN) install P2 - $(CHECK) -l install-3 ocaml.system P1.1 P2.1 - $(OPAMBIN) install P3 - $(CHECK) -l install-4 ocaml.system P1.1 P2.1 P3.1~weird-version.test - $(OPAMBIN) install P4 - $(CHECK) -l install-5 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.1 - -reinstall: - $(CHECK) -l reinstall-1 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.1 - $(OPAMBIN) reinstall P1 - $(CHECK) -l reinstall-2 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.1 - -upload-new: - mkdir $(OPAM_REPO)/packages/P4.2 - cp packages/P4-2.opam $(OPAM_REPO)/packages/P4.2/opam - cp packages/P4/README $(OPAM_REPO)/packages/P4.2/descr - $(call mkurl,P4.2,P4.tar.gz) - mkdir $(OPAM_REPO)/packages/P4.3 - cp packages/P4-3.opam $(OPAM_REPO)/packages/P4.3/opam - cp packages/P4/README $(OPAM_REPO)/packages/P4.3/descr - $(call mkurl,P4.3,P4.tar.gz) -ifeq ($(REPOKIND), git) - echo "(* new line *)" >> $(OPAM_GIT)/P1-1/p1.ml - cd $(OPAM_GIT)/P1-1 && git commit -a -m "a small change" - echo 'git: "$(OPAM_GIT)/P4"' > $(OPAM_REPO)/packages/P4.2/url - echo 'git: "$(OPAM_GIT)/P4"' > $(OPAM_REPO)/packages/P4.3/url - cd $(OPAM_REPO) && git add * && git commit -m "Adding P4.2 and P4.3" -else - mkdir $(OPAM_REPO)/packages/P1.2 - cp packages/P1-2.opam $(OPAM_REPO)/packages/P1.2/opam - cp packages/P1-2/README $(OPAM_REPO)/packages/P1.2/descr - $(call mkurl,P1.2,P1-2.tar.gz) -endif - $(OPAMBIN) update - -upgrade: - $(CHECK) -l upgrade-1 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.1 - eval `$(OPAMBIN) config env`; [ "X$$P1" = "Xversion1" ] - $(OPAMBIN) upgrade -ifeq ($(REPOKIND), git) - $(CHECK) -l upgrade-2 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.3 -else - $(CHECK) -l upgrade-2 ocaml.system P1.2 P2.1 P3.1~weird-version.test P4.3 - eval `$(OPAMBIN) config env`; [ "X$$P1" = "Xversion2" ] -endif - -downgrade: -ifeq ($(REPOKIND), git) - $(CHECK) -l downgrade-1 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.3 -else - $(CHECK) -l downgrade-1 ocaml.system P1.2 P2.1 P3.1~weird-version.test P4.3 -endif - $(OPAMBIN) install P4.2 - $(CHECK) -l downgrade-2 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.2 - -switch-alias: - $(CHECK) -l switch-alias-1 ocaml.system P1.1 P2.1 P3.1~weird-version.test P4.2 - $(OPAMBIN) remove P3.1~weird-version.test P4.2 - $(CHECK) -l switch-alias-2 ocaml.system P1.1 P2.1 - $(OPAMBIN) switch export $(TMP_DIR)/export - $(OPAMBIN) switch create test system - $(CHECK) -l switch-alias-3 ocaml.system - $(OPAMBIN) switch import $(TMP_DIR)/export - $(CHECK) -l switch-alias-4 ocaml.system P1.1 P2.1 - $(OPAMBIN) switch create test2 20 - $(CHECK) -l switch-alias-5 ocaml.20 - $(OPAMBIN) install P1 - $(CHECK) -l switch-alias-6 ocaml.20 P1.1 - $(OPAMBIN) switch system - $(CHECK) -l switch-alias-7 ocaml.system P1.1 P2.1 - $(OPAMBIN) switch remove test test2 - -switch-env-packages: - $(CHECK) -l switch-env-packages-1 ocaml.system P1.1 P2.1 - $(OPAMBIN) switch install 10+a+b --packages=ocaml.10+a+b,P1,P2,P3,P4 - $(CHECK) -l switch-env-packages-2 ocaml.10+a+b P1.1 P2.1 P3.1~weird-version.test P4.3 - ./test-TEST.sh $(wildcard $(OPAM_ROOT)/10+a+b/build/P4.3/P4*.env) "1" - -repo: - $(OPAMBIN) repo add $(REPO)2 $(OPAM_REPO) -k $(REPOKIND) - $(OPAMBIN) repo remove $(REPO)2 - $(OPAMBIN) repo remove $(REPO) - -packages/%.tar.gz: packages/% - cd packages && tar czf $*.tar.gz $* - -clean: - rm -f test.log fulltest.log - rm -f $(ARCHIVES) - rm -rf $(TMP_DIR) diff -Nru opam-2.0.10/tests/packages/ocaml/ocaml.10+a+b/files/ocaml.config opam-2.1.2/tests/packages/ocaml/ocaml.10+a+b/files/ocaml.config --- opam-2.0.10/tests/packages/ocaml/ocaml.10+a+b/files/ocaml.config 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/ocaml/ocaml.10+a+b/files/ocaml.config 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -opam-version: "1.3~dev4" -variables { - compiler: "10+a+b" - native: true - native-tools: true - native-dynlink: true - stubsdir: "%{lib}%/stublibs" -} diff -Nru opam-2.0.10/tests/packages/ocaml/ocaml.10+a+b/opam opam-2.1.2/tests/packages/ocaml/ocaml.10+a+b/opam --- opam-2.0.10/tests/packages/ocaml/ocaml.10+a+b/opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/ocaml/ocaml.10+a+b/opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -opam-version: "1.3" -maintainer: "contact@ocamlpro.com" -# depends: ["P1" "P2" "P3" "P4"] -flags: compiler -setenv: TEST = "1" diff -Nru opam-2.0.10/tests/packages/ocaml/ocaml.20/files/ocaml.config opam-2.1.2/tests/packages/ocaml/ocaml.20/files/ocaml.config --- opam-2.0.10/tests/packages/ocaml/ocaml.20/files/ocaml.config 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/ocaml/ocaml.20/files/ocaml.config 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -opam-version: "1.3~dev4" -variables { - compiler: "20" - native: true - native-tools: true - native-dynlink: true - stubsdir: "%{lib}%/stublibs" -} diff -Nru opam-2.0.10/tests/packages/ocaml/ocaml.20/opam opam-2.1.2/tests/packages/ocaml/ocaml.20/opam --- opam-2.0.10/tests/packages/ocaml/ocaml.20/opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/ocaml/ocaml.20/opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -opam-version: "1.3" -maintainer: "contact@ocamlpro.com" -flags: compiler -setenv: TEST = "1" diff -Nru opam-2.0.10/tests/packages/ocaml/ocaml.system/files/gen.sh opam-2.1.2/tests/packages/ocaml/ocaml.system/files/gen.sh --- opam-2.0.10/tests/packages/ocaml/ocaml.system/files/gen.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/ocaml/ocaml.system/files/gen.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -#!/bin/sh -ue - -if ! OCAMLC=$(command -v ocamlc); then - echo "No OCaml compiler was found on the system" >&2 - exit 2 -fi - -LIBDIR=$("$OCAMLC" -where) -STUBLIBS=$(cat "$LIBDIR/ld.conf" | tr '\n' ':') - -echo "Using ocaml compiler found at $OCAMLC with base lib at $LIBDIR" - -bool() { - if "$@"; then echo "true"; else echo "false"; fi -} - -cat >ocaml.config < 0 diff -Nru opam-2.0.10/tests/packages/P1-0.opam opam-2.1.2/tests/packages/P1-0.opam --- opam-2.0.10/tests/packages/P1-0.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-0.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -(* API version *) -opam-version: "1" - -name: "P1" -version: "0" - -setenv: [P1 = "version0"] -substs: "P1.config" - -build: [ - [ "ocamlc" "-a" "p1.ml" "-o" "p1.cma" ] - [ "ocamlopt" "-a" "p1.ml" "-o" "p1.cmxa" ] -] -depends: ["ocaml"] diff -Nru opam-2.0.10/tests/packages/P1-1/build.sh opam-2.1.2/tests/packages/P1-1/build.sh --- opam-2.0.10/tests/packages/P1-1/build.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-1/build.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -#! /bin/sh -eu - -if [ -n "${P1:-}" ]; then - echo "P1 ('$P1') should not be set yet" >&2 - exit 12 -fi - -ocamlc -a p1.ml -o p1.cma -ocamlopt -a p1.ml -o p1.cmxa diff -Nru opam-2.0.10/tests/packages/P1-1/P1.config.in opam-2.1.2/tests/packages/P1-1/P1.config.in --- opam-2.0.10/tests/packages/P1-1/P1.config.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-1/P1.config.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -opam-version: "1.3" -variables { - asmcomp: "-I %{lib}%/P1" - bytecomp: "-I %{lib}%/P1" - asmlink: "-I %{lib}%/P1 p1.cmxa" - bytelink: "-I %{lib}%/P1 p1.cma" - LOCAL: "local" - l: "L" - FOO: "foo" - bar: true -} diff -Nru opam-2.0.10/tests/packages/P1-1/P1.install opam-2.1.2/tests/packages/P1-1/P1.install --- opam-2.0.10/tests/packages/P1-1/P1.install 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-1/P1.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -lib: [ - "p1.cmi" - "p1.cma" - "p1.cmxa" - "p1.a" - "?this_file_will_not_exits_but_that's_ok" -] -share: [ "build.sh" ] -doc: [ - "p1.cmi" { "foo/bar/index.html" } -] diff -Nru opam-2.0.10/tests/packages/P1-1/p1.ml opam-2.1.2/tests/packages/P1-1/p1.ml --- opam-2.0.10/tests/packages/P1-1/p1.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-1/p1.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -let x () = - try Random.int 10 - with _ -> 0 diff -Nru opam-2.0.10/tests/packages/P1-1/README opam-2.1.2/tests/packages/P1-1/README --- opam-2.0.10/tests/packages/P1-1/README 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-1/README 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -A very useful package diff -Nru opam-2.0.10/tests/packages/P1-1.opam opam-2.1.2/tests/packages/P1-1.opam --- opam-2.0.10/tests/packages/P1-1.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-1.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ -(* API version *) -opam-version: "1" - -name: "P1" - -# Test -# Toto - -(* Version are arbitrary strings *) -version: "1" - -maintainer: "contact@ocamlpro.com" - -(* The command to run *) -build: [ - ["./build.sh"] # HAHAH - ["this" "should" "never" "run"] {ocaml:version > "z100"} - [make "this" ocaml:version "also"] {os = "NO"} - ["echo" "HAHA!"] {ocaml:version = "10"} - ["echo" make share ocaml:version] - ["this as well" {os = "myOS"}] -] -available: os != "NO" | os != "NO" & os != "YES" - -(* List of files to substitute env variables *) -substs: [ "P1.config" ] - -(* Libraries *) -libraries: [ "p1" ] - -(* External dependencies *) -depexts: [ - [ ["debian" "amd64"] ["foo" "bar"] ] - [ ["osx" ] ["foobar"] ] -] - -messages: [ "I'll always bother you displaying this message" ] - -post-messages: [ "Thanks SO MUCH for installing this humble package" - "Everything went well" {success} - "Nooo, something went wrong, this makes me feel sooo sad..." {failure} ] - -bug-reports: "TEST.com" - -setenv: [P1 = "version1"] -depends: [ - "ocaml" {(!= "20" | != "10") & (= "20" | = "10" | = "10+a+b" | = "system")} -] diff -Nru opam-2.0.10/tests/packages/P1-2/build.sh opam-2.1.2/tests/packages/P1-2/build.sh --- opam-2.0.10/tests/packages/P1-2/build.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-2/build.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -#! /bin/sh -eu - -if [ -n "${P1:-}" ]; then - echo "P1 ('$P1') should not be set yet" >&2 - exit 12 -fi - -ocamlc -a p1.ml -o p1.cma -ocamlopt -a p1.ml -o p1.cmxa diff -Nru opam-2.0.10/tests/packages/P1-2/P1.config.in opam-2.1.2/tests/packages/P1-2/P1.config.in --- opam-2.0.10/tests/packages/P1-2/P1.config.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-2/P1.config.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -opam-version: "1.3" -variables { - asmcomp: "-I %{lib}%/P1" - bytecomp: "-I %{lib}%/P1" - asmlink: "-I %{lib}%/P1 p1.cmxa" - bytelink: "-I %{lib}%/P1 p1.cma" - LOCAL: "local" - l: "L" - FOO: "foo" - bar: true -} diff -Nru opam-2.0.10/tests/packages/P1-2/P1.install opam-2.1.2/tests/packages/P1-2/P1.install --- opam-2.0.10/tests/packages/P1-2/P1.install 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-2/P1.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -lib: [ - "p1.cma" - "p1.cmxa" - "p1.a" - "p1.cmi" -] diff -Nru opam-2.0.10/tests/packages/P1-2/p1.ml opam-2.1.2/tests/packages/P1-2/p1.ml --- opam-2.0.10/tests/packages/P1-2/p1.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-2/p1.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -let x () = - failwith "the new version is not very good" diff -Nru opam-2.0.10/tests/packages/P1-2/README opam-2.1.2/tests/packages/P1-2/README --- opam-2.0.10/tests/packages/P1-2/README 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-2/README 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -A very useful package diff -Nru opam-2.0.10/tests/packages/P1-2.opam opam-2.1.2/tests/packages/P1-2.opam --- opam-2.0.10/tests/packages/P1-2.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P1-2.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -opam-version: "1" -name: "P1" -version: "2" -depends: [ "ocaml" {<= "10" | = "system"} ] -maintainer: "contact@ocamlpro.com" -substs: [ "P1.config" ] -libraries: [ "p1" ] -build: [ "./build.sh" ] -setenv: [P1 = "version2"] diff -Nru opam-2.0.10/tests/packages/P2/build.sh opam-2.1.2/tests/packages/P2/build.sh --- opam-2.0.10/tests/packages/P2/build.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P2/build.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -#! /bin/sh -eu - -OFLAGS="`${OPAM} config var P1:asmcomp`" -CFLAGS="`${OPAM} config var P1:bytecomp`" - -echo "Bytecode Compilation" -ocamlc ${CFLAGS} -a p2.ml -o p2.cma - -if which ocamlopt >/dev/null 2>&1; then - echo "Native Compilation" - ocamlopt ${OFLAGS} -a p2.ml -o p2.cmxa -fi - diff -Nru opam-2.0.10/tests/packages/P2/config.in opam-2.1.2/tests/packages/P2/config.in --- opam-2.0.10/tests/packages/P2/config.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P2/config.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -Foo is %{P1:FOO}% - -Foo also contains a variable with %{P1:l}%. Funny, isn't it? diff -Nru opam-2.0.10/tests/packages/P2/P2.config.in opam-2.1.2/tests/packages/P2/P2.config.in --- opam-2.0.10/tests/packages/P2/P2.config.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P2/P2.config.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -opam-version: "1.3" -variables { - asmcomp: "-I %{lib}%/P2" - bytecomp: "-I %{lib}%/P2" - asmlink: "-I %{lib}%/P2 p2.cmxa" - bytelink: "-I %{lib}%/P2 p2.cma" - requires: "p1" -} diff -Nru opam-2.0.10/tests/packages/P2/P2.install opam-2.1.2/tests/packages/P2/P2.install --- opam-2.0.10/tests/packages/P2/P2.install 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P2/P2.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -lib: [ - "p2.cma" - "p2.cmxa" - "p2.a" - "p2.cmi" -] diff -Nru opam-2.0.10/tests/packages/P2/p2.ml opam-2.1.2/tests/packages/P2/p2.ml --- opam-2.0.10/tests/packages/P2/p2.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P2/p2.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -let g () = - P1.x () diff -Nru opam-2.0.10/tests/packages/P2/README opam-2.1.2/tests/packages/P2/README --- opam-2.0.10/tests/packages/P2/README 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P2/README 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -An other very useful package - -The description can go on multiple lines. The first line is the package synopsis, -and the rest is the package description. diff -Nru opam-2.0.10/tests/packages/P2.opam opam-2.1.2/tests/packages/P2.opam --- opam-2.0.10/tests/packages/P2.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P2.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -opam-version: "1" -name: "P2" -version: "1" -maintainer: "contact@ocamlpro.com" -substs: [ "config" "P2.config" ] -depends: ["ocaml" "P1"] -libraries: [ "p2" ] -build: [ "./build.sh" ] diff -Nru opam-2.0.10/tests/packages/P3/build.sh opam-2.1.2/tests/packages/P3/build.sh --- opam-2.0.10/tests/packages/P3/build.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P3/build.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -#! /bin/sh -eu - -echo "Building P3 version ${OPAM_PACKAGE_VERSION}" - -if [ "x${OPAM_PACKAGE_NAME}" = "xP3" ]; then - LIB=$(${OPAM} config var lib) - ocamlc -a -I $LIB/P1 -I $LIB/P2 p3.ml -o p3.cma - ocamlopt -a -I $LIB/P1 -I $LIB/P2 p3.ml -o p3.cmxa - ocamlc -a -I $LIB/P1 -I $LIB/P2 p3_bar.ml -o p3_bar.cma - ocamlopt -a -I $LIB/P1 -I $LIB/P2 p3_bar.ml -o p3_bar.cmxa -else - exit 1 -fi diff -Nru opam-2.0.10/tests/packages/P3/p3_bar.ml opam-2.1.2/tests/packages/P3/p3_bar.ml --- opam-2.0.10/tests/packages/P3/p3_bar.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P3/p3_bar.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,5 +0,0 @@ -let f () = - Printf.printf "foo\n%!" - -let _ = - P3.z () diff -Nru opam-2.0.10/tests/packages/P3/P3.config.in opam-2.1.2/tests/packages/P3/P3.config.in --- opam-2.0.10/tests/packages/P3/P3.config.in 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P3/P3.config.in 1970-01-01 00:00:00.000000000 +0000 @@ -1,8 +0,0 @@ -opam-version: "1.3" -variables { - asmcomp : "-I %{lib}%/P3" - bytecomp: "-I %{lib}%/P3" - asmlink : "-I %{lib}%/P3 p3.cmxa p3_bar.cmxa" - bytelink: "-I %{lib}%/P3 p3.cma p3_bar.cma" - requires: "p1" -} diff -Nru opam-2.0.10/tests/packages/P3/P3.install opam-2.1.2/tests/packages/P3/P3.install --- opam-2.0.10/tests/packages/P3/P3.install 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P3/P3.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -lib: [ - (* p3 *) - "p3.cma" - "p3.cmxa" - "p3.a" - "p3.cmi" - - (* p3_bar *) - "p3_bar.cma" - "p3_bar.cmxa" - "p3_bar.a" - "p3_bar.cmi" -] diff -Nru opam-2.0.10/tests/packages/P3/p3.ml opam-2.1.2/tests/packages/P3/p3.ml --- opam-2.0.10/tests/packages/P3/p3.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P3/p3.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -let z () = - try P1.x () - with _ -> 0 diff -Nru opam-2.0.10/tests/packages/P3/README opam-2.1.2/tests/packages/P3/README --- opam-2.0.10/tests/packages/P3/README 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P3/README 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Testing version names diff -Nru opam-2.0.10/tests/packages/P3.opam opam-2.1.2/tests/packages/P3.opam --- opam-2.0.10/tests/packages/P3.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P3.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,7 +0,0 @@ -opam-version: "1" -name: "P3" -version: "1~weird-version.test" -maintainer: "contact@ocamlpro.com" -depends: ["ocaml" "P1"] -substs: [ "P3.config" ] -build: [ "./build.sh" ] diff -Nru opam-2.0.10/tests/packages/P4/build.sh opam-2.1.2/tests/packages/P4/build.sh --- opam-2.0.10/tests/packages/P4/build.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P4/build.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#! /bin/sh -ex - -if [ $OPAM_PACKAGE_VERSION -eq 2 ]; then - if [ "X${P1:-}" != "Xversion1" ]; then - echo "P1 not set to version1 while P1.1 should be installed" >&2 - exit 12 - fi -else - if [ -z "X${P1:-}" ]; then - echo "P1 not set while P1 should be installed" >&2 - exit 12 - fi -fi - -echo "Building P4 with ${OPAM}" -LIBDIR="`${OPAM} config var lib`" -COMP="-I ${LIBDIR}/P1 -I ${LIBDIR}/P2 -I ${LIBDIR}/P3" -LINK="p1.cmxa p2.cmxa p3.cmxa p3_bar.cmxa" - -OCAMLC=ocamlc -if which ocamlopt >/dev/null 2>&1; then OCAMLC=ocamlopt; fi - -$OCAMLC ${COMP} ${LINK} p4.ml -o p4.foo - -echo "TEST=${TEST}" diff -Nru opam-2.0.10/tests/packages/P4/P4.install opam-2.1.2/tests/packages/P4/P4.install --- opam-2.0.10/tests/packages/P4/P4.install 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P4/P4.install 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -bin: [ - "p4.foo" { "p4" } - "p4.foo" -] \ No newline at end of file diff -Nru opam-2.0.10/tests/packages/P4/p4.ml opam-2.1.2/tests/packages/P4/p4.ml --- opam-2.0.10/tests/packages/P4/p4.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P4/p4.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -let f = - try P3_bar.f (); P1.x () - with _ -> P3.z () - -let () = - let t = - try Sys.getenv "TEST" - with _ -> "" in - Printf.printf "TEST=%s\n%!" t diff -Nru opam-2.0.10/tests/packages/P4/README opam-2.1.2/tests/packages/P4/README --- opam-2.0.10/tests/packages/P4/README 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P4/README 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Testing transitive closure diff -Nru opam-2.0.10/tests/packages/P4-1.opam opam-2.1.2/tests/packages/P4-1.opam --- opam-2.0.10/tests/packages/P4-1.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P4-1.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -opam-version: "1" -name: "P4" -version: "1" -maintainer: "contact@ocamlpro.com" -depends: ["ocaml" "P2" "P3"] -build: [ "./build.sh" ] diff -Nru opam-2.0.10/tests/packages/P4-2.opam opam-2.1.2/tests/packages/P4-2.opam --- opam-2.0.10/tests/packages/P4-2.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P4-2.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -opam-version: "1" -name: "P4" -version: "2" -maintainer: "contact@ocamlpro.com" -depends: [ - "P1" { <= "1" } - "P2" - "P3" -] -build: [ "./build.sh" ] diff -Nru opam-2.0.10/tests/packages/P4-3.opam opam-2.1.2/tests/packages/P4-3.opam --- opam-2.0.10/tests/packages/P4-3.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P4-3.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -opam-version: "1" -name: "P4" -version: "3" -maintainer: "contact@ocamlpro.com" -depends: [ "P2" "P3" ] -build: [ "./build.sh" ] diff -Nru opam-2.0.10/tests/packages/P5/build.sh opam-2.1.2/tests/packages/P5/build.sh --- opam-2.0.10/tests/packages/P5/build.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P5/build.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -#! /bin/sh -eu - -FLAGS="-I `${OPAM} config var P1:lib`" - -echo "Bytecode Compilation" -ocamlc ${FLAGS} -a p5.ml -o p5.cma - -if which ocamlopt >/dev/null 2>&1; then - echo "Native Compilation" - ocamlopt ${FLAGS} -a p5.ml -o p5.cmxa -fi diff -Nru opam-2.0.10/tests/packages/P5/p5.ml opam-2.1.2/tests/packages/P5/p5.ml --- opam-2.0.10/tests/packages/P5/p5.ml 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P5/p5.ml 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -let g () = - P1.x () diff -Nru opam-2.0.10/tests/packages/P5/README opam-2.1.2/tests/packages/P5/README --- opam-2.0.10/tests/packages/P5/README 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P5/README 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Testing optional dependencies diff -Nru opam-2.0.10/tests/packages/P5.opam opam-2.1.2/tests/packages/P5.opam --- opam-2.0.10/tests/packages/P5.opam 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/packages/P5.opam 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -(* API version *) -opam-version: "1" -name: "P5" -version: "1" -maintainer: "contact@ocamlpro.com" -depends: ["ocaml" "P1"] -depopts: [ "P2" ] -build: [ [ "./build.sh" ] ] -install: [ [ "mkdir" "-p" "%{lib}%/p5" ] - [ "touch" "%{lib}%/p5/p2_present" ] {P2:installed} - [ "touch" "%{lib}%/p5/p2_absent" ] {!P2:installed} ] -remove: [ "rm" "-rf" "%{lib}%/p5" ] diff -Nru opam-2.0.10/tests/patcher.expected opam-2.1.2/tests/patcher.expected --- opam-2.0.10/tests/patcher.expected 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/patcher.expected 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,77 @@ +PATCH No CRLF adaptation necessary for b/always-crlf +PATCH No CRLF adaptation necessary for b/always-lf +PATCH CRLF adaptation skipped for b/no-eol-at-all +PATCH No CRLF adaptation necessary for b/no-eol-at-eof +PATCH CRLF adaptation skipped for b/null-file +PATCH No CRLF adaptation necessary for b/test1 +PATCH No CRLF adaptation necessary for b/test2 +PATCH No CRLF adaptation necessary for b/test3 +PATCH No CRLF adaptation necessary for b/will-null-file +Before patch state of c: + ./always-crlf: CRLF + ./always-lf: LF + ./no-eol-at-all: mixed (no eol-at-eof) + ./no-eol-at-eof: LF (no eol-at-eof) + ./null-file: mixed (no eol-at-eof) + ./test1: LF + ./test2: LF + ./test3: LF + ./will-null-file: LF +patching file always-crlf +patching file always-lf +patching file no-eol-at-all +patching file no-eol-at-eof +patching file null-file +patching file test1 +patching file test2 +patching file test3 +patching file will-null-file +After patch state of c: + ./always-crlf: CRLF + ./always-lf: LF + ./no-eol-at-all: mixed (no eol-at-eof) + ./no-eol-at-eof: LF (no eol-at-eof) + ./null-file: LF + ./test1: LF + ./test2: LF + ./will-null-file: mixed (no eol-at-eof) +PATCH No CRLF adaptation necessary for b/always-crlf +PATCH No CRLF adaptation necessary for b/always-lf +PATCH CRLF adaptation skipped for b/no-eol-at-all +PATCH Adding \r to patch chunks for b/no-eol-at-eof +PATCH CRLF adaptation skipped for b/null-file +PATCH No CRLF adaptation necessary for b/test1 +PATCH Adding \r to patch chunks for b/test2 +PATCH No CRLF adaptation necessary for b/test3 +PATCH Adding \r to patch chunks for b/will-null-file +PATCH Transform 32-36 +\r +PATCH Transform 62-67 +\r +PATCH Transform 82-87 +\r +Before patch state of c: + ./always-crlf: CRLF + ./always-lf: LF + ./no-eol-at-all: mixed (no eol-at-eof) + ./no-eol-at-eof: CRLF (no eol-at-eof) + ./null-file: mixed (no eol-at-eof) + ./test1: LF + ./test2: CRLF + ./test3: LF + ./will-null-file: CRLF +patching file always-crlf +patching file always-lf +patching file no-eol-at-all +patching file no-eol-at-eof +patching file null-file +patching file test1 +patching file test2 +patching file test3 +patching file will-null-file +After patch state of c: + ./always-crlf: CRLF + ./always-lf: LF + ./no-eol-at-all: mixed (no eol-at-eof) + ./no-eol-at-eof: CRLF (no eol-at-eof) + ./null-file: LF + ./test1: LF + ./test2: CRLF + ./will-null-file: mixed (no eol-at-eof) diff -Nru opam-2.0.10/tests/patcher.ml opam-2.1.2/tests/patcher.ml --- opam-2.0.10/tests/patcher.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/patcher.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,108 @@ +let set_debug_level n sections = + let debug_sections = + List.fold_left (fun map elt -> OpamStd.String.Map.add elt None map) OpamStd.String.Map.empty sections + in + OpamCoreConfig.(update ~noop:()) ~debug_level:n ~debug_sections () + +let test_dir = "patcher-test" + +let write_file ~dir ~name use_crlf ?(eol_at_eof = true) pattern = + let ch = open_out_bin (Filename.concat dir name) in + let eol = if use_crlf then "\r\n" else "\n" in + List.fold_left (fun a (n, s) -> for i = a to a + n - 1 do Printf.fprintf ch "Line %d%s" i eol done; a + n + s) 1 pattern |> ignore; + output_string ch "End of file"; + if eol_at_eof then + output_string ch eol; + close_out ch + +let write_single_line ~dir ~name line = + let ch = open_out_bin (Filename.concat dir name) in + output_string ch line; + close_out ch + +let touch ~dir name = + close_out (open_out_bin (Filename.concat dir name)) + +let setup_directory ~dir = + OpamSystem.remove dir; + OpamSystem.mkdir dir; + OpamSystem.chdir dir; + List.iter OpamSystem.mkdir ["a"; "b"; "c"] + +let pattern1 ?(test1 = false) ?(test2 = false) ?(test3 = false) ?(test4 = false) ?(eoleof_cr = false) dir = + write_file ~dir ~name:"always-lf" false [(5, 0)]; + write_file ~dir ~name:"always-crlf" true [(5, 0)]; + write_file ~dir ~name:"no-eol-at-eof" eoleof_cr ~eol_at_eof:false [(5, 0)]; + write_single_line ~dir ~name:"no-eol-at-all" "Original line"; + write_file ~dir ~name:"test1" test1 [(5, 0)]; + write_file ~dir ~name:"test2" test2 [(5, 0)]; + write_file ~dir ~name:"test3" test3 [(5, 0)]; + touch ~dir "null-file"; + write_file ~dir ~name:"will-null-file" test4 [(5, 0)] + +let pattern2 dir = + write_file ~dir ~name:"always-lf" false [(3, 1); (1, 0)]; + write_file ~dir ~name:"always-crlf" true [(6, 0)]; + write_file ~dir ~name:"no-eol-at-eof" false ~eol_at_eof:false [(3, 1); (1, 0)]; + write_single_line ~dir ~name:"no-eol-at-all" "Patched line"; + write_file ~dir ~name:"test1" false [(6, 0)]; + write_file ~dir ~name:"test2" false [(1, 1); (1, 1); (1, 0)]; + write_file ~dir ~name:"null-file" false [(5, 0)]; + touch ~dir "will-null-file" + +let eol_style ch fn = + let s = + match OpamSystem.get_eol_encoding fn with + | None -> "mixed" + | Some true -> "CRLF" + | Some false -> "LF" + in + let s = + let ch = open_in_bin fn in + let l = in_channel_length ch in + let r = + if l = 0 || (seek_in ch (l - 1); input_char ch <> '\n') then + s ^ " (no eol-at-eof)" + else + s + in close_in ch; r + in + output_string ch s + +let print_directory dir = + List.iter (fun fn -> Printf.eprintf " %s: %a\n%!" (OpamSystem.back_to_forward fn) eol_style fn) (OpamSystem.ls dir) + +let generate_patch () = + flush stderr; flush stdout; + if Sys.command "diff -Naur a b > input.patch" <> 1 then (Printf.eprintf "patch generation failed\n%!"; exit 2); + set_debug_level (-3) ["PATCH"]; + OpamSystem.translate_patch ~dir:"c" "input.patch" "output.patch"; + set_debug_level 0 []; + OpamSystem.chdir "c"; + Printf.eprintf "Before patch state of c:\n"; + print_directory "."; + flush stdout; + if Sys.command "patch -p1 -i ../output.patch" <> 0 then (Printf.eprintf "patch application failed\n%!"; exit 2); + Printf.eprintf "After patch state of c:\n"; + print_directory "."; + OpamSystem.chdir Filename.parent_dir_name + +let tests () = + set_debug_level 0 []; + let cwd = Sys.getcwd () in + setup_directory ~dir:test_dir; + pattern1 "a"; + pattern2 "b"; + pattern1 "c"; + generate_patch (); + pattern1 ~test2:true ~test4:true ~eoleof_cr:true "c"; + generate_patch (); + OpamSystem.chdir cwd; + OpamSystem.remove test_dir + +let () = + (* This causes Windows to use LF endings instead of CRLF, which simplifies the comparison with the reference file *) + Unix.putenv "LC_ALL" "C"; + set_binary_mode_out stdout true; + Unix.dup2 Unix.stdout Unix.stderr; + tests () diff -Nru opam-2.0.10/tests/README.unittest opam-2.1.2/tests/README.unittest --- opam-2.0.10/tests/README.unittest 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/README.unittest 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ - -# to create a new unit test - -* clean the test repository : - - ./init-repo.sh -c - -* init the test repository : - - ./init-repo.sh -i - -* load an initial scenario (this command can be invoked multiple times): - - ./init-repo.sh -s 1 - ./init-repo.sh -s 2 - -* install/remove/upgrade : - - OPAM_ROOT=/tmp/OPAM.ROOT PATH=/tmp/OPAM.BIN:$PATH opam --yes --root /tmp/OPAM.ROOT install P4 - -* crearte a new expected result file in as - - OPAM_ROOT=/tmp/OPAM.ROOT PATH=/tmp/OPAM.BIN:$PATH \ - opam --yes --root /tmp/OPAM.ROOT list > results/new-expected-result - -* Make sure that the result correct ! - -* Add a new test case in the file tests.py diff -Nru opam-2.0.10/tests/reftests/camelus.test.disabled opam-2.1.2/tests/reftests/camelus.test.disabled --- opam-2.0.10/tests/reftests/camelus.test.disabled 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/camelus.test.disabled 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,50 @@ +c7f8804c +### opam switch create camelus ocaml-base-compiler.4.08.0 --fake + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-base-compiler" {= "4.08.0"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.08.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.08.0 +Done. +### +opam-version: "2.0" +synopsis: "Test" +authors: "x" +homepage: "x" +bug-reports: "x" +maintainer: "Louis Gesbert " +depends: [ + "cohttp-lwt-unix" { < "2.0.0" } + "dune" + "git-unix" {>= "1.13" & < "2.0" | = "1.11.5" } + "github" + "github-unix" + "opam-format" {>= "2.0.0~beta5"} + "opam-solver" {>= "2.0.0~beta5"} + "opam-state" { >= "2.0.1"} + "tls" + "yojson" + ("ago" | ("utop")) + ("fpath" | "abella") + "lwt_ppx" { build } + "fpath" ] +### opam install . --deps-only --show +[ERROR] Package conflict! + * No agreement on the version of ocaml-base-compiler: + - (invariant) -> ocaml-base-compiler = 4.08.0 + - camelus -> tls -> nocrypto < 0.2.0 | ppx_sexp_conv < v0.11.0 | sexplib < 113.01.00 -> ocaml < 4.03.0 -> ocaml-base-compiler = 3.08.4 + You can temporarily relax the switch invariant with `--update-invariant' + * Incompatible packages: + - (invariant) -> ocaml-base-compiler = 4.08.0 -> ocaml = 4.08.0 + - camelus -> tls -> nocrypto >= 0.5.4 + * Missing dependency: + - camelus -> tls -> nocrypto < 0.2.0 | ppx_sexp_conv < v0.11.0 | sexplib < 113.01.00 -> ocaml < 4.03.0 -> ocaml-variants -> ocaml-beta + unmet availability conditions: 'enable-ocaml-beta-repository' + +No solution found, exiting diff -Nru opam-2.0.10/tests/reftests/cli-versioning.test opam-2.1.2/tests/reftests/cli-versioning.test --- opam-2.0.10/tests/reftests/cli-versioning.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/cli-versioning.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,172 @@ +N0REP0 +### +opam-version: "2.0" +flags: compiler +### +opam-version: "2.0" +flags: compiler +### opam update default + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +Now run 'opam upgrade' to apply any package updates. +### opam option depext=false +Set to 'false' the field depext in global configuration +### opam switch install cli-versioning --empty +opam: install was removed in version 2.1 of the opam CLI, but version 2.1 has been requested. Use create instead or set OPAMCLI environment variable to 2.0. +# Return code 2 # +### OPAMCLI=2.0 opam switch install cli-versioning --empty +[WARNING] OPAMNODEPEXTS was ignored because CLI 2.0 was requested and it was introduced in 2.1. +### OPAMSHOW=1 +### opam list --cli 31.4 +[ERROR] opam command-line version 31.4 is not supported. +# Return code 2 # +### opam install baz --assume-depexts --cli 2.1 +The following actions would be performed: + - install baz 2 +### OPAMCLI=2.0 opam install baz --assume-depexts +opam: --assume-depexts was added in version 2.1 of the opam CLI, but version 2.0 has been requested, which is older. +# Return code 2 # +### opam config set cli version +[WARNING] set was deprecated in version 2.1 of the opam CLI. Use opam var instead or set OPAMCLI environment variable to 2.0. +Added 'cli: "version"' to field variables in switch cli-versioning +### OPAMCLI=2.0 opam config set cli version +[WARNING] OPAMNODEPEXTS was ignored because CLI 2.0 was requested and it was introduced in 2.1. +Added 'cli: "version"' to field variables in switch cli-versioning +### OPAMSHOW=0 opam switch set-invariant baz.1 +The switch invariant was set to baz = 1 +### OPAMCLI=2.0 opam install baz.2 +[WARNING] OPAMNODEPEXTS was ignored because CLI 2.0 was requested and it was introduced in 2.1. +[ERROR] Package conflict! + * No agreement on the version of baz: + - (invariant) -> baz < 2 + - baz >= 2 + You can temporarily relax the switch invariant with `--update-invariant' + +No solution found, exiting +# Return code 20 # +### opam install baz.2 +[ERROR] Package conflict! + * No agreement on the version of baz: + - (invariant) -> baz < 2 + - baz >= 2 + You can temporarily relax the switch invariant with `--update-invariant' + +No solution found, exiting +# Return code 20 # +### OPAMCLI=2.0 opam install baz.2 --update-invariant +opam: --update-invariant was added in version 2.1 of the opam CLI, but version 2.0 has been requested, which is older. +# Return code 2 # +### opam install baz.2 --update-invariant +The following actions would be performed: + - install baz 2 +### OPAMCLI=2.0 opam install baz.2 --unlock-base +[WARNING] OPAMNODEPEXTS was ignored because CLI 2.0 was requested and it was introduced in 2.1. +The following actions would be performed: + - install baz 2 +### opam install baz.2 --unlock-base +opam: --unlock-base was removed in version 2.1 of the opam CLI, but version 2.1 has been requested. Use --update-invariant instead or set OPAMCLI environment variable to 2.0. +# Return code 2 # +### # opam option uses mk_command_ret +### opam option foo +[ERROR] No option named 'foo' found. Use 'opam option [--global]' to list them +# Return code 2 # +### OPAMCLI=2.0 opam option foo +opam: option was added in version 2.1 of the opam CLI, but version 2.0 has been requested, which is older. +# Return code 2 # +### opam option foo --global +[ERROR] Field or section foo not found +# Return code 5 # +### OPAMCLI=2.0 opam option foo --global +opam: --global was added in version 2.1 of the opam CLI, but version 2.0 has been requested, which is older. +# Return code 2 # +### # opam lock uses mk_command +### opam lock foo +[ERROR] No package matching foo +# Return code 5 # +### OPAMCLI=2.0 opam lock foo +opam: lock was added in version 2.1 of the opam CLI, but version 2.0 has been requested, which is older. +# Return code 2 # +### # Check for build test env +### # Note: you must have an installed opam with cli version enabled to pass these tests +### OPAMSHOW=0 +### +opam-version: "2.0" +build: ["sh" "-c" "env | grep -qFx 'OPAMCLI=2.0'"] +install: ["sh" "-c" "env | grep -qFx 'OPAMCLI=2.0'"] +remove: ["sh" "-c" "env | grep -qFx 'OPAMCLI=2.0'"] +### +opam-version: "2.0" +build-env: [OPAMCLI = "2.1"] +build: ["sh" "-c" "env | grep -qFx 'OPAMCLI=2.1'"] +install: ["sh" "-c" "env | grep -qFx 'OPAMCLI=2.1'"] +remove: ["sh" "-c" "env | grep -qFx 'OPAMCLI=2.1'"] +### opam pin opams -yn +This will pin the following packages: env-2-0, env-2-1. Continue? [Y/n] y +Package env-2-0 does not exist, create as a NEW package? [Y/n] y +env-2-0 is now pinned to file://${BASEDIR}/opams (version ~dev) +Package env-2-1 does not exist, create as a NEW package? [Y/n] y +env-2-1 is now pinned to file://${BASEDIR}/opams (version ~dev) +### opam switch set-invariant --formula "[]" +The switch invariant was set to [] +### opam install env-2-0 + +<><> Synchronising pinned packages ><><><><><><><><><><><><><><><><><><><><><><> +[env-2-0.~dev] synchronised (no changes) + +The following actions will be performed: + - install env-2-0 ~dev* + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved env-2-0.~dev (file://${BASEDIR}/opams) +-> installed env-2-0.~dev +Done. +### opam install env-2-1 + +<><> Synchronising pinned packages ><><><><><><><><><><><><><><><><><><><><><><> +[env-2-1.~dev] synchronised (no changes) + +The following actions will be performed: + - install env-2-1 ~dev* + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved env-2-1.~dev (file://${BASEDIR}/opams) +-> installed env-2-1.~dev +Done. +### opam remove env-2-0 +The following actions will be performed: + - remove env-2-0 ~dev* + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved env-2-0.~dev (no changes) +-> removed env-2-0.~dev +Done. +### opam remove env-2-1 +The following actions will be performed: + - remove env-2-1 ~dev* + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved env-2-1.~dev (no changes) +-> removed env-2-1.~dev +Done. +### # Environement variables cli versioning +### OPAMBUILDDOC=1 opam var share +[WARNING] OPAMBUILDDOC was ignored because CLI 2.1 was requested and it was removed in 2.1, set OPAMCLI environment variable to 2.0. +${BASEDIR}/OPAM/cli-versioning/share +### OPAMCLI=2.0 OPAMBUILDDOC=1 opam config var share +[WARNING] OPAMNODEPEXTS was ignored because CLI 2.0 was requested and it was introduced in 2.1. +${BASEDIR}/OPAM/cli-versioning/share +### # Switch creation with packages & formulaes +### opam switch create errored baz --packages baz.2 +[ERROR] Individual package and option '--packages' can not be specified at the same time. Use just '--packages' instead, e.g. + opam switch create flambda --packages=ocaml.4.12.0,ocaml-option-flambda + or '--formula' + opam switch create flambda --formula='["ocaml" {="4.12.0"} "ocaml-option-flambda"]' +# Return code 2 # +### OPAMCLI=2.0 opam switch create working baz --packages baz.2 --show +[WARNING] OPAMNODEPEXTS was ignored because CLI 2.0 was requested and it was introduced in 2.1. + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["baz" "baz" {= "2"}] +The following actions would be performed: + - install baz 2 diff -Nru opam-2.0.10/tests/reftests/conflict-3819.test.disabled opam-2.1.2/tests/reftests/conflict-3819.test.disabled --- opam-2.0.10/tests/reftests/conflict-3819.test.disabled 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/conflict-3819.test.disabled 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,73 @@ +40265f4 +### opam sw cr 4.03.0 --fake + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-base-compiler" {= "4.03.0"} | "ocaml-system" {= "4.03.0"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.03.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.03.0 +Done. +### opam install mirage-dns.2.0.0 --show +[ERROR] Package conflict! + * No agreement on the version of cstruct: + - mirage-dns < 2.5.0 -> cstruct >= 1.0.0 + * No agreement on the version of dns: + - mirage-dns < 2.5.0 -> dns + * No agreement on the version of cstruct: + - mirage-dns < 2.5.0 -> cstruct >= 1.0.0 + - mirage-dns < 2.5.0 -> dns -> cstruct < 0.6.0 + * No agreement on the version of cstruct: + - mirage-dns < 2.5.0 -> cstruct >= 1.0.0 + - mirage-dns < 2.5.0 -> dns -> cstruct < 1.0.0 + * No agreement on the version of cstruct: + - mirage-dns < 2.5.0 -> dns -> cstruct < 1.0.0 + * No agreement on the version of io-page: + - mirage-dns < 2.5.0 -> dns -> io-page < 1.3.0 + - mirage-dns < 2.5.0 -> mirage-types-lwt < 3.0.0 -> io-page >= 1.4.0 + * No agreement on the version of ipaddr: + - mirage-dns < 2.5.0 -> dns -> ipaddr + - mirage-dns < 2.5.0 -> dns -> ipaddr < 2.0.0 + * No agreement on the version of ipaddr: + - mirage-dns < 2.5.0 -> dns -> ipaddr < 2.0.0 + - mirage-dns < 2.5.0 -> dns -> ipaddr < 2.8.0 + * No agreement on the version of ipaddr: + - mirage-dns < 2.5.0 -> dns -> ipaddr < 2.8.0 + * No agreement on the version of ipaddr: + - mirage-dns < 2.5.0 -> dns -> ipaddr + - mirage-dns < 2.5.0 -> dns -> ipaddr < 2.8.0 + * No agreement on the version of mirage-types: + - mirage-dns < 2.5.0 -> mirage-types-lwt < 3.0.0 -> mirage-types + * Incompatible packages: + - mirage-dns < 2.5.0 -> dns + - mirage-dns < 2.5.0 -> mirage-types-lwt < 3.0.0 + * Incompatible packages: + - mirage-dns < 2.5.0 -> dns + - mirage-dns < 2.5.0 -> mirage-types-lwt < 3.0.0 -> io-page >= 1.4.0 + * Incompatible packages: + - mirage-dns < 2.5.0 -> dns + - mirage-dns < 2.5.0 -> mirage-types-lwt < 3.0.0 -> mirage-types + * Incompatible packages: + - mirage-dns < 2.5.0 -> dns -> ipaddr < 2.0.0 + - mirage-dns < 2.5.0 -> mirage-types-lwt < 3.0.0 -> mirage-types + +No solution found, exiting +### opam install tftp.0.1.4 --show +[ERROR] Package conflict! + * No agreement on the version of cstruct: + - tftp -> cstruct + * No agreement on the version of mirage: + - tftp -> mirage + * No agreement on the version of ocaml: + - (invariant) -> ocaml-base-compiler = 4.03.0 -> ocaml = 4.03.0 + - tftp -> mirage -> mirage-types-lwt < 3.0.0 -> sexplib < 113.01.00 -> ocaml < 4.03.0 + You can temporarily relax the switch invariant with `--update-invariant' + * Incompatible packages: + - tftp -> cstruct + - tftp -> mirage + +No solution found, exiting diff -Nru opam-2.0.10/tests/reftests/conflict-4373.test opam-2.1.2/tests/reftests/conflict-4373.test --- opam-2.0.10/tests/reftests/conflict-4373.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/conflict-4373.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,10 @@ +c1d23f0e +### OPAMVAR_enable_ocaml_beta_repository=1 opam switch create --fake 4.12.0 +[ERROR] No compiler matching `4.12.0' found, use `opam switch list-available' to see what is available, or use `--packages' to select packages explicitly. +# Return code 5 # +### opam install --show expect.0.0.6 +[ERROR] No switch is currently set. Please use 'opam switch' to set or install a switch +# Return code 50 # +### opam install --show ppx_expect.v0.14.0 +[ERROR] No switch is currently set. Please use 'opam switch' to set or install a switch +# Return code 50 # diff -Nru opam-2.0.10/tests/reftests/conflict-badversion.test opam-2.1.2/tests/reftests/conflict-badversion.test --- opam-2.0.10/tests/reftests/conflict-badversion.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/conflict-badversion.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,30 @@ +f372039d +### opam switch create --fake ocaml-base-compiler.4.02.3 + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-base-compiler" {= "4.02.3"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.02.3 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.02.3 +Faking installation of base-ocamlbuild.base +Done. +### opam install 'core>112.17.00' 'core<112.17.00' --show +[ERROR] Package conflict! + * No agreement on the version of core: + - core != 112.17.00 + +No solution found, exiting +# Return code 20 # +### opam remove ocaml --show + * Incompatible packages: + - (invariant) -> ocaml-base-compiler = 4.02.3 + - (request) + You can temporarily relax the switch invariant with `--update-invariant' + +No solution found, exiting +# Return code 20 # diff -Nru opam-2.0.10/tests/reftests/conflict-camlp4.test opam-2.1.2/tests/reftests/conflict-camlp4.test --- opam-2.0.10/tests/reftests/conflict-camlp4.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/conflict-camlp4.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,13 @@ +f372039d +### opam switch create test "--formula=[\"camlp4\" \"ocaml-system\"]" --show --yes + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["camlp4" "ocaml-system"] +[ERROR] Could not determine which packages to install for this switch: + * Incompatible packages: + - (invariant) -> camlp4 + - (invariant) -> ocaml-system + You can temporarily relax the switch invariant with `--update-invariant' + + +# Return code 20 # diff -Nru opam-2.0.10/tests/reftests/conflict-core.test opam-2.1.2/tests/reftests/conflict-core.test --- opam-2.0.10/tests/reftests/conflict-core.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/conflict-core.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,23 @@ +f372039d +### opam switch create ocaml-base-compiler.4.08.0 --fake + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-base-compiler" {= "4.08.0"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.08.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.08.0 +Done. +### opam install 'core_bench.112.17.00' 'core<112.17.00' --show +[ERROR] Package conflict! + * No agreement on the version of ocaml: + - (invariant) -> ocaml-base-compiler >= 4.08.0 -> ocaml = 4.08.0 + - core < 112.17.00 -> ocaml < 4.00.1 + You can temporarily relax the switch invariant with `--update-invariant' + +No solution found, exiting +# Return code 20 # diff -Nru opam-2.0.10/tests/reftests/conflict-solo5.test opam-2.1.2/tests/reftests/conflict-solo5.test --- opam-2.0.10/tests/reftests/conflict-solo5.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/conflict-solo5.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,27 @@ +f372039d +### opam switch create --fake ocaml-base-compiler.4.02.3 + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-base-compiler" {= "4.02.3"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.02.3 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.02.3 +Faking installation of base-ocamlbuild.base +Done. +### opam var arch=x86_64 --global +Added '[arch "x86_64" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var os=linux --global +Added '[os "linux" "Set through 'opam var'"]' to field global-variables in global configuration +### opam install --show solo5-kernel-virtio solo5-kernel-ukvm +[ERROR] Package conflict! + * Incompatible packages: + - solo5-kernel-ukvm + - solo5-kernel-virtio + +No solution found, exiting +# Return code 20 # diff -Nru opam-2.0.10/tests/reftests/cudf-preprocess.test opam-2.1.2/tests/reftests/cudf-preprocess.test --- opam-2.0.10/tests/reftests/cudf-preprocess.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/cudf-preprocess.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,350 @@ +ad4dd344 +### : Github issue #4624 +### OPAMFAKE=1 +### opam switch create 4.11.0 + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-base-compiler" {= "4.11.0"} | "ocaml-system" {= "4.11.0"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.11.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.11.0 +Done. +### opam install git-unix git-cohttp-mirage git-cohttp-unix git-cohttp git carton-git carton-lwt carton decompress checkseum optint.0.0.4 --yes | unordered +The following actions will be faked: + - install seq base [required by psq] + - install conf-gmp 3 [required by conf-gmp-powm-sec, zarith] + - install mirage-no-xen 1 [required by mirage-crypto-pk] + - install ocamlbuild 0.14.0 [required by bos] + - install conf-pkg-config 2 [required by checkseum] + - install ocamlfind 1.9.1 [required by git-unix] + - install mirage-no-solo5 1 [required by mirage-crypto-pk] + - install cmdliner 1.0.4 [required by decompress, carton, git-unix] + - install dune 2.8.5 [required by git-unix, git-cohttp-mirage, git, etc.] + - install conf-gmp-powm-sec 3 [required by mirage-crypto-pk] + - install uchar 0.0.2 [required by uutf, jsonm] + - install zarith 1.12 [required by awa] + - install topkg 1.0.3 [required by bos] + - install num 1.4 [required by sexplib] + - install base-bytes base [required by checkseum, decompress] + - install stdlib-shims 0.3.0 [required by ocamlgraph, digestif, duff, cohttp] + - install sexplib0 v0.14.0 [required by cohttp, cohttp-lwt] + - install result 1.5 [required by git-cohttp-unix, git-cohttp, carton-git, etc.] + - install re 1.9.0 [required by cohttp] + - install psq 0.2.0 [required by carton, git] + - install ppx_derivers 1.2.1 [required by ppxlib] + - install pecu 0.5 [required by emile] + - install optint 0.0.4 + - install ocaml-syntax-shims 1.0.0 [required by angstrom] + - install ocaml-migrate-parsetree 2.1.0 [required by ppxlib] + - install ocaml-compiler-libs v0.12.3 [required by ppxlib] + - install mmap 1.1.0 [required by carton-git, carton, git-unix] + - install mirage-clock 3.1.0 [required by git-unix] + - install magic-mime 1.1.3 [required by cohttp-mirage, cohttp-lwt-unix] + - install macaddr 5.0.1 [required by tcpip] + - install gmap 0.3.0 [required by dns, x509] + - install duration 0.1.3 [required by tcpip] + - install csexp 1.5.1 [required by dune-configurator] + - install cppo 1.6.7 [required by lwt] + - install bigarray-compat 1.0.0 [required by git, checkseum, carton, etc.] + - install base64 3.5.0 [required by git] + - install uutf 1.0.2 [required by emile] + - install mtime 1.2.0 [required by git-unix] + - install astring 0.8.5 [required by git, carton-git, git-unix] + - install stringext 1.6.0 [required by uri, cohttp] + - install ocamlgraph 2.0.0 [required by git] + - install fmt 0.8.9 [required by git-cohttp-unix, git-cohttp, carton-git, etc.] + - install rresult 0.6.0 [required by git-cohttp-unix, git-cohttp, carton, etc.] + - install ptime 0.8.5 [required by tls-mirage, tls, ca-certs] + - install lru 0.3.0 [required by tcpip] + - install ppxlib 0.22.0 [required by ppx_fields_conv, ppx_sexp_conv] + - install dune-configurator 2.8.5 [required by checkseum] + - install ocplib-endian 1.1 [required by lwt, mirage-profile] + - install cstruct 6.0.0 [required by carton, git] + - install bigstringaf 0.7.0 [required by git, carton-git, carton, etc.] + - install jsonm 1.0.1 [required by cohttp] + - install fpath 0.7.3 [required by git, carton, carton-git, git-unix] + - install metrics 0.2.0 [required by dns] + - install ke 0.4 [required by carton, git] + - install duff 0.4 [required by carton] + - install domain-name 0.3.0 [required by git, git-unix] + - install mirage-clock-unix 3.1.0 [required by git-unix] + - install checkseum 0.3.0 + - install base v0.14.1 [required by ppx_fields_conv, fieldslib, ppx_sexp_conv] + - install lwt 5.4.0 [required by git-cohttp-unix, git-cohttp, carton-lwt, etc.] + - install randomconv 0.1.3 [required by tcpip] + - install mirage-random 2.0.0 [required by tcpip] + - install macaddr-cstruct 5.0.1 [required by tcpip] + - install io-page 2.4.0 [required by vchan] + - install hex 1.4.0 [required by fiat-p256] + - install eqaf 0.7 [required by digestif] + - install cstruct-unix 6.0.0 [required by awa] + - install asn1-combinators 0.2.5 [required by x509] + - install angstrom 0.15.0 [required by git] + - install ipaddr 5.0.1 [required by git, git-unix] + - install decompress 1.3.0 + - install ppx_sexp_conv v0.14.3 [required by cohttp-lwt, cohttp, awa] + - install parsexp v0.14.0 [required by sexplib] + - install fieldslib v0.14.0 [required by cohttp] + - install mirage-time 2.0.1 [required by tcpip] + - install mirage-flow 2.0.1 [required by git, git-unix] + - install mirage-device 2.0.0 [required by mirage-net] + - install lwt-dllist 1.0.0 [required by tcpip] + - install logs 0.7.0 [required by git, carton, git-unix] + - install hxd 0.3.1 [required by carton] + - install cstruct-lwt 6.0.0 [required by tcpip] + - install mirage-crypto 0.9.0 [required by awa] + - install hacl_x25519 0.2.2 [required by awa] + - install fiat-p256 0.2.3 [required by tls, tls-mirage] + - install digestif 1.0.0 [required by git, carton, git-unix] + - install uri 4.1.0 [required by git-cohttp-mirage, git, git-cohttp, git-cohttp-unix] + - install encore 0.8 [required by git] + - install emile 1.1 [required by git] + - install ipaddr-sexp 5.0.1 [required by conduit-mirage, conduit, conduit-lwt-unix] + - install sexplib v0.14.0 [required by awa] + - install ppx_fields_conv v0.14.2 [required by cohttp] + - install mirage-protocols 5.0.0 [required by tcpip] + - install mirage-net 3.0.1 [required by tcpip] + - install mirage-kv 3.0.1 [required by cohttp-mirage] + - install mirage-flow-combinators 2.0.1 [required by conduit-mirage] + - install mirage-channel 4.0.1 [required by cohttp-mirage] + - install mimic 0.0.2 [required by git, git-cohttp-mirage] + - install dns 4.6.3 [required by dns-client] + - install bos 0.2.0 [required by carton, git-unix] + - install mirage-crypto-rng 0.9.0 [required by awa] + - install hkdf 1.0.4 [required by tls] + - install uri-sexp 4.1.0 [required by cohttp] + - install ppx_cstruct 6.0.0 [required by awa, tcpip] + - install cstruct-sexp 6.0.0 [required by awa] + - install conduit 2.3.0 [required by cohttp-mirage] + - install mirage-stack 2.2.0 [required by tcpip] + - install carton 0.4.0 + - install mirage-crypto-pk 0.9.0 [required by awa] + - install cohttp 2.5.5 [required by git-cohttp-mirage, git-cohttp, git-cohttp-unix] + - install xenstore 2.1.1 [required by conduit-mirage] + - install mirage-profile 0.9.1 [required by tcpip] + - install conduit-lwt 2.3.0 [required by conduit-mirage, conduit-lwt-unix] + - install dns-client 4.6.3 [required by conduit-mirage] + - install carton-lwt 0.4.0 + - install x509 0.11.2 [required by awa] + - install cohttp-lwt 2.5.5 [required by git-cohttp-mirage, git-cohttp, git-cohttp-unix] + - install xenstore_transport 1.3.0 [required by vchan] + - install ethernet 2.2.0 [required by tcpip] + - install carton-git 0.4.0 + - install tls 0.12.8 [required by conduit-mirage, conduit-lwt-unix] + - install ca-certs 0.2.0 [required by conduit-lwt-unix] + - install awa 0.0.1 [required by git-unix] + - install vchan 6.0.0 [required by conduit-mirage] + - install tcpip 6.1.0 [required by git-unix] + - install git 3.3.3 + - install tls-mirage 0.12.8 [required by conduit-mirage] + - install conduit-lwt-unix 2.3.0 [required by cohttp-lwt-unix] + - install awa-mirage 0.0.1 [required by git-unix] + - install git-cohttp 3.3.3 + - install conduit-mirage 2.2.1 [required by cohttp-mirage] + - install cohttp-lwt-unix 2.5.5 [required by git-cohttp-unix, git-unix] + - install cohttp-mirage 2.5.5 [required by git-cohttp-mirage] + - install git-cohttp-unix 3.3.3 + - install git-cohttp-mirage 3.3.3 + - install git-unix 3.3.3 +===== 135 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of cmdliner.1.0.4 +Faking installation of conf-gmp.3 +Faking installation of conf-gmp-powm-sec.3 +Faking installation of conf-pkg-config.2 +Faking installation of dune.2.8.5 +Faking installation of base64.3.5.0 +Faking installation of bigarray-compat.1.0.0 +Faking installation of bigstringaf.0.7.0 +Faking installation of cppo.1.6.7 +Faking installation of csexp.1.5.1 +Faking installation of cstruct.6.0.0 +Faking installation of cstruct-unix.6.0.0 +Faking installation of duration.0.1.3 +Faking installation of eqaf.0.7 +Faking installation of gmap.0.3.0 +Faking installation of hacl_x25519.0.2.2 +Faking installation of hex.1.4.0 +Faking installation of io-page.2.4.0 +Faking installation of macaddr.5.0.1 +Faking installation of macaddr-cstruct.5.0.1 +Faking installation of magic-mime.1.1.3 +Faking installation of mirage-clock.3.1.0 +Faking installation of mirage-no-solo5.1 +Faking installation of mirage-no-xen.1 +Faking installation of mirage-random.2.0.0 +Faking installation of mmap.1.1.0 +Faking installation of ocaml-compiler-libs.v0.12.3 +Faking installation of ocaml-migrate-parsetree.2.1.0 +Faking installation of ocaml-syntax-shims.1.0.0 +Faking installation of ocamlbuild.0.14.0 +Faking installation of ocamlfind.1.9.1 +Faking installation of base-bytes.base +Faking installation of num.1.4 +Faking installation of ocplib-endian.1.1 +Faking installation of optint.0.0.4 +Faking installation of pecu.0.5 +Faking installation of ppx_derivers.1.2.1 +Faking installation of randomconv.0.1.3 +Faking installation of result.1.5 +Faking installation of angstrom.0.15.0 +Faking installation of dune-configurator.2.8.5 +Faking installation of checkseum.0.3.0 +Faking installation of decompress.1.3.0 +Faking installation of fiat-p256.0.2.3 +Faking installation of mirage-clock-unix.3.1.0 +Faking installation of mirage-crypto.0.9.0 +Faking installation of hkdf.1.0.4 +Faking installation of seq.base +Faking installation of lwt.5.4.0 +Faking installation of cstruct-lwt.6.0.0 +Faking installation of hxd.0.3.1 +Faking installation of lwt-dllist.1.0.0 +Faking installation of mirage-time.2.0.1 +Faking installation of psq.0.2.0 +Faking installation of lru.0.3.0 +Faking installation of re.1.9.0 +Faking installation of sexplib0.v0.14.0 +Faking installation of base.v0.14.1 +Faking installation of fieldslib.v0.14.0 +Faking installation of parsexp.v0.14.0 +Faking installation of sexplib.v0.14.0 +Faking installation of cstruct-sexp.6.0.0 +Faking installation of stdlib-shims.0.3.0 +Faking installation of digestif.1.0.0 +Faking installation of ocamlgraph.2.0.0 +Faking installation of ppxlib.0.22.0 +Faking installation of ppx_cstruct.6.0.0 +Faking installation of mirage-profile.0.9.1 +Faking installation of ppx_fields_conv.v0.14.2 +Faking installation of ppx_sexp_conv.v0.14.3 +Faking installation of stringext.1.6.0 +Faking installation of topkg.1.0.3 +Faking installation of astring.0.8.5 +Faking installation of fmt.0.8.9 +Faking installation of domain-name.0.3.0 +Faking installation of duff.0.4 +Faking installation of encore.0.8 +Faking installation of fpath.0.7.3 +Faking installation of ipaddr.5.0.1 +Faking installation of ipaddr-sexp.5.0.1 +Faking installation of ke.0.4 +Faking installation of logs.0.7.0 +Faking installation of metrics.0.2.0 +Faking installation of mirage-device.2.0.0 +Faking installation of mirage-flow.2.0.1 +Faking installation of mirage-channel.4.0.1 +Faking installation of mirage-flow-combinators.2.0.1 +Faking installation of mirage-kv.3.0.1 +Faking installation of mirage-net.3.0.1 +Faking installation of mirage-protocols.5.0.0 +Faking installation of mirage-stack.2.2.0 +Faking installation of mtime.1.2.0 +Faking installation of mirage-crypto-rng.0.9.0 +Faking installation of ptime.0.8.5 +Faking installation of rresult.0.6.0 +Faking installation of bos.0.2.0 +Faking installation of carton.0.4.0 +Faking installation of carton-lwt.0.4.0 +Faking installation of carton-git.0.4.0 +Faking installation of dns.4.6.3 +Faking installation of dns-client.4.6.3 +Faking installation of ethernet.2.2.0 +Faking installation of mimic.0.0.2 +Faking installation of tcpip.6.1.0 +Faking installation of uchar.0.0.2 +Faking installation of uri.4.1.0 +Faking installation of conduit.2.3.0 +Faking installation of conduit-lwt.2.3.0 +Faking installation of uri-sexp.4.1.0 +Faking installation of uutf.1.0.2 +Faking installation of emile.1.1 +Faking installation of git.3.3.3 +Faking installation of jsonm.1.0.1 +Faking installation of cohttp.2.5.5 +Faking installation of cohttp-lwt.2.5.5 +Faking installation of git-cohttp.3.3.3 +Faking installation of xenstore.2.1.1 +Faking installation of xenstore_transport.1.3.0 +Faking installation of vchan.6.0.0 +Faking installation of zarith.1.12 +Faking installation of asn1-combinators.0.2.5 +Faking installation of mirage-crypto-pk.0.9.0 +Faking installation of x509.0.11.2 +Faking installation of awa.0.0.1 +Faking installation of awa-mirage.0.0.1 +Faking installation of ca-certs.0.2.0 +Faking installation of tls.0.12.8 +Faking installation of conduit-lwt-unix.2.3.0 +Faking installation of cohttp-lwt-unix.2.5.5 +Faking installation of git-cohttp-unix.3.3.3 +Faking installation of git-unix.3.3.3 +Faking installation of tls-mirage.0.12.8 +Faking installation of conduit-mirage.2.2.1 +Faking installation of cohttp-mirage.2.5.5 +Faking installation of git-cohttp-mirage.3.3.3 +Done. +### OPAMSHOW=1 +### opam install git.3.3.3 optint.0.1.0 +[NOTE] Package git is already installed (current version is 3.3.3). +The following actions would be faked: + - upgrade optint 0.0.4 to 0.1.0 + - upgrade checkseum 0.3.0 to 0.3.1 [required by git] + - recompile decompress 1.3.0 [uses optint] + - recompile carton 0.4.0 [uses optint] + - recompile carton-lwt 0.4.0 [uses optint] + - recompile carton-git 0.4.0 [uses carton, decompress] + - recompile git 3.3.3 + - recompile git-cohttp-mirage 3.3.3 [uses git] + - recompile git-cohttp 3.3.3 [uses git] + - recompile git-cohttp-unix 3.3.3 [uses git] + - recompile git-unix 3.3.3 [uses git] +===== 9 to recompile | 2 to upgrade ===== +### opam install optint.0.1.0 +The following actions would be faked: + - upgrade optint 0.0.4 to 0.1.0 + - upgrade checkseum 0.3.0 to 0.3.1 [uses optint] + - recompile decompress 1.3.0 [uses optint] + - recompile carton 0.4.0 [uses optint] + - recompile carton-lwt 0.4.0 [uses optint] + - recompile carton-git 0.4.0 [uses carton] + - recompile git 3.3.3 [uses optint] + - recompile git-cohttp-mirage 3.3.3 [uses git] + - recompile git-cohttp 3.3.3 [uses git] + - recompile git-cohttp-unix 3.3.3 [uses git] + - recompile git-unix 3.3.3 [uses decompress] +===== 9 to recompile | 2 to upgrade ===== +### OPAMCUDFTRIM=0 opam install optint.0.1.0 +The following actions would be faked: + - upgrade optint 0.0.4 to 0.1.0 + - upgrade checkseum 0.3.0 to 0.3.1 [uses optint] + - recompile decompress 1.3.0 [uses optint] + - recompile carton 0.4.0 [uses optint] + - recompile carton-lwt 0.4.0 [uses optint] + - recompile carton-git 0.4.0 [uses carton] + - recompile git 3.3.3 [uses optint] + - recompile git-cohttp-mirage 3.3.3 [uses git] + - recompile git-cohttp 3.3.3 [uses git] + - recompile git-cohttp-unix 3.3.3 [uses git] + - recompile git-unix 3.3.3 [uses decompress] +===== 9 to recompile | 2 to upgrade ===== +### OPAMCUDFTRIM=simple opam install optint.0.1.0 +The following actions would be faked: + - upgrade optint 0.0.4 to 0.1.0 + - upgrade checkseum 0.3.0 to 0.3.1 [uses optint] + - recompile decompress 1.3.0 [uses optint] + - recompile carton 0.4.0 [uses optint] + - recompile carton-lwt 0.4.0 [uses optint] + - recompile carton-git 0.4.0 [uses carton] + - recompile git 3.3.3 [uses optint] + - recompile git-cohttp-mirage 3.3.3 [uses git] + - recompile git-cohttp 3.3.3 [uses git] + - recompile git-cohttp-unix 3.3.3 [uses git] + - recompile git-unix 3.3.3 [uses decompress] +===== 9 to recompile | 2 to upgrade ===== diff -Nru opam-2.0.10/tests/reftests/dot-install.test opam-2.1.2/tests/reftests/dot-install.test --- opam-2.0.10/tests/reftests/dot-install.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/dot-install.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,142 @@ +N0REP0 +### +#load "str.cma" + +let read file = + let ic = open_in file in + let rec aux lines = + try aux (input_line ic :: lines) + with End_of_file -> lines + in + let r = Str.regexp "/\\|\\\\\\\\" in + try + List.rev_map + (Str.global_replace r "-") + (aux []) + with Sys_error _ -> ["Not found: "^file] + +let cat header path = + Printf.printf "==> %s\n" header; + let contents = read path in + Printf.printf "%s\n" (String.concat "\n" contents) + +let pkg = (Sys.argv).(1) +let root = Sys.getenv "OPAMROOT" +let (/) = Filename.concat +let share = root / "inst" / "share" +let inst_file = share / pkg / "file" +let changes = root / "inst" / ".opam-switch" / "install" / pkg ^ ".changes" +let _ = + cat (pkg ^" installed file") inst_file; + cat (pkg^" changes") changes +### +opam-version: "2.0" +depends: "nodot" +### +share: [ "file" ] +### +hellow +### +opam-version: "2.0" +install: [ "echo" "hellow" ] +### +share: [ "file" ] +### +hellow +### +opam-version: "2.0" +install: [ "echo" "hellow" ] +substs: "lot-of-files.install" +### +lib: [ "file" "fichier" "dosiero" ] +bin: [ "fichier" ] +etc: [ "dosiero" "file" ] +share: [ "fichier" "dosiero" ] +misc: [ "file" {"%{root}%/dosiero"} ] +### +opam-version: "2.0" +variables { lot: true } +### +hellow +### +bonjour +### +saluton +### OPAMYES=1 +### opam switch create inst --empty +### opam update default + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +Now run 'opam upgrade' to apply any package updates. +### OPAMPRECISETRACKING=1 OPAMDEBUGSECTIONS="TRACK ACTION" OPAMDEBUG=-1 +### opam install nodot +The following actions will be performed: + - install nodot ~dev + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +ACTION download_package: nodot.~dev +ACTION prepare_package_source: nodot.~dev at ${BASEDIR}/OPAM/inst/.opam-switch/build/nodot.~dev +ACTION Installing nodot.~dev. + +ACTION creating ${BASEDIR}/OPAM/inst/share/nodot +TRACK after install: 19 elements, 3 added, scanned in 0.000s +-> installed nodot.~dev +Done. +### ocaml cat.ml nodot +==> nodot installed file +hellow +==> nodot changes +opam-version: "2.0" +added: [ + "share" {"D"} + "share-nodot" {"D"} + "share-nodot-file" {"F:12fc204edeae5b57713c5ad7dcb97d39"} +] +### opam install dot +The following actions will be performed: + - install dot ~dev + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +ACTION download_package: dot.~dev +ACTION prepare_package_source: dot.~dev at ${BASEDIR}/OPAM/inst/.opam-switch/build/dot.~dev +ACTION Installing dot.~dev. + +ACTION creating ${BASEDIR}/OPAM/inst/share/dot +TRACK after install: 2 elements, 2 added, scanned in 0.000s +-> installed dot.~dev +Done. +### ocaml cat.ml dot +==> dot installed file +hellow +==> dot changes +opam-version: "2.0" +added: [ + "share-dot" {"D"} + "share-dot-file" {"F:12fc204edeae5b57713c5ad7dcb97d39"} +] +### OPAMDEBUGSECTIONS="SYSTEM FILE(.config)" OPAMDEBUG=-1 +### : check install files ordering : +### opam install lot-of-files | grep "mkdir\|install\|FILE" + - install lot-of-files ~dev +SYSTEM mkdir ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev +SYSTEM mkdir ${OPAMTMP} +SYSTEM copy ${OPAMTMP}/default/packages/lot-of-files/lot-of-files.~dev/files/lot-of-files.install.in -> ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/lot-of-files.install.in +FILE(.config) Wrote ${BASEDIR}/OPAM/inst/.opam-switch/config/lot-of-files.config in 0.000s +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/fichier -> ${BASEDIR}/OPAM/inst/bin/fichier (755) +SYSTEM mkdir ${BASEDIR}/OPAM/inst/lib/lot-of-files +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/file -> ${BASEDIR}/OPAM/inst/lib/lot-of-files/file (644) +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/fichier -> ${BASEDIR}/OPAM/inst/lib/lot-of-files/fichier (644) +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/dosiero -> ${BASEDIR}/OPAM/inst/lib/lot-of-files/dosiero (644) +SYSTEM mkdir ${BASEDIR}/OPAM/inst/share/lot-of-files +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/fichier -> ${BASEDIR}/OPAM/inst/share/lot-of-files/fichier (644) +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/dosiero -> ${BASEDIR}/OPAM/inst/share/lot-of-files/dosiero (644) +SYSTEM mkdir ${BASEDIR}/OPAM/inst/etc +SYSTEM mkdir ${BASEDIR}/OPAM/inst/etc/lot-of-files +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/dosiero -> ${BASEDIR}/OPAM/inst/etc/lot-of-files/dosiero (644) +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/file -> ${BASEDIR}/OPAM/inst/etc/lot-of-files/file (644) +SYSTEM install ${BASEDIR}/OPAM/inst/.opam-switch/build/lot-of-files.~dev/file -> ${BASEDIR}/OPAM/dosiero (644) +-> installed lot-of-files.~dev +SYSTEM mkdir ${BASEDIR}/OPAM/inst/.opam-switch/packages/lot-of-files.~dev +SYSTEM mkdir ${BASEDIR}/OPAM/inst/.opam-switch/packages/lot-of-files.~dev/files +SYSTEM copy ${OPAMTMP}/default/packages/lot-of-files/lot-of-files.~dev/files/lot-of-files.install.in -> ${BASEDIR}/OPAM/inst/.opam-switch/packages/lot-of-files.~dev/files/lot-of-files.install.in diff -Nru opam-2.0.10/tests/reftests/dune opam-2.1.2/tests/reftests/dune --- opam-2.0.10/tests/reftests/dune 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/dune 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,41 @@ +; Reftests are set up using aliases so that they can be run individually: +; +; - each test has its own alias (e.g. dune build @reftest-conflict-camlp4) +; - all tests are attached to @reftest (dune build @reftest runs all) +; - this alias is attached to @runtest too (so dune runtest will run these) + +(executable + (name run) + (libraries opam-core opam-file-format) + (modules run)) + +(executable + (name gen) + (libraries opam-core) + (modules gen)) + +(rule +(target dune.inc.gen) + (deps + (glob_files *.test)) + (action + (with-stdout-to + %{target} + (run ./gen.exe)))) + +(alias + (name runtest) + (deps + (alias reftest))) + +(alias + (name reftest) + (deps + (alias reftest-gen))) + +(alias + (name reftest-gen) + (action + (diff dune.inc dune.inc.gen))) + +(include dune.inc) diff -Nru opam-2.0.10/tests/reftests/dune.inc opam-2.1.2/tests/reftests/dune.inc --- opam-2.0.10/tests/reftests/dune.inc 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/dune.inc 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,437 @@ + +(alias + (name reftest-cli-versioning) + (action + (diff cli-versioning.test cli-versioning.out))) + +(alias + (name reftest) + (deps (alias reftest-cli-versioning))) + +(rule + (targets cli-versioning.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:cli-versioning.test} %{read-lines:testing-env})))) + +(alias + (name reftest-conflict-4373) + (action + (diff conflict-4373.test conflict-4373.out))) + +(alias + (name reftest) + (deps (alias reftest-conflict-4373))) + +(rule + (targets conflict-4373.out) + (deps root-c1d23f0e) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:conflict-4373.test} %{read-lines:testing-env})))) + +(alias + (name reftest-conflict-badversion) + (action + (diff conflict-badversion.test conflict-badversion.out))) + +(alias + (name reftest) + (deps (alias reftest-conflict-badversion))) + +(rule + (targets conflict-badversion.out) + (deps root-f372039d) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:conflict-badversion.test} %{read-lines:testing-env})))) + +(alias + (name reftest-conflict-camlp4) + (action + (diff conflict-camlp4.test conflict-camlp4.out))) + +(alias + (name reftest) + (deps (alias reftest-conflict-camlp4))) + +(rule + (targets conflict-camlp4.out) + (deps root-f372039d) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:conflict-camlp4.test} %{read-lines:testing-env})))) + +(alias + (name reftest-conflict-core) + (action + (diff conflict-core.test conflict-core.out))) + +(alias + (name reftest) + (deps (alias reftest-conflict-core))) + +(rule + (targets conflict-core.out) + (deps root-f372039d) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:conflict-core.test} %{read-lines:testing-env})))) + +(alias + (name reftest-conflict-solo5) + (action + (diff conflict-solo5.test conflict-solo5.out))) + +(alias + (name reftest) + (deps (alias reftest-conflict-solo5))) + +(rule + (targets conflict-solo5.out) + (deps root-f372039d) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:conflict-solo5.test} %{read-lines:testing-env})))) + +(alias + (name reftest-cudf-preprocess) + (action + (diff cudf-preprocess.test cudf-preprocess.out))) + +(alias + (name reftest) + (deps (alias reftest-cudf-preprocess))) + +(rule + (targets cudf-preprocess.out) + (deps root-ad4dd344) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:cudf-preprocess.test} %{read-lines:testing-env})))) + +(alias + (name reftest-dot-install) + (action + (diff dot-install.test dot-install.out))) + +(alias + (name reftest) + (deps (alias reftest-dot-install))) + +(rule + (targets dot-install.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:dot-install.test} %{read-lines:testing-env})))) + +(alias + (name reftest-env) + (action + (diff env.test env.out))) + +(alias + (name reftest) + (deps (alias reftest-env))) + +(rule + (targets env.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:env.test} %{read-lines:testing-env})))) + +(alias + (name reftest-init) + (action + (diff init.test init.out))) + +(alias + (name reftest) + (deps (alias reftest-init))) + +(rule + (targets init.out) + (deps root-009e00fa) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:init.test} %{read-lines:testing-env})))) + +(alias + (name reftest-install-pgocaml) + (action + (diff install-pgocaml.test install-pgocaml.out))) + +(alias + (name reftest) + (deps (alias reftest-install-pgocaml))) + +(rule + (targets install-pgocaml.out) + (deps root-f372039d) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:install-pgocaml.test} %{read-lines:testing-env})))) + +(alias + (name reftest-legacy-git) + (action + (diff legacy-git.test legacy-git.out))) + +(alias + (name reftest) + (deps (alias reftest-legacy-git))) + +(rule + (targets legacy-git.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:legacy-git.test} %{read-lines:testing-env})))) + +(alias + (name reftest-legacy-local) + (action + (diff legacy-local.test legacy-local.out))) + +(alias + (name reftest) + (deps (alias reftest-legacy-local))) + +(rule + (targets legacy-local.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:legacy-local.test} %{read-lines:testing-env})))) + +(alias + (name reftest-opamroot-versions) + (action + (diff opamroot-versions.test opamroot-versions.out))) + +(alias + (name reftest) + (deps (alias reftest-opamroot-versions))) + +(rule + (targets opamroot-versions.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:opamroot-versions.test} %{read-lines:testing-env})))) + +(alias + (name reftest-pat-sub) + (action + (diff pat-sub.test pat-sub.out))) + +(alias + (name reftest) + (deps (alias reftest-pat-sub))) + +(rule + (targets pat-sub.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:pat-sub.test} %{read-lines:testing-env})))) + +(alias + (name reftest-show) + (action + (diff show.test show.out))) + +(alias + (name reftest) + (deps (alias reftest-show))) + +(rule + (targets show.out) + (deps root-009e00fa) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:show.test} %{read-lines:testing-env})))) + +(alias + (name reftest-switch-creation) + (action + (diff switch-creation.test switch-creation.out))) + +(alias + (name reftest) + (deps (alias reftest-switch-creation))) + +(rule + (targets switch-creation.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:switch-creation.test} %{read-lines:testing-env})))) + +(alias + (name reftest-switch-invariant) + (action + (diff switch-invariant.test switch-invariant.out))) + +(alias + (name reftest) + (deps (alias reftest-switch-invariant))) + +(rule + (targets switch-invariant.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:switch-invariant.test} %{read-lines:testing-env})))) + +(alias + (name reftest-upgrade-format) + (action + (diff upgrade-format.test upgrade-format.out))) + +(alias + (name reftest) + (deps (alias reftest-upgrade-format))) + +(rule + (targets upgrade-format.out) + (deps root-009e00fa) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:upgrade-format.test} %{read-lines:testing-env})))) + +(alias + (name reftest-var-option) + (action + (diff var-option.test var-option.out))) + +(alias + (name reftest) + (deps (alias reftest-var-option))) + +(rule + (targets var-option.out) + (deps root-N0REP0) + (action + (with-stdout-to + %{targets} + (run ./run.exe %{bin:opam} %{dep:var-option.test} %{read-lines:testing-env})))) + +(rule + (targets opam-repo-N0REP0) + (action + (progn + (run mkdir -p %{targets}/packages) + (write-file repo "opam-version:\"2.0\"") + (run cp repo %{targets}/repo)))) + +(rule + (targets root-N0REP0) + (action + (progn + (ignore-stdout + (run %{bin:opam} init --root=%{targets} + --no-setup --bypass-checks --no-opamrc --bare + file://%{dep:opam-repo-N0REP0}))))) + +(rule + (targets opam-archive-009e00fa.tar.gz) + (action (run wget --quiet -O %{targets} https://github.com/ocaml/opam-repository/archive/009e00fa.tar.gz))) + +(rule + (targets opam-repo-009e00fa) + (action + (progn + (run mkdir -p %{targets}) + (run tar -C %{targets} -xzf %{dep:opam-archive-009e00fa.tar.gz} --strip-components=1)))) + +(rule + (targets root-009e00fa) + (action + (progn + (ignore-stdout + (run %{bin:opam} init --root=%{targets} + --no-setup --bypass-checks --no-opamrc --bare + file://%{dep:opam-repo-009e00fa}))))) + +(rule + (targets opam-archive-ad4dd344.tar.gz) + (action (run wget --quiet -O %{targets} https://github.com/ocaml/opam-repository/archive/ad4dd344.tar.gz))) + +(rule + (targets opam-repo-ad4dd344) + (action + (progn + (run mkdir -p %{targets}) + (run tar -C %{targets} -xzf %{dep:opam-archive-ad4dd344.tar.gz} --strip-components=1)))) + +(rule + (targets root-ad4dd344) + (action + (progn + (ignore-stdout + (run %{bin:opam} init --root=%{targets} + --no-setup --bypass-checks --no-opamrc --bare + file://%{dep:opam-repo-ad4dd344}))))) + +(rule + (targets opam-archive-c1d23f0e.tar.gz) + (action (run wget --quiet -O %{targets} https://github.com/ocaml/opam-repository/archive/c1d23f0e.tar.gz))) + +(rule + (targets opam-repo-c1d23f0e) + (action + (progn + (run mkdir -p %{targets}) + (run tar -C %{targets} -xzf %{dep:opam-archive-c1d23f0e.tar.gz} --strip-components=1)))) + +(rule + (targets root-c1d23f0e) + (action + (progn + (ignore-stdout + (run %{bin:opam} init --root=%{targets} + --no-setup --bypass-checks --no-opamrc --bare + file://%{dep:opam-repo-c1d23f0e}))))) + +(rule + (targets opam-archive-f372039d.tar.gz) + (action (run wget --quiet -O %{targets} https://github.com/ocaml/opam-repository/archive/f372039d.tar.gz))) + +(rule + (targets opam-repo-f372039d) + (action + (progn + (run mkdir -p %{targets}) + (run tar -C %{targets} -xzf %{dep:opam-archive-f372039d.tar.gz} --strip-components=1)))) + +(rule + (targets root-f372039d) + (action + (progn + (ignore-stdout + (run %{bin:opam} init --root=%{targets} + --no-setup --bypass-checks --no-opamrc --bare + file://%{dep:opam-repo-f372039d}))))) diff -Nru opam-2.0.10/tests/reftests/env.test opam-2.1.2/tests/reftests/env.test --- opam-2.0.10/tests/reftests/env.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/env.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,51 @@ +N0REP0 +### : Revert env : +### +opam-version: "2.0" +setenv: [ NV_VARS += "%{_:doc}%:%{_:share}%" ] +flags: compiler +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +Now run 'opam upgrade' to apply any package updates. +### opam switch create setenv nv + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["nv"] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed nv.1 +Done. +### opam env | grep "NV_VARS" | '[:;]' -> '-' | '[:;]' -> '-' | '[:;]' -> '-' +NV_VARS='${BASEDIR}/OPAM/setenv/doc/nv-${OPAMTMP}/OPAM/setenv/share/nv'- export NV_VARS- +### opam exec -- opam env --revert | grep "NV_VARS" | '[:;]' -> '-' | '[:;]' -> '-' | '[:;]' -> '-' +NV_VARS=''- export NV_VARS- +### NV_VARS=/another/path +### opam env | grep "NV_VARS" | '[:;]' -> '-' | '[:;]' -> '-' | '[:;]' -> '-' +NV_VARS='${BASEDIR}/OPAM/setenv/doc/nv-${OPAMTMP}/OPAM/setenv/share/nv-/another/path'- export NV_VARS- +### opam exec -- opam env --revert | grep "NV_VARS" | '[:;]' -> '-' +NV_VARS='/another/path'- export NV_VARS- +### : package variable available at install stage : +### +opam-version: "2.0" +setenv: [NV_VARS = "%{_:nv_config}%"] +flags: compiler +### +opam-version: "2.0" +variables { nv_config: "hej!!" } +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +Now run 'opam upgrade' to apply any package updates. +### opam switch create conffile nv + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["nv"] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed nv.1 +Done. +### opam env | grep NV_VARS +NV_VARS='hej!!'; export NV_VARS; diff -Nru opam-2.0.10/tests/reftests/gen.ml opam-2.1.2/tests/reftests/gen.ml --- opam-2.0.10/tests/reftests/gen.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/gen.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,117 @@ +let first_line ~path = + let ic = open_in path in + let s = input_line ic in + close_in ic; + s + +let null_hash= "N0REP0" +let default_repo = "opam-repo-"^null_hash + +let diff_rule base_name = + Format.sprintf + {| +(alias + (name reftest-%s) + (action + (diff %s.test %s.out))) + +(alias + (name reftest) + (deps (alias reftest-%s))) +|} + base_name base_name base_name base_name + +let tgz_name ~archive_hash = + Printf.sprintf "opam-archive-%s.tar.gz" archive_hash + +let repo_directory ~archive_hash = + Printf.sprintf "opam-repo-%s" archive_hash + +let opamroot_directory ~archive_hash = + Printf.sprintf "root-%s" archive_hash + +let run_rule ~base_name ~archive_hash = + Format.sprintf {| +(rule + (targets %s) + (deps %s) + (action + (with-stdout-to + %%{targets} + (run ./run.exe %%{bin:opam} %%{dep:%s.test} %%{read-lines:testing-env})))) +|} (base_name^".out") (opamroot_directory ~archive_hash) base_name + +let archive_download_rule archive_hash = + Format.sprintf {| +(rule + (targets %s) + (action (run wget --quiet -O %%{targets} https://github.com/ocaml/opam-repository/archive/%s.tar.gz))) +|} (tgz_name ~archive_hash) archive_hash + +let default_repo_rule = + Format.sprintf {| +(rule + (targets %s) + (action + (progn + (run mkdir -p %%{targets}/packages) + (write-file repo "opam-version:\"2.0\"") + (run cp repo %%{targets}/repo)))) +|} default_repo + +(* XXX this fails if the directory already exists ?! *) +let archive_unpack_rule archive_hash = + Format.sprintf {| +(rule + (targets %s) + (action + (progn + (run mkdir -p %%{targets}) + (run tar -C %%{targets} -xzf %%{dep:%s} --strip-components=1)))) +|} (repo_directory ~archive_hash) (tgz_name ~archive_hash) + +let opam_init_rule archive_hash = + Format.sprintf {| +(rule + (targets %s) + (action + (progn + (ignore-stdout + (run %%{bin:opam} init --root=%%{targets} + --no-setup --bypass-checks --no-opamrc --bare + file://%%{dep:%s}))))) +|} (opamroot_directory ~archive_hash) (repo_directory ~archive_hash) + +module StringSet = Set.Make(String) + +let () = + let () = set_binary_mode_out stdout true in + let contents = + Sys.readdir "." + |> Array.to_list + |> List.sort String.compare + in + let process archive_hashes filename = + let base_name = OpamStd.String.remove_suffix ~suffix:".test" filename in + if base_name = filename then archive_hashes else + (print_string (diff_rule base_name); + let archive_hash = first_line ~path:filename in + if archive_hash = null_hash then + (print_string (run_rule ~base_name ~archive_hash:null_hash); + archive_hashes) + else + (print_string (run_rule ~base_name ~archive_hash); + StringSet.add archive_hash archive_hashes)) + in + let archive_hashes = + List.fold_left process StringSet.empty contents + in + print_string default_repo_rule; + print_string (opam_init_rule null_hash); + StringSet.iter + (fun archive_hash -> + print_string (archive_download_rule archive_hash); + print_string (archive_unpack_rule archive_hash); + print_string (opam_init_rule archive_hash) + ) + archive_hashes diff -Nru opam-2.0.10/tests/reftests/gen.mli opam-2.1.2/tests/reftests/gen.mli --- opam-2.0.10/tests/reftests/gen.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/gen.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +(* empty *) diff -Nru opam-2.0.10/tests/reftests/init.test opam-2.1.2/tests/reftests/init.test --- opam-2.0.10/tests/reftests/init.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/init.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,92 @@ +009e00fa +### OPAMYES=1 +### tar xzf ${OPAMROOT}/repo/default.tar.gz +### rm -rf ${OPAMROOT} +### +eval-variables: [ sys-ocaml-version ["false"] "no system compiler" ] +### opam init --no-setup --bypass-checks default default/ --fake --config opamrc +Configuring from ${BASEDIR}/opamrc and then from built-in defaults. + +<><> Fetching repository information ><><><><><><><><><><><><><><><><><><><><><> +[default] Initialised + +<><> Creating initial switch 'default' (invariant ["ocaml" {>= "4.05.0"}] - initially with ocaml-base-compiler) + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {>= "4.05.0"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.10.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.10.0 +Done. +### opam switch invariant +["ocaml" {>= "4.05.0"}] +### rm -rf ${OPAMROOT} +### +eval-variables: [ sys-ocaml-version ["echo" "4.02.3"] "old system compiler" ] +### opam init --no-setup --bypass-checks default default/ --fake --config opamrc +Configuring from ${BASEDIR}/opamrc and then from built-in defaults. + +<><> Fetching repository information ><><><><><><><><><><><><><><><><><><><><><> +[default] Initialised + +<><> Creating initial switch 'default' (invariant ["ocaml" {>= "4.05.0"}] - initially with ocaml-base-compiler) + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {>= "4.05.0"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.10.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.10.0 +Done. +### opam switch invariant +["ocaml" {>= "4.05.0"}] +### rm -rf ${OPAMROOT} +### +eval-variables: [ sys-ocaml-version ["echo" "4.07.0"] "new system compiler" ] +### opam init --no-setup --bypass-checks default default/ --fake --config opamrc +Configuring from ${BASEDIR}/opamrc and then from built-in defaults. + +<><> Fetching repository information ><><><><><><><><><><><><><><><><><><><><><> +[default] Initialised + +<><> Creating initial switch 'default' (invariant ["ocaml" {>= "4.05.0"}] - initially with ocaml-system) + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {>= "4.05.0"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-system.4.07.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.07.0 +Done. +### opam switch invariant +["ocaml" {>= "4.05.0"}] +### opam upgrade --fake +Everything as up-to-date as possible (run with --verbose to show unavailable upgrades). +However, you may "opam upgrade" these packages explicitly, which will ask permission to downgrade or uninstall the conflicting packages. +Nothing to do. +### opam upgrade ocaml --fake +The following actions will be faked: + - remove ocaml-system 4.07.0 [conflicts with ocaml] + - install ocaml-base-compiler 4.10.0 [required by ocaml] + - recompile ocaml-config 1 [uses ocaml-system] + - upgrade ocaml 4.07.0 to 4.10.0 +===== 1 to install | 1 to recompile | 1 to upgrade | 1 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of ocaml-base-compiler.4.10.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.10.0 +Done. diff -Nru opam-2.0.10/tests/reftests/install-pgocaml.test opam-2.1.2/tests/reftests/install-pgocaml.test --- opam-2.0.10/tests/reftests/install-pgocaml.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/install-pgocaml.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,23 @@ +f372039d +### opam switch create --fake 4.06.1 + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-base-compiler" {= "4.06.1"} | "ocaml-system" {= "4.06.1"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of ocaml-base-compiler.4.06.1 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.06.1 +Done. +### opam install 'pgocaml<2.0' 'pgocaml>=1.7.1' --show +[ERROR] Package conflict! + * No agreement on the version of ocaml: + - (invariant) -> ocaml-base-compiler = 4.06.1 -> ocaml = 4.06.1 + - pgocaml < 2.0 -> ocaml < 4.06.0 + You can temporarily relax the switch invariant with `--update-invariant' + +No solution found, exiting +# Return code 20 # diff -Nru opam-2.0.10/tests/reftests/legacy-git.test opam-2.1.2/tests/reftests/legacy-git.test --- opam-2.0.10/tests/reftests/legacy-git.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/legacy-git.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,1371 @@ +N0REP0 +### : UTILITARY SCRIPTS : +### +set -ue +cd $1 +git init >/dev/null 2>&1 +git config --local core.autocrlf false +echo '*.sh text eol=lf' >.gitattributes +[ $# -lt 2 ] || touch "$2" +git add -A +git config --local user.name 'OPAM test environment' +git config --local user.email noreply@ocaml.org +git commit -qm "Initial commit" +echo "OK" +### +set -ue +PKG=$1; shift +ARCHIVE=$1; shift +if [ ! -e "packages/${ARCHIVE}" ]; then ( cd packages && tar czf ${ARCHIVE} ${ARCHIVE%.tar.gz}; ) fi +MD5=$(openssl md5 packages/${ARCHIVE} | cut -d' ' -f2) +echo 'src: "http://dev.null" checksum: "'$MD5'"' > REPO/packages/${PKG}/url +CACHEDIR=REPO/cache/md5/$(echo $MD5 |head -c2) +mkdir -p $CACHEDIR +cp "packages/$ARCHIVE" "$CACHEDIR/$MD5" +### : INIT : +### rm REPO/repo +### rmdir REPO/packages +### sh git-init.sh REPO README +OK +### opam repo add test REPO --set-default --yes +[test] Initialised +[WARNING] The repository 'test' at git+file://${BASEDIR}/REPO doesn't have a 'repo' file, and might not be compatible with this version of opam. +[NOTE] Repository at git+file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "test"... +### opam repo remove default -a +### : GEN FILES : +### +(* API version *) +opam-version: "1" + +name: "P1" +version: "0" + +setenv: [P1 = "version0"] +substs: [ "P1.config" "P1.install" ] + +build: [ + [ "ocamlc" "-a" "p1.ml" "-o" "p1.cma" ] + [ "ocamlopt" "-a" "p1.ml" "-o" "p1.cmxa" ] +] +depends: ["ocaml"] +### +(* API version *) +opam-version: "1" + +name: "P1" + +# Test +# Toto + +(* Version are arbitrary strings *) +version: "1" + +maintainer: "contact@ocamlpro.com" + +(* The command to run *) +build: [ + ["./build.sh"] # HAHAH + ["this" "should" "never" "run"] {ocaml:version > "z100"} + [make "this" ocaml:version "also"] {os = "NO"} + ["echo" "HAHA!"] {ocaml:version = "10"} + ["echo" make share ocaml:version] + ["this as well" {os = "myOS"}] +] +available: os != "NO" | os != "NO" & os != "YES" + +(* List of files to substitute env variables *) +substs: [ "P1.config" "P1.install" ] + +(* Libraries *) +libraries: [ "p1" ] + +(* External dependencies *) +depexts: [ + [ ["debian" "amd64"] ["apt" "dpkg"] ] + [ ["osx" ] ["curl"] ] +] + +messages: [ "I ll always bother you displaying this message" ] + +post-messages: [ "Thanks SO MUCH for installing this humble package" + "Everything went well" {success} + "Nooo, something went wrong, this makes me feel sooo sad..." {failure} ] + +bug-reports: "TEST.com" + +setenv: [P1 = "version1"] +depends: [ + "ocaml" {(!= "20" | != "10") & (= "20" | = "10" | = "10+a+b" | = "system")} +] +### +opam-version: "1" +name: "P1" +version: "2" +depends: [ "ocaml" {<= "10" | = "system"} ] +maintainer: "contact@ocamlpro.com" +substs: [ "P1.config" "P1.install" ] +libraries: [ "p1" ] +build: [ "./build.sh" ] +setenv: [P1 = "version2"] +### +opam-version: "1" +name: "P2" +version: "1" +maintainer: "contact@ocamlpro.com" +substs: [ "config" "P2.config" "P2.install" ] +depends: ["ocaml" "P1"] +libraries: [ "p2" ] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P3" +version: "1~weird-version.test" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +substs: [ "P3.config" "P3.install" ] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P2" "P3"] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "2" +maintainer: "contact@ocamlpro.com" +depends: [ + "P1" { <= "1" } + "P2" + "P3" +] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "3" +maintainer: "contact@ocamlpro.com" +depends: [ "P2" "P3" ] +build: [ "./build.sh" ] +### +(* API version *) +opam-version: "1" +name: "P5" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +depopts: [ "P2" ] +build: [ [ "./build.sh" ] ] +install: [ [ "mkdir" "-p" "%{lib}%/p5" ] + [ "touch" "%{lib}%/p5/p2_present" ] {P2:installed} + [ "touch" "%{lib}%/p5/p2_absent" ] {!P2:installed} ] +remove: [ "rm" "-rf" "%{lib}%/p5" ] +### +opam-version: "1.3~dev4" +variables { + compiler: "10+a+b" + native: true + native-tools: true + native-dynlink: true + stubsdir: "%{lib}%/stublibs" +} +### +opam-version: "1.3" +maintainer: "contact@ocamlpro.com" +# depends: ["P1" "P2" "P3" "P4"] +flags: compiler +setenv: TEST = "1" +### +opam-version: "1.3~dev4" +variables { + compiler: "20" + native: true + native-tools: true + native-dynlink: true + stubsdir: "%{lib}%/stublibs" +} +### +opam-version: "1.3" +maintainer: "contact@ocamlpro.com" +flags: compiler +setenv: TEST = "1" +### +#!/bin/sh -ue + +if ! OCAMLC=$(command -v ocamlc); then + echo "No OCaml compiler was found on the system" >&2 + exit 2 +fi + +if [ $($OCAMLC -config | sed -ne "s/os_type: //p" | tr -d '\r') = Win32 ] ; then + OCAMLC_FILE=$(echo $OCAMLC| cygpath -w -f - | sed -e 's/\\/\\\\/g') + LIBDIR=$("$OCAMLC" -where | tr -d '\r' | cygpath -f -) +else + OCAMLC_FILE=$OCAMLC + LIBDIR=$("$OCAMLC" -where) +fi + +STUBLIBS=$(cat "$LIBDIR/ld.conf" | tr -d '\r' | tr '\n' ':' | sed -e 's/\\/\\\\/g') + +echo "Using ocaml compiler found at $OCAMLC with base lib at $LIBDIR" + +bool() { + if "$@"; then echo "true"; else echo "false"; fi +} + +cat >ocaml.config < +opam-version: "1.3.0~dev4" +maintainer: "louis.gesbert@ocamlpro.com" +build: ["sh" "-uex" "./gen.sh"] +setenv: [CAML_LD_LIBRARY_PATH = "%{lib}%:%{_:ocaml-stublibs}%"] +depends: [ ] +flags: compiler +### +(* API version *) +opam-version: "1" + +name: "P1" +version: "0" + +setenv: [P1 = "version0"] +substs: [ "P1.config" "P1.install" ] + +build: [ + [ "ocamlc" "-a" "p1.ml" "-o" "p1.cma" ] + [ "ocamlopt" "-a" "p1.ml" "-o" "p1.cmxa" ] +] +depends: ["ocaml"] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P1" + bytecomp: "-I %{lib}%/P1" + asmlink: "-I %{lib}%/P1 p1.cmxa" + bytelink: "-I %{lib}%/P1 p1.cma" + LOCAL: "local" + l: "L" + FOO: "foo" + bar: true +} +### +lib: [ + "p1.cmi" + "p1.cma" + "p1.cmxa" + "p1.%{ext_lib}%" +] +### +let x () = + try Random.int 10 + with _ -> 0 +### +#! /bin/sh -eu + +if [ -n "${P1:-}" ]; then + echo "P1 ('$P1') should not be set yet" >&2 + exit 12 +fi + +ocamlc -a p1.ml -o p1.cma +ocamlopt -a p1.ml -o p1.cmxa +### chmod a+x packages/P1-1/build.sh +### +(* API version *) +opam-version: "1" + +name: "P1" + +# Test +# Toto + +(* Version are arbitrary strings *) +version: "1" + +maintainer: "contact@ocamlpro.com" + +(* The command to run *) +build: [ + ["./build.sh"] # HAHAH + ["this" "should" "never" "run"] {ocaml:version > "z100"} + [make "this" ocaml:version "also"] {os = "NO"} + ["echo" "HAHA!"] {ocaml:version = "10"} + ["echo" make share ocaml:version] + ["this as well" {os = "myOS"}] +] +available: os != "NO" | os != "NO" & os != "YES" + +(* List of files to substitute env variables *) +substs: [ "P1.config" "P1.install" ] + +(* Libraries *) +libraries: [ "p1" ] + +(* External dependencies *) +depexts: [ + [ ["debian" "amd64"] ["apt" "dpkg"] ] + [ ["osx" ] ["curl"] ] +] + +messages: [ "I ll always bother you displaying this message" ] + +post-messages: [ "Thanks SO MUCH for installing this humble package" + "Everything went well" {success} + "Nooo, something went wrong, this makes me feel sooo sad..." {failure} ] + +bug-reports: "TEST.com" + +setenv: [P1 = "version1"] +depends: [ + "ocaml" {(!= "20" | != "10") & (= "20" | = "10" | = "10+a+b" | = "system")} +] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P1" + bytecomp: "-I %{lib}%/P1" + asmlink: "-I %{lib}%/P1 p1.cmxa" + bytelink: "-I %{lib}%/P1 p1.cma" + LOCAL: "local" + l: "L" + FOO: "foo" + bar: true +} +### +lib: [ + "p1.cmi" + "p1.cma" + "p1.cmxa" + "p1.%{ext_lib}%" + "?this_file_will_not_exist_but_that's_ok" +] +share: [ "build.sh" ] +doc: [ + "p1.cmi" { "foo/bar/index.html" } +] +### +let x () = + try Random.int 10 + with _ -> 0 +### +A very useful package +### +#! /bin/sh -eu + +if [ -n "${P1:-}" ]; then + echo "P1 ('$P1') should not be set yet" >&2 + exit 12 +fi + +ocamlc -a p1.ml -o p1.cma +ocamlopt -a p1.ml -o p1.cmxa +### chmod a+x packages/P1-2/build.sh +### +opam-version: "1" +name: "P1" +version: "2" +depends: [ "ocaml" {<= "10" | = "system"} ] +maintainer: "contact@ocamlpro.com" +substs: [ "P1.config" "P1.install" ] +libraries: [ "p1" ] +build: [ "./build.sh" ] +setenv: [P1 = "version2"] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P1" + bytecomp: "-I %{lib}%/P1" + asmlink: "-I %{lib}%/P1 p1.cmxa" + bytelink: "-I %{lib}%/P1 p1.cma" + LOCAL: "local" + l: "L" + FOO: "foo" + bar: true +} +### +lib: [ + "p1.cma" + "p1.cmxa" + "p1.%{ext_lib}%" + "p1.cmi" +] +### +let x () = + failwith "the new version is not very good" +### +A very useful package +### +#! /bin/sh -eu + +OFLAGS="`opam var P1:asmcomp | tr -d '\r'`" +CFLAGS="`opam var P1:bytecomp | tr -d '\r'`" + +echo "Bytecode Compilation" +ocamlc ${CFLAGS} -a p2.ml -o p2.cma + +if which ocamlopt >/dev/null 2>&1; then + echo "Native Compilation" + ocamlopt ${OFLAGS} -a p2.ml -o p2.cmxa +fi + +### chmod a+x packages/P2/build.sh +### +Foo is %{P1:FOO}% + +Foo also contains a variable with %{P1:l}%. Funny, isn\'t it? +### +opam-version: "1" +name: "P2" +version: "1" +maintainer: "contact@ocamlpro.com" +substs: [ "config" "P2.config" "P2.install" ] +depends: ["ocaml" "P1"] +libraries: [ "p2" ] +build: [ "./build.sh" ] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P2" + bytecomp: "-I %{lib}%/P2" + asmlink: "-I %{lib}%/P2 p2.cmxa" + bytelink: "-I %{lib}%/P2 p2.cma" + requires: "p1" +} +### +lib: [ + "p2.cma" + "p2.cmxa" + "p2.%{ext_lib}%" + "p2.cmi" +] +### +let g () = + P1.x () +### +An other very useful package + +The description can go on multiple lines. The first line is the package synopsis, +and the rest is the package description. +### +#! /bin/sh -eu + +OPAM=$(cygpath ${OPAM} 2>/dev/null || echo ${OPAM}) + +echo "Building P3 version ${OPAM_PACKAGE_VERSION}" + +if [ "x${OPAM_PACKAGE_NAME}" = "xP3" ]; then + LIB=$(${OPAM} var lib | tr -d '\r') + ocamlc -a -I $LIB/P1 -I $LIB/P2 p3.ml -o p3.cma + ocamlopt -a -I $LIB/P1 -I $LIB/P2 p3.ml -o p3.cmxa + ocamlc -a -I $LIB/P1 -I $LIB/P2 p3_bar.ml -o p3_bar.cma + ocamlopt -a -I $LIB/P1 -I $LIB/P2 p3_bar.ml -o p3_bar.cmxa +else + exit 1 +fi +### chmod a+x packages/P3/build.sh +### +opam-version: "1" +name: "P3" +version: "1~weird-version.test" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +substs: [ "P3.config" "P3.install" ] +build: [ "./build.sh" ] +### +let f () = + Printf.printf "foo\n%!" + +let _ = + P3.z () +### +opam-version: "1.3" +variables { + asmcomp : "-I %{lib}%/P3" + bytecomp: "-I %{lib}%/P3" + asmlink : "-I %{lib}%/P3 p3.cmxa p3_bar.cmxa" + bytelink: "-I %{lib}%/P3 p3.cma p3_bar.cma" + requires: "p1" +} +### +lib: [ + (* p3 *) + "p3.cma" + "p3.cmxa" + "p3.%{ext_lib}%" + "p3.cmi" + + (* p3_bar *) + "p3_bar.cma" + "p3_bar.cmxa" + "p3_bar.%{ext_lib}%" + "p3_bar.cmi" +] +### +let z () = + try P1.x () + with _ -> 0 +### +Testing version names +### +opam-version: "1" +name: "P4" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P2" "P3"] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "2" +maintainer: "contact@ocamlpro.com" +depends: [ + "P1" { <= "1" } + "P2" + "P3" +] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "3" +maintainer: "contact@ocamlpro.com" +depends: [ "P2" "P3" ] +build: [ "./build.sh" ] +### +#! /bin/sh -ex + +OPAM=$(cygpath ${OPAM} 2>/dev/null || echo ${OPAM}) + +opam() { $OPAM "$@" | tr -d '\r'; } + +if [ $OPAM_PACKAGE_VERSION -eq 2 ]; then + if [ "X${P1:-}" != "Xversion1" ]; then + echo "P1 not set to version1 while P1.1 should be installed" >&2 + exit 12 + fi +else + if [ -z "X${P1:-}" ]; then + echo "P1 not set while P1 should be installed" >&2 + exit 12 + fi +fi + +echo "Building P4 with ${OPAM}" +COMP="-I $(opam var P1:lib) -I $(opam var P2:lib) -I $(opam var P3:lib)" +LINK="p1.cmxa p2.cmxa p3.cmxa p3_bar.cmxa" + +OCAMLC=ocamlc +if which ocamlopt >/dev/null 2>&1; then OCAMLC=ocamlopt; fi + +$OCAMLC ${COMP} ${LINK} p4.ml -o p4.foo.exe + +echo "TEST=${TEST}" +### chmod a+x packages/P4/build.sh +### +bin: [ + "p4.foo.exe" { "p4.exe" } + "p4.foo.exe" +] +### +let f = + try P3_bar.f (); P1.x () + with _ -> P3.z () + +let () = + let t = + try Sys.getenv "TEST" + with _ -> "" in + Printf.printf "TEST=%s\n%!" t +### +Testing transitive closure +### +#! /bin/sh -eu + +OPAM=$(cygpath ${OPAM} 2>/dev/null || echo ${OPAM}) + +FLAGS="-I `${OPAM} var P1:lib | tr -d '\r'`" + +echo "Bytecode Compilation" +ocamlc ${FLAGS} -a p5.ml -o p5.cma + +if which ocamlopt >/dev/null 2>&1; then + echo "Native Compilation" + ocamlopt ${FLAGS} -a p5.ml -o p5.cmxa +fi +### chmod a+x packages/P5/build.sh +### +(* API version *) +opam-version: "1" +name: "P5" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +depopts: [ "P2" ] +build: [ [ "./build.sh" ] ] +install: [ [ "mkdir" "-p" "%{lib}%/p5" ] + [ "touch" "%{lib}%/p5/p2_present" ] {P2:installed} + [ "touch" "%{lib}%/p5/p2_absent" ] {!P2:installed} ] +remove: [ "rm" "-rf" "%{lib}%/p5" ] +### +let g () = + P1.x () +### +Testing optional dependencies +### : UPLOAD : +### cp -r packages/ocaml REPO/packages +### mkdir -p REPO/packages/P1.0 +### cp packages/P1-0.opam REPO/packages/P1.0/opam +### sh mkurl.sh P1.0 P1-0.tar.gz +### mkdir -p REPO/packages/P1.1 +### cp packages/P1-1.opam REPO/packages/P1.1/opam +### cp packages/P1-1/README REPO/packages/P1.1/descr +### sh mkurl.sh P1.1 P1-1.tar.gz +### mkdir -p REPO/packages/P2.1 +### cp packages/P2/README REPO/packages/P2.1/descr +### cp packages/P2.opam REPO/packages/P2.1/opam +### sh mkurl.sh P2.1 P2.tar.gz +### mkdir -p REPO/packages/P3.1~weird-version.test +### cp packages/P3.opam REPO/packages/P3.1~weird-version.test/opam +### cp packages/P3/README REPO/packages/P3.1~weird-version.test/descr +### sh mkurl.sh P3.1~weird-version.test P3.tar.gz +### mkdir -p REPO/packages/P4.1 +### cp packages/P4-1.opam REPO/packages/P4.1/opam +### cp packages/P4/README REPO/packages/P4.1/descr +### sh mkurl.sh P4.1 P4.tar.gz +### mkdir -p REPO/packages/P5.1 +### cp packages/P5.opam REPO/packages/P5.1/opam +### cp packages/P5/README REPO/packages/P5.1/descr +### sh mkurl.sh P5.1 P5.tar.gz +### git -C REPO/packages/ocaml.system add * +### git -C REPO/packages/ocaml.system commit -qm "Adding ocaml.system" +### git -C REPO/packages/ocaml.20 add * +### git -C REPO/packages/ocaml.20 commit -qm "Adding ocaml.20" +### git -C REPO/packages/ocaml.10+a+b add * +### git -C REPO/packages/ocaml.10+a+b commit -qm "Adding ocaml.10+a+b" +### +git: "GIT/P1-0" +### git -C REPO/packages/P1.0/ add * +### git -C REPO/packages/P1.0/ commit -qm "Adding P0" +### +git: "GIT/P1-1" +### git -C REPO/packages/P1.1/ add * +### git -C REPO/packages/P1.1/ commit -qm "Adding P1" +### +git: "GIT/P2" +### git -C REPO/packages/P2.1/ add * +### git -C REPO/packages/P2.1/ commit -qm "Adding P2" +### +git: "GIT/P3" +### git -C REPO/packages/P3.1~weird-version.test/ add * +### git -C REPO/packages/P3.1~weird-version.test/ commit -qm "Adding P3" +### +git: "GIT/P4" +### git -C REPO/packages/P4.1/ add * +### git -C REPO/packages/P4.1/ commit -qm "Adding P4" +### +git: "GIT/P5" +### git -C REPO/packages/P5.1/ add * +### git -C REPO/packages/P5.1/ commit -qm "Adding P5" +### rm -rf GIT +### mkdir GIT +### cp -r packages/P1-0 GIT/ +### cp -r packages/P1-1 GIT/ +### cp -r packages/P2 GIT/ +### cp -r packages/P3 GIT/ +### cp -r packages/P4 GIT/ +### cp -r packages/P5 GIT/ +### sh git-init.sh GIT/P1-0 +OK +### sh git-init.sh GIT/P1-1 +OK +### sh git-init.sh GIT/P2 +OK +### sh git-init.sh GIT/P3 +OK +### sh git-init.sh GIT/P4 +OK +### sh git-init.sh GIT/P5 +OK +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[test] synchronised from git+file://${BASEDIR}/REPO +[WARNING] The repository 'test' at git+file://${BASEDIR}/REPO doesn't have a 'repo' file, and might not be compatible with this version of opam. +[NOTE] Repository at git+file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "test"... +Updated ${OPAMTMP}/test/packages/P1.0/opam +Updated ${OPAMTMP}/test/packages/P1.1/opam +Updated ${OPAMTMP}/test/packages/P2.1/opam +Updated ${OPAMTMP}/test/packages/P3.1~weird-version.test/opam +Updated ${OPAMTMP}/test/packages/P4.1/opam +Updated ${OPAMTMP}/test/packages/P5.1/opam +Now run 'opam upgrade' to apply any package updates. +### opam switch create system "--formula=[\"ocaml\" {= \"system\"}]" + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {= "system"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.system +Done. +### opam exec -- ocamlc -config | 'ext_lib: .' -> '#' | '^[^#].*' -> '\c' | '^#' -> '' >$ LIB_EXT +### opam var ext_lib=$LIB_EXT --switch system | '.*' -> '\c' +### : INSTALL-REMOVE : +### opam list -is --columns=package +ocaml.system +### opam install P1 +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> installed P1.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +### opam remove P1 +The following actions will be performed: + - remove P1 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 -- A very useful package +P2 -- An other very useful package +P3 -- Testing version names +P4 -- Testing transitive closure +P5 -- Testing optional dependencies +### : INSTALL-OPT : +### opam list -is --columns=package +ocaml.system +### opam install --yes P5 | unordered +The following actions will be performed: + - install P1 1 [required by P5] I ll always bother you displaying this message + - install P5 1 +===== 2 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> retrieved P5.1 (git+file://${BASEDIR}/GIT/P5) +-> installed P1.1 +-> installed P5.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### test -f ${OPAMROOT}/system/lib/p5/p2_absent +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam remove P5 +The following actions will be performed: + - remove P5 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +### opam install P5 +The following actions will be performed: + - install P5 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P5.1 (git+file://${BASEDIR}/GIT/P5) +-> installed P5.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam remove P5 -a --yes +The following actions will be performed: + - remove P5 1 + - remove P1 1 +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### opam install P5 --yes | unordered +The following actions will be performed: + - install P1 1 [required by P5] I ll always bother you displaying this message + - install P5 1 +===== 2 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> retrieved P5.1 (git+file://${BASEDIR}/GIT/P5) +-> installed P1.1 +-> installed P5.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam install P2 --yes | unordered +The following actions will be performed: + - install P2 1 + - recompile P5 1 [uses P2] +===== 1 to install | 1 to recompile ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P2.1 (git+file://${BASEDIR}/GIT/P2) +-> retrieved P5.1 (no changes) +-> removed P5.1 +-> installed P2.1 +-> installed P5.1 +Done. +### test -f ${OPAMROOT}/system/lib/p5/p2_present +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P5.1 +### opam remove P5 -a +The following actions will be performed: + - remove P5 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam remove P2 -a --yes +The following actions will be performed: + - remove P2 1 + - remove P1 1 +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P2.1 +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### opam install P1 P2 P5 | unordered +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + - install P2 1 + - install P5 1 +===== 3 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> retrieved P2.1 (git+file://${BASEDIR}/GIT/P2) +-> installed P1.1 +-> retrieved P5.1 (git+file://${BASEDIR}/GIT/P5) +-> installed P2.1 +-> installed P5.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### test -f ${OPAMROOT}/system/lib/p5/p2_present +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P5.1 +### opam remove P2 -a --yes +The following actions will be performed: + - remove P2 1 + - recompile P5 1 [uses P2] +===== 1 to recompile | 1 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P5.1 (no changes) +-> removed P5.1 +-> removed P2.1 +-> installed P5.1 +Done. +### test -f ${OPAMROOT}/system/lib/p5/p2_absent +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam remove P1 --yes +The following actions will be performed: + - remove P5 1 [uses P1] + - remove P1 1 +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 -- A very useful package +P2 -- An other very useful package +P3 -- Testing version names +P4 -- Testing transitive closure +P5 -- Testing optional dependencies +### : INSTALL : +### opam list -is --columns=package +ocaml.system +### opam install P1 +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> installed P1.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +### opam install P2 +The following actions will be performed: + - install P2 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P2.1 (git+file://${BASEDIR}/GIT/P2) +-> installed P2.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam install P3 +The following actions will be performed: + - install P3 1~weird-version.test + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P3.1~weird-version.test (git+file://${BASEDIR}/GIT/P3) +-> installed P3.1~weird-version.test +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +### opam install P4 +The following actions will be performed: + - install P4 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P4.1 (git+file://${BASEDIR}/GIT/P4) +-> installed P4.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 1 Testing transitive closure +P5 -- Testing optional dependencies +### : REINSTALL : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### opam reinstall P1 --yes | unordered +The following actions will be performed: + - recompile P1 1 I ll always bother you displaying this message + - recompile P3 1~weird-version.test [uses P1] + - recompile P2 1 [uses P1] + - recompile P4 1 [uses P2, P3] +===== 4 to recompile ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (no changes) +-> retrieved P2.1 (no changes) +-> retrieved P3.1~weird-version.test (no changes) +-> retrieved P4.1 (no changes) +-> removed P4.1 +-> removed P2.1 +-> removed P3.1~weird-version.test +-> removed P1.1 +-> installed P1.1 +-> installed P2.1 +-> installed P3.1~weird-version.test +-> installed P4.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 1 Testing transitive closure +P5 -- Testing optional dependencies +### : UPLOAD-NEW : +### mkdir REPO/packages/P4.2 +### cp packages/P4-2.opam REPO/packages/P4.2/opam +### cp packages/P4/README REPO/packages/P4.2/descr +### sh mkurl.sh P4.2 P4.tar.gz +### mkdir REPO/packages/P4.3 +### cp packages/P4-3.opam REPO/packages/P4.3/opam +### cp packages/P4/README REPO/packages/P4.3/descr +### sh mkurl.sh P4.3 P4.tar.gz +### +let x () = + try Random.int 10 + with _ -> 0 +(* new line *) +### git -C GIT/P1-1 commit -a -qm "a small change" +### +git: "GIT/P4" +### +git: "GIT/P4" +### git -C REPO add * +### git -C REPO commit -qm "Adding P4.2 and P4.3" +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[test] synchronised from git+file://${BASEDIR}/REPO +[WARNING] The repository 'test' at git+file://${BASEDIR}/REPO doesn't have a 'repo' file, and might not be compatible with this version of opam. +[NOTE] Repository at git+file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "test"... +Updated ${OPAMTMP}/test/packages/P1.0/opam +Updated ${OPAMTMP}/test/packages/P1.1/opam +Updated ${OPAMTMP}/test/packages/P2.1/opam +Updated ${OPAMTMP}/test/packages/P3.1~weird-version.test/opam +Updated ${OPAMTMP}/test/packages/P4.1/opam +Updated ${OPAMTMP}/test/packages/P4.2/opam +Updated ${OPAMTMP}/test/packages/P4.3/opam +Updated ${OPAMTMP}/test/packages/P5.1/opam + +<><> Synchronising development packages <><><><><><><><><><><><><><><><><><><><> +[P1.1] synchronised (git+file://${BASEDIR}/GIT/P1-1) +[P2.1] synchronised (no changes) +[P3.1~weird-version.test] synchronised (no changes) +[P4.1] synchronised (no changes) +Now run 'opam upgrade' to apply any package updates. +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 1 Testing transitive closure +P5 -- Testing optional dependencies +### : UPGRADE : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### opam env --sexp | grep '"P1"' + ("P1" "version1") +### opam upgrade --yes | unordered +The following actions will be performed: + - recompile P1 1 [upstream or system changes] I ll always bother you displaying this message + - recompile P3 1~weird-version.test [uses P1] + - recompile P2 1 [uses P1] + - upgrade P4 1 to 3 +===== 3 to recompile | 1 to upgrade ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (no changes) +-> retrieved P2.1 (no changes) +-> retrieved P3.1~weird-version.test (no changes) +-> retrieved P4.3 (git+file://${BASEDIR}/GIT/P4) +-> removed P4.1 +-> removed P2.1 +-> removed P3.1~weird-version.test +-> removed P1.1 +-> installed P1.1 +-> installed P2.1 +-> installed P3.1~weird-version.test +-> installed P4.3 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.3 +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 3 Testing transitive closure +P5 -- Testing optional dependencies +### : DOWNGRADE : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.3 +### opam install P4.2 --yes | unordered +The following actions will be performed: + - downgrade P4 3 to 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P4.2 (git+file://${BASEDIR}/GIT/P4) +-> removed P4.3 +-> installed P4.2 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.2 +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 2 Testing transitive closure +P5 -- Testing optional dependencies +### : SWITCH-ALIAS : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.2 +### opam remove P3.1~weird-version.test P4.2 +The following actions will be performed: + - remove P4 2 + - remove P3 1~weird-version.test +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P4.2 +-> removed P3.1~weird-version.test +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch export test.export +### opam switch create test system + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {= "system"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.system +Done. +### opam var ext_lib=$LIB_EXT --switch test | '.*' -> '\c' +### opam list -is --columns=package +ocaml.system +### opam switch import test.export | unordered +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + - install P2 1 +===== 2 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> retrieved P2.1 (git+file://${BASEDIR}/GIT/P2) +-> installed P1.1 +-> installed P2.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch create test2 20 + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {= "20"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.20 +Done. +### opam var ext_lib=$LIB_EXT --switch test2 | '.*' -> '\c' +### opam list -is --columns=package +ocaml.20 +### opam install P1 +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> installed P1.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.20 +P1.1 +### opam switch system +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch remove test test2 --yes +Switch test and all its packages will be wiped. Are you sure? [Y/n] y +Switch test2 and all its packages will be wiped. Are you sure? [Y/n] y +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 -- Testing version names +P4 -- Testing transitive closure +P5 -- Testing optional dependencies +### : SWITCH-ENV-PACKAGES : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch create 10+a+b --empty +### opam var ext_lib=$LIB_EXT --switch 10+a+b | '.*' -> '\c' +### opam install ocaml.10+a+b P1 P2 P3 P4 | unordered +The following actions will be performed: + - install ocaml 10+a+b + - install P1 1 I ll always bother you displaying this message + - install P3 1~weird-version.test + - install P2 1 + - install P4 3 +===== 5 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.10+a+b +-> retrieved P1.1 (git+file://${BASEDIR}/GIT/P1-1) +-> retrieved P2.1 (git+file://${BASEDIR}/GIT/P2) +-> retrieved P3.1~weird-version.test (git+file://${BASEDIR}/GIT/P3) +-> installed P1.1 +-> retrieved P4.3 (git+file://${BASEDIR}/GIT/P4) +-> installed P2.1 +-> installed P3.1~weird-version.test +-> installed P4.3 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.10+a+b +P1.1 +P2.1 +P3.1~weird-version.test +P4.3 +### sh -c '. ${OPAMROOT}/opam-init/variables.sh && echo "PASS $TEST"' +PASS 1 +### : REPO : +### opam repo add REPO2 REPO -k git +[REPO2] Initialised +[WARNING] The repository 'REPO2' at git+file://${BASEDIR}/REPO doesn't have a 'repo' file, and might not be compatible with this version of opam. +[NOTE] Repository at git+file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "REPO2"... +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P1.0/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P1.1/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P2.1/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P3.1~weird-version.test/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P4.1/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P4.2/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P4.3/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P5.1/opam +[NOTE] Repository REPO2 has been added to the selections of switch 10+a+b only. + Run `opam repository add REPO2 --all-switches|--set-default' to use it in all existing switches, or in newly created switches, respectively. + +### opam repo remove REPO2 --all +### opam repo remove test --all +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml 10+a+b +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 3 Testing transitive closure diff -Nru opam-2.0.10/tests/reftests/legacy-local.test opam-2.1.2/tests/reftests/legacy-local.test --- opam-2.0.10/tests/reftests/legacy-local.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/legacy-local.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,1311 @@ +N0REP0 +### : UTILITARY SCRIPTS : +### +set -ue +PKG=$1; shift +ARCHIVE=$1; shift +if [ ! -e "packages/${ARCHIVE}" ]; then ( cd packages && tar czf ${ARCHIVE} ${ARCHIVE%.tar.gz}; ) fi +MD5=$(openssl md5 packages/${ARCHIVE} | cut -d' ' -f2) +echo 'src: "http://dev.null" checksum: "'$MD5'"' > REPO/packages/${PKG}/url +CACHEDIR=REPO/cache/md5/$(echo $MD5 |head -c2) +mkdir -p $CACHEDIR +cp "packages/$ARCHIVE" "$CACHEDIR/$MD5" +### : INIT : +### rm REPO/repo +### rmdir REPO/packages +### opam repo add test REPO --set-default --yes +"file://${BASEDIR}/REPO" doesn't contain a "packages" directory. +Is it really the directory of your repo? [Y/n] y +[test] Initialised +[WARNING] The repository 'test' at file://${BASEDIR}/REPO doesn't have a 'repo' file, and might not be compatible with this version of opam. +[NOTE] Repository at file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "test"... +### opam repo remove default -a +### : GEN FILES : +### +(* API version *) +opam-version: "1" + +name: "P1" +version: "0" + +setenv: [P1 = "version0"] +substs: [ "P1.config" "P1.install" ] + +build: [ + [ "ocamlc" "-a" "p1.ml" "-o" "p1.cma" ] + [ "ocamlopt" "-a" "p1.ml" "-o" "p1.cmxa" ] +] +depends: ["ocaml"] +### +(* API version *) +opam-version: "1" + +name: "P1" + +# Test +# Toto + +(* Version are arbitrary strings *) +version: "1" + +maintainer: "contact@ocamlpro.com" + +(* The command to run *) +build: [ + ["./build.sh"] # HAHAH + ["this" "should" "never" "run"] {ocaml:version > "z100"} + [make "this" ocaml:version "also"] {os = "NO"} + ["echo" "HAHA!"] {ocaml:version = "10"} + ["echo" make share ocaml:version] + ["this as well" {os = "myOS"}] +] +available: os != "NO" | os != "NO" & os != "YES" + +(* List of files to substitute env variables *) +substs: [ "P1.config" "P1.install" ] + +(* Libraries *) +libraries: [ "p1" ] + +(* External dependencies *) +depexts: [ + [ ["debian" "amd64"] ["apt" "dpkg"] ] + [ ["osx" ] ["curl"] ] +] + +messages: [ "I ll always bother you displaying this message" ] + +post-messages: [ "Thanks SO MUCH for installing this humble package" + "Everything went well" {success} + "Nooo, something went wrong, this makes me feel sooo sad..." {failure} ] + +bug-reports: "TEST.com" + +setenv: [P1 = "version1"] +depends: [ + "ocaml" {(!= "20" | != "10") & (= "20" | = "10" | = "10+a+b" | = "system")} +] +### +opam-version: "1" +name: "P1" +version: "2" +depends: [ "ocaml" {<= "10" | = "system"} ] +maintainer: "contact@ocamlpro.com" +substs: [ "P1.config" "P1.install" ] +libraries: [ "p1" ] +build: [ "./build.sh" ] +setenv: [P1 = "version2"] +### +opam-version: "1" +name: "P2" +version: "1" +maintainer: "contact@ocamlpro.com" +substs: [ "config" "P2.config" "P2.install" ] +depends: ["ocaml" "P1"] +libraries: [ "p2" ] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P3" +version: "1~weird-version.test" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +substs: [ "P3.config" "P3.install" ] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P2" "P3"] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "2" +maintainer: "contact@ocamlpro.com" +depends: [ + "P1" { <= "1" } + "P2" + "P3" +] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "3" +maintainer: "contact@ocamlpro.com" +depends: [ "P2" "P3" ] +build: [ "./build.sh" ] +### +(* API version *) +opam-version: "1" +name: "P5" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +depopts: [ "P2" ] +build: [ [ "./build.sh" ] ] +install: [ [ "mkdir" "-p" "%{lib}%/p5" ] + [ "touch" "%{lib}%/p5/p2_present" ] {P2:installed} + [ "touch" "%{lib}%/p5/p2_absent" ] {!P2:installed} ] +remove: [ "rm" "-rf" "%{lib}%/p5" ] +### +opam-version: "1.3~dev4" +variables { + compiler: "10+a+b" + native: true + native-tools: true + native-dynlink: true + stubsdir: "%{lib}%/stublibs" +} +### +opam-version: "1.3" +maintainer: "contact@ocamlpro.com" +# depends: ["P1" "P2" "P3" "P4"] +flags: compiler +setenv: TEST = "1" +### +opam-version: "1.3~dev4" +variables { + compiler: "20" + native: true + native-tools: true + native-dynlink: true + stubsdir: "%{lib}%/stublibs" +} +### +opam-version: "1.3" +maintainer: "contact@ocamlpro.com" +flags: compiler +setenv: TEST = "1" +### +#!/bin/sh -ue + +if ! OCAMLC=$(command -v ocamlc); then + echo "No OCaml compiler was found on the system" >&2 + exit 2 +fi + +if [ $($OCAMLC -config | sed -ne "s/os_type: //p" | tr -d '\r') = Win32 ] ; then + OCAMLC_FILE=$(echo $OCAMLC| cygpath -w -f - | sed -e 's/\\/\\\\/g') + LIBDIR=$("$OCAMLC" -where | tr -d '\r' | cygpath -f -) +else + OCAMLC_FILE=$OCAMLC + LIBDIR=$("$OCAMLC" -where) +fi + +STUBLIBS=$(cat "$LIBDIR/ld.conf" | tr -d '\r' | tr '\n' ':' | sed -e 's/\\/\\\\/g') + +echo "Using ocaml compiler found at $OCAMLC with base lib at $LIBDIR" + +bool() { + if "$@"; then echo "true"; else echo "false"; fi +} + +cat >ocaml.config < +opam-version: "1.3.0~dev4" +maintainer: "louis.gesbert@ocamlpro.com" +build: ["sh" "-uex" "./gen.sh"] +setenv: [CAML_LD_LIBRARY_PATH = "%{lib}%:%{_:ocaml-stublibs}%"] +depends: [ ] +flags: compiler +### +(* API version *) +opam-version: "1" + +name: "P1" +version: "0" + +setenv: [P1 = "version0"] +substs: [ "P1.config" "P1.install" ] + +build: [ + [ "ocamlc" "-a" "p1.ml" "-o" "p1.cma" ] + [ "ocamlopt" "-a" "p1.ml" "-o" "p1.cmxa" ] +] +depends: ["ocaml"] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P1" + bytecomp: "-I %{lib}%/P1" + asmlink: "-I %{lib}%/P1 p1.cmxa" + bytelink: "-I %{lib}%/P1 p1.cma" + LOCAL: "local" + l: "L" + FOO: "foo" + bar: true +} +### +lib: [ + "p1.cmi" + "p1.cma" + "p1.cmxa" + "p1.%{ext_lib}%" +] +### +let x () = + try Random.int 10 + with _ -> 0 +### +#! /bin/sh -eu + +if [ -n "${P1:-}" ]; then + echo "P1 ('$P1') should not be set yet" >&2 + exit 12 +fi + +ocamlc -a p1.ml -o p1.cma +ocamlopt -a p1.ml -o p1.cmxa +### chmod a+x packages/P1-1/build.sh +### +(* API version *) +opam-version: "1" + +name: "P1" + +# Test +# Toto + +(* Version are arbitrary strings *) +version: "1" + +maintainer: "contact@ocamlpro.com" + +(* The command to run *) +build: [ + ["./build.sh"] # HAHAH + ["this" "should" "never" "run"] {ocaml:version > "z100"} + [make "this" ocaml:version "also"] {os = "NO"} + ["echo" "HAHA!"] {ocaml:version = "10"} + ["echo" make share ocaml:version] + ["this as well" {os = "myOS"}] +] +available: os != "NO" | os != "NO" & os != "YES" + +(* List of files to substitute env variables *) +substs: [ "P1.config" "P1.install" ] + +(* Libraries *) +libraries: [ "p1" ] + +(* External dependencies *) +depexts: [ + [ ["debian" "amd64"] ["apt" "dpkg"] ] + [ ["osx" ] ["curl"] ] +] + +messages: [ "I ll always bother you displaying this message" ] + +post-messages: [ "Thanks SO MUCH for installing this humble package" + "Everything went well" {success} + "Nooo, something went wrong, this makes me feel sooo sad..." {failure} ] + +bug-reports: "TEST.com" + +setenv: [P1 = "version1"] +depends: [ + "ocaml" {(!= "20" | != "10") & (= "20" | = "10" | = "10+a+b" | = "system")} +] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P1" + bytecomp: "-I %{lib}%/P1" + asmlink: "-I %{lib}%/P1 p1.cmxa" + bytelink: "-I %{lib}%/P1 p1.cma" + LOCAL: "local" + l: "L" + FOO: "foo" + bar: true +} +### +lib: [ + "p1.cmi" + "p1.cma" + "p1.cmxa" + "p1.%{ext_lib}%" + "?this_file_will_not_exist_but_that's_ok" +] +share: [ "build.sh" ] +doc: [ + "p1.cmi" { "foo/bar/index.html" } +] +### +let x () = + try Random.int 10 + with _ -> 0 +### +A very useful package +### +#! /bin/sh -eu + +if [ -n "${P1:-}" ]; then + echo "P1 ('$P1') should not be set yet" >&2 + exit 12 +fi + +ocamlc -a p1.ml -o p1.cma +ocamlopt -a p1.ml -o p1.cmxa +### chmod a+x packages/P1-2/build.sh +### +opam-version: "1" +name: "P1" +version: "2" +depends: [ "ocaml" {<= "10" | = "system"} ] +maintainer: "contact@ocamlpro.com" +substs: [ "P1.config" "P1.install" ] +libraries: [ "p1" ] +build: [ "./build.sh" ] +setenv: [P1 = "version2"] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P1" + bytecomp: "-I %{lib}%/P1" + asmlink: "-I %{lib}%/P1 p1.cmxa" + bytelink: "-I %{lib}%/P1 p1.cma" + LOCAL: "local" + l: "L" + FOO: "foo" + bar: true +} +### +lib: [ + "p1.cma" + "p1.cmxa" + "p1.%{ext_lib}%" + "p1.cmi" +] +### +let x () = + failwith "the new version is not very good" +### +A very useful package +### +#! /bin/sh -eu + +OFLAGS="`opam var P1:asmcomp | tr -d '\r'`" +CFLAGS="`opam var P1:bytecomp | tr -d '\r'`" + +echo "Bytecode Compilation" +ocamlc ${CFLAGS} -a p2.ml -o p2.cma + +if which ocamlopt >/dev/null 2>&1; then + echo "Native Compilation" + ocamlopt ${OFLAGS} -a p2.ml -o p2.cmxa +fi + +### chmod a+x packages/P2/build.sh +### +Foo is %{P1:FOO}% + +Foo also contains a variable with %{P1:l}%. Funny, isn\'t it? +### +opam-version: "1" +name: "P2" +version: "1" +maintainer: "contact@ocamlpro.com" +substs: [ "config" "P2.config" "P2.install" ] +depends: ["ocaml" "P1"] +libraries: [ "p2" ] +build: [ "./build.sh" ] +### +opam-version: "1.3" +variables { + asmcomp: "-I %{lib}%/P2" + bytecomp: "-I %{lib}%/P2" + asmlink: "-I %{lib}%/P2 p2.cmxa" + bytelink: "-I %{lib}%/P2 p2.cma" + requires: "p1" +} +### +lib: [ + "p2.cma" + "p2.cmxa" + "p2.%{ext_lib}%" + "p2.cmi" +] +### +let g () = + P1.x () +### +An other very useful package + +The description can go on multiple lines. The first line is the package synopsis, +and the rest is the package description. +### +#! /bin/sh -eu + +OPAM=$(cygpath ${OPAM} 2>/dev/null || echo ${OPAM}) + +echo "Building P3 version ${OPAM_PACKAGE_VERSION}" + +if [ "x${OPAM_PACKAGE_NAME}" = "xP3" ]; then + LIB=$(${OPAM} var lib | tr -d '\r') + ocamlc -a -I $LIB/P1 -I $LIB/P2 p3.ml -o p3.cma + ocamlopt -a -I $LIB/P1 -I $LIB/P2 p3.ml -o p3.cmxa + ocamlc -a -I $LIB/P1 -I $LIB/P2 p3_bar.ml -o p3_bar.cma + ocamlopt -a -I $LIB/P1 -I $LIB/P2 p3_bar.ml -o p3_bar.cmxa +else + exit 1 +fi +### chmod a+x packages/P3/build.sh +### +opam-version: "1" +name: "P3" +version: "1~weird-version.test" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +substs: [ "P3.config" "P3.install" ] +build: [ "./build.sh" ] +### +let f () = + Printf.printf "foo\n%!" + +let _ = + P3.z () +### +opam-version: "1.3" +variables { + asmcomp : "-I %{lib}%/P3" + bytecomp: "-I %{lib}%/P3" + asmlink : "-I %{lib}%/P3 p3.cmxa p3_bar.cmxa" + bytelink: "-I %{lib}%/P3 p3.cma p3_bar.cma" + requires: "p1" +} +### +lib: [ + (* p3 *) + "p3.cma" + "p3.cmxa" + "p3.%{ext_lib}%" + "p3.cmi" + + (* p3_bar *) + "p3_bar.cma" + "p3_bar.cmxa" + "p3_bar.%{ext_lib}%" + "p3_bar.cmi" +] +### +let z () = + try P1.x () + with _ -> 0 +### +Testing version names +### +opam-version: "1" +name: "P4" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P2" "P3"] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "2" +maintainer: "contact@ocamlpro.com" +depends: [ + "P1" { <= "1" } + "P2" + "P3" +] +build: [ "./build.sh" ] +### +opam-version: "1" +name: "P4" +version: "3" +maintainer: "contact@ocamlpro.com" +depends: [ "P2" "P3" ] +build: [ "./build.sh" ] +### +#! /bin/sh -ex + +OPAM=$(cygpath ${OPAM} 2>/dev/null || echo ${OPAM}) + +opam() { $OPAM "$@" | tr -d '\r'; } + +if [ $OPAM_PACKAGE_VERSION -eq 2 ]; then + if [ "X${P1:-}" != "Xversion1" ]; then + echo "P1 not set to version1 while P1.1 should be installed" >&2 + exit 12 + fi +else + if [ -z "X${P1:-}" ]; then + echo "P1 not set while P1 should be installed" >&2 + exit 12 + fi +fi + +echo "Building P4 with ${OPAM}" +COMP="-I $(opam var P1:lib) -I $(opam var P2:lib) -I $(opam var P3:lib)" +LINK="p1.cmxa p2.cmxa p3.cmxa p3_bar.cmxa" + +OCAMLC=ocamlc +if which ocamlopt >/dev/null 2>&1; then OCAMLC=ocamlopt; fi + +$OCAMLC ${COMP} ${LINK} p4.ml -o p4.foo.exe + +echo "TEST=${TEST}" +### chmod a+x packages/P4/build.sh +### +bin: [ + "p4.foo.exe" { "p4.exe" } + "p4.foo.exe" +] +### +let f = + try P3_bar.f (); P1.x () + with _ -> P3.z () + +let () = + let t = + try Sys.getenv "TEST" + with _ -> "" in + Printf.printf "TEST=%s\n%!" t +### +Testing transitive closure +### +#! /bin/sh -eu + +OPAM=$(cygpath ${OPAM} 2>/dev/null || echo ${OPAM}) + +FLAGS="-I `${OPAM} var P1:lib | tr -d '\r'`" + +echo "Bytecode Compilation" +ocamlc ${FLAGS} -a p5.ml -o p5.cma + +if which ocamlopt >/dev/null 2>&1; then + echo "Native Compilation" + ocamlopt ${FLAGS} -a p5.ml -o p5.cmxa +fi +### chmod a+x packages/P5/build.sh +### +(* API version *) +opam-version: "1" +name: "P5" +version: "1" +maintainer: "contact@ocamlpro.com" +depends: ["ocaml" "P1"] +depopts: [ "P2" ] +build: [ [ "./build.sh" ] ] +install: [ [ "mkdir" "-p" "%{lib}%/p5" ] + [ "touch" "%{lib}%/p5/p2_present" ] {P2:installed} + [ "touch" "%{lib}%/p5/p2_absent" ] {!P2:installed} ] +remove: [ "rm" "-rf" "%{lib}%/p5" ] +### +let g () = + P1.x () +### +Testing optional dependencies +### : UPLOAD : +### cp -r packages/ocaml REPO/packages +### mkdir -p REPO/packages/P1.0 +### cp packages/P1-0.opam REPO/packages/P1.0/opam +### sh mkurl.sh P1.0 P1-0.tar.gz +### mkdir -p REPO/packages/P1.1 +### cp packages/P1-1.opam REPO/packages/P1.1/opam +### cp packages/P1-1/README REPO/packages/P1.1/descr +### sh mkurl.sh P1.1 P1-1.tar.gz +### mkdir -p REPO/packages/P2.1 +### cp packages/P2/README REPO/packages/P2.1/descr +### cp packages/P2.opam REPO/packages/P2.1/opam +### sh mkurl.sh P2.1 P2.tar.gz +### mkdir -p REPO/packages/P3.1~weird-version.test +### cp packages/P3.opam REPO/packages/P3.1~weird-version.test/opam +### cp packages/P3/README REPO/packages/P3.1~weird-version.test/descr +### sh mkurl.sh P3.1~weird-version.test P3.tar.gz +### mkdir -p REPO/packages/P4.1 +### cp packages/P4-1.opam REPO/packages/P4.1/opam +### cp packages/P4/README REPO/packages/P4.1/descr +### sh mkurl.sh P4.1 P4.tar.gz +### mkdir -p REPO/packages/P5.1 +### cp packages/P5.opam REPO/packages/P5.1/opam +### cp packages/P5/README REPO/packages/P5.1/descr +### sh mkurl.sh P5.1 P5.tar.gz +### +archive-mirrors: "cache" +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[test] synchronised from file://${BASEDIR}/REPO +[NOTE] Repository at file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "test"... +Updated ${OPAMTMP}/test/packages/P1.0/opam +Updated ${OPAMTMP}/test/packages/P1.1/opam +Updated ${OPAMTMP}/test/packages/P2.1/opam +Updated ${OPAMTMP}/test/packages/P3.1~weird-version.test/opam +Updated ${OPAMTMP}/test/packages/P4.1/opam +Updated ${OPAMTMP}/test/packages/P5.1/opam +Now run 'opam upgrade' to apply any package updates. +### opam switch create system "--formula=[\"ocaml\" {= \"system\"}]" + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {= "system"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.system +Done. +### opam exec -- ocamlc -config | 'ext_lib: .' -> '#' | '^[^#].*' -> '\c' | '^#' -> '' >$ LIB_EXT +### opam var ext_lib=$LIB_EXT --switch system | .* -> '\c' +### : INSTALL-REMOVE : +### opam list -is --columns=package +ocaml.system +### opam install P1 +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (${BASEDIR}/REPO/cache) +-> installed P1.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +### opam remove P1 +The following actions will be performed: + - remove P1 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 -- A very useful package +P2 -- An other very useful package +P3 -- Testing version names +P4 -- Testing transitive closure +P5 -- Testing optional dependencies +### : INSTALL-OPT : +### opam list -is --columns=package +ocaml.system +### opam install --yes P5 | unordered +The following actions will be performed: + - install P1 1 [required by P5] I ll always bother you displaying this message + - install P5 1 +===== 2 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> retrieved P5.1 (${BASEDIR}/REPO/cache) +-> installed P1.1 +-> installed P5.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### test -f ${OPAMROOT}/system/lib/p5/p2_absent +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam remove P5 +The following actions will be performed: + - remove P5 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +### opam install P5 +The following actions will be performed: + - install P5 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P5.1 (cached) +-> installed P5.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam remove P5 -a --yes +The following actions will be performed: + - remove P5 1 + - remove P1 1 +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### opam install P5 --yes | unordered +The following actions will be performed: + - install P1 1 [required by P5] I ll always bother you displaying this message + - install P5 1 +===== 2 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> retrieved P5.1 (cached) +-> installed P1.1 +-> installed P5.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam install P2 --yes | unordered +The following actions will be performed: + - install P2 1 + - recompile P5 1 [uses P2] +===== 1 to install | 1 to recompile ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P2.1 (${BASEDIR}/REPO/cache) +-> retrieved P5.1 (cached) +-> removed P5.1 +-> installed P2.1 +-> installed P5.1 +Done. +### test -f ${OPAMROOT}/system/lib/p5/p2_present +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P5.1 +### opam remove P5 -a +The following actions will be performed: + - remove P5 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam remove P2 -a --yes +The following actions will be performed: + - remove P2 1 + - remove P1 1 +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P2.1 +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### opam install P1 P2 P5 | unordered +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + - install P2 1 + - install P5 1 +===== 3 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> retrieved P2.1 (cached) +-> retrieved P5.1 (cached) +-> installed P1.1 +-> installed P2.1 +-> installed P5.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### test -f ${OPAMROOT}/system/lib/p5/p2_present +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P5.1 +### opam remove P2 -a --yes +The following actions will be performed: + - remove P2 1 + - recompile P5 1 [uses P2] +===== 1 to recompile | 1 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P5.1 (cached) +-> removed P5.1 +-> removed P2.1 +-> installed P5.1 +Done. +### test -f ${OPAMROOT}/system/lib/p5/p2_absent +### opam list -is --columns=package +ocaml.system +P1.1 +P5.1 +### opam remove P1 --yes +The following actions will be performed: + - remove P5 1 [uses P1] + - remove P1 1 +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P5.1 +-> removed P1.1 +Done. +### opam list -is --columns=package +ocaml.system +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 -- A very useful package +P2 -- An other very useful package +P3 -- Testing version names +P4 -- Testing transitive closure +P5 -- Testing optional dependencies +### : INSTALL : +### opam list -is --columns=package +ocaml.system +### opam install P1 +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> installed P1.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +### opam install P2 +The following actions will be performed: + - install P2 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P2.1 (cached) +-> installed P2.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam install P3 +The following actions will be performed: + - install P3 1~weird-version.test + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P3.1~weird-version.test (${BASEDIR}/REPO/cache) +-> installed P3.1~weird-version.test +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +### opam install P4 +The following actions will be performed: + - install P4 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P4.1 (${BASEDIR}/REPO/cache) +-> installed P4.1 +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 1 Testing transitive closure +P5 -- Testing optional dependencies +### : REINSTALL : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### opam reinstall P1 --yes | unordered +The following actions will be performed: + - recompile P1 1 I ll always bother you displaying this message + - recompile P3 1~weird-version.test [uses P1] + - recompile P2 1 [uses P1] + - recompile P4 1 [uses P2, P3] +===== 4 to recompile ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> retrieved P2.1 (cached) +-> retrieved P3.1~weird-version.test (cached) +-> retrieved P4.1 (cached) +-> removed P4.1 +-> removed P2.1 +-> removed P3.1~weird-version.test +-> removed P1.1 +-> installed P1.1 +-> installed P2.1 +-> installed P3.1~weird-version.test +-> installed P4.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 1 Testing transitive closure +P5 -- Testing optional dependencies +### : UPLOAD-NEW : +### mkdir REPO/packages/P4.2 +### cp packages/P4-2.opam REPO/packages/P4.2/opam +### cp packages/P4/README REPO/packages/P4.2/descr +### sh mkurl.sh P4.2 P4.tar.gz +### mkdir REPO/packages/P4.3 +### cp packages/P4-3.opam REPO/packages/P4.3/opam +### cp packages/P4/README REPO/packages/P4.3/descr +### sh mkurl.sh P4.3 P4.tar.gz +### mkdir REPO/packages/P1.2 +### cp packages/P1-2.opam REPO/packages/P1.2/opam +### cp packages/P1-2/README REPO/packages/P1.2/descr +### sh mkurl.sh P1.2 P1-2.tar.gz +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[test] synchronised from file://${BASEDIR}/REPO +[NOTE] Repository at file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "test"... +Updated ${OPAMTMP}/test/packages/P1.0/opam +Updated ${OPAMTMP}/test/packages/P1.1/opam +Updated ${OPAMTMP}/test/packages/P1.2/opam +Updated ${OPAMTMP}/test/packages/P2.1/opam +Updated ${OPAMTMP}/test/packages/P3.1~weird-version.test/opam +Updated ${OPAMTMP}/test/packages/P4.1/opam +Updated ${OPAMTMP}/test/packages/P4.2/opam +Updated ${OPAMTMP}/test/packages/P4.3/opam +Updated ${OPAMTMP}/test/packages/P5.1/opam +Now run 'opam upgrade' to apply any package updates. +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 1 Testing transitive closure +P5 -- Testing optional dependencies +### : UPGRADE : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.1 +### opam env --sexp | grep '"P1"' + ("P1" "version1") +### opam upgrade --yes | unordered +The following actions will be performed: + - upgrade P1 1 to 2 + - recompile P3 1~weird-version.test [uses P1] + - recompile P2 1 [uses P1] + - upgrade P4 1 to 3 +===== 2 to recompile | 2 to upgrade ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.2 (${BASEDIR}/REPO/cache) +-> retrieved P2.1 (cached) +-> retrieved P3.1~weird-version.test (cached) +-> retrieved P4.3 (cached) +-> removed P4.1 +-> removed P2.1 +-> removed P3.1~weird-version.test +-> removed P1.1 +-> installed P1.2 +-> installed P2.1 +-> installed P3.1~weird-version.test +-> installed P4.3 +Done. +### opam list -is --columns=package +ocaml.system +P1.2 +P2.1 +P3.1~weird-version.test +P4.3 +### opam env --sexp | grep '"P1"' + ("P1" "version2") +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 2 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 3 Testing transitive closure +P5 -- Testing optional dependencies +### : DOWNGRADE : +### opam list -is --columns=package +ocaml.system +P1.2 +P2.1 +P3.1~weird-version.test +P4.3 +### opam install P4.2 --yes | unordered +The following actions will be performed: + - downgrade P1 2 to 1 [required by P4] I ll always bother you displaying this message + - recompile P3 1~weird-version.test [uses P1] + - recompile P2 1 [uses P1] + - downgrade P4 3 to 2 +===== 2 to recompile | 2 to downgrade ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> retrieved P2.1 (cached) +-> retrieved P3.1~weird-version.test (cached) +-> retrieved P4.2 (cached) +-> removed P4.3 +-> removed P2.1 +-> removed P3.1~weird-version.test +-> removed P1.2 +-> installed P1.1 +-> installed P2.1 +-> installed P3.1~weird-version.test +-> installed P4.2 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.2 +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 2 Testing transitive closure +P5 -- Testing optional dependencies +### : SWITCH-ALIAS : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +P3.1~weird-version.test +P4.2 +### opam remove P3.1~weird-version.test P4.2 +The following actions will be performed: + - remove P4 2 + - remove P3 1~weird-version.test +===== 2 to remove ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> removed P4.2 +-> removed P3.1~weird-version.test +Done. +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch export test.export +### opam switch create test system + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {= "system"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.system +Done. +### opam var ext_lib=$LIB_EXT --switch test | '.*' -> '\c' +### opam list -is --columns=package +ocaml.system +### opam switch import test.export | unordered +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + - install P2 1 +===== 2 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> retrieved P2.1 (cached) +-> installed P1.1 +-> installed P2.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch create test2 20 + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml" {= "20"}] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.20 +Done. +### opam var ext_lib=$LIB_EXT --switch test2 | '.*' -> '\c' +### opam list -is --columns=package +ocaml.20 +### opam install P1 +The following actions will be performed: + - install P1 1 I ll always bother you displaying this message + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> retrieved P1.1 (cached) +-> installed P1.1 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.20 +P1.1 +### opam switch system +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch remove test test2 --yes +Switch test and all its packages will be wiped. Are you sure? [Y/n] y +Switch test2 and all its packages will be wiped. Are you sure? [Y/n] y +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml system +P1 1 A very useful package +P2 1 An other very useful package +P3 -- Testing version names +P4 -- Testing transitive closure +P5 -- Testing optional dependencies +### : SWITCH-ENV-PACKAGES : +### opam list -is --columns=package +ocaml.system +P1.1 +P2.1 +### opam switch create 10+a+b --empty +### opam var ext_lib=$LIB_EXT --switch 10+a+b | '.*' -> '\c' +### opam install ocaml.10+a+b P1 P2 P3 P4 | unordered +The following actions will be performed: + - install ocaml 10+a+b + - install P1 1 I ll always bother you displaying this message + - install P3 1~weird-version.test + - install P2 1 + - install P4 3 +===== 5 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed ocaml.10+a+b +-> retrieved P1.1 (cached) +-> retrieved P2.1 (cached) +-> retrieved P3.1~weird-version.test (cached) +-> retrieved P4.3 (cached) +-> installed P1.1 +-> installed P2.1 +-> installed P3.1~weird-version.test +-> installed P4.3 +Done. + +<><> P1.1 installed successfully ><><><><><><><><><><><><><><><><><><><><><><><> +=> Thanks SO MUCH for installing this humble package +=> Everything went well +### opam list -is --columns=package +ocaml.10+a+b +P1.1 +P2.1 +P3.1~weird-version.test +P4.3 +### sh -c '. ${OPAMROOT}/opam-init/variables.sh && echo "PASS $TEST"' +PASS 1 +### : REPO : +### opam repo add REPO2 REPO -k local +[REPO2] Initialised +[NOTE] Repository at file://${BASEDIR}/REPO doesn't define its version, assuming it's 1.2. + +<><> Upgrading repositories from older opam format ><><><><><><><><><><><><><><> +Upgrading repository "REPO2"... +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P1.0/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P1.1/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P1.2/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P2.1/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P3.1~weird-version.test/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P4.1/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P4.2/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P4.3/opam +Updated ${BASEDIR}/OPAM/repo/REPO2/packages/P5.1/opam +[NOTE] Repository REPO2 has been added to the selections of switch 10+a+b only. + Run `opam repository add REPO2 --all-switches|--set-default' to use it in all existing switches, or in newly created switches, respectively. + +### opam repo remove REPO2 --all +### opam repo remove test --all +### : LIST : +### opam list -A +# Packages matching: any +# Name # Installed # Synopsis +ocaml 10+a+b +P1 1 A very useful package +P2 1 An other very useful package +P3 1~weird-version.test Testing version names +P4 3 Testing transitive closure diff -Nru opam-2.0.10/tests/reftests/opamroot-versions.test opam-2.1.2/tests/reftests/opamroot-versions.test --- opam-2.0.10/tests/reftests/opamroot-versions.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/opamroot-versions.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,2205 @@ +N0REP0 +### OPAMYES=1 OPAMDEBUG=-1 OPAMSTRICT=0 OPAMDEBUGSECTIONS="FMT_UPG FORMAT GSTATE RSTATE STATE" +### : setup +### +let opam_version = Printf.sprintf "opam-version: %S" +let root_version = Printf.sprintf "opam-root-version: %S" +let switches = {| +installed-switches: "foo" +switch: "foo" +|} +let neant = "neant: 0" +let repo = {|repositories: [ "default" {"file:///${BASEDIR/dontexist"} ]|} +let switch_config = {|synopsis: "foo"|} +let _ = + let configs = + ( let opam_v = opam_version "2.0" in + List.map (fun v -> [ + "config."^v, [ opam_v ; root_version v ]; + "config-w-swfoo."^v, [ opam_v; root_version v; switches ]; + ]) ["2.0"; "2.1"; "2.2"] + |> List.flatten) + @ (let opam_v = opam_version "2.1" in + let v = "2.3" in [ + "config."^v, [ opam_v; root_version v ]; + "config-w-swfoo."^v, [ opam_v; root_version v; switches ]; + ]) + in + let files = [ + "repos-config", [ repo ]; + "switch-config", [ opam_version "2.0"; switch_config ]; + ] @ configs + in + let errs = + List.map (fun (n,c) -> n^".err" , c@[neant]) files + @ (let v = "2.1" in + let opam_v = opam_version "2.1" in [ + "config."^v^".wrong", [ opam_v; root_version v ]; + "switch-config.wrong", [ opam_v; switch_config ]; + ]) + in + List.iter (fun (name, content) -> + let fd = open_out name in + List.iter (fun l -> output_string fd (l^"\n")) content; + close_out fd) + (files @ errs) +### ocaml generate.ml +### +opam-version: "2.0" +### mkdir root-config/packages +### : : +### :I: Current opam root : +### : : +### :I:1:a: Bad config file +### cp config.2.1.err $OPAMROOT/config +### # ro global state +### opam switch +[WARNING] Errors in ${BASEDIR}/OPAM/config, some fields have been ignored: + - At ${BASEDIR}/OPAM/config:3:0-3:8:: + Invalid field neant + +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Errors in ${BASEDIR}/OPAM/config, some fields have been ignored: + - At ${BASEDIR}/OPAM/config:3:0-3:8:: + Invalid field neant + +# switch compiler description +### :I:1:b: Good config file +### cp config.2.1 $OPAMROOT/config +### # ro global state +### opam switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +# switch compiler description +### :I:2:a: Bad repo config file : +### cp repos-config.err $OPAMROOT/repo/repos-config +### # ro global state, ro repo state +### opam list --no-switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +[WARNING] Errors in ${BASEDIR}/OPAM/repo/repos-config, some fields have been ignored: + - At ${BASEDIR}/OPAM/repo/repos-config:2:0-2:8:: + Invalid field neant + +RSTATE Cache found +# No matches found +### # ro global state, rw repo state +### opam repo add root-config ./root-config --set-default +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +[WARNING] Errors in ${BASEDIR}/OPAM/repo/repos-config, some fields have been ignored: + - At ${BASEDIR}/OPAM/repo/repos-config:2:0-2:8:: + Invalid field neant + +RSTATE Cache found +[root-config] Initialised +### opam repo remove root-config --all --debug-level=0 +### :I:2:b: Good repo config file : +### cp repos-config $OPAMROOT/repo/repos-config +### # ro global state, ro repo state +### opam list --no-switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +# No matches found +### # ro global state, rw repo state +### opam repo add root-config ./root-config --set-default +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +[root-config] Initialised +### opam switch create foo --empty --debug-level=0 +### :I:3:a: Bad switch config file : +### cp switch-config.err $OPAMROOT/foo/.opam-switch/switch-config +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +[WARNING] Errors in ${BASEDIR}/OPAM/foo/.opam-switch/switch-config, some fields have been ignored: + - At ${BASEDIR}/OPAM/foo/.opam-switch/switch-config:3:0-3:8:: + Invalid field neant + +STATE Inferred invariant: from base packages {}, (roots {}) => [] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# No matches found +### # ro global state, ro repo state, rw switch state +### opam install bar +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +[WARNING] Errors in ${BASEDIR}/OPAM/foo/.opam-switch/switch-config, some fields have been ignored: + - At ${BASEDIR}/OPAM/foo/.opam-switch/switch-config:3:0-3:8:: + Invalid field neant + +STATE Inferred invariant: from base packages {}, (roots {}) => [] +STATE Switch state loaded in 0.000s +[ERROR] No package named bar found. +# Return code 5 # +### :I:3:b: Good switch config file : +### cp switch-config $OPAMROOT/foo/.opam-switch/switch-config +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +STATE Inferred invariant: from base packages {}, (roots {}) => [] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# No matches found +### # ro global state, ro repo state, rw switch state +### opam install bar +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +STATE Inferred invariant: from base packages {}, (roots {}) => [] +STATE Switch state loaded in 0.000s +[ERROR] No package named bar found. +# Return code 5 # +### :I:1:a: Bad config file +### cp config-w-swfoo.2.1.err $OPAMROOT/config +### # rw global state +### opam switch remove foo +[WARNING] Errors in ${BASEDIR}/OPAM/config, some fields have been ignored: + - At ${BASEDIR}/OPAM/config:7:0-7:8:: + Invalid field neant + +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Errors in ${BASEDIR}/OPAM/config, some fields have been ignored: + - At ${BASEDIR}/OPAM/config:7:0-7:8:: + Invalid field neant + +Switch foo and all its packages will be wiped. Are you sure? [Y/n] y +### :I:1:b: Good config file +### opam switch create foo --empty --debug-level=0 +### cp config-w-swfoo.2.1 $OPAMROOT/config +### # rw global state +### opam switch remove foo +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Switch foo and all its packages will be wiped. Are you sure? [Y/n] y +### : : +### :II: Current opam root & newer opam file version : +### : : +### :II:1: config with newer opam version file & no update of root version +### cp config.2.1.wrong $OPAMROOT/config +### # ro global state +### opam switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### opam option jobs +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### # rw global state +### opam switch bar +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### :II:2: switch-config with newer opam version file & no update of root version +### cp config.2.1 $OPAMROOT/config +### opam switch create foo --empty --debug-level=0 +### cp switch-config.wrong $OPAMROOT/foo/.opam-switch/switch-config +### # ro global state +### opam option jobs +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +### # rw global state +### # opam option synopsis="bar" --switch foo +### opam switch foo +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +Fatal error: +In ${BASEDIR}/OPAM/foo/.opam-switch/switch-config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### : : +### : Newer opam root : +### : : +### :III:1:a: Bad config file +### cp config.2.2.err $OPAMROOT/config +### # ro global state +### opam switch +FORMAT File errors in ${BASEDIR}/OPAM/config, ignored fields: At ${BASEDIR}/OPAM/config:3:0-3:8:: +Invalid field neant +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FORMAT File errors in ${BASEDIR}/OPAM/config, ignored fields: At ${BASEDIR}/OPAM/config:3:0-3:8:: +Invalid field neant +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +# switch compiler description +### :III:1:b: Good config file +### cp config.2.2 $OPAMROOT/config +### # ro global state +### opam switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +# switch compiler description +### :III:2:a: Bad repo config file : +### cp repos-config.err $OPAMROOT/repo/repos-config +### # ro global state, ro repo state +### opam list --no-switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +FORMAT File errors in ${BASEDIR}/OPAM/repo/repos-config, ignored fields: At ${BASEDIR}/OPAM/repo/repos-config:2:0-2:8:: +Invalid field neant +RSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE Cache found +# No matches found +### # ro global state, rw repo state +### opam repo add root-config ./root-config --set-default +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.2 > 2.1), aborting. +# Return code 15 # +### :III:2:b: Good repo config file : +### cp repos-config $OPAMROOT/repo/repos-config +### # ro global state, ro repo state +### opam list --no-switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE Cache found +# No matches found +### # ro global state, rw repo state +### opam repo add root-config ./root-config --set-default +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.2 > 2.1), aborting. +# Return code 15 # +### cp config.2.1 $OPAMROOT/config +### cp config-w-swfoo.2.2 $OPAMROOT/config +### :III:3:a: Bad switch config file : +### cp switch-config.err $OPAMROOT/foo/.opam-switch/switch-config +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +FORMAT File errors in ${BASEDIR}/OPAM/foo/.opam-switch/switch-config, ignored fields: At ${BASEDIR}/OPAM/foo/.opam-switch/switch-config:3:0-3:8:: +Invalid field neant +STATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +STATE Inferred invariant: from base packages {}, (roots {}) => [] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# No matches found +### # ro global state, ro repo state, rw switch state +### opam install bar +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.2 > 2.1), aborting. +# Return code 15 # +### :III:3:b: Good switch config file : +### cp switch-config $OPAMROOT/foo/.opam-switch/switch-config +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +STATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +STATE Inferred invariant: from base packages {}, (roots {}) => [] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# No matches found +### # ro global state, ro repo state, rw switch state +### opam install bar +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +GSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE root version (2.2) is greater than running binary's (2.1); load with best-effort (read-only) +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ foo +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.2 > 2.1), aborting. +# Return code 15 # +### :III:1:a: Bad config file +### cp config-w-swfoo.2.2.err $OPAMROOT/config +### # rw global state +### opam switch remove foo +FORMAT File errors in ${BASEDIR}/OPAM/config, ignored fields: At ${BASEDIR}/OPAM/config:7:0-7:8:: +Invalid field neant +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.2 > 2.1), aborting. +# Return code 15 # +### :III:1:b: Good config file +### cp config-w-swfoo.2.2 $OPAMROOT/config +### # rw global state +### opam switch remove foo +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.2 > 2.1), aborting. +# Return code 15 # +### : : +### : Newer opam root & new file version : +### : : +### :IV:1:a: Bad config file +### cp config.2.3.err $OPAMROOT/config +### # ro global state +### opam switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### :IV:1:b: Good config file +### cp config.2.3 $OPAMROOT/config +### # ro global state +### opam switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### :IV:2:a: Bad repo config file : +### cp repos-config.err $OPAMROOT/repo/repos-config +### # ro global state, ro repo state +### opam list --no-switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### # ro global state, rw repo state +### opam repo add root-config ./root-config --set-default +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### :IV:2:b: Good repo config file : +### cp repos-config $OPAMROOT/repo/repos-config +### # ro global state, ro repo state +### opam list --no-switch +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### # ro global state, rw repo state +### opam repo add root-config ./root-config --set-default +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### cp config.2.1 $OPAMROOT/config +### #opam switch create foo --empty --debug-level=0 +### cp config-w-swfoo.2.3 $OPAMROOT/config +### :IV:3:a: Bad switch config file : +### cp switch-config.err $OPAMROOT/foo/.opam-switch/switch-config +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### # ro global state, ro repo state, rw switch state +### opam install bar +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### :IV:3:b: Good switch config file : +### cp switch-config $OPAMROOT/foo/.opam-switch/switch-config +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### # ro global state, ro repo state, rw switch state +### opam install bar +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Fatal error: +In ${BASEDIR}/OPAM/config: +unsupported or missing file format version; should be 2.0 or older +# Return code 99 # +### :IV:1:a: Bad config file +### cp config-w-swfoo.2.3.err $OPAMROOT/config +### # rw global state +### opam switch remove foo +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.3 > 2.1), aborting. +# Return code 15 # +### :IV:1:b: Good config file +### cp config-w-swfoo.2.3 $OPAMROOT/config +### # rw global state +### opam switch remove foo +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[ERROR] Refusing write access to ${BASEDIR}/OPAM, which is more recent than this version of opam (2.3 > 2.1), aborting. +# Return code 15 # +### : : +### : Older opam root : +### : : +### +let content = {|opam-version: "2.0" +synopsis: "One-line description" +maintainer: "Name " +authors: "Name " +license: "MIT" +homepage: " " +bug-reports: " " +dev-repo: "git://url.net/name"|} +let compiler="flags: compiler" +let files = [ + "repo", [{|opam-version: "2.0"|}]; + "packages/i-am-compiler/i-am-compiler.1/opam", [ content; compiler ]; + "packages/i-am-compiler/i-am-compiler.2/opam", [ content; compiler ]; + "packages/i-am-sys-compiler/i-am-sys-compiler.1/opam", [ content; compiler; {|available: sys-comp-version = "1"|}]; + "packages/i-am-sys-compiler/i-am-sys-compiler.2/opam", [ content; compiler; {|available: sys-comp-version = "2"|}]; + "packages/i-am-package/i-am-package.1/opam", [ content; {|depends: ["i-am-compiler"|"i-am-sys-compiler"]|} ]; + "packages/i-am-package/i-am-package.2/opam", [ content; {|depends: ["i-am-compiler"|"i-am-sys-compiler"]|} ]; + "packages/i-am-another-package/i-am-another-package.1/opam", [ content; {|depends: "i-am-package"|} ]; + "packages/i-am-another-package/i-am-another-package.2/opam", [ content; {|depends: "i-am-package"|} ]; +] +let _ = + List.iter (fun (name, content) -> + let fd = open_out name in + List.iter (fun l -> output_string fd (l^"\n")) content; + close_out fd) + (List.map (fun (n,c) -> "default/"^n, c) files) +### +for d in i-am-compiler i-am-sys-compiler i-am-package i-am-another-package; do + for v in 1 2 ; do + mkdir -p default/packages/$d/$d.$v + done +done +### sh generate_dirs.sh +### ocaml generate_repo.ml +### +#load "unix.cma";; + +let rec mkdir_p dir = + if Sys.file_exists dir then () + else let () = mkdir_p (Filename.dirname dir) in + if not (Sys.file_exists dir) then + Unix.mkdir dir 0o777 + else () + +let mode = + if Array.length Sys.argv = 2 then `Root else + let version = + if Array.length Sys.argv = 3 then Sys.argv.(1) else Sys.argv.(3) + in + match Sys.argv.(2) with + | "local" -> `Local version + | "orphaned" -> `Orphaned version + | _ -> assert false + +let opam_version = Printf.sprintf "opam-version: %S" +let opam_version_2_0 = opam_version "2.0" +let opam_version_2_1 = opam_version "2.1" +let repo = {|repositories: "default"|} +let installed_switches = + let local_switch = + match mode with + | `Local _ -> Printf.sprintf " %S" (Sys.getcwd ()) + | _ -> "" + in + Format.sprintf {| +installed-switches: ["sw-sys-comp" "sw-comp"%s "default" %S "this-internal-error"] +switch: "sw-sys-comp" +|} local_switch (Filename.concat (Sys.getcwd ()) "why-did-you-delete-me") +let default_compiler = {|default-compiler: ["i-am-sys-compiler" "i-am-compiler"]|} +let default_invariant = {|default-invariant: ["i-am-sys-compiler"]|} +let depext = {| +depext: true +depext-run-installs: true +depext-cannot-install: false +|} +let eval = {|eval-variables: [ sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version" ]|} +let sw_state_default = {| +compiler: ["i-am-sys-compiler.2"] +roots: ["i-am-sys-compiler.2"] +installed: ["i-am-sys-compiler.2"] +|} +let sw_state_sw_comp = {| +compiler: ["i-am-compiler.2"] +roots: ["i-am-package.2" "i-am-compiler.2"] +installed: ["i-am-compiler.2" "i-am-package.2"] +|} +let sw_state_sw_sys_comp = {| +compiler: ["i-am-sys-compiler.1"] +roots: ["i-am-another-package.2" "i-am-sys-compiler.1"] +installed: ["i-am-another-package.2" "i-am-package.2" "i-am-sys-compiler.1"] +|} +let invariant_default = {|invariant: ["i-am-sys-compiler" | "i-am-compiler"]|} +let invariant_sw_comp = {|invariant: ["i-am-compiler"]|} +let invariant_sw_sys_comp = {|invariant: ["i-am-sys-compiler"]|} +let root_version = {|opam-root-version: "2.1~rc"|} +let root_version_21 = {|opam-root-version: "2.1"|} +let synopsis = Printf.sprintf "synopsis: %S" +let opam_root = Printf.sprintf "opam-root: %S" (Sys.getenv "OPAMROOT") +let opam_20 = + [ "config", [ opam_version_2_0; repo; installed_switches; eval; default_compiler ]; + "default/.opam-switch/switch-config", [ opam_version_2_0; synopsis "default switch" ]; + "default/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + "sw-comp/.opam-switch/switch-config", [ opam_version_2_0; synopsis "switch with compiler" ]; + "sw-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_comp ]; + "sw-sys-comp/.opam-switch/switch-config", [ opam_version_2_0; synopsis "switch with system compiler" ]; + "sw-sys-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_sys_comp ] + ], [ + "_opam/.opam-switch/switch-config", [ opam_version_2_0; synopsis "local switch"; opam_root ]; + "_opam/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + ] +let opam_21alpha = + [ "config", [ opam_version_2_0; repo; installed_switches; eval; default_compiler ]; + "default/.opam-switch/switch-config", [ opam_version_2_1; synopsis "default switch"; invariant_default ]; + "default/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + "sw-comp/.opam-switch/switch-config", [ opam_version_2_1; synopsis "switch with compiler"; invariant_sw_comp ]; + "sw-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_comp ]; + "sw-sys-comp/.opam-switch/switch-config", [ opam_version_2_1; synopsis "switch with system compiler"; invariant_sw_sys_comp ]; + "sw-sys-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_sys_comp ] + ], [ + "_opam/.opam-switch/switch-config", [ opam_version_2_1; synopsis "local switch"; invariant_default; opam_root ]; + "_opam/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + ] +let opam_21alpha2 = + [ "config", [ opam_version_2_1; repo; installed_switches; eval; default_compiler; depext; ]; + "default/.opam-switch/switch-config", [ opam_version_2_1; synopsis "default switch"; invariant_default ]; + "default/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + "sw-comp/.opam-switch/switch-config", [ opam_version_2_1; synopsis "switch with compiler"; invariant_sw_comp ]; + "sw-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_comp ]; + "sw-sys-comp/.opam-switch/switch-config", [ opam_version_2_1; synopsis "switch with system compiler"; invariant_sw_sys_comp ]; + "sw-sys-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_sys_comp ] + ], [ + "_opam/.opam-switch/switch-config", [ opam_version_2_1; synopsis "local switch"; invariant_default; opam_root ]; + "_opam/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + ] +let opam_21rc = + [ "config", [ opam_version_2_0; root_version; repo; installed_switches; eval; default_compiler; default_invariant; depext ]; + "default/.opam-switch/switch-config", [ opam_version_2_0; synopsis "default switch"; invariant_default ]; + "default/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + "sw-comp/.opam-switch/switch-config", [ opam_version_2_0; synopsis "switch with compiler"; invariant_sw_comp ]; + "sw-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_comp ]; + "sw-sys-comp/.opam-switch/switch-config", [ opam_version_2_0; synopsis "switch with system compiler"; invariant_sw_sys_comp ]; + "sw-sys-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_sys_comp ] + ], [ + "_opam/.opam-switch/switch-config", [ opam_version_2_0; synopsis "local switch"; invariant_default; opam_root ]; + "_opam/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + ] +let opam_21 = + [ "config", [ opam_version_2_0; root_version_21; repo; installed_switches; eval; default_compiler; default_invariant; depext ]; + "default/.opam-switch/switch-config", [ opam_version_2_0; synopsis "default switch"; invariant_default ]; + "default/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + "sw-comp/.opam-switch/switch-config", [ opam_version_2_0; synopsis "switch with compiler"; invariant_sw_comp ]; + "sw-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_comp ]; + "sw-sys-comp/.opam-switch/switch-config", [ opam_version_2_0; synopsis "switch with system compiler"; invariant_sw_sys_comp ]; + "sw-sys-comp/.opam-switch/switch-state", [ opam_version_2_0; sw_state_sw_sys_comp ] + ], [ + "_opam/.opam-switch/switch-config", [ opam_version_2_0; synopsis "local switch"; invariant_default; opam_root ]; + "_opam/.opam-switch/switch-state", [ opam_version_2_0; sw_state_default ]; + ] +let _ = + let get_files = function + | "2.0" -> opam_20 + | "2.1~alpha" -> opam_21alpha + | "2.1~alpha2" -> opam_21alpha2 + | "2.1~rc" -> opam_21rc + | "2.1" -> opam_21 + | _ -> assert false + in + let write dir (name, content) = + let name = Filename.concat dir name in + mkdir_p (Filename.dirname name); + let fd = open_out name in + List.iter (fun l -> output_string fd (l^"\n")) content; + close_out fd + in + let root,_ = get_files Sys.argv.(1) in + List.iter (write (Sys.getenv "OPAMROOT")) root; + match mode with + | `Local v | `Orphaned v -> + let _, local = get_files v in + List.iter (write (Sys.getcwd ())) local + | _ -> () +### rm -rf $OPAMROOT +### opam init -na default ./default --bare --bypass-checks --no-opamrc --debug-level=0 +No configuration file found, using built-in defaults. + +<><> Fetching repository information ><><><><><><><><><><><><><><><><><><><><><> +[default] Initialised +### mkdir -p $OPAMROOT $OPAMROOT/repo $OPAMROOT/default/.opam-switch $OPAMROOT/sw-comp/.opam-switch $OPAMROOT/sw-sys-comp/.opam-switch +### :V:1:a: From 2.0 root, global +### ocaml generate.ml 2.0 +### # ro global state +### opam option jobs +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.0 to 2.1 +FMT_UPG Format upgrade done +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.0 to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-sys-comp +STATE Inferred invariant: from base packages { i-am-sys-compiler.1 }, (roots { i-am-sys-compiler.1 }) => ["i-am-sys-compiler"] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-another-package 2 One-line description +i-am-package 2 One-line description +i-am-sys-compiler 1 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-another-package --switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.0 to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Definition missing for installed package i-am-compiler.2, copying from repo +STATE Definition missing for installed package i-am-package.2, copying from repo +STATE Inferred invariant: from base packages { i-am-compiler.2 }, (roots { i-am-compiler.2 }) => ["i-am-compiler" {>= "2"}] +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-another-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-another-package.2 +Done. +### # rw global state +### opam switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.0 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.0 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Switch state loaded in 0.000s +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-comp" +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-config +invariant: ["i-am-compiler" {>= "2"}] +opam-version: "2.0" +synopsis: "switch with compiler" +paths { + +} +variables { + +} +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-state +compiler: ["i-am-compiler.2"] +installed: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +opam-version: "2.0" +roots: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +### :V:1:b: From 2.0 root, local +### ocaml generate.ml 2.0 local +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.0 to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Inferred invariant: from base packages { i-am-sys-compiler.2 }, (roots { i-am-sys-compiler.2 }) => ["i-am-sys-compiler"] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.0 to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Inferred invariant: from base packages { i-am-sys-compiler.2 }, (roots { i-am-sys-compiler.2 }) => ["i-am-sys-compiler"] +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.0 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.0 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "${BASEDIR}" "default"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### rm -rf _opam +### :V:1:c: From 2.0 root, local unknown from config +### ocaml generate.ml 2.0 orphaned +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.0 to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Inferred invariant: from base packages { i-am-sys-compiler.2 }, (roots { i-am-sys-compiler.2 }) => ["i-am-sys-compiler"] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.0 to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Inferred invariant: from base packages { i-am-sys-compiler.2 }, (roots { i-am-sys-compiler.2 }) => ["i-am-sys-compiler"] +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.0 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.0 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:1:d: Upgraded root and local 2.0 switch not recorded +### ocaml generate.ml 2.1 orphaned 2.0 +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Inferred invariant: from base packages { i-am-sys-compiler.2 }, (roots { i-am-sys-compiler.2 }) => ["i-am-sys-compiler"] +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Inferred invariant: from base packages { i-am-sys-compiler.2 }, (roots { i-am-sys-compiler.2 }) => ["i-am-sys-compiler"] +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default" "this-internal-error"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:1:e: reinit from 2.0 +### ocaml generate.ml 2.0 +### opam init --reinit --bypass-checks --no-setup +No configuration file found, using built-in defaults. +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.0 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.0 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +### opam switch --short +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +${BASEDIR} +default +sw-comp +sw-sys-comp +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-package 2 One-line description +i-am-sys-compiler 2 One-line description +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### rm -rf _opam +### :V:2:a: From 2.1~alpha root, global +### ocaml generate.ml 2.1~alpha +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done, please now retry your command. +# Return code 10 # +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-sys-comp +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-another-package 2 One-line description +i-am-package 2 One-line description +i-am-sys-compiler 1 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-another-package --switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-another-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-another-package.2 +Done. +### # rw global state +### opam switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Switch state loaded in 0.000s +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-config +invariant: ["i-am-compiler"] +opam-version: "2.0" +synopsis: "switch with compiler" +paths { + +} +variables { + +} +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-state +compiler: ["i-am-compiler.2"] +installed: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +opam-version: "2.0" +roots: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +### :V:2:b: From 2.1~alpha root, local +### ocaml generate.ml 2.1~alpha local +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done, please now retry your command. +# Return code 10 # +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "${BASEDIR}" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### rm -rf _opam +### :V:2:c: From 2.1~alpha root, local unknown from config +### ocaml generate.ml 2.1~alpha orphaned +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done, please now retry your command. +# Return code 10 # +### opam install i-am-package +FILE(switch-config) Wrote ${BASEDIR}/_opam/.opam-switch/switch-config in 0.000s +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:2:d: Upgraded root and local 2.1~alpha switch not recorded +### ocaml generate.ml 2.1 orphaned 2.1~alpha +### # ro global state, ro repo state, ro switch state +### opam list +FILE(switch-config) Wrote ${BASEDIR}/_opam/.opam-switch/switch-config in 0.000s +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default" "this-internal-error"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:2:e: reinit from 2.1~alpha +### ocaml generate.ml 2.1~alpha +### opam init --reinit --bypass-checks --no-setup +No configuration file found, using built-in defaults. +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done, please now retry your command. +# Return code 10 # +### opam switch --short +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +${BASEDIR} +default +sw-comp +sw-sys-comp +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-package 2 One-line description +i-am-sys-compiler 2 One-line description +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### rm -rf _opam +### :V:3:a: From 2.1~alpha2 root, global +### ocaml generate.ml 2.1~alpha2 +### # ro global state +### opam option jobs +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG Intermediate opam root detected, launch hard upgrade +FMT_UPG Downgrade config opam-version to fix up +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha2 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha2 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done, please now retry your command. +# Return code 10 # +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-sys-comp +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-another-package 2 One-line description +i-am-package 2 One-line description +i-am-sys-compiler 1 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-another-package --switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-another-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-another-package.2 +Done. +### # rw global state +### opam switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Switch state loaded in 0.000s +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-config +invariant: ["i-am-compiler"] +opam-version: "2.0" +synopsis: "switch with compiler" +paths { + +} +variables { + +} +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-state +compiler: ["i-am-compiler.2"] +installed: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +opam-version: "2.0" +roots: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +### :V:3:b: From 2.1~alpha2 root, local +### ocaml generate.ml 2.1~alpha2 local +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG Intermediate opam root detected, launch hard upgrade +FMT_UPG Downgrade config opam-version to fix up +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha2 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha2 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done, please now retry your command. +# Return code 10 # +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "${BASEDIR}" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### rm -rf _opam +### :V:3:c: From 2.1~alpha2 root, local unknown from config +### ocaml generate.ml 2.1~alpha2 orphaned +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG Intermediate opam root detected, launch hard upgrade +FMT_UPG Downgrade config opam-version to fix up +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha2 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha2 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done, please now retry your command. +# Return code 10 # +### opam install i-am-package +FILE(switch-config) Wrote ${BASEDIR}/_opam/.opam-switch/switch-config in 0.000s +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:3:d: Upgraded root and local 2.1~alpha2 switch not recorded +### ocaml generate.ml 2.1 orphaned 2.1~alpha2 +### # ro global state, ro repo state, ro switch state +### opam list +FILE(switch-config) Wrote ${BASEDIR}/_opam/.opam-switch/switch-config in 0.000s +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default" "this-internal-error"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +paths { + +} +variables { + +} +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:3:e: reinit from 2.1~alpha2 +### ocaml generate.ml 2.1~alpha2 +### opam init --reinit --bypass-checks --no-setup +No configuration file found, using built-in defaults. +FMT_UPG Intermediate opam root detected, launch hard upgrade +FMT_UPG Downgrade config opam-version to fix up +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Hard config upgrade, from 2.1~alpha2 to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~alpha2 to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Perform the update and continue? [Y/n] y +Format upgrade done. + +<><> Rerunning init and update ><><><><><><><><><><><><><><><><><><><><><><><><> +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +Update done. +### opam switch --short +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +${BASEDIR} +default +sw-comp +sw-sys-comp +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-package 2 One-line description +i-am-sys-compiler 2 One-line description +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["ocaml" {>= "4.05.0"}] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### rm -rf _opam +### :V:4:a: From 2.1~rc root, global +### ocaml generate.ml 2.1~rc +### # ro global state +### opam option jobs +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.1~rc to 2.1 +FMT_UPG Format upgrade done +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.1~rc to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-sys-comp +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-another-package 2 One-line description +i-am-package 2 One-line description +i-am-sys-compiler 1 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-another-package --switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.1~rc to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-another-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-another-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.1~rc to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~rc to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-config +invariant: ["i-am-compiler"] +opam-version: "2.0" +synopsis: "switch with compiler" +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-state +compiler: ["i-am-compiler.2"] +installed: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +opam-version: "2.0" +roots: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +### :V:4:b: From 2.1~rc root, local +### ocaml generate.ml 2.1~rc local +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.1~rc to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.1~rc to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.1~rc to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~rc to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "${BASEDIR}" "default"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### rm -rf _opam +### :V:4:c: From 2.1~rc root, local unknown from config +### ocaml generate.ml 2.1~rc local +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.1~rc to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +FMT_UPG On-the-fly config upgrade, from 2.1~rc to 2.1 +FMT_UPG Format upgrade done +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.1~rc to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~rc to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "${BASEDIR}" "default"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:4:d: Upgraded root and local 2.1~rc switch not recorded +### ocaml generate.ml 2.1 orphaned 2.1~rc +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default" "this-internal-error"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:4:e: reinit from 2.1~rc +### ocaml generate.ml 2.1~rc +### opam init --reinit --bypass-checks --no-setup +No configuration file found, using built-in defaults. +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +[WARNING] Removing global switch 'this-internal-error' as it no longer exists +FMT_UPG Light config upgrade, from 2.1~rc to 2.1 +This version of opam requires an update to the layout of ${BASEDIR}/OPAM from version 2.1~rc to version 2.1, which can't be reverted. +You may want to back it up before going further. + +Continue? [Y/n] y +Format upgrade done. +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +### opam switch --short +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +${BASEDIR} +default +sw-comp +sw-sys-comp +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-package 2 One-line description +i-am-sys-compiler 2 One-line description +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### rm -rf _opam +### :V:5:a: From 2.1 root, global +### ocaml generate.ml 2.1 +### # ro global state +### opam option jobs +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-sys-comp +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-another-package 2 One-line description +i-am-package 2 One-line description +i-am-sys-compiler 1 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-another-package --switch sw-comp +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ sw-comp +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-another-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-another-package.2 +Done. +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default" "${BASEDIR}/why-did-you-delete-me" "this-internal-error"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-config +invariant: ["i-am-compiler"] +opam-version: "2.0" +synopsis: "switch with compiler" +### opam-cat $OPAMROOT/sw-comp/.opam-switch/switch-state +compiler: ["i-am-compiler.2"] +installed: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +opam-version: "2.0" +roots: ["i-am-another-package.2" "i-am-compiler.2" "i-am-package.2"] +### :V:5:b: From 2.1 root, local +### ocaml generate.ml 2.1 local +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Definition missing for installed package i-am-sys-compiler.2, copying from repo +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "${BASEDIR}" "default" "${BASEDIR}/why-did-you-delete-me" "this-internal-error"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:5:c: From 2.1 root, local unknown from config +### ocaml generate.ml 2.1 local +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "${BASEDIR}" "default" "${BASEDIR}/why-did-you-delete-me" "this-internal-error"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:5:d: Upgraded root and local 2.1 switch not recorded +### ocaml generate.ml 2.1 orphaned 2.1 +### # ro global state, ro repo state, ro switch state +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-sys-compiler 2 One-line description +### # ro global state, ro repo state, rw switch state +### opam install i-am-package +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +STATE Detected changed packages (marked for reinstall): {} +The following actions will be performed: + - install i-am-package 2 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed i-am-package.2 +Done. +### opam option jobs=4 +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +Set to '4' the field jobs in global configuration +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 1 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default" "this-internal-error"] +jobs: 4 +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +### opam-cat _opam/.opam-switch/switch-config +invariant: ["i-am-sys-compiler" | "i-am-compiler"] +opam-root: "${BASEDIR}/OPAM" +opam-version: "2.0" +synopsis: "local switch" +### opam-cat _opam/.opam-switch/switch-state +compiler: ["i-am-sys-compiler.2"] +installed: ["i-am-package.2" "i-am-sys-compiler.2"] +opam-version: "2.0" +roots: ["i-am-package.2" "i-am-sys-compiler.2"] +### :V:5:e: reinit from 2.1 +### ocaml generate.ml 2.1 +### opam init --reinit --bypass-checks --no-setup +No configuration file found, using built-in defaults. +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found + +<><> Updating repositories ><><><><><><><><><><><><><><><><><><><><><><><><><><> +[default] no changes from file://${BASEDIR}/default +### opam-cat $OPAMROOT/config +default-compiler: ["i-am-sys-compiler" "i-am-compiler"] +default-invariant: ["i-am-sys-compiler"] +depext: true +depext-cannot-install: false +depext-run-installs: true +download-jobs: 3 +eval-variables: [sys-comp-version ["sh" "-c" "echo $OPAMSYSCOMP"] "comp version"] +installed-switches: ["sw-sys-comp" "sw-comp" "default" "${BASEDIR}/why-did-you-delete-me" "this-internal-error"] +opam-root-version: "2.1" +opam-version: "2.0" +repositories: "default" +switch: "sw-sys-comp" +wrap-build-commands: ["%{hooks}%/sandbox.sh" "build"] {os = "linux" | os = "macos"} +wrap-install-commands: ["%{hooks}%/sandbox.sh" "install"] {os = "linux" | os = "macos"} +wrap-remove-commands: ["%{hooks}%/sandbox.sh" "remove"] {os = "linux" | os = "macos"} +### opam switch --short +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +${BASEDIR} +default +sw-comp +sw-sys-comp +this-internal-error +### opam list +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +RSTATE LOAD-REPOSITORY-STATE @ ${BASEDIR}/OPAM +RSTATE Cache found +STATE LOAD-SWITCH-STATE @ ${BASEDIR} +STATE Switch state loaded in 0.000s +# Packages matching: installed +# Name # Installed # Synopsis +i-am-package 2 One-line description +i-am-sys-compiler 2 One-line description diff -Nru opam-2.0.10/tests/reftests/pat-sub.test opam-2.1.2/tests/reftests/pat-sub.test --- opam-2.0.10/tests/reftests/pat-sub.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/pat-sub.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,73 @@ +N0REP0 +### +blabla +pioupiou +bloblob +### +--- a/bar 2020-12-02 14:22:55.364620832 +0100 ++++ b/bar 2020-12-02 14:23:05.668686881 +0100 +@@ -1,3 +1,3 @@ + blabla +-pioupiou ++ploplop + bloblob +### +blabla +pat-sub +bloblob +### +--- a/baz 2020-12-02 14:22:55.364620832 +0100 ++++ b/baz 2020-12-02 14:23:05.668686881 +0100 +@@ -1,3 +1,3 @@ + blabla +-%{name}% ++ploplop + bloblob +### +bla +%{name}% +blo +%{version}% +### +opam-version: "2.0" +patches: +[ + "bar-update.patch" + "baz-update.patch" +] +substs: +[ + "foo" + "baz-update.patch" +] +build: [ + ["grep" "pat-sub" "foo"] + ["grep" "0.1" "foo"] + ["grep" "ploplop" "bar"] + ["grep" "ploplop" "baz"] +] +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +Now run 'opam upgrade' to apply any package updates. +### opam switch create sw --empty +### OPAMDEBUGSECTIONS="ACTION" opam install pat-sub -y --debug --debug-level=-1 +The following actions will be performed: + - install pat-sub 0.1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +ACTION download_package: pat-sub.0.1 +ACTION prepare_package_source: pat-sub.0.1 at ${BASEDIR}/OPAM/sw/.opam-switch/build/pat-sub.0.1 +ACTION pat-sub: expanding opam variables in baz-update.patch.in, generating baz-update.patch. + +ACTION pat-sub: applying bar-update.patch. + +ACTION pat-sub: applying baz-update.patch. + +ACTION pat-sub: expanding opam variables in foo.in, generating foo. + +ACTION Installing pat-sub.0.1. + +-> installed pat-sub.0.1 +Done. diff -Nru opam-2.0.10/tests/reftests/run.ml opam-2.1.2/tests/reftests/run.ml --- opam-2.0.10/tests/reftests/run.ml 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/run.ml 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,593 @@ +(**************************************************************************) +(* *) +(* Copyright 2012-2021 OCamlPro *) +(* Copyright 2012 INRIA *) +(* *) +(* All rights reserved. This file is distributed under the terms of the *) +(* GNU Lesser General Public License version 2.1, with the special *) +(* exception on linking described in the file LICENSE. *) +(* *) +(**************************************************************************) + +(* Simple CRAM-like test framework for opam tests. + Features and format: + - first line is + * the git hash of the opam repository to use, an opamroot is already + initialised with that repo as "default" + * or N0REP0 for no dependency on opam repository, and an opamroot is + already initialised with an empty `default` repository in `./REPO` + directory, that you need to populate and not forget to run `opam update` + - 'opam' is automatically redirected to the correct binary + - the command prefix is `### ` + - use `### `, then the contents below to create a file verbatim + - use `### `, then the contents of an opam file below to + add this package to `default` repository in `./REPO` + - use `### `, then the contents below to add this + file as a extra-file of the given package in the `default` repository + - `### FOO=x BAR=y` to export variables for subsequent commands + - shell-like command handling: + * **NO pattern expansion, shell pipes, sequences or redirections** + * `FOO=x BAR=y command` + * Arguments can be quoted: eg `"foo\"bar"`, `'foo\bar'`, but not combined + (`foo'bar'` is not translated to `foobar`) + * Variable expansion in arguments (`$FOO` or `${FOO}`). Undefined variables + are left as-is + * rewrites: `| 'REGEXP' -> 'STR'` (can be repeated; set `STR` to `\c` to + clear the line) + * `| grep REGEXP` + * `| unordered` compares lines without considering their ordering + * variables from command outputs: `cmd args >$ VAR` + * `### : comment` + * opam-cat: prints a nromalised opam file + - if you need more shell power, create a script using then run it. + Or just use `sh -c`... but beware for compatibility. + + The opam roots are generated using dynamically generated dune rules (see + gen.ml and dune.inc), then the tests are run using this script. +*) + +type test = { + repo_hash: string; + commands: (string * string list) list; +} + +let cmd_prompt = "### " +let no_opam_repo = "N0REP0" +let default_repo = "REPO" + +let is_prefix pfx s = + String.length s >= String.length pfx && + String.sub s 0 (String.length pfx) = pfx + +let rem_prefix pfx s = + if not (is_prefix pfx s) then invalid_arg "rem_prefix" + else String.sub s (String.length pfx) (String.length s - String.length pfx) + +(* Test file format: {v + REPO_HASH + ### opam command + output line 1 + output... + ### + contents... + ### opam command + output... + ### ENV_VAR=x opam command + output... +v}*) + +let load_test f = + let ic = open_in f in + let repo_hash = try input_line ic with + | End_of_file -> failwith "Malformed test file" + in + let commands = + let rec aux commands = + match input_line ic, commands with + | s, commands when is_prefix cmd_prompt s -> + aux ((rem_prefix cmd_prompt s, []) :: commands) + | s, ((cmd,out) :: commands) -> + aux ((cmd, s::out) :: commands) + | exception End_of_file -> + List.rev_map (fun (cmd, out) -> cmd, List.rev out) commands + | _ -> failwith "Malformed test file" + in + aux [] + in + close_in ic; + { repo_hash; commands } + +let base_env = + let propagate v = try [v, Sys.getenv v] with Not_found -> [] in + propagate "PATH" @ + propagate "HOME" @ + propagate "COMSPEC" @ + propagate "LIB" @ + propagate "SYSTEMROOT" @ + propagate "TMPDIR" @ + propagate "TMP" @ + propagate "TEMP" @ + [ + "OPAMKEEPBUILDDIR", "1"; + "OPAMCOLOR", "never"; + "OPAMUTF8", "never"; + "OPAMNOENVNOTICE", "1"; + "OPAMNODEPEXTS", "1"; + "OPAMDOWNLOADJOBS", "1"; + ] + +(* See [opamprocess.safe_wait] *) +let rec waitpid pid = + match Unix.waitpid [] pid with + | exception Unix.Unix_error (Unix.EINTR,_,_) -> waitpid pid + | exception Unix.Unix_error (Unix.ECHILD,_,_) -> 256 + | _, Unix.WSTOPPED _ -> waitpid pid + | _, Unix.WEXITED n -> n + | _, Unix.WSIGNALED _ -> failwith "signal" + +exception Command_failure of int * string * string + +let str_replace_path ?(escape=false) whichway filters s = + let escape = + if escape then Re.(replace_string (compile @@ char '\\') ~by:"\\\\") + else fun s -> s + in + List.fold_left (fun s (re, by) -> + let re_path = Re.( + seq [re; group (rep (diff any space))] + ) in + match by with + | Some by -> + Re.replace (Re.compile re_path) s + ~f:(fun g -> + escape (by ^ whichway (Re.Group.get g 1))) + | None -> + if Re.execp (Re.compile re) s then s else "\\c") + s filters + +let command + ?(allowed_codes = [0]) ?(vars=[]) ?(silent=false) ?(filter=[]) + cmd args = + let env = + Array.of_list @@ + List.map (fun (var, value) -> Printf.sprintf "%s=%s" var value) @@ + (base_env @ vars) + in + let input, stdout = Unix.pipe () in + Unix.set_close_on_exec input; + let ic = Unix.in_channel_of_descr input in + set_binary_mode_in ic false; + let cmd, orig_cmd = + let maybe_resolved_cmd = + if Sys.win32 then + OpamStd.Option.default cmd @@ OpamSystem.resolve_command cmd + else + cmd + in + maybe_resolved_cmd, cmd + in + let args = + if orig_cmd = "tar" || orig_cmd = "tar.exe" then + List.map (Lazy.force (OpamSystem.get_cygpath_function ~command:cmd)) args + else args + in + let pid = + OpamProcess.create_process_env cmd (Array.of_list (cmd::args)) env + Unix.stdin stdout stdout + in + Unix.close stdout; + let out_buf = Buffer.create 273 in + let rec filter_output ?(first=true) ic = + match input_line ic with + | s -> + let s = str_replace_path OpamSystem.back_to_forward filter s in + if s = "\\c" then filter_output ~first ic + else + (if not first then Buffer.add_char out_buf '\n'; + Buffer.add_string out_buf s; + if not silent then print_endline s; + filter_output ~first:false ic) + | exception End_of_file -> () + in + filter_output ic; + let ret = waitpid pid in + close_in ic; + let out = Buffer.contents out_buf in + if not (List.mem ret allowed_codes) then + raise (Command_failure (ret, String.concat " " (cmd :: args), out)) + else + out + +let finally f x k = match f x with + | r -> k (); r + | exception e -> + (* When we're at least 4.05+ + let bt = Printexc.get_raw_backtrace () in*) + (try k () with _ -> ()); + (*Printexc.raise_with_backtrace e bt*) + raise e + +(* Borrowed from ocamltest_stdlib.ml *) +let rec mkdir_p dir = + if Sys.file_exists dir then () + else let () = mkdir_p (Filename.dirname dir) in + if not (Sys.file_exists dir) then + Unix.mkdir dir 0o777 + else () + +let erase_file path = + try Sys.remove path + with Sys_error _ when Sys.win32 -> + (* Deal with read-only attribute on Windows. Ignore any error from chmod + so that the message always come from Sys.remove *) + let () = try Unix.chmod path 0o666 with Sys_error _ -> () in + Sys.remove path + +let rm_rf path = + let rec erase path = + if Sys.file_exists path && Sys.is_directory path then begin + Array.iter (fun entry -> erase (Filename.concat path entry)) + (Sys.readdir path); + Unix.rmdir path + end else erase_file path + in + try if Sys.file_exists path then erase path + with Sys_error err -> + raise (Sys_error (Printf.sprintf "Failed to remove %S (%s)" path err)) + +let rec with_temp_dir f = + let s = + Filename.concat + (Filename.get_temp_dir_name ()) + (Printf.sprintf "opam-reftest-%06x" (Random.int 0xffffff)) + in + if Sys.file_exists s then + with_temp_dir f + else + (mkdir_p s; + finally f s @@ fun () -> rm_rf s) + +type command = + | File_contents of string + | Cat of string list + | Run of { env: (string * string) list; + cmd: string; + args: string list; (* still escaped *) + filter: (Re.t * string option) list; + output: string option; + unordered: bool; } + | Export of (string * string) list + | Comment of string + +module Parse = struct + + open Re + + let re_str_atom = + alt [ + seq [char '"'; + rep @@ alt + [diff any (set "\"\\"); + seq [char '\\'; any]]; + char '"']; + seq [char '\''; + rep @@ diff any (char '\''); + char '\'']; + seq [diff any (set "\"' "); + rep @@ alt + [diff any (set "\\ "); + seq [char '\\'; any]]]; + ] + + let get_str s = + let unescape = + replace (compile @@ seq [char '\\'; group any]) + ~f:(fun g -> Group.get g 1) + in + let trim s = String.sub s 1 (String.length s - 2) in + if s = "" then "" + else match s.[0] with + | '"' -> unescape (trim s) + | '\'' -> trim s + | _ -> unescape s + + let re_varbind = + seq [ + group @@ seq [alpha; rep (alt [alnum; set "_-"])]; + char '='; + group @@ re_str_atom; + rep space; + ] + + let re_package = + seq [ + str "' + ] + + let command str = + if str.[0] = '<' && str.[String.length str - 1] = '>' then + let f = + try + let grs = exec (compile re_package) str in + let name = Group.get grs 1 in + let version = Group.get grs 2 in + try + let files = Group.get grs 3 in + Printf.sprintf "%s/packages/%s/%s.%s/files/%s" + default_repo name name version files + with Not_found -> + Printf.sprintf "%s/packages/%s/%s.%s/opam" + default_repo name name version + with Not_found -> + String.sub str 1 (String.length str - 2) + in + File_contents f + else if str.[0] = ':' || str.[0] = '#' then + Comment str + else + match OpamStd.String.cut_at str ' ' with + | Some ("opam-cat", files) -> + Cat (OpamStd.String.split files ' ') + | _ -> + let varbinds, pos = + let gr = exec (compile @@ rep re_varbind) str in + List.map (fun gr -> Group.get gr 1, get_str (Group.get gr 2)) + (all (compile @@ re_varbind) (Group.get gr 0)), + Group.stop gr 0 + in + let cmd, pos = + try + let gr = exec ~pos (compile re_str_atom) str in + Some (get_str (Group.get gr 0)), + Group.stop gr 0 + with Not_found -> None, pos + in + let args = + let grs = all ~pos (compile re_str_atom) str in + List.map (fun gr -> Group.get gr 0) grs + in + let rec get_args_rewr acc = function + | [] -> List.rev acc, false, [], None + | "|" :: _ as rewr -> + let rec get_rewr (unordered, acc) = function + | "|" :: re :: "->" :: str :: r -> + get_rewr (unordered, (Posix.re (get_str re), Some (get_str str)) :: acc) r + | "|" :: "grep" :: re :: r -> + get_rewr (unordered, (Posix.re (get_str re), None) :: acc) r + | "|" :: "unordered" :: r -> + get_rewr (true, acc) r + | ">$" :: output :: [] -> + unordered, List.rev acc, Some (get_str output) + | [] -> + unordered, List.rev acc, None + | r -> + Printf.printf + "Bad rewrite %S, expecting '| RE -> STR' or '>$ VAR'\n%!" + (String.concat " " r); + unordered, List.rev acc, None + in + let unordered, rewr, out = get_rewr (false, []) rewr in + List.rev acc, unordered, rewr, out + | arg :: r -> get_args_rewr (arg :: acc) r + in + let args, unordered, rewr, output = get_args_rewr [] args in + match cmd with + | Some cmd -> + Run { + env = varbinds; + cmd; + args; + filter = rewr; + output; + unordered; + } + | None -> + Export varbinds +end + +let parse_command = Parse.command + +let common_filters dir = + let tmpdir = Filename.get_temp_dir_name () in + Re.[ + alt [str dir; str (OpamSystem.back_to_forward dir)], + Some "${BASEDIR}"; + seq [opt (str "/private"); + alt [str tmpdir; + str (OpamSystem.back_to_forward tmpdir)]; + rep (set "/\\"); + str "opam-"; + rep1 (alt [alnum; char '-'])], + Some "${OPAMTMP}"; + ] + +let run_cmd ~opam ~dir ?(vars=[]) ?(filter=[]) ?(silent=false) cmd args = + let filter = common_filters dir @ filter in + let opamroot = Filename.concat dir "OPAM" in + let env_vars = [ + "OPAM", opam; + "OPAMROOT", opamroot; + ] @ vars + in + let var_filters = + List.rev_map (fun (v, x) -> + Re.(alt [seq [str "${"; str v; str "}"]; + seq [char '$'; str v; eow]];), + Some x) + env_vars + in + let cmd = if cmd = "opam" then opam else cmd in + let args = + List.map (fun a -> + let expanded = + if a <> "" && a.[0] = '\'' then a + else + str_replace_path ~escape:true OpamSystem.forward_to_back + var_filters a + in + Parse.get_str expanded) + args + in + try command ~vars:env_vars ~filter ~silent cmd args, None + with Command_failure (n,_, out) -> out, Some n + +let write_file ~path ~contents = + mkdir_p (Filename.dirname path); + let oc = open_out_bin path in + output_string oc contents; + close_out oc + +let rec list_remove x = function + | [] -> [] + | y :: r -> if x = y then r else y :: list_remove x r + +let run_test ?(vars=[]) ~opam t = + let opamroot0 = Filename.concat (Sys.getcwd ()) ("root-"^t.repo_hash) in + with_temp_dir @@ fun dir -> + let old_cwd = Sys.getcwd () in + let opamroot = Filename.concat dir "OPAM" in + if Sys.win32 then + ignore @@ command ~allowed_codes:[0; 1] ~silent:true + "robocopy" + ["/e"; "/copy:dat"; "/dcopy:dat"; "/sl"; opamroot0; opamroot] + else + ignore @@ command "cp" ["-a"; opamroot0; opamroot]; + Sys.chdir dir; + let dir = Sys.getcwd () in (* because it may need to be normalised on OSX *) + if t.repo_hash = no_opam_repo then + (mkdir_p (default_repo^"/packages"); + write_file ~path:(default_repo^"/repo") ~contents:{|opam-version: "2.0"|}; + ignore @@ command opam ~silent:true + [ "repository"; "set-url"; "default"; "./"^default_repo; + "--root"; opamroot]); + ignore @@ command ~silent:true opam + ["var"; "--quiet"; "--root"; opamroot; "--global"; "--cli=2.1"; + "sys-ocaml-version=4.08.0"]; + print_endline t.repo_hash; + let _vars = + List.fold_left (fun vars (cmd, out) -> + print_string cmd_prompt; + print_endline cmd; + match parse_command cmd with + | Comment _ -> vars + | File_contents path -> + let contents = String.concat "\n" out ^ "\n" in + write_file ~path ~contents; + print_string contents; + vars + | Export bindings -> + List.fold_left + (fun vars (v, r) -> (v, r) :: List.filter (fun (w, _) -> v <> w) vars) + vars bindings + | Cat files -> + let print_opamfile header file = + let content = + let open OpamParserTypes.FullPos in + let original = OpamParser.FullPos.file file in + let rec mangle item = + match item.pelem with + | Section s -> + {item with pelem = Section {s with section_name = OpamStd.Option.map (fun v -> {v with pelem = mangle_string v.pelem}) s.section_name; + section_items = {s.section_items with pelem = List.map mangle s.section_items.pelem}}} + | Variable(name, value) -> + {item with pelem = Variable(name, mangle_value value)} + and mangle_value item = + match item.pelem with + | String s -> + {item with pelem = String(mangle_string s)} + | Relop(op, l, r) -> + {item with pelem = Relop(op, mangle_value l, mangle_value r)} + | Prefix_relop(relop, v) -> + {item with pelem = Prefix_relop(relop, mangle_value v)} + | Logop(op, l, r) -> + {item with pelem = Logop(op, mangle_value l, mangle_value r)} + | Pfxop(op, v) -> + {item with pelem = Pfxop(op, mangle_value v)} + | List l -> + {item with pelem = List{l with pelem = List.map mangle_value l.pelem}} + | Group l -> + {item with pelem = Group{l with pelem = List.map mangle_value l.pelem}} + | Option(v, l) -> + {item with pelem = Option(mangle_value v, {l with pelem = List.map mangle_value l.pelem})} + | Env_binding(name, op, v) -> + {item with pelem = Env_binding(name, op, mangle_value v)} + | Bool _ + | Int _ + | Ident _ -> item + and mangle_string = String.map (function '\\' -> '/' | c -> c) + in + let mangled = + {original with file_contents = List.map mangle original.file_contents} + in + OpamPrinter.FullPos.Normalise.opamfile mangled + in + let str = if header then Printf.sprintf "=> %s <=\n" file else "" in + let str = Printf.sprintf "%s%s" str content in + let str = + str_replace_path OpamSystem.back_to_forward + (common_filters dir) str + in + print_string str + in + let files = + List.map (fun s -> Re.(replace_string (compile @@ str "$OPAMROOT") + ~by:opamroot s)) files + in + (match files with + | file::[] -> print_opamfile false file + | _::_ -> List.iter (print_opamfile true) files + | [] -> ()); + vars + | Run {env; cmd; args; filter; output; unordered} -> + let silent = output <> None || unordered in + let r, errcode = + run_cmd ~opam ~dir ~vars:(vars @ env) ~filter ~silent cmd args + in + (if unordered then + (* print lines from Result, but respecting order from Expect *) + let rec diffl acc r e = + let expect_has rl = + let matching = List.filter (( = ) rl) in + List.length (matching e) > List.length (matching acc) + in + match r, e with + | r, el::e when List.mem el acc -> + print_endline el; diffl (list_remove el acc) r e + | rl::r, el::e -> + if rl = el then (print_endline el; diffl acc r e) + else if expect_has rl then diffl (rl::acc) r (el :: e) + else (print_endline rl; diffl acc r (el :: e)) + | [], _::el -> + diffl acc [] el + | r, [] -> + assert (acc = []); List.iter print_endline r + in + diffl [] (String.split_on_char '\n' r) out); + OpamStd.Option.iter (Printf.printf "# Return code %d #\n") errcode; + match output with + | None -> vars + | Some v -> (v, r) :: List.filter (fun (w,_) -> v <> w) vars) + vars + t.commands + in + Sys.chdir old_cwd + +let () = + Random.self_init (); + match Array.to_list Sys.argv with + | _ :: opam :: input :: env -> + let opam = OpamFilename.(to_string (of_string opam)) in + let vars = + List.map (fun s -> match OpamStd.String.cut_at s '=' with + | Some (var, value) -> var, value + | None -> failwith "Bad 'var=value' argument") + env + in + load_test input |> run_test ~opam ~vars + | _ -> + failwith "Expected arguments: opam.exe opam file.test [env-bindings]" diff -Nru opam-2.0.10/tests/reftests/run.mli opam-2.1.2/tests/reftests/run.mli --- opam-2.0.10/tests/reftests/run.mli 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/run.mli 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +(* empty *) diff -Nru opam-2.0.10/tests/reftests/show.test opam-2.1.2/tests/reftests/show.test --- opam-2.0.10/tests/reftests/show.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/show.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,314 @@ +009e00fa +### opam show "conf-llvm>7.0.0" + +<><> conf-llvm: information on all versions <><><><><><><><><><><><><><><><><><> +name conf-llvm +all-versions 3.4 3.8 3.9 4.0.0 5.0.0 6.0.0 7.0.0 8.0.0 9.0.0 10.0.0 + +<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><> +version 10.0.0 +repository default +homepage "http://llvm.org" +bug-reports "https://llvm.org/bugs/" +authors "The LLVM team" +maintainer "Kate " +license "MIT" +flags conf +synopsis Virtual package relying on llvm library installation +### opam show --all-versions "conf-llvm>7.0.0" + +<><> conf-llvm: information on all versions <><><><><><><><><><><><><><><><><><> +name conf-llvm +all-versions 3.4 3.8 3.9 4.0.0 5.0.0 6.0.0 7.0.0 8.0.0 9.0.0 10.0.0 + +<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><> +version 8.0.0 +repository default +homepage "http://llvm.org" +bug-reports "https://llvm.org/bugs/" +authors "The LLVM team" +maintainer "Kate " +license "MIT" +flags conf +synopsis Virtual package relying on llvm library installation + +<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><> +version 9.0.0 +repository default +homepage "http://llvm.org" +bug-reports "https://llvm.org/bugs/" +authors "The LLVM team" +maintainer "Kate " +license "MIT" +flags conf +synopsis Virtual package relying on llvm library installation + +<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><> +version 10.0.0 +repository default +homepage "http://llvm.org" +bug-reports "https://llvm.org/bugs/" +authors "The LLVM team" +maintainer "Kate " +license "MIT" +flags conf +synopsis Virtual package relying on llvm library installation +### opam show "conf-llvm>7.0.0" --raw +opam-version: "2.0" +name: "conf-llvm" +version: "10.0.0" +synopsis: "Virtual package relying on llvm library installation" +maintainer: "Kate " +authors: "The LLVM team" +license: "MIT" +homepage: "http://llvm.org" +bug-reports: "https://llvm.org/bugs/" +flags: conf +build: ["bash" "-ex" "configure.sh" version] +depexts: [ + ["llvm@10"] {os-distribution = "homebrew" & os = "macos"} + ["llvm-10.0"] {os-distribution = "macports" & os = "macos"} + ["llvm-10-dev"] {os-family = "debian"} + ["llvm10-dev"] {os-distribution = "alpine"} + ["llvm10-devel"] {os-family = "suse"} + ["devel/llvm10"] {os = "freebsd"} +] +extra-files: ["configure.sh" "md5=b22c1cc2fb7743accdf5d72b9f480307"] +### opam show "conf-llvm>7.0.0" --raw --sort +opam-version: "2.0" +name: "conf-llvm" +version: "10.0.0" +synopsis: "Virtual package relying on llvm library installation" +maintainer: "Kate " +authors: "The LLVM team" +license: "MIT" +homepage: "http://llvm.org" +bug-reports: "https://llvm.org/bugs/" +flags: conf +build: ["bash" "-ex" "configure.sh" version] +depexts: [ + ["devel/llvm10"] {os = "freebsd"} + ["llvm-10-dev"] {os-family = "debian"} + ["llvm-10.0"] {os-distribution = "macports" & os = "macos"} + ["llvm10-dev"] {os-distribution = "alpine"} + ["llvm10-devel"] {os-family = "suse"} + ["llvm@10"] {os-distribution = "homebrew" & os = "macos"} +] +extra-files: ["configure.sh" "md5=b22c1cc2fb7743accdf5d72b9f480307"] +### opam show "conf-llvm>7.0.0" --sort + +<><> conf-llvm: information on all versions <><><><><><><><><><><><><><><><><><> +name conf-llvm +all-versions 3.4 3.8 3.9 4.0.0 5.0.0 6.0.0 7.0.0 8.0.0 9.0.0 10.0.0 + +<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><> +version 10.0.0 +repository default +homepage "http://llvm.org" +bug-reports "https://llvm.org/bugs/" +authors "The LLVM team" +maintainer "Kate " +license "MIT" +flags conf +synopsis Virtual package relying on llvm library installation +### opam show "conf-llvm>7.0.0" -f depexts: --sort +["devel/llvm10"] {os = "freebsd"} +["llvm-10-dev"] {os-family = "debian"} +["llvm-10.0"] {os-distribution = "macports" & os = "macos"} +["llvm10-dev"] {os-distribution = "alpine"} +["llvm10-devel"] {os-family = "suse"} +["llvm@10"] {os-distribution = "homebrew" & os = "macos"} +### +opam-version: "2.0" +name: "showtest" +version: "1.0" +synopsis: "A word" +description: "Two words." +maintainer: "opam-devel@lists.ocaml.org" +authors: [ "Z" "A" "R" ] +homepage: "x" +bug-reports: "x" +depends: [ + "ocaml" {>= "4.02.3"} + "base-unix" + "base-bigarray" + "ocamlgraph" + "re" {>= "1.5.0"} + "dune" {>= "1.2.1"} + "cppo" {build} +] +conflicts: ["extlib-compat"] +build: [ + [ "./configure" ] + [ make ] +] +dev-repo: "git+https://github.com/ocaml/opam.git" +### opam show ./showtest.opam --sort --raw +opam-version: "2.0" +name: "showtest" +version: "1.0" +synopsis: "A word" +description: "Two words." +maintainer: "opam-devel@lists.ocaml.org" +authors: ["A" "R" "Z"] +homepage: "x" +bug-reports: "x" +depends: [ + "base-bigarray" + "base-unix" + "ocamlgraph" + "cppo" {build} + "dune" {>= "1.2.1"} + "ocaml" {>= "4.02.3"} + "re" {>= "1.5.0"} +] +conflicts: ["extlib-compat"] +build: [ + ["./configure"] + [make] +] +dev-repo: "git+https://github.com/ocaml/opam.git" +### opam show ./showtest.opam --sort + +<><> showtest: information on all versions ><><><><><><><><><><><><><><><><><><> +name showtest +all-versions 1.0 + +<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><> +version 1.0 +pin -- +homepage "x" +bug-reports "x" +dev-repo "git+https://github.com/ocaml/opam.git" +authors "A" "R" "Z" +maintainer "opam-devel@lists.ocaml.org" +depends "base-bigarray" + "base-unix" + "ocamlgraph" + "cppo" {build} + "dune" {>= "1.2.1"} + "ocaml" {>= "4.02.3"} + "re" {>= "1.5.0"} +conflicts "extlib-compat" +synopsis A word +description Two words. +### opam show ./showtest.opam --sort --just-file +opam-version: "2.0" +name: "showtest" +version: "1.0" +synopsis: "A word" +description: "Two words." +maintainer: "opam-devel@lists.ocaml.org" +authors: ["A" "R" "Z"] +homepage: "x" +bug-reports: "x" +depends: [ + "base-bigarray" + "base-unix" + "ocamlgraph" + "cppo" {build} + "dune" {>= "1.2.1"} + "ocaml" {>= "4.02.3"} + "re" {>= "1.5.0"} +] +conflicts: ["extlib-compat"] +build: [ + ["./configure"] + [make] +] +dev-repo: "git+https://github.com/ocaml/opam.git" +### opam show ./showtest.opam -f depends: --sort +"base-bigarray" +"base-unix" +"ocamlgraph" +"cppo" {build} +"dune" {>= "1.2.1"} +"ocaml" {>= "4.02.3"} +"re" {>= "1.5.0"} +### OPAMDEBUGSECTIONS=AUXCMD opam show ./showtest.opam --debug-level=-1 --debug --just-file +opam-version: "2.0" +name: "showtest" +version: "1.0" +synopsis: "A word" +description: "Two words." +maintainer: "opam-devel@lists.ocaml.org" +authors: ["Z" "A" "R"] +homepage: "x" +bug-reports: "x" +depends: [ + "ocaml" {>= "4.02.3"} + "base-unix" + "base-bigarray" + "ocamlgraph" + "re" {>= "1.5.0"} + "dune" {>= "1.2.1"} + "cppo" {build} +] +conflicts: ["extlib-compat"] +build: [ + ["./configure"] + [make] +] +dev-repo: "git+https://github.com/ocaml/opam.git" +### OPAMDEBUGSECTIONS=AUXCMD opam show ./showtest.opam --debug-level=-1 +AUXCMD autopin: { showtest => file://${BASEDIR} } + +<><> showtest: information on all versions ><><><><><><><><><><><><><><><><><><> +name showtest +all-versions 1.0 + +<><> Version-specific details <><><><><><><><><><><><><><><><><><><><><><><><><> +version 1.0 +pin -- +homepage "x" +bug-reports "x" +dev-repo "git+https://github.com/ocaml/opam.git" +authors "Z" "A" "R" +maintainer "opam-devel@lists.ocaml.org" +depends "ocaml" {>= "4.02.3"} + "base-unix" + "base-bigarray" + "ocamlgraph" + "re" {>= "1.5.0"} + "dune" {>= "1.2.1"} + "cppo" {build} +conflicts "extlib-compat" +synopsis A word +description Two words. +### opam show ./showtest.opam --normalise -f depends: +["ocaml" {>= "4.02.3"} "base-unix" "base-bigarray" "ocamlgraph" "re" {>= "1.5.0"} "dune" {>= "1.2.1"} "cppo" {build}] +### OPAMVAR_os=freebsd opam show "conf-llvm>7.0.0" -f name,version,depexts: --all-versions +name conf-llvm +version 8.0.0 +depexts: ["llvm@8"] {os-distribution = "homebrew" & os = "macos"} + ["llvm-8.0"] {os-distribution = "macports" & os = "macos"} + ["llvm-8-dev"] {os-family = "debian"} + ["llvm8-dev"] {os-distribution = "alpine"} + ["llvm8-devel"] {os-family = "suse"} + ["devel/llvm80"] {os = "freebsd"} +name conf-llvm +version 9.0.0 +depexts: ["llvm@9"] {os-distribution = "homebrew" & os = "macos"} + ["llvm-9.0"] {os-distribution = "macports" & os = "macos"} + ["llvm-9-dev"] {os-family = "debian"} + ["llvm9-dev"] {os-distribution = "alpine"} + ["llvm9-devel"] {os-family = "suse"} + ["devel/llvm90"] {os = "freebsd"} +name conf-llvm +version 10.0.0 +depexts: ["llvm@10"] {os-distribution = "homebrew" & os = "macos"} + ["llvm-10.0"] {os-distribution = "macports" & os = "macos"} + ["llvm-10-dev"] {os-family = "debian"} + ["llvm10-dev"] {os-distribution = "alpine"} + ["llvm10-devel"] {os-family = "suse"} + ["devel/llvm10"] {os = "freebsd"} +### OPAMVAR_os=freebsd OPAMVAR_os_family=debian opam show "conf-llvm>7.0.0" -f name,version,depexts --all-versions +name conf-llvm +version 8.0.0 +depexts devel/llvm80 llvm-8-dev +name conf-llvm +version 9.0.0 +depexts devel/llvm90 llvm-9-dev +name conf-llvm +version 10.0.0 +depexts devel/llvm10 llvm-10-dev diff -Nru opam-2.0.10/tests/reftests/switch-creation.test opam-2.1.2/tests/reftests/switch-creation.test --- opam-2.0.10/tests/reftests/switch-creation.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/switch-creation.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,53 @@ +N0REP0 +### +opam-version: "2.0" +build: ["opam" "option" "jobs" "--safe" "--cli=2.1"] +flags: compiler +### +opam-version: "2.0" +build: ["opam" "option" "jobs" "--safe" "--cli=2.1"] +### +opam-version: "2.0" +flags: compiler +build: ["sh" "./prefix"] +install: ["cp" "pref" "%{lib}%/prefix"] +### +opam var prefix --safe > pref +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +Now run 'opam upgrade' to apply any package updates. +### # switch not undefined for opam calls in build, on build creation +### opam switch create undef-prefix baz + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["baz"] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed baz.1 +Done. +### cat $OPAMROOT/undef-prefix/lib/prefix +${BASEDIR}/OPAM/undef-prefix +### # No deadlock on concurent opam calls in build +### opam switch create nohang foo bar | unordered + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["foo" "bar"] + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed bar.1 +-> installed foo.1 +Done. +### # switch propagation to opam calls in build +### opam switch create undef-switch --empty +### opam switch nohang +### opam install baz --sw undef-switch +The following actions will be performed: + - install baz 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed baz.1 +Done. +### cat $OPAMROOT/undef-switch/lib/prefix +${BASEDIR}/OPAM/undef-switch diff -Nru opam-2.0.10/tests/reftests/switch-invariant.test opam-2.1.2/tests/reftests/switch-invariant.test --- opam-2.0.10/tests/reftests/switch-invariant.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/switch-invariant.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,35 @@ +N0REP0 +### +opam-version: "2.0" +### +opam-version: "2.0" +### +opam-version: "2.0" +### opam repository add --dont-select non-default ./repo2 +[non-default] Initialised +### opam update --all + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +[non-default] no changes from file://${BASEDIR}/repo2 +Now run 'opam upgrade' to apply any package updates. +### opam switch create inv --empty --repo=default,non-default +### opam switch set-invariant foo +The switch invariant was set to foo +### opam install foo +The following actions will be performed: + - install foo 1 + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed foo.1 +Done. +### opam switch set-invariant foo +[WARNING] Packages foo don't have the 'compiler' flag set. +The switch invariant was set to foo +### opam switch set-invariant --package foo +[WARNING] Packages foo don't have the 'compiler' flag set. +The switch invariant was set to foo +### opam switch set-invariant bar +The switch invariant was set to bar +### opam switch set-invariant --package bar +The switch invariant was set to bar diff -Nru opam-2.0.10/tests/reftests/testing-env opam-2.1.2/tests/reftests/testing-env --- opam-2.0.10/tests/reftests/testing-env 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/testing-env 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1 @@ +OPAMSTRICT=1 diff -Nru opam-2.0.10/tests/reftests/upgrade-format.test opam-2.1.2/tests/reftests/upgrade-format.test --- opam-2.0.10/tests/reftests/upgrade-format.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/upgrade-format.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,405 @@ +009e00fa +### +opam-version: "1.2" +version: "2.0.7" +maintainer: "opam-devel@lists.ocaml.org" +authors: [ + "Vincent Bernardoff " + "Raja Boujbel " + "Roberto Di Cosmo " + "Thomas Gazagnaire " + "Louis Gesbert " + "Fabrice Le Fessant " + "Anil Madhavapeddy " + "Guillem Rieu " + "Ralf Treinen " + "Frederic Tuong " +] +homepage: "https://opam.ocaml.org/" +bug-reports: "https://github.com/ocaml/opam/issues" +dev-repo: "https://github.com/ocaml/opam.git" +build: [ + ["./configure" "--disable-checks" "--prefix" prefix] + [make "%{name}%.install"] +] +depends: [ + "base-unix" + "base-bigarray" + "ocamlgraph" + "re" {>= "1.5.0"} + "dune" {build & >= "1.2.1"} + "cppo" {build} +] +conflicts: "extlib-compat" +available: ocaml-version >= "4.02.3" +### opam show --raw ./opam-core.opam +[WARNING] Failed checks on opam-core package definition from source at file://${BASEDIR}: + error 57: Synopsis and description must not be both empty +opam-version: "2.0" +name: "opam-core" +version: "2.0.7" +maintainer: "opam-devel@lists.ocaml.org" +authors: [ + "Vincent Bernardoff " + "Raja Boujbel " + "Roberto Di Cosmo " + "Thomas Gazagnaire " + "Louis Gesbert " + "Fabrice Le Fessant " + "Anil Madhavapeddy " + "Guillem Rieu " + "Ralf Treinen " + "Frederic Tuong " +] +homepage: "https://opam.ocaml.org/" +bug-reports: "https://github.com/ocaml/opam/issues" +depends: [ + "ocaml" {>= "4.02.3"} + "base-unix" + "base-bigarray" + "ocamlgraph" + "re" {>= "1.5.0"} + "dune" {build & >= "1.2.1"} + "cppo" {build} +] +conflicts: ["extlib-compat"] +build: [ + ["./configure" "--disable-checks" "--prefix" prefix] + [make "%{name}%.install"] +] +dev-repo: "git+https://github.com/ocaml/opam.git" +### opam switch create . ocaml-system --fake -y +opam-core is now pinned to file://${BASEDIR} (version 2.0.7) + +<><> Installing new switch packages <><><><><><><><><><><><><><><><><><><><><><> +Switch invariant: ["ocaml-system"] +The following actions will be faked: + - install base-bigarray base [required by opam-core] + - install base-threads base [required by dune] + - install base-unix base [required by opam-core] + - install ocaml-system 4.08.0 + - install conf-m4 1 [required by ocamlfind] + - install ocaml-config 1 [required by ocaml] + - install ocaml 4.08.0 [required by opam-core] + - install seq base [required by re] + - install ocamlfind 1.8.1 [required by ocamlgraph] + - install dune 2.5.1 [required by opam-core] + - install ocamlgraph 1.8.8 [required by opam-core] + - install re 1.9.0 [required by opam-core] + - install cppo 1.6.6 [required by opam-core] + - install opam-core 2.0.7* +===== 14 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +Faking installation of base-bigarray.base +Faking installation of base-threads.base +Faking installation of base-unix.base +Faking installation of conf-m4.1 +Faking installation of ocaml-system.4.08.0 +Faking installation of ocaml-config.1 +Faking installation of ocaml.4.08.0 +Faking installation of dune.2.5.1 +Faking installation of cppo.1.6.6 +Faking installation of ocamlfind.1.8.1 +Faking installation of ocamlgraph.1.8.8 +Faking installation of seq.base +Faking installation of re.1.9.0 +Faking installation of opam-core.2.0.7 +Done. +### opam pin add git://github.com/ocaml/opam.git#59a71e3cf1 -yn +This will pin the following packages: opam-client, opam-core, opam-devel, opam-format, opam-installer, opam-repository, opam-solver, opam-state. Continue? [Y/n] y +opam-client is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +[NOTE] Package opam-core is currently pinned to file://${BASEDIR} (version 2.0.7). +opam-core is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +opam-devel is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +opam-format is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +opam-installer is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +opam-repository is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +opam-solver is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +opam-state is now pinned to git://github.com/ocaml/opam.git#59a71e3cf1 (version 2.1.0~beta3) +### opam install opam-format --show + +<><> Synchronising pinned packages ><><><><><><><><><><><><><><><><><><><><><><> +[opam-format.2.1.0~beta3] synchronised (no changes) + +The following actions would be performed: + - upgrade opam-core 2.0.7 to 2.1.0~beta3* [required by opam-format] + - install opam-file-format 2.0.0 [required by opam-format] + - install opam-format 2.1.0~beta3* +===== 2 to install | 1 to upgrade ===== +### : Testing preserved format with upgrade : +### +opam-version: "2.0" +build: ["opam"] +#comment +depends: ["config" "ocaml" "base"] #comment +#comment +flags: compiler +### +#comment +opam-version: "2.0" +#comment +build: ["opam"] +#comment +depends: [ +# comment + "config" #comment + "ocaml" #comment + "base" #comment +#comment +] +#comment +flags: compiler +### +#comment +opam-version: "2.0" +#comment +flags: compiler +#comment +depends: [ +#comment + "config" #comment + "ocaml" #comment + "base" #comment +#comment +] +#comment +build: ["opam" "var" "prefix" "--safe"] +license: "MIT" +#comment +### +# taken from repository/packages/ocamlbuild/ocamlbuild.0.9.0/opam at 437319f58e +opam-version: "1.2" +name: "ocamlbuild" +maintainer: "Gabriel Scherer " +version: "1" + +authors: [ + "Nicolas Pouillard" + "Berke Durak" +] + +license: "LGPL-2 with OCaml linking exception" +dev-repo: "https://github.com/ocaml/ocamlbuild.git" +homepage: "https://github.com/ocaml/ocamlbuild/" +bug-reports: "https://github.com/ocaml/ocamlbuild/issues" +doc: "https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc" + +build: [ + [make "-f" "configure.make" "Makefile.config" "src/ocamlbuild_config.ml" + "OCAMLBUILD_PREFIX=%{prefix}%" + "OCAMLBUILD_BINDIR=%{bin}%" + "OCAMLBUILD_LIBDIR=%{lib}%" + "OCAML_NATIVE=%{ocaml-native}%" + "OCAML_NATIVE_TOOLS=%{ocaml-native}%"] + [make "check-if-preinstalled" "all" "opam-install"] +] + +available: [ocaml-version >= "4.03" & ocaml-version < "4.04"] +depends: [ ] +conflicts: [ + "base-ocamlbuild" + "ocamlfind" {< "1.6.2"} +] +### opam admin upgrade +Updated ${BASEDIR}/packages/ocamlbuild/ocamlbuild.1/opam +### cat packages/ocamlbuild/ocamlbuild.1/opam +# taken from repository/packages/ocamlbuild/ocamlbuild.0.9.0/opam at 437319f58e +opam-version: "2.0" +name: "ocamlbuild" +maintainer: "Gabriel Scherer " +version: "1" + +authors: [ + "Nicolas Pouillard" + "Berke Durak" +] + +license: "LGPL-2 with OCaml linking exception" +dev-repo: "git+https://github.com/ocaml/ocamlbuild.git" +homepage: "https://github.com/ocaml/ocamlbuild/" +bug-reports: "https://github.com/ocaml/ocamlbuild/issues" +doc: "https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc" + +build: [ + [ + make + "-f" + "configure.make" + "Makefile.config" + "src/ocamlbuild_config.ml" + "OCAMLBUILD_PREFIX=%{prefix}%" + "OCAMLBUILD_BINDIR=%{bin}%" + "OCAMLBUILD_LIBDIR=%{lib}%" + "OCAML_NATIVE=%{ocaml:native}%" + "OCAML_NATIVE_TOOLS=%{ocaml:native}%" +] + [make "check-if-preinstalled" "all" "opam-install"] +] +depends: [ + "ocaml" {>= "4.03" & < "4.04"} +] +conflicts: [ + "base-ocamlbuild" + "ocamlfind" {< "1.6.2"} +] +### opam admin add-constraint "ocaml<4.03.5" +### cat packages/ocamlbuild/ocamlbuild.1/opam +# taken from repository/packages/ocamlbuild/ocamlbuild.0.9.0/opam at 437319f58e +opam-version: "2.0" +name: "ocamlbuild" +maintainer: "Gabriel Scherer " +version: "1" + +authors: [ + "Nicolas Pouillard" + "Berke Durak" +] + +license: "LGPL-2 with OCaml linking exception" +dev-repo: "git+https://github.com/ocaml/ocamlbuild.git" +homepage: "https://github.com/ocaml/ocamlbuild/" +bug-reports: "https://github.com/ocaml/ocamlbuild/issues" +doc: "https://github.com/ocaml/ocamlbuild/blob/master/manual/manual.adoc" + +build: [ + [ + make + "-f" + "configure.make" + "Makefile.config" + "src/ocamlbuild_config.ml" + "OCAMLBUILD_PREFIX=%{prefix}%" + "OCAMLBUILD_BINDIR=%{bin}%" + "OCAMLBUILD_LIBDIR=%{lib}%" + "OCAML_NATIVE=%{ocaml:native}%" + "OCAML_NATIVE_TOOLS=%{ocaml:native}%" +] + [make "check-if-preinstalled" "all" "opam-install"] +] +depends: [ + "ocaml" {>= "4.03" & < "4.03.5"} +] +conflicts: [ + "base-ocamlbuild" + "ocamlfind" {< "1.6.2"} +] +### cat packages/foo/foo.1/opam +opam-version: "2.0" +build: ["opam"] +#comment +depends: ["config" "ocaml" {< "4.03.5"} "base"] #comment +#comment +flags: compiler +### cat packages/bar/bar.1/opam +#comment +opam-version: "2.0" +#comment +build: ["opam"] +#comment +depends: [ +# comment + "config" #comment + "ocaml" {< "4.03.5"} #comment + "base" #comment +#comment +] +#comment +flags: compiler +### cat packages/baz/baz.1/opam +#comment +opam-version: "2.0" +#comment +flags: compiler +#comment +depends: [ +#comment + "config" #comment + "ocaml" {< "4.03.5"} #comment + "base" #comment +#comment +] +#comment +build: ["opam" "var" "prefix" "--safe"] +license: "MIT" +#comment +### opam admin add-constraint "config<1" +### cat packages/foo/foo.1/opam +opam-version: "2.0" +build: ["opam"] +#comment +depends: ["config" {< "1"} "ocaml" {< "4.03.5"} "base"] #comment +#comment +flags: compiler +### cat packages/bar/bar.1/opam +#comment +opam-version: "2.0" +#comment +build: ["opam"] +#comment +depends: [ +# comment + "config" {< "1"} #comment + "ocaml" {< "4.03.5"} #comment + "base" #comment +#comment +] +#comment +flags: compiler +### cat packages/baz/baz.1/opam +#comment +opam-version: "2.0" +#comment +flags: compiler +#comment +depends: [ +#comment + "config" {< "1"} #comment + "ocaml" {< "4.03.5"} #comment + "base" #comment +#comment +] +#comment +build: ["opam" "var" "prefix" "--safe"] +license: "MIT" +#comment +### opam admin add-constraint "base>1" +### cat packages/foo/foo.1/opam +opam-version: "2.0" +build: ["opam"] +#comment +depends: ["config" {< "1"} "ocaml" {< "4.03.5"} "base" {> "1"}] #comment +#comment +flags: compiler +### cat packages/bar/bar.1/opam +#comment +opam-version: "2.0" +#comment +build: ["opam"] +#comment +depends: [ +# comment + "config" {< "1"} #comment + "ocaml" {< "4.03.5"} #comment + "base" {> "1"} #comment +#comment +] +#comment +flags: compiler +### cat packages/baz/baz.1/opam +#comment +opam-version: "2.0" +#comment +flags: compiler +#comment +depends: [ +#comment + "config" {< "1"} #comment + "ocaml" {< "4.03.5"} #comment + "base" {> "1"} #comment +#comment +] +#comment +build: ["opam" "var" "prefix" "--safe"] +license: "MIT" +#comment diff -Nru opam-2.0.10/tests/reftests/var-option.test opam-2.1.2/tests/reftests/var-option.test --- opam-2.0.10/tests/reftests/var-option.test 1970-01-01 00:00:00.000000000 +0000 +++ opam-2.1.2/tests/reftests/var-option.test 2021-12-07 16:09:27.000000000 +0000 @@ -0,0 +1,455 @@ +N0REP0 +### +opam-version: "2.0" +depends: [ "base" ] +available: !(os = "macos" & arch = "arm64") +flags: compiler +setenv: IGTV_PATH = "%{lib}%/stublibs" +### +opam-version: "2.0" +### +opam-version: "2.0" +### opam update + +<><> Updating package repositories ><><><><><><><><><><><><><><><><><><><><><><> +[default] synchronised from file://${BASEDIR}/REPO +Now run 'opam upgrade' to apply any package updates. +### opam switch create var-option --empty +### opam var user= --switch var-option +Removed variable user in switch var-option +### opam var group= --switch var-option +Removed variable group in switch var-option +### opam var arch=x86_64 --global +Added '[arch "x86_64" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var jobs=7 --global +Added '[jobs "7" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var make=make --global +Added '[make "make" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var opam-version=68.79 --global +Added '[opam-version "68.79" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var os=linux --global +Added '[os "linux" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var os-distribution=lorem --global +Added '[os-distribution "lorem" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var os-family=ipsum --global +Added '[os-family "ipsum" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var os-version=dolor --global +Added '[os-version "dolor" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var sys-ocaml-arch= --global +Removed variable sys-ocaml-arch in global configuration +### opam var sys-ocaml-cc= --global +Removed variable sys-ocaml-cc in global configuration +### opam var sys-ocaml-libc= --global +Removed variable sys-ocaml-libc in global configuration +### opam option wrap-build-commands=[] --global +Set to '[]' the field wrap-build-commands in global configuration +### opam option wrap-install-commands=[] --global +Set to '[]' the field wrap-install-commands in global configuration +### opam option wrap-remove-commands=[] --global +Set to '[]' the field wrap-remove-commands in global configuration +### opam install i-got-the-variables --yes +The following actions will be performed: + - install base 2 [required by i-got-the-variables] + - install i-got-the-variables 2.4.6 +===== 2 to install ===== + +<><> Processing actions <><><><><><><><><><><><><><><><><><><><><><><><><><><><> +-> installed base.2 +-> installed i-got-the-variables.2.4.6 +Done. +### opam var | " *" -> " " | "[.]exe " -> "" + +<><> Global opam variables ><><><><><><><><><><><><><><><><><><><><><><><><><><> +arch x86_64 # Set through 'opam var' +exe # Suffix needed for executable filenames (Windows) +jobs 7 # Set through 'opam var' +make make # Set through 'opam var' +opam-version 68.79 # Set through 'opam var' +os linux # Set through 'opam var' +os-distribution lorem # Set through 'opam var' +os-family ipsum # Set through 'opam var' +os-version dolor # Set through 'opam var' +root ${BASEDIR}/OPAM # The current opam root directory +switch var-option # The identifier of the current switch +sys-ocaml-version 4.08.0 # Set through 'opam var' + +<><> Configuration variables from the current switch ><><><><><><><><><><><><><> +prefix ${BASEDIR}/OPAM/var-option +lib ${BASEDIR}/OPAM/var-option/lib +bin ${BASEDIR}/OPAM/var-option/bin +sbin ${BASEDIR}/OPAM/var-option/sbin +share ${BASEDIR}/OPAM/var-option/share +doc ${BASEDIR}/OPAM/var-option/doc +etc ${BASEDIR}/OPAM/var-option/etc +man ${BASEDIR}/OPAM/var-option/man +toplevel ${BASEDIR}/OPAM/var-option/lib/toplevel +stublibs ${BASEDIR}/OPAM/var-option/lib/stublibs + +<><> Package variables ('opam var --package PKG' to show) <><><><><><><><><><><> +PKG:name # Name of the package +PKG:version # Version of the package +PKG:depends # Resolved direct dependencies of the package +PKG:installed # Whether the package is installed +PKG:enable # Takes the value "enable" or "disable" depending on whether the package is installed +PKG:pinned # Whether the package is pinned +PKG:bin # Binary directory for this package +PKG:sbin # System binary directory for this package +PKG:lib # Library directory for this package +PKG:man # Man directory for this package +PKG:doc # Doc directory for this package +PKG:share # Share directory for this package +PKG:etc # Etc directory for this package +PKG:build # Directory where the package was built +PKG:hash # Hash of the package archive +PKG:dev # True if this is a development package +PKG:build-id # A hash identifying the precise package version with all its dependencies +PKG:opamfile # Path of the curent opam file +### opam var --switch var-option +prefix ${BASEDIR}/OPAM/var-option +lib ${BASEDIR}/OPAM/var-option/lib +bin ${BASEDIR}/OPAM/var-option/bin +sbin ${BASEDIR}/OPAM/var-option/sbin +share ${BASEDIR}/OPAM/var-option/share +doc ${BASEDIR}/OPAM/var-option/doc +etc ${BASEDIR}/OPAM/var-option/etc +man ${BASEDIR}/OPAM/var-option/man +toplevel ${BASEDIR}/OPAM/var-option/lib/toplevel +stublibs ${BASEDIR}/OPAM/var-option/lib/stublibs +### opam var --global | " *" -> " " | "[.]exe " -> "" +arch x86_64 # Set through 'opam var' +exe # Suffix needed for executable filenames (Windows) +jobs 7 # Set through 'opam var' +make make # Set through 'opam var' +opam-version 68.79 # Set through 'opam var' +os linux # Set through 'opam var' +os-distribution lorem # Set through 'opam var' +os-family ipsum # Set through 'opam var' +os-version dolor # Set through 'opam var' +root ${BASEDIR}/OPAM # The current opam root directory +switch var-option # The identifier of the current switch +sys-ocaml-version 4.08.0 # Set through 'opam var' +### OPAMDEBUG=-1 OPAMDEBUGSECTIONS="GSTATE STATE" opam var bin +GSTATE LOAD-GLOBAL-STATE @ ${BASEDIR}/OPAM +${BASEDIR}/OPAM/var-option/bin +### opam var bin --switch var-option +${BASEDIR}/OPAM/var-option/bin +### opam var bin --global +[ERROR] Variable bin not found in global config +# Return code 5 # +### opam var bin=global-bin --global +Added '[bin "global-bin" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var bin=switch-bin --switch var-option +Added 'bin: "switch-bin"' to field variables in switch var-option +### opam var bin +${BASEDIR}/OPAM/var-option/bin +### opam var bin --switch var-option +${BASEDIR}/OPAM/var-option/bin +### opam var bin --global +global-bin +### opam var foo=global-foo --global +Added '[foo "global-foo" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var foo=switch-foo --switch var-option +Added 'foo: "switch-foo"' to field variables in switch var-option +### opam var foo +switch-foo +### opam var foo --switch var-option +switch-foo +### opam var foo --global +global-foo +### opam var ocaml-base-compiler:version +[ERROR] Variable ocaml-base-compiler:version not found in switch var-option +# Return code 5 # +### opam var ocaml-base-compiler:version --switch var-option +[ERROR] Variable ocaml-base-compiler:version not found in switch var-option +# Return code 5 # +### opam var --package ocaml | ".*build-id.*" -> '\c' | ".*opamfile.*" -> '\c' | " *" -> " " +Fatal error: +Not_found +# Return code 99 # +### opam var i-got-the-variables:version +2.4.6 +### opam var i-got-the-variables:version --switch var-option +2.4.6 +### opam var --package i-got-the-variables | ".*build-id.*" -> '\c' | ".*opamfile.*" -> '\c' | " *" -> " " +i-got-the-variables:name i-got-the-variables # Name of the package +i-got-the-variables:version 2.4.6 # Version of the package +i-got-the-variables:depends base.2 # Resolved direct dependencies of the package +i-got-the-variables:installed true # Whether the package is installed +i-got-the-variables:enable enable # Takes the value "enable" or "disable" depending on whether the package is installed +i-got-the-variables:pinned false # Whether the package is pinned +i-got-the-variables:bin ${BASEDIR}/OPAM/var-option/bin # Binary directory for this package +i-got-the-variables:sbin ${BASEDIR}/OPAM/var-option/sbin # System binary directory for this package +i-got-the-variables:lib ${BASEDIR}/OPAM/var-option/lib/i-got-the-variables # Library directory for this package +i-got-the-variables:man ${BASEDIR}/OPAM/var-option/man # Man directory for this package +i-got-the-variables:doc ${BASEDIR}/OPAM/var-option/doc/i-got-the-variables # Doc directory for this package +i-got-the-variables:share ${BASEDIR}/OPAM/var-option/share/i-got-the-variables # Share directory for this package +i-got-the-variables:etc ${BASEDIR}/OPAM/var-option/etc/i-got-the-variables # Etc directory for this package +i-got-the-variables:build ${BASEDIR}/OPAM/var-option/.opam-switch/build/i-got-the-variables.2.4.6 # Directory where the package was built +i-got-the-variables:dev false # True if this is a development package +### opam option + +<><> Global configuration <><><><><><><><><><><><><><><><><><><><><><><><><><><> +best-effort-prefix-criteria {} +depext true +depext-bypass {} +depext-cannot-install false +depext-run-installs true +download-command {} +download-jobs 3 +jobs {} +post-build-commands {} +post-install-commands {} +post-remove-commands {} +post-session-commands {} +pre-build-commands {} +pre-install-commands {} +pre-remove-commands {} +pre-session-commands {} +repository-validation-command {} +solver {} +solver-criteria {} +solver-fixup-criteria {} +solver-upgrade-criteria {} +wrap-build-commands {} +wrap-install-commands {} +wrap-remove-commands {} + +<><> Switch configuration (var-option) ><><><><><><><><><><><><><><><><><><><><> +depext-bypass {} +post-build-commands {} +post-install-commands {} +post-remove-commands {} +post-session-commands {} +pre-build-commands {} +pre-install-commands {} +pre-remove-commands {} +pre-session-commands {} +setenv {} +synopsis "var-option" +wrap-build-commands {} +wrap-install-commands {} +wrap-remove-commands {} +### # Check global & switch option setting +### opam option download-jobs +3 +### opam option download-jobs --global +3 +### opam option download-jobs --switch var-option +[ERROR] Field or section download-jobs not found +# Return code 5 # +### opam option jobs +### opam option jobs --global +### opam option jobs --switch var-option +[ERROR] Field or section jobs not found +# Return code 5 # +### opam option depext-bypass +[] +### opam option depext-bypass --global +[] +### opam option depext-bypass --switch var-option +[] +### opam option synopsis +"var-option" +### opam option 'synopsis="sit amet"' +Set to '"sit amet"' the field synopsis in switch var-option +### opam option synopsis +"sit amet" +### opam option synopsis --switch var-option +"sit amet" +### opam option synopsis --global +[ERROR] Field or section synopsis not found +# Return code 5 # +### opam option synopsis= +Reverted field synopsis in switch var-option +### opam option synopsis +"" +### opam option 'synopsis="consectetur adipiscing"' --switch var-option +Set to '"consectetur adipiscing"' the field synopsis in switch var-option +### opam option synopsis +"consectetur adipiscing" +### opam option synopsis= --switch var-option +Reverted field synopsis in switch var-option +### opam option synopsis +"" +### opam option download-jobs +3 +### opam option download-jobs=10 +Set to '10' the field download-jobs in global configuration +### opam option download-jobs +10 +### opam option download-jobs --switch var-option +[ERROR] Field or section download-jobs not found +# Return code 5 # +### opam option download-jobs --global +10 +### opam option download-jobs= +Reverted field download-jobs in global configuration +### opam option download-jobs +1 +### opam option download-jobs=12 --global +Set to '12' the field download-jobs in global configuration +### opam option download-jobs +12 +### opam option download-jobs= --global +Reverted field download-jobs in global configuration +### opam option download-jobs +1 +### # Check same option name setting through global & switch +### opam option pre-session-commands +[] +### opam option 'pre-session-commands=["elit"]' +Set to '["elit"]' the field pre-session-commands in switch var-option +### opam option pre-session-commands +"elit" +### opam option pre-session-commands --switch var-option +"elit" +### opam option pre-session-commands --global +[] +### opam option pre-session-commands= +Reverted field pre-session-commands in switch var-option +### opam option pre-session-commands +[] +### opam option pre-session-commands --switch var-option +[] +### opam option pre-session-commands --global +[] +### opam option 'pre-session-commands=["sed"]' --switch var-option +Set to '["sed"]' the field pre-session-commands in switch var-option +### opam option pre-session-commands +"sed" +### opam option pre-session-commands --switch var-option +"sed" +### opam option pre-session-commands --global +[] +### opam option pre-session-commands= --switch var-option +Reverted field pre-session-commands in switch var-option +### opam option pre-session-commands +[] +### opam option pre-session-commands --switch var-option +[] +### opam option pre-session-commands --global +[] +### opam option 'pre-session-commands=["do"]' --global +Set to '["do"]' the field pre-session-commands in global configuration +### opam option pre-session-commands +[] +### opam option pre-session-commands --switch var-option +[] +### opam option pre-session-commands --global +"do" +### opam option pre-session-commands= --global +Reverted field pre-session-commands in global configuration +### opam option pre-session-commands +[] +### opam option pre-session-commands --switch var-option +[] +### opam option pre-session-commands --global +[] +### # Check addition & removal on set +### opam option depext-bypass +[] +### opam option 'depext-bypass+=["tempor"]' +Added '["tempor"]' to field depext-bypass in switch var-option +### opam option 'depext-bypass+=["incididunt"]' +Added '["incididunt"]' to field depext-bypass in switch var-option +### opam option depext-bypass +["incididunt" "tempor"] +### opam option 'depext-bypass-=["incididunt"]' +Removed '["incididunt"]' from field depext-bypass in switch var-option +### opam option depext-bypass +["tempor"] +### opam option 'depext-bypass=["incididunt" "tempor"]' +Set to '["incididunt" "tempor"]' the field depext-bypass in switch var-option +### opam option 'depext-bypass-=["tempor"]' +Removed '["tempor"]' from field depext-bypass in switch var-option +### opam option depext-bypass +["incididunt"] +### opam option 'depext-bypass-=["ut"]' +No modification in switch var-option +### opam option depext-bypass +["incididunt"] +### opam option 'depext-bypass-=[]' +No modification in switch var-option +### opam option depext-bypass +["incididunt"] +### opam option depext-bypass= +Reverted field depext-bypass in switch var-option +### opam option 'depext-bypass-=["tempor"]' +No modification in switch var-option +### opam option depext-bypass +[] +### # Check addition & removal on list +### opam option 'setenv+=lorem="labore"' +Added 'lorem="labore"' to field setenv in switch var-option +### opam option 'setenv+=ipsum="dolore"' +Added 'ipsum="dolore"' to field setenv in switch var-option +### opam option setenv +[[ipsum = "dolore"] [lorem = "labore"]] +### opam option 'setenv-=lorem="labore"' +Removed 'lorem="labore"' from field setenv in switch var-option +### opam option setenv +ipsum = "dolore" +### opam option 'setenv=[[lorem="labore"] [ipsum="dolore"]]' +Set to '[[lorem="labore"] [ipsum="dolore"]]' the field setenv in switch var-option +### opam option 'setenv-=lorem="labore"' +Removed 'lorem="labore"' from field setenv in switch var-option +### opam option setenv +ipsum = "dolore" +### opam option 'setenv-=lorem="et"' +No modification in switch var-option +### opam option setenv +ipsum = "dolore" +### opam option setenv= +Reverted field setenv in switch var-option +### opam option 'setenv-=lorem="labore"' +No modification in switch var-option +### opam option setenv +[] +### # Check that eval-variable is removed when a global variable is removed +### opam option global-variables +[[foo "global-foo" "Set through 'opam var'"] [bin "global-bin" "Set through 'opam var'"] [os-version "dolor" "Set through 'opam var'"] [os-family "ipsum" "Set through 'opam var'"] [os-distribution "lorem" "Set through 'opam var'"] [os "linux" "Set through 'opam var'"] [opam-version "68.79" "Set through 'opam var'"] [make "make" "Set through 'opam var'"] [jobs "7" "Set through 'opam var'"] [arch "x86_64" "Set through 'opam var'"] [sys-ocaml-version "4.08.0" "Set through 'opam var'"]] +### opam option 'eval-variables=[dolore ["mania"] "alica"]' +Set to '[dolore ["mania"] "alica"]' the field eval-variables in global configuration +### opam option eval-variables +[dolore ["mania"] "alica"] +### opam var dolore=mania --global +Added '[dolore "mania" "Set through 'opam var'"]' to field global-variables in global configuration +### opam var dolore= --global +Removed variable dolore in global configuration +### opam option eval-variables +[] +### opam option global-variables +[[foo "global-foo" "Set through 'opam var'"] [bin "global-bin" "Set through 'opam var'"] [os-version "dolor" "Set through 'opam var'"] [os-family "ipsum" "Set through 'opam var'"] [os-distribution "lorem" "Set through 'opam var'"] [os "linux" "Set through 'opam var'"] [opam-version "68.79" "Set through 'opam var'"] [make "make" "Set through 'opam var'"] [jobs "7" "Set through 'opam var'"] [arch "x86_64" "Set through 'opam var'"] [sys-ocaml-version "4.08.0" "Set through 'opam var'"]] +### # Check uniable operations +### opam option 'variables+={esse: "cillum"}' +[ERROR] Field variables can't be directly appended to, use `opam var` instead +# Return code 2 # +### opam option 'invariant="inv"' +[ERROR] Field invariant is not modifiable +# Return code 2 # +### opam option 'jobs+=3' +[ERROR] Field jobs can't be appended +# Return code 2 # +### opam option 'jobs-=3' +[ERROR] Field jobs can't be substracted +# Return code 2 # +### opam option bar +[ERROR] No option named 'bar' found. Use 'opam option [--global]' to list them +# Return code 2 # +### opam option bar --global +[ERROR] Field or section bar not found +# Return code 5 # +### opam option bar --switch var-option +[ERROR] Field or section bar not found +# Return code 5 # +### opam option bar=sit +[ERROR] No option named 'bar' found. Use 'opam option [--global]' to list them +# Return code 2 # +### opam option bar=sit --global +[ERROR] There is no option named 'bar'. The allowed options are: +jobs download-command download-jobs solver-criteria solver-upgrade-criteria solver-fixup-criteria best-effort-prefix-criteria solver global-variables eval-variables repository-validation-command depext depext-run-installs depext-cannot-install depext-bypass pre-build-commands pre-install-commands pre-remove-commands pre-session-commands wrap-build-commands wrap-install-commands wrap-remove-commands post-build-commands post-install-commands post-remove-commands post-session-commands +# Return code 2 # +### opam option bar=sit --switch var-option +[ERROR] There is no option named 'bar'. The allowed options are: +synopsis setenv depext-bypass pre-build-commands pre-install-commands pre-remove-commands pre-session-commands wrap-build-commands wrap-install-commands wrap-remove-commands post-build-commands post-install-commands post-remove-commands post-session-commands variables +# Return code 2 # diff -Nru opam-2.0.10/tests/results/install-opt opam-2.1.2/tests/results/install-opt --- opam-2.0.10/tests/results/install-opt 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/results/install-opt 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -Available packages for system: -P1 -- A very useful package -P2 -- An other very useful package -P3 -- Testing version names -P4 -- Testing transitive closure -P5 -- Testing optional dependencies diff -Nru opam-2.0.10/tests/results/install-P1 opam-2.1.2/tests/results/install-P1 --- opam-2.0.10/tests/results/install-P1 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/results/install-P1 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -Available packages for system: -P1 1 A very useful package -P2 -- An other very useful package -P3 -- Testing version names -P4 -- Testing transitive closure -P5 -- Testing optional dependencies diff -Nru opam-2.0.10/tests/results/install-P1-P2-P3-P4 opam-2.1.2/tests/results/install-P1-P2-P3-P4 --- opam-2.0.10/tests/results/install-P1-P2-P3-P4 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/results/install-P1-P2-P3-P4 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -Available packages for system: -P1 1 A very useful package -P2 1 An other very useful package -P3 1~weird-version.test Testing version names -P4 1 Testing transitive closure -P5 -- Testing optional dependencies diff -Nru opam-2.0.10/tests/results/install-remove-P1 opam-2.1.2/tests/results/install-remove-P1 --- opam-2.0.10/tests/results/install-remove-P1 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/results/install-remove-P1 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -Available packages for system: -P1 -- A very useful package -P2 -- An other very useful package -P3 -- Testing version names -P4 -- Testing transitive closure -P5 -- Testing optional dependencies diff -Nru opam-2.0.10/tests/results/install-upgrade-P2 opam-2.1.2/tests/results/install-upgrade-P2 --- opam-2.0.10/tests/results/install-upgrade-P2 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/results/install-upgrade-P2 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -Available packages for system: -P1 1 A very useful package -P2 1 An other very useful package -P3 1~weird-version.test Testing version names -P4 1 Testing transitive closure -P5 -- Testing optional dependencies diff -Nru opam-2.0.10/tests/results/README.tests opam-2.1.2/tests/results/README.tests --- opam-2.0.10/tests/results/README.tests 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/results/README.tests 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ - -# All tests are performed in a clean repository - -Initial state of the repository - - Available packages for system: - P1 -- A very useful package - P2 -- An other very useful package - P3 -- Testing version names - P4 -- Testing transitive closure - P5 -- Testing optional dependencies - - -* install-P1 - install P1 - -* install-P1-P2-P3-P4 - install P1, P2, P3, P4 - -* install-remove-P1 - install P1 and then remove P1 - -* install-upgrade-P2 - install P4 and then upgrade P2 - -* reinstall-P2 - install P2 and the re-install P2 - -* install_opt - install P5 , install P2, remove P5, remove P2, remove P1 diff -Nru opam-2.0.10/tests/results/reinstall-P2 opam-2.1.2/tests/results/reinstall-P2 --- opam-2.0.10/tests/results/reinstall-P2 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/results/reinstall-P2 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -Available packages for system: -P1 1 A very useful package -P2 1 An other very useful package -P3 -- Testing version names -P4 -- Testing transitive closure -P5 -- Testing optional dependencies diff -Nru opam-2.0.10/tests/tests.py opam-2.1.2/tests/tests.py --- opam-2.0.10/tests/tests.py 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/tests.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,152 +0,0 @@ -#!/usr/bin/python - -import unittest -from subprocess import Popen, STDOUT, PIPE -from subprocess import call -import uuid -import os,sys,time -import argparse -import difflib - -tmpdir = "/tmp/OPAM.UNITTEST" -results = "results" -opamcmd = "/tmp/OPAM.BIN/opam --yes --root /tmp/OPAM.ROOT" - -verbose=0 -FNULL=open(os.devnull, "w") - -def diff(fromfile,tofile,verbose=0): - fromdate = time.ctime(os.stat(fromfile).st_mtime) - todate = time.ctime(os.stat(tofile).st_mtime) - fromlines = open(fromfile, 'U').readlines() - tolines = open(tofile, 'U').readlines() - - diff = difflib.unified_diff(fromlines, tolines, fromfile, tofile)#, fromdate, todate) - l = list(diff) - - if l : - print "File Differences : " - print ''.join(l) - return False - else : - return True - -def opam(cmd, diffile=None,verbose=0): - # first we exectute the action, then - # we compare the output of list with - # the diffile - env = os.environ.copy() - env['PATH'] = ':'.join(['/tmp/OPAM.BIN',env['PATH']]) - env['OPAM_ROOT'] = '/tmp/OPAM.ROOT' - cmd = opamcmd.split() + cmd - if verbose > 1 : - print "Env\nPATH=%s\nOPAM_ROOT=%s" % (env['PATH'],env['OPAM_ROOT']) - print "CMD=%s" % (' '.join(cmd)) - if verbose <= 1 : - call(cmd, stdout=FNULL, env=env) - else : - call(cmd, env=env) - - if diffile : - f = "%s/%s.opamtest" % (tmpdir,uuid.uuid1()) - output = open(f,'w') - - call(opamcmd.split() + ["list"], stdout=output, env=env) - - output.close() - d = diff(f,diffile,verbose) - os.remove(f) - return d - - return None - -def load_scenario(scenario,verbose=0): - if verbose > 0 : - print "Loading scenario %d" % scenario - if verbose <= 1 : - call(["./init-repo.sh", "-s", str(scenario)],stdout=FNULL) - else : - call(["./init-repo.sh", "-s", str(scenario)]) - -class OpamTests(unittest.TestCase): - - def setUp(self): - if verbose > 0 : print "\nsetting up repository" - call(["./init-repo.sh", "-i"], stdout=FNULL) - if not os.path.exists(tmpdir): - os.makedirs(tmpdir) - - def tearDown(self): - if verbose > 0 : print "tearing down repository" - call(["./init-repo.sh", "-c"], stdout=FNULL) - - def test_install(self): - load_scenario(1,verbose) - diffile="%s/install-P1" % results - d = opam(["install", "P1"],diffile,verbose) - self.assertTrue(d) - - def test_install_many(self): - load_scenario(1,verbose) - diffile="%s/install-P1-P2-P3-P4" % results - opam(["install", "P1"]) - opam(["install", "P2"]) - opam(["install", "P3"]) - d = opam(["install", "P4"], diffile,verbose) - self.assertTrue(d) - - def test_remove(self): - load_scenario(1,verbose) - diffile="%s/install-remove-P1" % results - opam(["install", "P1"]) - d = opam(["remove", "P1"],diffile,verbose) - self.assertTrue(d) - - def test_upgrade(self): - load_scenario(1,verbose) - diffile="%s/install-upgrade-P2" % results - opam(["install", "P4"]) - d = opam(["upgrade", "P2"],diffile,verbose) - self.assertTrue(d) - -# @unittest.skip("skipping") - def test_reinstall(self): - load_scenario(1,verbose) - diffile="%s/reinstall-P2" % results - opam(["install", "P2"]) - d = opam(["reinstall", "P2"],diffile,verbose) - self.assertTrue(d) - - def test_install_opt(self): - load_scenario(1,verbose) - load_scenario(2,verbose) - diffile="%s/install-opt" % results - opam(["install", "P5"]) - opam(["install", "P2"]) - opam(["remove", "P5"]) - opam(["remove", "P2"]) - d = opam(["remove", "P1"],diffile,verbose) - self.assertTrue(d) - -def main(): - global verbose - - parser = argparse.ArgumentParser(description='description of you program') - parser.add_argument('-v', '--verbose', action='store_true') - parser.add_argument('-d', '--debug', action='store_true') - args = parser.parse_args() - - verbosity=0 - if args.verbose == True : - verbose = 1 - verbosity=2 - - if args.debug == True : - verbose = 2 - - suite = unittest.TestLoader().loadTestsFromTestCase(OpamTests) - unittest.TextTestRunner(verbosity=verbosity).run(suite) - -if __name__ == '__main__': - main() - diff -Nru opam-2.0.10/tests/test-TEST.sh opam-2.1.2/tests/test-TEST.sh --- opam-2.0.10/tests/test-TEST.sh 2021-10-29 15:42:26.000000000 +0000 +++ opam-2.1.2/tests/test-TEST.sh 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -#!/bin/sh - -if [ -f $1 ]; then - . $1 -fi - -if [ x${TEST} != x$2 ]; then - echo "Error: TEST=${TEST} instead of $2" - exit 2 -fi