diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..5a88fc4 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,25 @@ +repos: + - repo: https://github.com/pre-commit/pre-commit-hooks + rev: v5.0.0 + hooks: + - id: trailing-whitespace + exclude: '^(third-party|licenses)/.*$' + - id: end-of-file-fixer + exclude: '^(third-party|licenses)/.*$' + - id: check-merge-conflict + exclude: '^(third-party|licenses)/.*$' + - id: check-case-conflict + exclude: '^(third-party|licenses)/.*$' + - repo: https://github.com/pre-commit/mirrors-clang-format + rev: v19.1.5 + hooks: + - id: clang-format + 'types_or': [c++, c] + exclude: '^third-party/.*$' + +ci: + autofix_commit_msg: "[pre-commit.ci] Auto fixes from pre-commit.com hooks." + autofix_prs: true + autoupdate_commit_msg: "[pre-commit.ci] Pre-commit autoupdate." + autoupdate_schedule: quarterly + submodules: false diff --git a/bootstrap.ps1 b/bootstrap.ps1 index 98decdc..f4d6e1b 100644 --- a/bootstrap.ps1 +++ b/bootstrap.ps1 @@ -13,7 +13,7 @@ if (!$root) { $output = if ($Verbose) { "Out-Default" } else { "Out-Null" } -cmake -B $root/build $root | & $output +cmake -B $root/build -G "Visual Studio 17 2022" $root | & $output $installationPath = & $root\third-party\bin\vswhere.exe -products * -nologo -prerelease -latest -property installationPath if (! $?) { diff --git a/licenses/7zip.txt b/licenses/7zip.txt index 56f6574..47fc237 100644 --- a/licenses/7zip.txt +++ b/licenses/7zip.txt @@ -1,27 +1,28 @@ - 7-Zip - ~~~~~ + 7-Zip source code + ~~~~~~~~~~~~~~~~~ License for use and distribution ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - 7-Zip Copyright (C) 1999-2023 Igor Pavlov. + 7-Zip Copyright (C) 1999-2024 Igor Pavlov. The licenses for files are: - 1) 7z.dll: - - The "GNU LGPL" as main license for most of the code - - The "GNU LGPL" with "unRAR license restriction" for some code - - The "BSD 3-clause License" for some code - 2) All other files: the "GNU LGPL". + - CPP/7zip/Compress/Rar* files: the "GNU LGPL" with "unRAR license restriction" + - CPP/7zip/Compress/LzfseDecoder.cpp: the "BSD 3-clause License" + - C/ZstdDec.c: the "BSD 3-clause License" + - C/Xxh64.c: the "BSD 2-clause License" + - Some files are "public domain" files, if "public domain" status is stated in source file. + - the "GNU LGPL" for all other files. If there is no license information in + some source file, that file is under the "GNU LGPL". - Redistributions in binary form must reproduce related license information from this file. - - Note: - You can use 7-Zip on any computer, including a computer in a commercial - organization. You don't need to register or pay for 7-Zip. + The "GNU LGPL" with "unRAR license restriction" means that you must follow both + "GNU LGPL" rules and "unRAR license restriction" rules. - GNU LGPL information - -------------------- + + +GNU LGPL information +-------------------- This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public @@ -33,58 +34,118 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - You can receive a copy of the GNU Lesser General Public License from + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, + you can get a copy of the GNU Lesser General Public License from http://www.gnu.org/ - BSD 3-clause License - -------------------- +BSD 3-clause License in 7-Zip code +---------------------------------- - The "BSD 3-clause License" is used for the code in 7z.dll that implements LZFSE data decompression. - That code was derived from the code in the "LZFSE compression library" developed by Apple Inc, - that also uses the "BSD 3-clause License": + The "BSD 3-clause License" is used for the following code in 7z.dll + 1) LZFSE data decompression. + CPP/7zip/Compress/LzfseDecoder.cpp. + That code was derived from the code in the "LZFSE compression library" developed by Apple Inc, + that also uses the "BSD 3-clause License". + 2) ZSTD data decompression. + C/ZstdDec.c + that code was developed using original zstd decoder code as reference code. + The original zstd decoder code was developed by Facebook Inc, + that also uses the "BSD 3-clause License". - ---- - Copyright (c) 2015-2016, Apple Inc. All rights reserved. + Copyright (c) 2015-2016, Apple Inc. All rights reserved. + Copyright (c) Facebook, Inc. All rights reserved. + Copyright (c) 2023-2024 Igor Pavlov. - Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +Text of the "BSD 3-clause License" +---------------------------------- - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer - in the documentation and/or other materials provided with the distribution. +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. - 3. Neither the name of the copyright holder(s) nor the names of any contributors may be used to endorse or promote products derived - from this software without specific prior written permission. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ---- +3. Neither the name of the copyright holder nor the names of its contributors may + be used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- - unRAR license restriction - ------------------------- +BSD 2-clause License in 7-Zip code +---------------------------------- - The decompression engine for RAR archives was developed using source - code of unRAR program. - All copyrights to original unRAR code are owned by Alexander Roshal. + The "BSD 2-clause License" is used for the XXH64 code in 7-Zip. + C/Xxh64.c - The license for original unRAR code has the following restriction: + XXH64 code in 7-Zip was derived from the original XXH64 code developed by Yann Collet. - The unRAR sources cannot be used to re-create the RAR compression algorithm, - which is proprietary. Distribution of modified unRAR sources in separate form - or as a part of other software is permitted, provided that it is clearly - stated in the documentation and source comments that the code may - not be used to develop a RAR (WinRAR) compatible archiver. + Copyright (c) 2012-2021 Yann Collet. + Copyright (c) 2023-2024 Igor Pavlov. + +Text of the "BSD 2-clause License" +---------------------------------- + +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- - -- - Igor Pavlov + + +unRAR license restriction +------------------------- + +The decompression engine for RAR archives was developed using source +code of unRAR program. +All copyrights to original unRAR code are owned by Alexander Roshal. + +The license for original unRAR code has the following restriction: + + The unRAR sources cannot be used to re-create the RAR compression algorithm, + which is proprietary. Distribution of modified unRAR sources in separate form + or as a part of other software is permitted, provided that it is clearly + stated in the documentation and source comments that the code may + not be used to develop a RAR (WinRAR) compatible archiver. + +-- diff --git a/licenses/fmt.txt b/licenses/fmt.txt index f0ec3db..1cd1ef9 100644 --- a/licenses/fmt.txt +++ b/licenses/fmt.txt @@ -1,4 +1,4 @@ -Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2012 - present, Victor Zverovich and {fmt} contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/licenses/python.txt b/licenses/python.txt index f42f8ad..f26bcf4 100644 --- a/licenses/python.txt +++ b/licenses/python.txt @@ -2,12 +2,12 @@ A. HISTORY OF THE SOFTWARE ========================== Python was created in the early 1990s by Guido van Rossum at Stichting -Mathematisch Centrum (CWI, see http://www.cwi.nl) in the Netherlands +Mathematisch Centrum (CWI, see https://www.cwi.nl) in the Netherlands as a successor of a language called ABC. Guido remains Python's principal author, although it includes many contributions from others. In 1995, Guido continued his work on Python at the Corporation for -National Research Initiatives (CNRI, see http://www.cnri.reston.va.us) +National Research Initiatives (CNRI, see https://www.cnri.reston.va.us) in Reston, Virginia where he released several versions of the software. @@ -19,7 +19,7 @@ https://www.python.org/psf/) was formed, a non-profit organization created specifically to own Python-related Intellectual Property. Zope Corporation was a sponsoring member of the PSF. -All Python releases are Open Source (see http://www.opensource.org for +All Python releases are Open Source (see https://opensource.org for the Open Source Definition). Historically, most, but not all, Python releases have also been GPL-compatible; the table below summarizes the various releases. @@ -84,7 +84,7 @@ analyze, test, perform and/or display publicly, prepare derivative works, distribute, and otherwise use Python alone or in any derivative version, provided, however, that PSF's License Agreement and PSF's notice of copyright, i.e., "Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, -2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020 Python Software Foundation; +2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022, 2023 Python Software Foundation; All Rights Reserved" are retained in Python alone or in any derivative version prepared by Licensee. @@ -191,9 +191,9 @@ version prepared by Licensee. Alternately, in lieu of CNRI's License Agreement, Licensee may substitute the following text (omitting the quotes): "Python 1.6.1 is made available subject to the terms and conditions in CNRI's License Agreement. This Agreement together with -Python 1.6.1 may be located on the Internet using the following +Python 1.6.1 may be located on the internet using the following unique, persistent identifier (known as a handle): 1895.22/1013. This -Agreement may also be obtained from a proxy server on the Internet +Agreement may also be obtained from a proxy server on the internet using the following URL: http://hdl.handle.net/1895.22/1013". 3. In the event Licensee prepares a derivative work that is based on diff --git a/licenses/sip.txt b/licenses/sip.txt index 9406c30..0e4caa6 100644 --- a/licenses/sip.txt +++ b/licenses/sip.txt @@ -1,49 +1,22 @@ -RIVERBANK COMPUTING LIMITED LICENSE AGREEMENT FOR SIP +Copyright 2024 Phil Thompson -1. This LICENSE AGREEMENT ("the SIP License") is between Riverbank Computing -Limited ("Riverbank"), and the Individual or Organization ("Licensee") -accessing and otherwise using SIP software in source or binary form and its -associated documentation. SIP comprises a software tool for generating Python -bindings for software C and C++ libraries, and a Python extension module used -at runtime by those generated bindings. This License Agreement may also be -applied to other software packages written by Riverbank. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: -2. Subject to the terms and conditions of this License Agreement, Riverbank -hereby grants Licensee a nonexclusive, royalty-free, world-wide license to -reproduce, analyze, test, perform and/or display publicly, prepare derivative -works, distribute, and otherwise use SIP alone or in any derivative version, -provided, however, that Riverbank's License Agreement and Riverbank's notice of -copyright, e.g., "Copyright (c) 2015 Riverbank Computing Limited; All Rights -Reserved" are retained in SIP alone or in any derivative version prepared by -Licensee. +1. Redistributions of source code must retain the above copyright notice, this +list of conditions and the following disclaimer. -3. In the event Licensee prepares a derivative work that is based on or -incorporates SIP or any part thereof, and wants to make the derivative work -available to others as provided herein, then Licensee hereby agrees to include -in any such work a brief summary of the changes made to SIP. +2. Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. -4. Licensee may not use SIP to generate Python bindings for any C or C++ -library for which bindings are already provided by Riverbank. - -5. Riverbank is making SIP available to Licensee on an "AS IS" basis. -RIVERBANK MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED. BY WAY -OF EXAMPLE, BUT NOT LIMITATION, RIVERBANK MAKES NO AND DISCLAIMS ANY -REPRESENTATION OR WARRANTY OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR -PURPOSE OR THAT THE USE OF SIP WILL NOT INFRINGE ANY THIRD PARTY RIGHTS. - -6. RIVERBANK SHALL NOT BE LIABLE TO LICENSEE OR ANY OTHER USERS OF SIP FOR ANY -INCIDENTAL, SPECIAL, OR CONSEQUENTIAL DAMAGES OR LOSS AS A RESULT OF MODIFYING, -DISTRIBUTING, OR OTHERWISE USING SIP, OR ANY DERIVATIVE THEREOF, EVEN IF -ADVISED OF THE POSSIBILITY THEREOF. - -7. This License Agreement will automatically terminate upon a material breach -of its terms and conditions. - -8. Nothing in this License Agreement shall be deemed to create any relationship -of agency, partnership, or joint venture between Riverbank and Licensee. This -License Agreement does not grant permission to use Riverbank trademarks or -trade name in a trademark sense to endorse or promote products or services of -Licensee, or any third party. - -9. By copying, installing or otherwise using SIP, Licensee agrees to be bound -by the terms and conditions of this License Agreement. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/licenses/spdlog.txt b/licenses/spdlog.txt index c15941b..6546da0 100644 --- a/licenses/spdlog.txt +++ b/licenses/spdlog.txt @@ -22,5 +22,5 @@ THE SOFTWARE. -- NOTE: Third party dependency used by this software -- This software depends on the fmt lib (MIT License), -and users must comply to its license: https://github.com/fmtlib/fmt/blob/master/LICENSE.rst +and users must comply to its license: https://raw.githubusercontent.com/fmtlib/fmt/master/LICENSE diff --git a/licenses/zlib.txt b/licenses/zlib.txt index 51c2378..ab8ee6f 100644 --- a/licenses/zlib.txt +++ b/licenses/zlib.txt @@ -1,4 +1,6 @@ - (C) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright notice: + + (C) 1995-2022 Jean-loup Gailly and Mark Adler This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages diff --git a/mob.ini b/mob.ini index 0f81c0d..44b1887 100644 --- a/mob.ini +++ b/mob.ini @@ -51,6 +51,7 @@ git_shallow = false [usvfs:task] git_shallow = false +configuration = Release [installer:task] enabled = false @@ -88,40 +89,14 @@ force = false configure = true pull = true -[prebuilt] -boost = true -lz4 = true -openssl = true -pyqt = true -python = true - [versions] vs = 17 vs_year = 2022 vs_toolset = 14.3 -sdk = 10.0.22621.0 -sevenz = 24.05 -boost = 1.85.0 -boost_vs = 14.3 -fmt = 10.2.1 -gtest = main -directxtex = main -libloot = 0.23.0 -lz4 = v1.9.4 -nmm = 0.71.2 -openssl = 3.3.0 +sdk = 10.0.26100.0 pyqt = 6.7.1 -pyqt_builder = 1.16.4 -pyqt_sip = 13.8.0 -python = v3.12.3 -pybind11 = v2.12.0 -bzip2 = 1.0.8 -sip = 6.8.6 -spdlog = v1.14.1 -qt = 6.7.1 -qt_vs = 2019 -zlib = v1.3.1 -libbsarch = 0.0.12 +qt = 6.7.3 +qt_vs = 2022 usvfs = master explorerpp = 1.4.0 ss_paper_lad_6788 = 7.2 @@ -134,17 +109,11 @@ ss_starfield_trosski = V1.11 ss_fallout3_trosski = v1.11 ss_fallout4_trosski = v1.11 -[build-types] -libbsarch = release -pyqt = release -python = release - [paths] third_party = prefix = cache = icons = -patches = licenses = build = install = @@ -153,12 +122,11 @@ install_installer = install_libs = install_pdbs = install_dlls = -install_loot = install_plugins = install_extensions = install_stylesheets = install_licenses = -install_pythoncore = +install_translations = vs = vcpkg = qt_install = diff --git a/patches/pyqt/builder.py.manual_patch b/patches/pyqt/builder.py.manual_patch deleted file mode 100644 index e2f536a..0000000 --- a/patches/pyqt/builder.py.manual_patch +++ /dev/null @@ -1,36 +0,0 @@ ---- builder.py (date 1575938575861) -+++ builder.py (date 1575938575861) -@@ -402,17 +402,29 @@ - 'QMAKE_LFLAGS += -Wl,--version-script={}.exp'.format( - buildable.target)) - -- pro_lines.append( -+ if project.py_platform == 'win32': -+ for include_dir in project.py_include_dir.split(';'): -+ pro_lines.append( -+ 'INCLUDEPATH += {}'.format( -+ self.qmake_quote(include_dir))) -+ else: -+ pro_lines.append( - 'INCLUDEPATH += {}'.format( -- self.qmake_quote(project.py_include_dir))) -+ self.qmake_quote(project.py_include_dir))) - -+ if project.py_pylib_shlib != "": -+ pro_lines.append( -+ 'DEFINES += {}'.format( -+ self.qmake_quote("PYTHON_LIB=\"{}\"".format( -+ project.py_pylib_shlib)))) -+ - # Python.h on Windows seems to embed the need for pythonXY.lib, so tell - # it where it is. - # TODO: is this still necessary for Python v3.8? - if not buildable.static: - pro_lines.extend(['win32 {', -- ' LIBS += -L{}'.format( -- self.qmake_quote(project.py_pylib_dir)), -+ ' LIBS += -L{}{}'.format( -+ self.qmake_quote(project.py_pylib_dir), '' if project.py_pylib_lib == '' else ' -l' + project.py_pylib_lib), - '}']) - - # Add any installables from the buildable diff --git a/patches/python/sources/openssl.props.patch b/patches/python/sources/openssl.props.patch deleted file mode 100644 index d60f6cb..0000000 --- a/patches/python/sources/openssl.props.patch +++ /dev/null @@ -1,10 +0,0 @@ ---- PCbuild/openssl.props (revision 5c02a39a0b31a330e06b4d6f44835afb205dc7cc) -+++ PCbuild/openssl.props (date 1575994670797) -@@ -13,6 +13,7 @@ - <_DLLSuffix>-3 - <_DLLSuffix Condition="$(Platform) == 'ARM'">$(_DLLSuffix)-arm - <_DLLSuffix Condition="$(Platform) == 'ARM64'">$(_DLLSuffix)-arm64 -+ <_DLLSuffix Condition="$(Platform) == 'x64'">$(_DLLSuffix)-x64 - $(_DLLSuffix) - - diff --git a/readme.md b/readme.md index 99ecc0f..54f46cb 100644 --- a/readme.md +++ b/readme.md @@ -1,63 +1,69 @@ +# `mob` - ModOrganizer Build Tool + +This README explains how to use `mob` and build ModOrganizer2 with it. +If you want to contribute to a single plugin of MO2 or even write your own plugins, +check the [ModOrganizer2 Wiki](https://github.com/ModOrganizer2/modorganizer/wiki). + ## Table of contents - [Quick start](#quick-start) -- [Slow start](#slow-start) +- [Extended start](#extended-start) - [Changing options](#changing-options) - * [INI files](#ini-files) - * [Command line](#command-line) - * [INI format](#ini-format) + - [INI files](#override-options-using-ini-files) + - [Command line](#override-options-using-command-line) + - [INI format](#ini-format) - [Options](#options) - * [`[global]`](#global) - * [`[task]`](#task) - * [`[tools]`](#tools) - * [`[prebuilt]`](#prebuilt) - * [`[versions]`](#versions) - * [`[paths]`](#paths) -- [Command line](#command-line-1) - * [Global options](#global-options) - * [`build`](#build) - * [`list`](#list) - * [`options`](#options) - * [`release`](#release) - * [`git`](#git) - * [`cmake`](#cmake) - * [`inis`](#inis) - + - [`[global]`](#global) + - [`[task]`](#task) + - [`[tools]`](#tools) + - [`[versions]`](#versions) + - [`[paths]`](#paths) +- [Command line](#command-line) + - [Global options](#global-options) + - [`build`](#build) + - [`list`](#list) + - [`options`](#options) + - [`release`](#release) + - [`git`](#git) + - [`cmake-config`](#cmake-config) + - [`inis`](#inis) ## Quick start + ```powershell -> git clone https://github.com/ModOrganizer2/mob -> cd mob -> bootstrap -> mob -d c:\somewhere build +git clone https://github.com/ModOrganizer2/mob +cd mob +./bootstrap +mob -d c:\somewhere build ``` ## Extended start -### Qt - CLI based install using [aqt](https://github.com/miurahr/aqtinstall) +### Qt Installation -[aqt](https://github.com/miurahr/aqtinstall) is a CLI installer for Qt, it makes installing Qt extremely quick and painless, and doesn't require a login +Check [`mob.ini`](https://github.com/ModOrganizer2/mob/blob/master/mob.ini#L94) to find +out which version of Qt you need. -#### Installing aqt via package manager -- Start an administrative terminal -- Install aqt using one of the two popular packaage managers - - `winget install --id=miurahr.aqtinstall -e` - - `choco install aqt` +#### CLI based install using [aqt](https://github.com/miurahr/aqtinstall) -#### Installing aqt via python/pip +[aqt](https://github.com/miurahr/aqtinstall) is a CLI installer for Qt, it makes +installing Qt extremely quick and painless, and doesn't require a login. +Check the [documentation](https://aqtinstall.readthedocs.io/en/latest/installation.html) +to install **aqt** itself. -- Install [Python 3.9](https://www.python.org/downloads/release/python-390/) -- Install [aqt](https://github.com/miurahr/aqtinstall) using `pip` +When using **aqt**, you can choose which modules to install but we recommend installing +all of them in case of changes. -#### Using aqt -- Open a terminal with administrative rights, and run ```powershell -aqt install-qt --outputdir "C:\Qt" windows desktop 6.7.0 win64_msvc2019_64 -m qtwebengine qtimageformats qtpositioning qtserialport qtwebchannel qtwebsockets +# you can also use -m all to install all modules +aqt install-qt --outputdir "C:\Qt" windows desktop ${QT_VERSION} win64_msvc2022_64 ` + -m qtwebengine qtimageformats qtpositioning qtserialport qtwebchannel qtwebsockets ``` -### Qt - Manual installation -- Install Qt 6.7.0 ([Installer](https://download.qt.io/official_releases/online_installers/qt-unified-windows-x64-online.exe)) and select these components: - - MSVC 2019 64-bit +#### Manual installation + +- Install Qt ([Installer](https://download.qt.io/official_releases/online_installers/qt-unified-windows-x64-online.exe)) and select these components: + - MSVC 2022 64-bit - Additional Libraries: - Qt WebEngine (display nexus pages) - Qt Image Formats (display images in image tab and preview) @@ -70,12 +76,15 @@ aqt install-qt --outputdir "C:\Qt" windows desktop 6.7.0 win64_msvc2019_64 -m qt - Qt Debug Files ### Visual Studio + - Install Visual Studio 2022 ([Installer](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=Community&channel=Release&version=VS2022&source=VSLandingPage&cid=2030&passive=false)) + - Desktop development with C++ - Desktop .NET desktop development (needed by OMOD and FOMOD installers) - Individual Components: - .Net Framework 4.8 SDK - - .Net Framework 4.7.2 targeting pack (OMOD targets 4.8 but VS still requires the package for other .Net components) + - .Net Framework 4.7.2 targeting pack (OMOD targets 4.8 but VS still requires + the package for other .Net components) - Windows Universal C Runtime - C++ ATL for latest v143 build Tools (x86 & x64) - C++ /CLI support for v143 build Tools (Latest) (for OMOD and FOMOD installers) @@ -85,44 +94,62 @@ aqt install-qt --outputdir "C:\Qt" windows desktop 6.7.0 win64_msvc2019_64 -m qt - CMake tools for Windows (Skip if you have this already installed outside of the VS installer) ## Setting up MOB -- Open a shell. If you've installed Git or CMake through the VS installer, they won't be on your PATH, so you must use x64 Native Tools Command Prompt for VS 2022.- Pick a folder to develop MO2 in, for these steps it'll be `C:\dev` -- Execute these commands + ```powershell mkdir C:\dev cd C:\dev + +# close this repository git clone https://github.com/ModOrganizer2/mob + +# build mob itself - this will create mob.exe in the current directory ./bootstrap.ps1 + +# build MO2 itself - this will take a LONG time mob -d C:\dev\modorganizer build ``` -- Once `mob` is finished, everything will be in `C:\dev\modorganizer`. Mod Organizer can be run from `install\bin\ModOrganizer.exe`. The Visual Studio solution for Mod Organizer itself is `build\modorganizer_super\modorganizer\vsbuild\organizer.sln`. - -### Prebuilts -Some third-parties are not normally built from source, but this can be changed in the options by setting the various tasks in `[prebuilt]` to `false`. If OpenSSL is built from source, perl is required ([Strawberry Perl 5.30.2.1 installer](http://strawberryperl.com/download/5.30.2.1/strawberry-perl-5.30.2.1-64bit.msi)). +Once `mob` is finished, everything will be in `C:\dev\modorganizer`. +Mod Organizer can be run from `install\bin\ModOrganizer.exe`. +The Visual Studio solution for Mod Organizer itself is `build\modorganizer_super\modorganizer\vsbuild\organizer.sln`. ## Changing options -`mob` has two ways of setting options: from INI files, the `MOBINI` environment variable, or from the command line. -### INI files -`mob` builds a list of available INI files in order of priority. Higher numbers override lower numbers: +`mob` has two ways of setting options: from INI files, the `MOBINI` environment +variable, or from the command line. + +### OVerride options using INI files + +`mob` builds a list of available INI files in order of priority. Higher numbers +override lower numbers: 1) The master INI `mob.ini` in the directory where `mob.exe` lives (required). 2) Any files set in `MOBINI` (separated by semicolons). 3) Another `mob.ini` in the current directory. 4) Files given with `--ini`. -Use `mob inis` to see the list of INI files in order. If `--no-default-inis` is given, `mob` will skip 1) and 2). The first INI it finds after that is considered the master. +Use `mob inis` to see the list of INI files in order. If `--no-default-inis` is given, +`mob` will skip 1) and 2). The first INI it finds after that is considered the master. -### Command line -Any option can be overridden from the command like with `-s task:section/key=value`, where `task:` is optional. Some options have shortcuts, such as `--dry` for `-s global/dry=true` and `-l5` for `-s global:output_log_level=5`. See `mob options` for the list of options. +### Override options using command line + +Any option can be overridden from the command like with `-s task:section/key=value`, +where `task:` is optional. Some options have shortcuts, such as `--dry` +for `-s global/dry=true` and `-l5` for `-s global:output_log_level=5`. +See `mob options` for the list of options. ### INI format -Inside the INI file are `[sections]` and `key = value` pairs. The `[task]` section is special because it can be changed for specific tasks instead of globally. Any value under a `[task_name:task]` section will only apply to a task named `task_name`. The list of available tasks can be seen with `mob list`. See [Task names](#task-names). +Inside the INI file are `[sections]` and `key = value` pairs. The `[task]` section is +special because it can be changed for specific tasks instead of globally. +Any value under a `[task_name:task]` section will only apply to a task +named `task_name`. The list of available tasks can be seen with `mob list`. +See [Task names](#task-names). ## Options ### `[global]` + | Option | Type | Description | | --- | --- | --- | | `dry` | bool | Whether filesystem operations are simulated. Note that many operations will fail and that the build process will most probably not complete. This is mostly useful to get a dump of the options. | @@ -133,12 +160,13 @@ Inside the INI file are `[sections]` and `key = value` pairs. The `[task]` secti | `clean_task` | bool | For `build`, whether tasks are cleaned. | | `fetch_task` | bool | For `build`, whether tasks are fetched (download, git, etc.) | | `build_task` | bool | For `build`, whether tasks are built (msbuild, jobm etc.) | -| `output_log_level` | [0-6]| The log level for stdout: 0=silent, 1=errors, 2=warnings, 3=info (default), 4=debug, 5=trace, 6=dump. Note that 6 will dump _a lot_ of stuff, such as debug information from curl during downloads. +| `output_log_level` | [0-6]| The log level for stdout: 0=silent, 1=errors, 2=warnings, 3=info (default), 4=debug, 5=trace, 6=dump. Note that 6 will dump _a lot_ of stuff, such as debug information from curl during downloads. | | `file_log_level` | [0-6]| The log level for the log file. | | `log_file` | path | The path to a log file. | | `ignore_uncommitted` | bool | When `--redownload` or `--reextract` is given, directories controlled by git will be deleted even if they contain uncommitted changes.| ### `[task]` + Options for individual tasks. Can be `[task_name:task]`, where `task_name` is the name of a task (see `mob list`) , `super` for all MO tasks or a glob like `installer_*`. | Option | Type | Description | @@ -147,20 +175,21 @@ Options for individual tasks. Can be `[task_name:task]`, where `task_name` is th | `configuration` | enum | Which configuration to build, should be one of Debug, Release or RelWithDebInfo with RelWithDebInfo being the default.| #### Common git options + Unless otherwise stated, applies to any task that is a git repo. | Option | Type | Description | | --- | --- | --- | | `mo_org` | string | The organisation name when pulling from Github. Only applies to ModOrganizer projects, plus NCC and usvfs. | | `mo_branch` | string | The branch name when pulling from Github. Only applies to ModOrganizer projects, plus NCC and usvfs. | -| `mo_master` | string | The fallback branch name when pulling from Github. Only applies to ModOrganizer projects, plus NCC and usvfs. -This branch is used when `mo_branch` does not exists. If this value is empty, the fallback mechanism is disabled (default behavior). | +| `mo_master` | string | The fallback branch name when pulling from Github. Only applies to ModOrganizer projects, plus NCC and usvfs. This branch is used when `mo_branch` does not exists. If this value is empty, the fallback mechanism is disabled (default behavior). | | `no_pull` | bool | If a repo is already cloned, a `git pull` will be done on it every time `mob build` is run. Set to `false` to never pull and build with whatever is in there. | | `ignore_ts` | bool | Marks all the `.ts` files in a repo with `--assume-unchanged`. Note that `mob git ignore-ts off` can be used to revert it. | | `git_url_prefix` | string | When cloning a repo, the URL will be `$(git_url_prefix)mo_org/repo.git`. | | `git_shallow` | bool | When true, clones with `--depth 1` to avoid having to fetch all the history. Defaults to true for third-parties. | #### Git credentials + These are used to set `user.name` and `user.email`. Applies to any task that is a git repo. | Option | Type | Description | @@ -169,7 +198,9 @@ These are used to set `user.name` and `user.email`. Applies to any task that is | `git_email` | string | Email | #### Origin and upstream remotes + When `git` clones, it automatically creates a remote named `origin` for the URL that was cloned. When `set_origin_remote` is `true`, this will: + 1) Rename `origin` to `upstream`, and 2) Create a new `origin` with the given parameters. @@ -183,7 +214,7 @@ When `git` clones, it automatically creates a remote named `origin` for the URL For example, this will create an `origin` remote for all ModOrganizer2 projects and mark all `.ts` files as `--assume-unchanged`. -``` +```ini [super:task] ignore_ts = true git_username = isanae @@ -196,21 +227,22 @@ remote_push_default_origin = true ``` ### `[tools]` + The various tools in this section are used verbatim when creating processes and so will be looked in the `PATH` environment variable. `vcvars` is best left empty, it will be found using the `vswhere.exe` that's bundled as a third-party. -### `[prebuilt]` -Some tasks can use prebuilt binaries instead of building from source. - ### `[versions]` + The versions for all the tasks. ### `[paths]` + The only path that's required is `prefix`, which is where `mob` will put everything. Within this directory will be `build/`, `downloads/` and `install/`. Everything else is derived from it. -If `mob` is unable to find the Qt installation directory, it can be specified in `qt_install`. This directory should contain `bin/`, `include/`, etc. It's typically something like `C:\Qt\6.5.1\msvc2019_64\`. The other path `qt_bin` will be derived from it, it's just `$qt_install/bin/`. - +If `mob` is unable to find the Qt installation directory, it can be specified in `qt_install`. This directory should contain `bin/`, `include/`, etc. +It's typically something like `C:\Qt\6.7.3\msvc2022_64\`. The other path `qt_bin` will be derived from it, it's just `$qt_install/bin/`. ## Command line + Do `mob --help` for global options and the list of available commands. Do `mob --help` for more help about a command. To use global options with command options, ensure command options are together, with no global options in the middle. @@ -219,15 +251,15 @@ To use global options with command options, ensure command options are together, | Option | Description | | --- | --- | -| `--ini` | Adds an INI file, see [INI files](#ini-files). | +| `--ini` | Adds an INI file, see [INI files](#override-options-using-ini-files). | | `--dry` | Simulates filesystem operations. Note that many operations will fail and that the build process will stop with errors. This is mostly useful to get a dump of the options. | -| `-log-level` | The log level for stdout: 0=silent, 1=errors, 2=warnings, 3=info (default), 4=debug, 5=trace, 6=dump. Note that 6 will dump _a lot_ of stuff, such as debug information from curl during downloads. -| `--destination` | The build directory where `mob` will put everything. -| `--set` | Sets an option: `-s task:section/key=value`. +| `-log-level` | The log level for stdout: 0=silent, 1=errors, 2=warnings, 3=info (default), 4=debug, 5=trace, 6=dump. Note that 6 will dump _a lot_ of stuff, such as debug information from curl during downloads. | +| `--destination` | The build directory where `mob` will put everything. | +| `--set` | Sets an option: `-s task:section/key=value`. | | `--no-default-inis` | Does not auto detect INI files, only uses `--ini`. | - ### `build` + Builds tasks. The order in which tasks have to be built is handled by `mob`, but dependencies will not be built automatically when specifying tasks manually. That is, `mob build` will build `python` before `pyqt`, but `mob build pyqt` will not build `python`. Many tasks will be able to run in parallel, but not all, either because they hog the CPU (such as `usvfs`) or because they have dependencies that have to be built first. If any task fails to build, all the active tasks are aborted as quickly as possible. @@ -236,7 +268,7 @@ If any task fails to build, all the active tasks are aborted as quickly as possi Each task has a name, some have more. MO tasks for example have a full name that corresponds to their git repo (such as `modorganizer-game_features`) and a shorter name (such as `game_features`). Both can be used interchangeably. The task name can also be `super`, which refers to all repos hosted on the Mod Organizer Github account, minus `libbsarch`, `usvfs` and `NexusClientCli`. Globs can be used, like `installer_*`. See `mob list` for a list of all available tasks. -#### Options +#### Options for `build` | Option | Description | | --- | --- | @@ -245,75 +277,78 @@ Each task has a name, some have more. MO tasks for example have a full name that | `--reconfigure` | Reconfigures the task by running cmake, configure scripts, etc. Some tasks might have to delete the whole source directory. | | `--rebuild` | Cleans and rebuilds projects. Some tasks might have to delete the whole source directory | | `--new` | Implies all the four flags above. | -| `--clean-task`,
`--no-clean-task` | Sets whether tasks are cleaned. With `--no-clean-task`, the flags above are ignored. | -| `--fetch-task`,
`--no-fetch-task` | Sets whether tasks are fetched. With `--no-fetch-task`, nothing is downloaded, extracted, cloned or pulled. | -| `--build-task`,
`--no-build-task` | Sets whether tasks are built. With `--no-build-task`, nothing is ever built or installed. | -| `--pull`,
`--no-pull` | For repos that are controlled by git, whether to pull repos that are already cloned. With `--no-pull`, once a repo is cloned, it is never updated automatically. | -| `--revert-ts`,
`--no-revert-ts` | Most projects will generate `.ts` files for translations. These files are typically not committed to Github and so will often conflict when trying to pull. With `--revert-ts`, any `.ts` file is reverted before pulling. | +| `--clean-task`, `--no-clean-task` | Sets whether tasks are cleaned. With `--no-clean-task`, the flags above are ignored. | +| `--fetch-task`, `--no-fetch-task` | Sets whether tasks are fetched. With `--no-fetch-task`, nothing is downloaded, extracted, cloned or pulled. | +| `--build-task`, `--no-build-task` | Sets whether tasks are built. With `--no-build-task`, nothing is ever built or installed. | +| `--pull`, `--no-pull` | For repos that are controlled by git, whether to pull repos that are already cloned. With `--no-pull`, once a repo is cloned, it is never updated automatically. | +| `--revert-ts`, `--no-revert-ts` | Most projects will generate `.ts` files for translations. These files are typically not committed to Github and so will often conflict when trying to pull. With `--revert-ts`, any `.ts` file is reverted before pulling. | | `--ignore-uncommitted-changes` | With `--reextract`, ignores repos that have uncommitted changes and deletes the directory without confirmation. | | `--keep-msbuild` | `mob` starts a lot of `msbuild.exe` processes, some of which hold locks on the build directory. Because that's pretty darn annoying, `mob` will kill all `msbuild.exe` processes when it finished, unless this flag is given. | | `...` | List of tasks to run, see [Task names](#task-names). | - - ### `list` + Lists all the available task names. If a task has multiple names, they are all shown, separated by a comma. -#### Options +#### Options for `list` + | Option | Description | | --- | --- | | `--all` | Shows a task tree to see which are built in parallel. | | `...` | This is the same list of tasks that can be given in the `build` command. With `--all`, this will only show the tasks that would be built. | - ### `options` + Lists all the options after parsing the INIs and the command line. - ### `release` + Creates a release in `prefix/releases`. Only supports devbuilds for now. A release is made out of three archives: - - Binaries from `prefix/install/bin`; - - PDBs from `prefix/install/pdb`; - - Sources from various directories in `prefix/build`. + +- Binaries from `prefix/install/bin`; +- PDBs from `prefix/install/pdb`; +- Sources from various directories in `prefix/build`. The archive filename is `Mod.Organizer-version-suffix-what.7z`, where: - - `version` is taken from `ModOrganizer.exe`, `version.rc` or from `--version`; - - `suffix` is the optional `--suffix` argument; - - `what` is either nothing, `src` or `pdbs`. +- `version` is taken from `ModOrganizer.exe`, `version.rc` or from `--version`; +- `suffix` is the optional `--suffix` argument; +- `what` is either nothing, `src` or `pdbs`. +#### Options for `release` -#### Options | Option | Description | | --- | --- | -| `--bin`,
`--no-bin` | Whether the binary archive is created [default: yes] | -| `--pdbs`,
`--no-pdbs` | Whether the PDBs archive is created [default: yes] | -| `--src`,
`--no-src` | Whether the source archive is created [default: yes] | +| `--bin`, `--no-bin` | Whether the binary archive is created [default: yes] | +| `--pdbs`, `--no-pdbs` | Whether the PDBs archive is created [default: yes] | +| `--src`,, `--no-src` | Whether the source archive is created [default: yes] | | `--version-from-exe` | Retrieves version information from ModOrganizer.exe [default] | | `--version-from-rc` | Retrieves version information from `modorganizer/src/version.rc` | | `--rc ` | Overrides the path to `version.rc` | | `--version ` | Overrides the version string, ignores `--version-from-exe` and `--version-from-rc` | | `--output-dir ` | Sets the output directory to use instead of `prefix/releases` | -| `--suffix ` | Optional suffix to add to the archive filenames +| `--suffix ` | Optional suffix to add to the archive filenames. | | `--force` | `mob` will refuse to create a source archive over 20MB because it would probably be incorrect. This ignores the file size warnings and creates the archive regardless of its size. | - ### `git` + Various commands to manage the git repos. Includes `usvfs`, `NexusClientCli` and all the projects under `modorganizer_super`. #### `set-remotes` + Does the same thing as the when `set_origin_remotes` is set in the INI: renames `origin` to `upstream` and adds a new `origin` with the options below. See [Origin and upstream remotes](#origin-and-upstream-remotes). | Option | Description | | --- | --- | -| `--username ` | Git username | -| `--email ` | Git email | -| `--key ` | Path to a putty key | -| `--no-push` | Disables pushing to `upstream` by changing the push url to `nopushurl` to avoid accidental pushes | -| `--push-origin` | Sets the new `origin` remote as the default push target -| `` | Only use this repo instead of going through all of them | +| `--username ` | Git username. | +| `--email ` | Git email. | +| `--key ` | Path to a putty key. | +| `--no-push` | Disables pushing to `upstream` by changing the push url to `nopushurl` to avoid accidental pushes. | +| `--push-origin` | Sets the new `origin` remote as the default push target. | +| `` | Only use this repo instead of going through all of them. | #### `add-remote` + Simply adds a new remote with the given parameters to all the git repos. | Option | Description | @@ -324,31 +359,22 @@ Simply adds a new remote with the given parameters to all the git repos. | `--push-origin` | Sets this new remote as the default push target | | `` | Only use this repo instead of going through all of them | +### `cmake-config` -### `cmake` -Runs `cmake` in a directory, as if `mob` had done it as part of the build process for a `modorganizer` project. The `cmake` invocation will contain all variables required for `cmake_common` to work. +The `cmake-config` command can display the `CMAKE_INSTALL_PREFIX` and +`CMAKE_PREFIX_PATH` variable using by `mob` itself when building so that you can run +your own `cmake`. -By default, this runs: +Below is a typical use of the command: + +```powershell +# configure the current project with preset vs2022-windows using mob PATH +cmake --preset vs2022-windows ` + ("-DCMAKE_INSTALL_PREFIX=" + (mob cmake-config install-prefix)) ` + ("-DCMAKE_PREFIX_PATH=" + (mob cmake-config prefix-path)) ``` -cmake - -G "Visual Studio version year" - -A x64 - -DCMAKE_BUILD_TYPE=Release - -DCMAKE_INSTALL_MESSAGE=NEVER - - --log-level=ERROR - --no-warn-unused-cli - .. -``` - -#### Options -| Option | Description | -| --- | --- | -| `--generator ` | Sets the `-G` option | -| `--cmd` | Sets the last bit of the command, defaults to `..` | -| `--x86`, `--x64` | Whether to use the 32-bit or 64-bit vcvars. If `--generator` is not given, also sets `-A`. | -| `--install-prefix ` | Passes `-DCMAKE_INSTALL_PREFIX=` to cmake | - ### `inis` -Shows a list of the all the INIs that would be loaded, in order of priority. See [INI files](#ini-files). + +Shows a list of the all the INIs that would be loaded, in order of priority. +See [INI files](#override-options-using-ini-files). diff --git a/src/cmd/cmake_config.cpp b/src/cmd/cmake_config.cpp new file mode 100644 index 0000000..ccd7589 --- /dev/null +++ b/src/cmd/cmake_config.cpp @@ -0,0 +1,43 @@ +#include "pch.h" + +#include "../core/conf.h" +#include "../tasks/tasks.h" + +#include "commands.h" + +namespace mob { + + cmake_config_command::cmake_config_command() : command(requires_options) {} + + command::meta_t cmake_config_command::meta() const + { + return {"cmake-config", "print CMake configuration variables"}; + } + + clipp::group cmake_config_command::do_group() + { + return clipp::group( + clipp::command("cmake-config").set(picked_), + (clipp::option("-h", "--help") >> help_) % ("shows this message"), + clipp::command("prefix-path").set(var_, variable::prefix_path) | + clipp::command("install-prefix").set(var_, variable::install_prefix)); + } + + std::string cmake_config_command::do_doc() + { + return "Print CMake variables to be used when configuring projects.\n"; + } + + int cmake_config_command::do_run() + { + switch (var_) { + case variable::prefix_path: + u8cout << tasks::modorganizer::cmake_prefix_path(); + break; + case variable::install_prefix: + u8cout << conf().path().install().string(); + break; + } + return 0; + } +} // namespace mob diff --git a/src/cmd/commands.h b/src/cmd/commands.h index beb177c..801dec0 100644 --- a/src/cmd/commands.h +++ b/src/cmd/commands.h @@ -281,7 +281,6 @@ namespace mob { void make_bin(); void make_pdbs(); void make_src(); - void make_uibase(); void make_installer(); protected: @@ -297,7 +296,6 @@ namespace mob { bool bin_ = true; bool src_ = true; bool pdbs_ = true; - bool uibase_ = true; bool installer_ = false; std::string utf8out_; fs::path out_; @@ -406,4 +404,20 @@ namespace mob { void do_build(); }; + // print CMake configuration variables + // + class cmake_config_command : public command { + public: + cmake_config_command(); + meta_t meta() const override; + + protected: + clipp::group do_group() override; + int do_run() override; + std::string do_doc() override; + + enum class variable { prefix_path, install_prefix }; + variable var_; + }; + } // namespace mob diff --git a/src/cmd/release.cpp b/src/cmd/release.cpp index 31691b4..8ae786f 100644 --- a/src/cmd/release.cpp +++ b/src/cmd/release.cpp @@ -44,9 +44,19 @@ namespace mob { u8cout << "making src archive " << path_to_utf8(out) << "\n"; const std::vector ignore = {"\\..+", // dot files - ".*\\.log", ".*\\.tlog", ".*\\.dll", - ".*\\.exe", ".*\\.lib", ".*\\.obj", - ".*\\.ts", ".*\\.aps", "vsbuild"}; + "explorer\\+\\+", + "stylesheets", + "transifex-translations" + ".*\\.log", + ".*\\.tlog", + ".*\\.dll", + ".*\\.exe", + ".*\\.lib", + ".*\\.obj", + ".*\\.ts", + ".*\\.aps", + "(bin|lib)", + "vsbuild(32|64)?"}; const std::vector ignore_re(ignore.begin(), ignore.end()); @@ -77,25 +87,6 @@ namespace mob { op::archive_from_files(gcx(), files, tasks::modorganizer::super_path(), out); } - void release_command::make_uibase() - { - const auto out = out_ / make_filename("uibase"); - u8cout << "making uibase archive " << path_to_utf8(out) << "\n"; - - std::vector files; - - if (!fs::exists(tasks::modorganizer::super_path())) { - gcx().bail_out(context::generic, "modorganizer super path not found: {}", - tasks::modorganizer::super_path()); - } - - op::archive_from_glob( - gcx(), tasks::modorganizer::super_path() / "uibase" / "src" / "*.h", out, - {}); - op::archive_from_files(gcx(), {conf().path().install_libs() / "uibase.lib"}, - conf().path().install_libs(), out); - } - void release_command::make_installer() { const auto file = "Mod.Organizer-" + version_ + ".exe"; @@ -178,11 +169,6 @@ namespace mob { clipp::option("--no-src").set(src_, false)) % "sets whether the source archive is created [default: yes]", - //( - // clipp::option("--uibase").set(uibase_, true) | - // clipp::option("--no-uibase").set(uibase_, false) - //) % "sets whether the uibase archive is created [default: yes]", - (clipp::option("--inst").set(installer_, true) | clipp::option("--no-inst").set(installer_, false)) % "sets whether the installer is copied [default: no]", @@ -271,9 +257,6 @@ namespace mob { if (src_) make_src(); - if (uibase_) - make_uibase(); - if (installer_) make_installer(); @@ -299,7 +282,6 @@ namespace mob { make_bin(); make_pdbs(); make_src(); - make_uibase(); make_installer(); return 0; diff --git a/src/core/conf.cpp b/src/core/conf.cpp index 1801d99..eb024e7 100644 --- a/src/core/conf.cpp +++ b/src/core/conf.cpp @@ -519,7 +519,6 @@ namespace mob { set_path_if_empty("qt_install", find_qt); set_path_if_empty("temp_dir", find_temp_dir); set_path_if_empty("icons", find_in_root("icons")); - set_path_if_empty("patches", find_in_root("patches")); set_path_if_empty("licenses", find_in_root("licenses")); set_path_if_empty("qt_bin", qt::installation_path() / "bin"); set_path_if_empty("qt_translations", qt::installation_path() / "translations"); @@ -541,11 +540,10 @@ namespace mob { resolve_path("install_libs", p.install(), "lib"); resolve_path("install_pdbs", p.install(), "pdb"); resolve_path("install_dlls", p.install_bin(), "dlls"); - resolve_path("install_loot", p.install_bin(), "loot"); resolve_path("install_plugins", p.install_bin(), "plugins"); resolve_path("install_licenses", p.install_bin(), "licenses"); - resolve_path("install_pythoncore", p.install_bin(), "pythoncore"); resolve_path("install_stylesheets", p.install_bin(), "stylesheets"); + resolve_path("install_translations", p.install_bin(), "translations"); resolve_path("install_extensions", p.install_bin(), "extensions"); // finally, resolve the tools that are unlikely to be in PATH; all the @@ -697,11 +695,6 @@ namespace mob { return {}; } - conf_prebuilt conf::prebuilt() - { - return {}; - } - conf_versions conf::version() { return {}; diff --git a/src/core/conf.h b/src/core/conf.h index 40ebbda..cc18d63 100644 --- a/src/core/conf.h +++ b/src/core/conf.h @@ -264,7 +264,6 @@ namespace mob { VALUE(prefix); VALUE(cache); VALUE(icons); - VALUE(patches); VALUE(licenses); VALUE(build); @@ -275,11 +274,10 @@ namespace mob { VALUE(install_pdbs); VALUE(install_dlls); - VALUE(install_loot); VALUE(install_extensions); VALUE(install_stylesheets); VALUE(install_licenses); - VALUE(install_pythoncore); + VALUE(install_translations); VALUE(vs); VALUE(vcpkg); diff --git a/src/main.cpp b/src/main.cpp index f5be353..c0e71f1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,37 +23,6 @@ namespace mob { // // mob doesn't have a concept of task dependencies, just task ordering, so // if a task depends on another, it has to be earlier in the order - - // third-party tasks - - // add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task(); - - // add_task() - // .add_task() - // .add_task() - // .add_task(); - - // add_task() - // .add_task() - // .add_task() - // .add_task(); - - // add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task() - // .add_task(); - // super tasks using mo = modorganizer; @@ -93,6 +62,9 @@ namespace mob { .add_task("modorganizer-plugin_python"); add_task() + .add_task() + .add_task() + .add_task() .add_task({"modorganizer-tool_configurator", "pycfg"}) .add_task("modorganizer-fnistool") .add_task("modorganizer-basic_games") @@ -103,7 +75,6 @@ namespace mob { .add_task({"modorganizer", "organizer"}); // other tasks - add_task(); add_task(); } @@ -126,7 +97,8 @@ namespace mob { std::make_unique(), std::make_unique(), std::make_unique(), - std::make_unique()}; + std::make_unique(), + std::make_unique()}; // commands are shown in the help help->set_commands(commands); @@ -143,7 +115,7 @@ namespace mob { for (auto& c : commands) all_groups.push_back(c->group()); - // vs reports a no-op on the left side of the command, which is incorrect + // vs reports a no-op on the left side of the command, which is incorrect #pragma warning(suppress : 4548) auto cli = (all_groups, command::common_options_group()); auto pr = clipp::parse(args, cli); diff --git a/src/tasks/boost.cpp b/src/tasks/boost.cpp deleted file mode 100644 index f64038e..0000000 --- a/src/tasks/boost.cpp +++ /dev/null @@ -1,319 +0,0 @@ -#include "pch.h" -#include "../core/process.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - std::string boost_version_no_tags() - { - const auto v = boost::parsed_version(); - - // 1.72[.1] - std::string s = v.major + "." + v.minor; - - if (v.patch != "") - s += "." + v.patch; - - return s; - } - - std::string boost_version_no_tags_underscores() - { - return replace_all(boost_version_no_tags(), ".", "_"); - } - - std::string boost_version_all_underscores() - { - const auto v = boost::parsed_version(); - - // boost_1_72[_0[_b1_rc1]] - std::string s = "boost_" + v.major + "_" + v.minor; - - if (v.patch != "") - s += "_" + v.patch; - - if (v.rest != "") - s += "_" + replace_all(v.rest, "-", "_"); - - return s; - } - - std::string address_model_for_arch(arch a) - { - switch (a) { - case arch::x86: - return "32"; - - case arch::x64: - case arch::dont_care: - return "64"; - - default: - gcx().bail_out(context::generic, "boost: bad arch"); - } - } - - fs::path config_jam_file() - { - return boost::source_path() / "user-config-64.jam"; - } - - url prebuilt_url() - { - const auto underscores = replace_all(boost::version(), ".", "_"); - return make_prebuilt_url("boost_prebuilt_" + underscores + ".7z"); - } - - url source_url() - { - return "https://boostorg.jfrog.io/artifactory/main/release/" + - boost_version_no_tags() + "/source/" + - boost_version_all_underscores() + ".7z"; - } - - fs::path b2_exe() - { - return boost::source_path() / "b2.exe"; - } - - } // namespace - - boost::boost() : basic_task("boost") {} - - std::string boost::version() - { - return conf().version().get("boost"); - } - - std::string boost::version_vs() - { - return conf().version().get("boost_vs"); - } - - bool boost::prebuilt() - { - return conf().prebuilt().get("boost"); - } - - fs::path boost::source_path() - { - // ex: build/boost_1_74_0 - return conf().path().build() / ("boost_" + boost_version_no_tags_underscores()); - } - - fs::path boost::lib_path(arch a) - { - // ex: build/boost_1_74_0/lib64-msvc-14.2/lib - return root_lib_path(a) / "lib"; - } - - fs::path boost::root_lib_path(arch a) - { - // ex: build/boost_1_74_0/lib64-msvc-14.2 - - const std::string lib = - "lib" + address_model_for_arch(a) + "-msvc-" + version_vs(); - - return source_path() / lib; - } - - void boost::do_clean(clean c) - { - if (is_set(c, clean::redownload)) { - // delete downloaded file - - if (prebuilt()) - run_tool(downloader(prebuilt_url(), downloader::clean)); - else - run_tool(downloader(source_url(), downloader::clean)); - } - - if (is_set(c, clean::reextract)) { - // delete the whole thing - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - - // no need for the rest - return; - } - - // those don't make sense for prebults - if (!prebuilt()) { - if (is_set(c, clean::reconfigure)) { - // delete bin and b2.exe to make sure bootstrap runs again - op::delete_directory(cx(), source_path() / "bin.v2", op::optional); - op::delete_file(cx(), b2_exe(), op::optional); - - // delete jam files - op::delete_file(cx(), config_jam_file(), op::optional); - op::delete_file(cx(), source_path() / "project-config.jam", - op::optional); - } - - if (is_set(c, clean::rebuild)) { - // delete libs - op::delete_directory(cx(), root_lib_path(arch::x86), op::optional); - op::delete_directory(cx(), root_lib_path(arch::x64), op::optional); - } - } - } - - void boost::do_fetch() - { - if (prebuilt()) - fetch_prebuilt(); - else - fetch_from_source(); - } - - void boost::do_build_and_install() - { - if (prebuilt()) - build_and_install_prebuilt(); - else - build_and_install_from_source(); - } - - void boost::fetch_prebuilt() - { - cx().trace(context::generic, "using prebuilt boost"); - - const auto file = run_tool(downloader(prebuilt_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void boost::build_and_install_prebuilt() {} - - void boost::fetch_from_source() - { - const auto file = run_tool(downloader(source_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void boost::bootstrap() - { - // bootstrap b2 - - write_config_jam(); - - const auto bootstrap = source_path() / "bootstrap.bat"; - - run_tool(process_runner(process() - .binary(bootstrap) - .external_error_log(source_path() / "bootstrap.log") - .cwd(source_path()))); - } - - void boost::build_and_install_from_source() - { - // bypass boostrap - if (fs::exists(b2_exe())) { - cx().trace(context::bypass, "{} exists, boost already bootstrapped", - b2_exe()); - } - else { - bootstrap(); - } - - // we do not need all variants of all components but since people should - // usually be using the pre-built, we can build everything without losing too - // much time and it is much easier to deal when mixing - // - // note: filesystem is only required by USVFS I think, so maybe think about - // removing it if we switch to std::filesystem in USVFS - // - clipp::arg_list components{"thread", "date_time", "filesystem", "locale", - "program_options"}; - - // static link, static runtime, x64 - do_b2(components, "static", "static", arch::x64); - - // static link, static/shared runtime, x86, required by usvfs 32-bit - do_b2(components, "static", "static", arch::x86); - do_b2(components, "static", "shared", arch::x86); - - // static link, shared runtime, x64 - do_b2(components, "static", "shared", arch::x64); - - // shared link, shared runtime, x64 - do_b2(components, "shared", "shared", arch::x64); - } - - void boost::do_b2(const std::vector& components, - const std::string& link, const std::string& runtime_link, arch a) - { - // will transform all components to --with-X - - run_tool(process_runner(process() - .binary(b2_exe()) - .arg("address-model=", address_model_for_arch(a)) - .arg("link=", link) - .arg("runtime-link=", runtime_link) - .arg("toolset=", "msvc-" + vs::toolset()) - .arg("--user-config=", config_jam_file()) - .arg("--stagedir=", root_lib_path(a)) - .arg("--libdir=", root_lib_path(a)) - .args(map(components, - [](auto&& c) { - return "--with-" + c; - })) - .env(env::vs(a)) - .cwd(source_path()))); - } - - void boost::write_config_jam() - { - // b2 requires forward slashes - auto forward_slashes = [](auto&& p) { - std::string s = path_to_utf8(p); - return replace_all(s, "\\", "/"); - }; - - // this currently writes an empty configuration file, at some point it was used - // to configure the Boost.Python built - // - // kept here in case we need a custom user-configuration in the future - // - std::ostringstream oss; - - // logging - { - cx().trace(context::generic, - "writing config file at {}:", config_jam_file()); - - for_each_line(oss.str(), [&](auto&& line) { - cx().trace(context::generic, " {}", line); - }); - } - - // writing - op::write_text_file(cx(), encodings::utf8, config_jam_file(), oss.str()); - } - - boost::version_info boost::parsed_version() - { - // 1.72.0-b1-rc1 - // everything but 1.72 is optional - std::regex re("(\\d+)\\." // 1. - "(\\d+)" // 72 - "(?:" - "\\.(\\d+)" // .0 - "(?:" // - "-(.+)" // -b1-rc1 - ")?" - ")?"); - - std::smatch m; - - const auto s = version(); - - if (!std::regex_match(s, m, re)) - gcx().bail_out(context::generic, "bad boost version '{}'", s); - - return {m[1], m[2], m[3], m[4]}; - } - -} // namespace mob::tasks diff --git a/src/tasks/boost_di.cpp b/src/tasks/boost_di.cpp deleted file mode 100644 index fddbaad..0000000 --- a/src/tasks/boost_di.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - // boost-di is needed by bsapacker - - boost_di::boost_di() : basic_task("boost-di", "boostdi", "boost_di") {} - - std::string boost_di::version() - { - return {}; - } - - bool boost_di::prebuilt() - { - // prebuilts don't exist for this, it's headers only - return false; - } - - fs::path boost_di::source_path() - { - return conf().path().build() / "di"; - } - - void boost_di::do_clean(clean c) - { - // delete the whole thing - if (is_set(c, clean::reclone)) - git_wrap::delete_directory(cx(), source_path()); - } - - void boost_di::do_fetch() - { - run_tool(make_git() - .url(make_git_url("boost-experimental", "di")) - .branch("cpp14") - .root(source_path())); - } - -} // namespace mob::tasks diff --git a/src/tasks/bzip2.cpp b/src/tasks/bzip2.cpp deleted file mode 100644 index 253ed4c..0000000 --- a/src/tasks/bzip2.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - // required by python - - namespace { - - url source_url() - { - return "https://sourceware.org/pub/bzip2/" - "bzip2-" + - bzip2::version() + ".tar.gz"; - } - - } // namespace - - bzip2::bzip2() : basic_task("bzip2") {} - - std::string bzip2::version() - { - return conf().version().get("bzip2"); - } - - bool bzip2::prebuilt() - { - // no prebuilts, just the source, required by python, which uses the - // source files directly - return false; - } - - fs::path bzip2::source_path() - { - return conf().path().build() / ("bzip2-" + version()); - } - - void bzip2::do_clean(clean c) - { - // delete download - if (is_set(c, clean::redownload)) - run_tool(downloader(source_url(), downloader::clean)); - - // delete the whole directory - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - } - } - - void bzip2::do_fetch() - { - const auto file = run_tool(downloader(source_url())); - - run_tool(extractor().file(file).output(source_path())); - } - -} // namespace mob::tasks diff --git a/src/tasks/directxtex.cpp b/src/tasks/directxtex.cpp deleted file mode 100644 index b7d79f2..0000000 --- a/src/tasks/directxtex.cpp +++ /dev/null @@ -1,80 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - - msbuild create_msbuild_tool(arch a, config config, - msbuild::ops o = msbuild::build) - { - return std::move(msbuild(o).architecture(a).configuration(config).solution( - directxtex::source_path() / "DirectXTex" / - "DirectXTex_Desktop_2022.vcxproj")); - } - - } // namespace - - directxtex::directxtex() : basic_task("directxtex") {} - - std::string directxtex::version() - { - return conf().version().get("directxtex"); - } - - bool directxtex::prebuilt() - { - return false; - } - - fs::path directxtex::source_path() - { - return conf().path().build() / "DirectXTex"; - } - - void directxtex::do_clean(clean c) - { - if (is_set(c, clean::reclone)) { - git_wrap::delete_directory(cx(), source_path()); - return; - } - - if (is_set(c, clean::rebuild)) { - run_tool(create_msbuild_tool(arch::x64, config::release, msbuild::clean)); - run_tool(create_msbuild_tool(arch::x64, config::debug, msbuild::clean)); - } - } - - void directxtex::do_fetch() - { - run_tool(make_git() - .url(make_git_url("microsoft", "DirectXTex")) - .branch(version()) - .root(source_path())); - } - - void directxtex::do_build_and_install() - { - op::create_directories(cx(), directxtex::source_path() / "Include"); - op::create_directories(cx(), directxtex::source_path() / "Lib" / "Debug"); - op::create_directories(cx(), directxtex::source_path() / "Lib" / "Release"); - - // DO NOT run these in parallel because both generate files that are shared - // between release and debug - run_tool(create_msbuild_tool(arch::x64, config::release)); - run_tool(create_msbuild_tool(arch::x64, config::debug)); - - const auto binary_path = - source_path() / "DirectXTex" / "Bin" / "Desktop_2022" / "x64"; - - for (const auto& header : {"DDS.h", "DirectXTex.h", "DirectXTex.inl "}) { - op::copy_file_to_dir_if_better(cx(), source_path() / "DirectXTex" / header, - source_path() / "Include"); - } - op::copy_file_to_dir_if_better(cx(), binary_path / "Debug" / "DirectXTex.lib", - source_path() / "Lib" / "Debug"); - op::copy_file_to_dir_if_better(cx(), binary_path / "Release" / "DirectXTex.lib", - source_path() / "Lib" / "Release"); - } - -} // namespace mob::tasks diff --git a/src/tasks/gtest.cpp b/src/tasks/gtest.cpp deleted file mode 100644 index 359a433..0000000 --- a/src/tasks/gtest.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - - cmake create_cmake_tool(arch a, config config, cmake::ops o = cmake::generate) - { - return std::move(cmake(o) - .generator(cmake::vs) - .architecture(a) - .arg("-Wno-deprecated") - .arg("-Dgtest_force_shared_crt=ON") - .prefix(gtest::build_path(a, config)) - .root(gtest::source_path())); - } - - msbuild create_msbuild_tool(arch a, config config, - msbuild::ops o = msbuild::build) - { - const fs::path build_path = create_cmake_tool(a, config).build_path(); - - return std::move(msbuild(o).architecture(a).configuration(config).solution( - build_path / "INSTALL.vcxproj")); - } - - } // namespace - - gtest::gtest() : basic_task("gtest", "googletest") {} - - std::string gtest::version() - { - return conf().version().get("gtest"); - } - - bool gtest::prebuilt() - { - return false; - } - - fs::path gtest::source_path() - { - return conf().path().build() / "googletest"; - } - - fs::path gtest::build_path(arch a, config c) - { - return source_path() / "build" / (a == arch::x64 ? "x64" : "Win32") / - msbuild::configuration_name(c); - } - - void gtest::do_clean(clean c) - { - if (is_set(c, clean::reclone)) { - git_wrap::delete_directory(cx(), source_path()); - return; - } - - if (is_set(c, clean::reconfigure)) { - run_tool(create_cmake_tool(arch::x86, config::release, cmake::clean)); - run_tool(create_cmake_tool(arch::x64, config::release, cmake::clean)); - } - - if (is_set(c, clean::rebuild)) { - run_tool(create_msbuild_tool(arch::x86, config::release, msbuild::clean)); - run_tool(create_msbuild_tool(arch::x86, config::debug, msbuild::clean)); - run_tool(create_msbuild_tool(arch::x64, config::release, msbuild::clean)); - run_tool(create_msbuild_tool(arch::x64, config::debug, msbuild::clean)); - } - } - - void gtest::do_fetch() - { - run_tool(make_git() - .url(make_git_url("google", "googletest")) - .branch(version()) - .root(source_path())); - } - - void gtest::do_build_and_install() - { - op::create_directories(cx(), gtest::build_path(arch::x64).parent_path()); - op::create_directories(cx(), gtest::build_path(arch::x86).parent_path()); - - parallel({{"gtest64", - [&] { - run_tool(create_cmake_tool(arch::x64, config::release)); - run_tool(create_msbuild_tool(arch::x64, config::release)); - - run_tool(create_cmake_tool(arch::x64, config::debug)); - run_tool(create_msbuild_tool(arch::x64, config::debug)); - }}, - - {"gtest32", [&] { - run_tool(create_cmake_tool(arch::x86, config::release)); - run_tool(create_msbuild_tool(arch::x86, config::release)); - - run_tool(create_cmake_tool(arch::x86, config::debug)); - run_tool(create_msbuild_tool(arch::x86, config::debug)); - }}}); - } - -} // namespace mob::tasks diff --git a/src/tasks/installer.cpp b/src/tasks/installer.cpp index f90db80..8feb3c6 100644 --- a/src/tasks/installer.cpp +++ b/src/tasks/installer.cpp @@ -36,9 +36,21 @@ namespace mob::tasks { { const std::string repo = "modorganizer-Installer"; + // find the best suitable branch + const auto fallback = task_conf().mo_fallback_branch(); + auto branch = task_conf().mo_branch(); + if (!fallback.empty() && + !git_wrap::remote_branch_exists(make_git_url(task_conf().mo_org(), repo), + branch)) { + cx().warning(context::generic, + "{} has no remote {} branch, switching to {}", repo, branch, + fallback); + branch = fallback; + } + run_tool(make_git() .url(make_git_url(task_conf().mo_org(), repo)) - .branch(task_conf().mo_branch()) + .branch(branch) .root(source_path())); } diff --git a/src/tasks/libbsarch.cpp b/src/tasks/libbsarch.cpp deleted file mode 100644 index de41c56..0000000 --- a/src/tasks/libbsarch.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - - std::string dir_name() - { - return "libbsarch-" + libbsarch::version() + "-" + - (libbsarch::build_type() == config::debug ? "debug" : "release") + - "-x64"; - } - - url source_url() - { - return "https://github.com/ModOrganizer2/libbsarch/releases/download/" + - libbsarch::version() + "/" + dir_name() + ".7z"; - } - - } // namespace - - libbsarch::libbsarch() : basic_task("libbsarch") {} - - std::string libbsarch::version() - { - return conf().version().get("libbsarch"); - } - - config libbsarch::build_type() - { - return conf().build_types().get("libbsarch"); - } - - bool libbsarch::prebuilt() - { - return false; - } - - fs::path libbsarch::source_path() - { - return conf().path().build() / dir_name(); - } - - void libbsarch::do_clean(clean c) - { - // delete the download - if (is_set(c, clean::redownload)) - run_tool(downloader(source_url(), downloader::clean)); - - // delete the whole directory - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - } - } - - void libbsarch::do_fetch() - { - const auto file = run_tool(downloader(source_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void libbsarch::do_build_and_install() - { - // copy dll - op::copy_file_to_dir_if_better(cx(), source_path() / "libbsarch.dll", - conf().path().install_dlls()); - } - -} // namespace mob::tasks diff --git a/src/tasks/libloot.cpp b/src/tasks/libloot.cpp deleted file mode 100644 index 0715848..0000000 --- a/src/tasks/libloot.cpp +++ /dev/null @@ -1,67 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - - std::string release_name() - { - // the naming convention is `libloot-version-win64.7z`, - // such as libloot-0.19.3-win64.7z. - return "libloot-" + libloot::version() + "-" + "win64"; - } - - url source_url() - { - return "https://github.com/loot/libloot/releases/download/" + - libloot::version() + "/" + release_name() + ".7z"; - } - - } // namespace - - libloot::libloot() : basic_task("libloot") {} - - std::string libloot::version() - { - return conf().version().get("libloot"); - } - - bool libloot::prebuilt() - { - return false; - } - - fs::path libloot::source_path() - { - return conf().path().build() / release_name(); - } - - void libloot::do_clean(clean c) - { - // delete download - if (is_set(c, clean::redownload)) - run_tool(downloader(source_url(), downloader::clean)); - - // delete the whole directory - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - } - } - - void libloot::do_fetch() - { - const auto file = run_tool(downloader(source_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void libloot::do_build_and_install() - { - // copy dll - op::copy_file_to_dir_if_better(cx(), source_path() / "bin" / "loot.dll", - conf().path().install_loot()); - } - -} // namespace mob::tasks diff --git a/src/tasks/lz4.cpp b/src/tasks/lz4.cpp deleted file mode 100644 index 281ca7d..0000000 --- a/src/tasks/lz4.cpp +++ /dev/null @@ -1,142 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - - fs::path solution_dir() - { - return lz4::source_path() / "build" / "VS2022"; - } - - fs::path solution_file() - { - return solution_dir() / "lz4.sln"; - } - - fs::path out_dir() - { - return solution_dir() / "bin" / "x64_Release"; - } - - url prebuilt_url() - { - return make_prebuilt_url("lz4_prebuilt_" + lz4::version() + ".7z"); - } - - } // namespace - - lz4::lz4() : basic_task("lz4") {} - - std::string lz4::version() - { - return conf().version().get("lz4"); - } - - bool lz4::prebuilt() - { - return conf().prebuilt().get("lz4"); - } - - fs::path lz4::source_path() - { - return conf().path().build() / ("lz4-" + version()); - } - - void lz4::do_clean(clean c) - { - if (prebuilt()) { - // delete download - if (is_set(c, clean::redownload)) - run_tool(downloader(prebuilt_url(), downloader::clean)); - - // delete the whole directory - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - } - } - else { - // delete the whole directory - if (is_set(c, clean::reclone)) { - git_wrap::delete_directory(cx(), source_path()); - - // no point in doing anything more - return; - } - - // msbuild clean - if (is_set(c, clean::rebuild)) - run_tool(create_msbuild_tool(msbuild::clean)); - } - } - - void lz4::do_fetch() - { - if (prebuilt()) - fetch_prebuilt(); - else - fetch_from_source(); - } - - void lz4::do_build_and_install() - { - if (prebuilt()) - build_and_install_prebuilt(); - else - build_and_install_from_source(); - } - - void lz4::fetch_prebuilt() - { - cx().trace(context::generic, "using prebuilt lz4"); - - const auto file = run_tool(downloader(prebuilt_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void lz4::build_and_install_prebuilt() - { - // copy pdb and dll - - op::copy_file_to_dir_if_better(cx(), source_path() / "bin" / "liblz4.pdb", - conf().path().install_pdbs()); - - op::copy_file_to_dir_if_better(cx(), source_path() / "bin" / "liblz4.dll", - conf().path().install_dlls()); - } - - void lz4::fetch_from_source() - { - // clone - run_tool(make_git() - .url(make_git_url("lz4", "lz4")) - .branch(version()) - .root(source_path())); - } - - void lz4::build_and_install_from_source() - { - run_tool(create_msbuild_tool()); - - // cmake_common looks for the lib files in the bin/ directory, which is - // correct for prebuilts, but not from source, so copy the files in there - op::copy_glob_to_dir_if_better(cx(), out_dir() / "*", source_path() / "bin", - op::copy_files); - - // copy dll and pdb - op::copy_file_to_dir_if_better(cx(), source_path() / "bin" / "liblz4.dll", - conf().path().install_dlls()); - - op::copy_file_to_dir_if_better(cx(), source_path() / "bin" / "liblz4.pdb", - conf().path().install_pdbs()); - } - - msbuild lz4::create_msbuild_tool(msbuild::ops o) - { - return std::move(msbuild(o).solution(solution_file()).targets({"liblz4-dll"})); - } - -} // namespace mob::tasks diff --git a/src/tasks/modorganizer.cpp b/src/tasks/modorganizer.cpp index 9a053d1..139027d 100644 --- a/src/tasks/modorganizer.cpp +++ b/src/tasks/modorganizer.cpp @@ -3,15 +3,6 @@ namespace mob::tasks { - // build CMAKE_PREFIX_PATH for MO2 tasks - // - std::string cmake_prefix_path() - { - return conf().path().qt_install().string() + ";" + - (modorganizer::super_path() / "cmake_common").string() + ";" + - (conf().path().install() / "lib" / "cmake").string(); - } - // given a vector of names (some projects have more than one, see add_tasks() in // main.cpp), this prepends the simplified name to the vector and returns it // @@ -89,6 +80,13 @@ namespace mob::tasks { } } + std::string modorganizer::cmake_prefix_path() + { + return conf().path().qt_install().string() + ";" + + (modorganizer::super_path() / "cmake_common").string() + ";" + + (conf().path().install() / "lib" / "cmake").string(); + } + fs::path modorganizer::source_path() const { // something like build/modorganizer_super/uibase @@ -182,6 +180,7 @@ namespace mob::tasks { .generator(cmake::vs) .def("CMAKE_INSTALL_PREFIX:PATH", conf().path().install()) .def("CMAKE_PREFIX_PATH", cmake_prefix_path()) + .configuration_types({task_conf().configuration()}) .preset("vs2022-windows") .root(source_path())); @@ -193,12 +192,13 @@ namespace mob::tasks { .root(source_path()) .arg("--parallel") .arg("16") - .configuration(mob::config::relwithdebinfo)); + .configuration(task_conf().configuration())); // run cmake --install - run_tool(cmake(cmake::install) + run_tool(cmake(cmake::build) .root(source_path()) - .configuration(mob::config::relwithdebinfo)); + .targets("INSTALL") + .configuration(task_conf().configuration())); } } // namespace mob::tasks diff --git a/src/tasks/openssl.cpp b/src/tasks/openssl.cpp deleted file mode 100644 index 5b1f502..0000000 --- a/src/tasks/openssl.cpp +++ /dev/null @@ -1,235 +0,0 @@ -#include "pch.h" -#include "../core/process.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - - url source_url() - { - return "https://www.openssl.org/source/" - "openssl-" + - openssl::version() + ".tar.gz"; - } - - url prebuilt_url() - { - return make_prebuilt_url("openssl-prebuilt-" + openssl::version() + ".7z"); - } - - std::string version_no_patch_underscores(bool primaryOnly = true) - { - auto v = openssl::parsed_version(); - - std::string s = v.major; - - if (v.minor != "" && !primaryOnly) - s += "_" + v.minor; - - return s; - } - - // the filenames of the dlls, but without extension, because this is used for - // both dlls and pdbs - // - std::vector output_names() - { - return {"libcrypto-" + version_no_patch_underscores() + "-x64", - "libssl-" + version_no_patch_underscores() + "-x64"}; - } - - } // namespace - - openssl::openssl() : basic_task("openssl") {} - - std::string openssl::version() - { - return conf().version().get("openssl"); - } - - bool openssl::prebuilt() - { - return conf().prebuilt().get("openssl"); - } - - fs::path openssl::source_path() - { - return conf().path().build() / ("openssl-" + version()); - } - - fs::path openssl::build_path() - { - return source_path() / "build"; - } - - fs::path openssl::bin_path() - { - return build_path() / "bin"; - } - - void openssl::do_clean(clean c) - { - if (prebuilt()) { - // delete prebuilt download - if (is_set(c, clean::redownload)) - run_tool(downloader(prebuilt_url(), downloader::clean)); - } - else { - // delete source download - if (is_set(c, clean::redownload)) - run_tool(downloader(source_url(), downloader::clean)); - } - - // there's no easy way to clean anything for openssl, it puts files all - // over the place, just delete the whole thing - if (is_any_set(c, clean::reextract | clean::reconfigure | clean::rebuild)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - } - } - - void openssl::do_fetch() - { - if (prebuilt()) - fetch_prebuilt(); - else - fetch_from_source(); - } - - void openssl::do_build_and_install() - { - if (prebuilt()) - build_and_install_prebuilt(); - else - build_and_install_from_source(); - } - - void openssl::fetch_prebuilt() - { - cx().trace(context::generic, "using prebuilt openssl"); - - const auto file = run_tool(downloader(prebuilt_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void openssl::fetch_from_source() - { - const auto file = run_tool(downloader(source_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void openssl::build_and_install_prebuilt() - { - // nothing to built for prebuilt, just copy - copy_files(); - } - - void openssl::build_and_install_from_source() - { - // running the Configure perl script generates a file `makefile`; since - // Configuring takes forever and will fully run every time, don't run it if - // the makefile already exists - if (fs::exists(source_path() / "makefile")) - cx().trace(context::bypass, "openssl already configured"); - else - configure(); - - // run the install_engines target in the makefile, this builds everything - // required - install_engines(); - - // applink.c is required when building python from source, the .vcxproj - // assumes it's in the include path for whatever reason, so copy it there - op::copy_file_to_dir_if_better(cx(), source_path() / "ms" / "applink.c", - include_path()); - - copy_files(); - } - - void openssl::configure() - { - run_tool(process_runner(process() - .binary(perl::binary()) - .arg("Configure") - .arg("VC-WIN64A") - .arg("--openssldir=", build_path()) - .arg("--prefix=", build_path()) - .arg("-FS") - .arg("-MP1") - .arg("-wd4566") - .cwd(source_path()) - .env(env::vs(arch::x64)))); - } - - void openssl::install_engines() - { - build_loop(cx(), [&](bool mp) { - // jom defaults to multiprocess, give allow_failure for multiprocess - // builds and force single_job for the last single process build - - const int exit_code = - run_tool(jom() - .path(source_path()) - .target("install_engines") - .flag(mp ? jom::allow_failure : jom::single_job)); - - return (exit_code == 0); - }); - } - - void openssl::copy_files() - { - // duplicate the dlls to both bin/ and bin/dlls, they're needed by both - // MO and Qt - copy_dlls_to(conf().path().install_bin()); - copy_dlls_to(conf().path().install_dlls()); - - // pdbs - copy_pdbs_to(conf().path().install_pdbs()); - } - - void openssl::copy_dlls_to(const fs::path& dir) - { - for (auto&& name : output_names()) { - op::copy_file_to_dir_if_better(cx(), bin_path() / (name + ".dll"), dir); - } - } - - void openssl::copy_pdbs_to(const fs::path& dir) - { - for (auto&& name : output_names()) { - op::copy_file_to_dir_if_better(cx(), bin_path() / (name + ".pdb"), dir); - } - } - - fs::path openssl::include_path() - { - return openssl::source_path() / "include"; - } - - openssl::version_info openssl::parsed_version() - { - // 1.2.3d - // everything but 1 is optional - std::regex re("(\\d+)" // 1 - "(?:" // - "\\.(\\d+)" // .2 - "(?:" // - "\\.(\\d+)([a-zA-Z]+)?" // .3d - ")?" // - ")?"); - - std::smatch m; - - const auto s = version(); - - if (!std::regex_match(s, m, re)) - gcx().bail_out(context::generic, "bad openssl version '{}'", s); - - return {m[1], m[2], m[3]}; - } - -} // namespace mob::tasks diff --git a/src/tasks/pybind11.cpp b/src/tasks/pybind11.cpp deleted file mode 100644 index 82edd10..0000000 --- a/src/tasks/pybind11.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "pch.h" -#include "../core/process.h" -#include "tasks.h" - -// see the top of pyqt.cpp for some stuff about python/sip/pyqt - -namespace mob::tasks { - - pybind11::pybind11() : basic_task("pybind11") {} - - bool pybind11::prebuilt() - { - // header only available - return true; - } - - std::string pybind11::version() - { - return conf().version().get("pybind11"); - } - - fs::path pybind11::source_path() - { - return conf().path().build() / "pybind11"; - } - - void pybind11::do_clean(clean c) - { - if (is_set(c, clean::reclone)) { - git_wrap::delete_directory(cx(), source_path()); - return; - } - } - - void pybind11::do_fetch() - { - run_tool(make_git() - .url(make_git_url("pybind", "pybind11")) - .branch(version()) - .root(source_path())); - } - - void pybind11::do_build_and_install() {} -} // namespace mob::tasks diff --git a/src/tasks/pyqt.cpp b/src/tasks/pyqt.cpp deleted file mode 100644 index d27d592..0000000 --- a/src/tasks/pyqt.cpp +++ /dev/null @@ -1,331 +0,0 @@ -#include "pch.h" -#include "../core/process.h" -#include "tasks.h" - -// build process for python, sip and pyqt; if one is built from source, all -// three need to be built from source, plus openssl because python needs it -// -// 1) build openssl -// -// 2) build python, needs openssl -// -// 3) build sip, needs python: -// - download and extract source archive -// - run `python setup.py install` in sip's directory, this generates -// sip-install.exe and sip-module.exe in python-XX/Scripts (among others) -// - run `sip-module.exe --sip-h` to generate a sip.h file in sip's source -// directory -// - that header file is copied into python/include and is included by -// by plugin_python in sipapiaccess.h -// -// 4) build pyqt, needs sip -// - download and extract source archive -// - use `pip install` to install PyQt-builder -// - run `sip-install.exe` with the list of required modules, creating a -// folder for each module in PyQt6-XX/build/ with `.pyd` files -// - run `sip-module.exe --sdist`, which creates -// downloads/PyQt6_sip-XXX.tar.gz -// - run `pip install` with that file, which creates -// `python-XX/Lib/site-packages/PyQt6/sip.cp32-win_amd64.pyd` -// - for installation, a bunch of files from site-packages/PyQt6/ are -// copied into install/bin/plugins/data/PyQt6, including a .pyi file from -// sip - -namespace mob::tasks { - - namespace { - - url source_url() - { - const auto version = pyqt::version(); - std::string base = "https://pypi.io/packages/source/P/PyQt6/"; - if (version.find("dev") != std::string::npos) { - base = "https://riverbankcomputing.com/pypi/packages/PyQt6/"; - } - return base + "PyQt6-" + pyqt::version() + ".tar.gz"; - } - - url prebuilt_url() - { - return make_prebuilt_url( - "PyQt6_gpl-prebuilt-" + pyqt::version() + - (pyqt::build_type() == config::debug ? "-debug" : "") + ".7z"); - } - - // file created by sip-module.exe - // - fs::path sip_install_file() - { - return "PyQt6_sip-" + sip::version_for_pyqt() + ".tar.gz"; - } - - } // namespace - - pyqt::pyqt() : basic_task("pyqt") {} - - std::string pyqt::version() - { - return conf().version().get("pyqt"); - } - - std::string pyqt::builder_version() - { - return conf().version().get("pyqt_builder"); - } - - bool pyqt::prebuilt() - { - return conf().prebuilt().get("pyqt"); - } - - fs::path pyqt::source_path() - { - return conf().path().build() / ("PyQt6-" + version()); - } - - fs::path pyqt::build_path() - { - return source_path() / "build"; - } - - config pyqt::build_type() - { - return conf().build_types().get("pyqt"); - } - - std::string pyqt::pyqt_sip_module_name() - { - return "PyQt6.sip"; - } - - void pyqt::do_clean(clean c) - { - if (prebuilt()) { - // delete prebuilt download - if (is_set(c, clean::redownload)) - run_tool(downloader(prebuilt_url(), downloader::clean)); - } - else { - // delete source download - if (is_set(c, clean::redownload)) - run_tool(downloader(source_url(), downloader::clean)); - } - - // delete whole directory - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - - // no need to do anything else - return; - } - - if (!prebuilt()) { - // delete the pyqt-sip file that's created when building from source - if (is_set(c, clean::rebuild)) { - op::delete_file(cx(), conf().path().cache() / sip_install_file(), - op::optional); - } - } - } - - void pyqt::do_fetch() - { - if (prebuilt()) - fetch_prebuilt(); - else - fetch_from_source(); - } - - void pyqt::do_build_and_install() - { - if (prebuilt()) - build_and_install_prebuilt(); - else - build_and_install_from_source(); - } - - void pyqt::fetch_prebuilt() - { - const auto file = run_tool(downloader(prebuilt_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void pyqt::build_and_install_prebuilt() - { - // copy the prebuilt files directly into the python directory, they're - // required by sip, which is always built from source - op::copy_glob_to_dir_if_better(cx(), source_path() / "*", python::source_path(), - op::copy_files | op::copy_dirs); - - // copy files to build/install for MO - copy_files(); - } - - void pyqt::fetch_from_source() - { - const auto file = run_tool(downloader(source_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void pyqt::build_and_install_from_source() - { - // use pip to install the pyqt builder - if (python::build_type() == config::debug) { - // PyQt-builder has sip as a dependency, so installing it directly will - // replace the sip we have installed manually, but the installed sip will - // not work (see comment in sip::build() for details) - // - // the workaround is to install the dependencies manually (only packaging), - // and then use a --no-dependencies install with pip - // - run_tool(pip(pip::install).package("packaging")); - run_tool(pip(pip::install) - .package("PyQt-builder") - .no_dependencies() - .version(builder_version())); - } - else { - run_tool( - pip(pip::install).package("PyQt-builder").version(builder_version())); - } - - // patch for builder.py - run_tool(patcher() - .task(name()) - .file("builder.py.manual_patch") - .root(python::site_packages_path() / "pyqtbuild")); - - // build modules and generate the PyQt6_sip-XX.tar.gz file - sip_build(); - - // run pip install for the PyQt6_sip-XX.tar.gz file - install_sip_file(); - - // copy files to build/install for MO - copy_files(); - } - - void pyqt::sip_build() - { - // put qt and python in the path, set CL and LIB, which are used by the - // visual c++ compiler that's eventually spawned, and set PYTHONHOME - auto pyqt_env = - env::vs_x64() - .append_path({qt::bin_path(), python::build_path(), - python::source_path(), python::scripts_path()}) - .set("CL", " /MP") - .set("LIB", ";" + path_to_utf8(conf().path().install_libs()), - env::append) - .set("PYTHONHOME", path_to_utf8(python::source_path())); - - // create a bypass file, because pyqt always tries to build stuff and it - // takes forever - bypass_file built_bypass(cx(), source_path(), "built"); - - if (built_bypass.exists()) { - cx().trace(context::bypass, "pyqt already built"); - } - else { - // sip-install.exe has trouble with deleting the build/ directory and - // trying to recreate it too fast, giving an access denied error; do it - // here instead - op::delete_directory(cx(), source_path() / "build", op::optional); - - // build modules - run_tool(process_runner(process() - .binary(sip::sip_install_exe()) - .arg("--confirm-license") - .arg("--verbose", process::log_trace) - .arg("--pep484-pyi") - .arg("--link-full-dll") - .arg("--build-dir", build_path()) - // .arg("--enable", - //"pylupdate") // these are not in modules so - // they .arg("--enable", "pyrcc") // don't - // get copied below - // .args(zip(repeat("--enable"), modules())) - .cwd(source_path()) - .env(pyqt_env))); - - // done, create the bypass file - built_bypass.create(); - } - - // generate the PyQt6_sip-XX.tar.gz file - run_tool(process_runner(process() - .binary(sip::sip_module_exe()) - .arg("--sdist") - .arg(pyqt_sip_module_name()) - .cwd(conf().path().cache()) - .env(pyqt_env))); - } - - void pyqt::install_sip_file() - { - // create a bypass file, because pyqt always tries to install stuff and it - // takes forever - bypass_file installed_bypass(cx(), source_path(), "installed"); - - if (installed_bypass.exists()) { - cx().trace(context::bypass, "pyqt already installed"); - } - else { - if (python::build_type() == config::debug) { - - const auto pyqt_sip_folder = source_path() / "PyQt6_sip" / - ("pyqt6_sip-" + sip::version_for_pyqt()); - - // in debug, we need to do stuff manually because pip does not work - // as expected - run_tool(extractor() - .file(conf().path().cache() / sip_install_file()) - .output(pyqt_sip_folder.parent_path())); - - run_tool(mob::python() - .root(pyqt_sip_folder) - .arg("setup.py") - .arg("build") - .arg("--debug")); - - run_tool(pip(pip::install).file(pyqt_sip_folder)); - } - else { - // run `pip install` on the generated PyQt6_sip-XX.tar.gz file - run_tool( - pip(pip::install).file(conf().path().cache() / sip_install_file())); - } - - // done, create the bypass file - installed_bypass.create(); - } - } - - void pyqt::copy_files() - { - // pyqt puts its files in python-XX/Lib/site-packages/PyQt6 - const fs::path site_packages_pyqt = python::site_packages_path() / "PyQt6"; - - // copying some dlls from Qt's installation directory into - // python-XX/PCBuild/amd64, those are needed by PyQt6 when building several - // projects - - const std::vector dlls{"Qt6Core", "Qt6Xml"}; - - for (auto dll : dlls) { - if (build_type() == config::debug) { - dll += "d"; - } - dll += ".dll"; - op::copy_file_to_dir_if_better( - cx(), qt::bin_path() / dll, python::build_path(), - op::unsafe); // source file is outside prefix - } - - // installation of PyQt6 python files (.pyd, etc.) is done - // by the python plugin directly - } - -} // namespace mob::tasks diff --git a/src/tasks/python.cpp b/src/tasks/python.cpp deleted file mode 100644 index b70d804..0000000 --- a/src/tasks/python.cpp +++ /dev/null @@ -1,276 +0,0 @@ -#include "pch.h" -#include "../core/process.h" -#include "tasks.h" - -// see the top of pyqt.cpp for some stuff about python/sip/pyqt - -namespace mob::tasks { - - namespace { - - std::string version_without_v() - { - const auto v = python::parsed_version(); - - // 3.8.1 - std::string s = v.major + "." + v.minor; - if (v.patch != "") - s += "." + v.patch; - - return s; - } - - std::string version_for_dll() - { - const auto v = python::parsed_version(); - - // 38 - return v.major + v.minor; - } - - url prebuilt_url() - { - return make_prebuilt_url( - "python-prebuilt-" + version_without_v() + - (python::build_type() == config::debug ? "-debug" : "") + ".7z"); - } - - fs::path solution_file() - { - return python::source_path() / "PCBuild" / "pcbuild.sln"; - } - - fs::path python_core_zip_file() - { - return python::build_path() / "pythoncore" / - ("python" + version_for_dll() + ".zip"); - } - - } // namespace - - python::python() : basic_task("python") {} - - std::string python::version() - { - return conf().version().get("python"); - } - - bool python::prebuilt() - { - return conf().prebuilt().get("python"); - } - - python::version_info python::parsed_version() - { - // v3.8.1 - // v and .1 are optional - std::regex re(R"(v?(\d+)\.(\d+)(?:\.(\d+))?)"); - std::smatch m; - - const auto s = version(); - - if (!std::regex_match(s, m, re)) - gcx().bail_out(context::generic, "bad python version '{}'", s); - - version_info v; - - v.major = m[1]; - v.minor = m[2]; - v.patch = m[3]; - - return v; - } - - fs::path python::source_path() - { - return conf().path().build() / ("python-" + version_without_v()); - } - - fs::path python::build_path() - { - return source_path() / "PCBuild" / "amd64"; - } - - config python::build_type() - { - return conf().build_types().get("python"); - } - - void python::do_clean(clean c) - { - if (prebuilt()) { - // delete download - if (is_set(c, clean::redownload)) - run_tool(downloader(prebuilt_url(), downloader::clean)); - - // delete the whole directory - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - } - } - else { - // delete the whole directory - if (is_set(c, clean::reclone)) { - git_wrap::delete_directory(cx(), source_path()); - - // no need to do anything else - return; - } - - // msbuild clean - if (is_set(c, clean::rebuild)) - run_tool(create_msbuild_tool(msbuild::clean)); - } - } - - void python::do_fetch() - { - if (prebuilt()) - fetch_prebuilt(); - else - fetch_from_source(); - } - - void python::do_build_and_install() - { - if (prebuilt()) - build_and_install_prebuilt(); - else - build_and_install_from_source(); - } - - void python::fetch_prebuilt() - { - const auto file = run_tool(downloader(prebuilt_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void python::build_and_install_prebuilt() - { - install_pip(); - copy_files(); - } - - void python::fetch_from_source() - { - run_tool(make_git() - .url(make_git_url("python", "cpython")) - .branch(version()) - .root(source_path())); - } - - void python::build_and_install_from_source() - { - // download externals - prepare_dependencies(); - - // build - run_tool(create_msbuild_tool()); - - // package stuff into pythoncore.zip - package(); - - // install pip for other tasks that need it - install_pip(); - - // boost.python expects pyconfig.h to be in the include path - op::copy_file_to_dir_if_better(cx(), source_path() / "PC" / "pyconfig.h", - include_path()); - - copy_files(); - } - - void python::prepare_dependencies() - { - const auto bat = source_path() / "PCBuild" / "get_externals.bat"; - run_tool(process_runner(process().binary(bat).cwd(source_path()))); - } - - void python::package() - { - if (fs::exists(python_core_zip_file())) { - cx().trace(context::bypass, "python already packaged"); - return; - } - - const auto bat = source_path() / "python.bat"; - - auto p = process() - .binary(bat) - .arg(fs::path("PC/layout")) - .arg("--source", source_path()) - .arg("--build", build_path()) - .arg("--temp", (build_path() / "pythoncore_temp")) - .arg("--copy", (build_path() / "pythoncore")) - .arg("--preset-embed") - .cwd(source_path()); - - if (build_type() == config::debug) { - p = p.arg("--debug"); - } - - // package libs into pythonXX.zip - run_tool(process_runner(p)); - } - - void python::copy_files() - { - // libs - op::copy_glob_to_dir_if_better(cx(), build_path() / "*.lib", - conf().path().install_libs(), op::copy_files); - - // pdbs - op::copy_file_to_dir_if_better( - cx(), - build_path() / ("python" + version_for_dll() + - (build_type() == config::debug ? "_d" : "") + ".pdb"), - conf().path().install_pdbs()); - - // dlls and python libraries are installed by the python plugin - } - - void python::install_pip() - { - cx().trace(context::generic, "installing pip"); - run_tool(pip(pip::ensure)); - } - - msbuild python::create_msbuild_tool(msbuild::ops o) - { - return std::move( - msbuild(o) - .solution(solution_file()) - .targets({"python", "pythonw", "python3dll", "select", "pyexpat", - "unicodedata", "_queue", "_bz2", "_ssl", "_overlapped"}) - .configuration(build_type()) - .properties( - {"bz2Dir=" + path_to_utf8(bzip2::source_path()), - "zlibDir=" + path_to_utf8(zlib::source_path()), - "opensslIncludeDir=" + path_to_utf8(openssl::include_path()), - "opensslOutDir=" + path_to_utf8(openssl::source_path())})); - } - - fs::path python::python_exe() - { - return build_path() / - (build_type() == config::debug ? "python_d.exe" : "python.exe"); - } - - fs::path python::include_path() - { - return source_path() / "Include"; - } - - fs::path python::scripts_path() - { - return source_path() / "Scripts"; - } - - fs::path python::site_packages_path() - { - return source_path() / "Lib" / "site-packages"; - } - -} // namespace mob::tasks diff --git a/src/tasks/sevenz.cpp b/src/tasks/sevenz.cpp deleted file mode 100644 index 1a107dd..0000000 --- a/src/tasks/sevenz.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - namespace { - - std::string version_for_url() - { - return replace_all(sevenz::version(), ".", ""); - } - - url source_url() - { - return "https://www.7-zip.org/a/7z" + version_for_url() + "-src.7z"; - } - - // 7z has a bunch of modules, like the gui, etc., just build the dll - // - fs::path module_to_build() - { - return sevenz::source_path() / "CPP" / "7zip" / "Bundles" / "Format7zF"; - } - - } // namespace - - sevenz::sevenz() : basic_task("7z", "sevenz") {} - - std::string sevenz::version() - { - return conf().version().get("sevenz"); - } - - bool sevenz::prebuilt() - { - return false; - } - - fs::path sevenz::source_path() - { - return conf().path().build() / ("7zip-" + version()); - } - - void sevenz::do_clean(clean c) - { - // delete download - if (is_set(c, clean::redownload)) - run_tool(downloader(source_url(), downloader::clean)); - - // delete whole directory - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - - // no need to do anything else - return; - } - - // delete whole output directory - if (is_set(c, clean::rebuild)) - op::delete_directory(cx(), module_to_build() / "x64", op::optional); - } - - void sevenz::do_fetch() - { - const auto file = run_tool(downloader(source_url())); - - run_tool(extractor().file(file).output(source_path())); - } - - void sevenz::do_build_and_install() - { - build(); - - // copy 7z.dll to install/bin/dlls - op::copy_file_to_dir_if_better(cx(), module_to_build() / "x64/7z.dll", - conf().path().install_dlls()); - } - - void sevenz::build() - { - build_loop(cx(), [&]([[maybe_unused]] bool mp) { - const int exit_code = run_tool(nmake() - .path(module_to_build()) - .def("CPU=x64") - .def("NEW_COMPILER=1") - .def("MY_STATIC_LINK=1") - .def("NO_BUFFEROVERFLOWU=1")); - - return (exit_code == 0); - }); - } - -} // namespace mob::tasks diff --git a/src/tasks/sip.cpp b/src/tasks/sip.cpp deleted file mode 100644 index aed6e34..0000000 --- a/src/tasks/sip.cpp +++ /dev/null @@ -1,206 +0,0 @@ -#include "pch.h" -#include "../core/process.h" -#include "tasks.h" - -// see the top of pyqt.cpp for some stuff about python/sip/pyqt - -namespace mob::tasks { - - namespace { - - fs::path download_file() - { - return conf().path().cache() / ("sip-" + sip::version() + ".tar.gz"); - } - - std::string version_for_module_source() - { - // 12.7.2 - // .2 is optional - std::regex re(R"((\d+)\.(\d+)(?:\.(\d+))?)"); - std::smatch m; - - const auto s = sip::version_for_pyqt(); - - if (!std::regex_match(s, m, re)) - gcx().bail_out(context::generic, "bad pyqt sip version {}", s); - - // 12.7 - return m[1].str(); // + "." + m[2].str(); - } - - // header file generated by `sip-module.exe` at the end of the build process, - // used as a bypass file and also copied into python's include directory, - // plugin_python needs it - // - fs::path sip_header_file() - { - return sip::source_path() / "sip.h"; - } - - } // namespace - - sip::sip() : basic_task("sip") {} - - std::string sip::version() - { - return conf().version().get("sip"); - } - - std::string sip::version_for_pyqt() - { - return conf().version().get("pyqt_sip"); - } - - bool sip::prebuilt() - { - return false; - } - - fs::path sip::source_path() - { - return conf().path().build() / ("sip-" + version()); - } - - fs::path sip::sip_module_exe() - { - return python::scripts_path() / "sip-module.exe"; - } - - fs::path sip::sip_install_exe() - { - return python::scripts_path() / "sip-install.exe"; - } - - fs::path sip::module_source_path() - { - // 12.7 - const auto dir = version_for_module_source(); - - return source_path() / "sipbuild" / "module" / "source" / dir; - } - - void sip::do_clean(clean c) - { - // delete file downloaded by pip - if (is_set(c, clean::redownload)) { - if (fs::exists(download_file())) { - cx().trace(context::redownload, "deleting {}", download_file()); - op::delete_file(cx(), download_file(), op::optional); - } - } - - // delete the whole thing - if (is_set(c, clean::reextract)) { - cx().trace(context::reextract, "deleting {}", source_path()); - op::delete_directory(cx(), source_path(), op::optional); - } - - // delete the whole build directory - if (is_set(c, clean::rebuild)) { - op::delete_directory(cx(), source_path() / "build", op::optional); - - if (fs::exists(sip_header_file())) - op::delete_file(cx(), sip_header_file(), op::optional); - } - - // note that there's a bunch of files still left python-XX/Scripts that - // can't be easily deleted except by deleting something like "sip-*", but - // there might be other stuff in there - } - - void sip::do_fetch() - { - if (fs::exists(download_file())) { - cx().trace(context::bypass, "sip: {} already exists", download_file()); - } - else { - // download - run_tool(pip(pip::download).package("sip").version(version())); - } - - // extract - run_tool(extractor().file(download_file()).output(source_path())); - } - - void sip::do_build_and_install() - { - if (fs::exists(sip_header_file())) { - cx().trace(context::bypass, "{} already exists", sip_header_file()); - } - else { - build(); - generate_header(); - } - - // sip.h is included by sipapiaccess.h in plugin_python and it assumes it's - // in the include path - op::copy_file_to_dir_if_better(cx(), sip_header_file(), python::include_path()); - } - - void sip::build() - { - if (python::build_type() == config::debug) { - // if Python is build in debug mode, fall back to old setup.py because pip - // install seems to generated broken script wrapper that point to a - // non-existing python.exe instead of python_d.exe - run_tool(pip(pip::install).package("setuptools")); - run_tool(mob::python().root(source_path()).arg("setup.py").arg("install")); - } - else { - run_tool(pip(pip::install).file(source_path())); - } - } - - void sip::convert_script_file_to_acp(const std::string& filename) - { - // all the various .py files that were installed in python-XX/Scripts/ - // by build() above have a shebang that has the absolute path to the python - // executable - // - // these .py files are used by their corresponding .exe file, like - // `sip-module.exe` calls into `sip-module.py` - // - // the files are encoded in utf-8, but the .exe's will fail to execute them - // if they have non-ascii characters in the python path - // - // this converts the files into ACP, hoping that the characters actually - // exist in the codepage, so that the .exe can run - // - // if the path contains utf-8 characters that don't exist in the ACP, - // conversion will fail and U+FFFD will be written, but it would have failed - // anyway - - const fs::path src = python::scripts_path() / filename; - const fs::path backup = python::scripts_path() / (filename + ".bak"); - const fs::path dest = python::scripts_path() / (filename + ".acp"); - - if (!fs::exists(backup)) { - cx().debug(context::generic, "converting {} to acp", src); - - // read the utf8 file - const std::string utf8 = op::read_text_file(cx(), encodings::utf8, src); - - // convert to acp and write to filename.acp - op::write_text_file(cx(), encodings::acp, dest, utf8); - - // rename the source to .bak and rename filename.acp to the original - op::replace_file(cx(), src, dest, backup); - } - } - - void sip::generate_header() - { - // generate sip.h, will be copied to python's include directory, used - // by plugin_python - run_tool(process_runner(process() - .binary(sip_module_exe()) - .chcp(65001) - .stdout_encoding(encodings::acp) - .stderr_encoding(encodings::acp) - .arg("--sip-h") - .arg(pyqt::pyqt_sip_module_name()) - .cwd(source_path()))); - } - -} // namespace mob::tasks diff --git a/src/tasks/spdlog.cpp b/src/tasks/spdlog.cpp deleted file mode 100644 index 913c438..0000000 --- a/src/tasks/spdlog.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - spdlog::spdlog() : basic_task("spdlog") {} - - std::string spdlog::version() - { - return conf().version().get("spdlog"); - } - - bool spdlog::prebuilt() - { - return false; - } - - fs::path spdlog::source_path() - { - return conf().path().build() / ("spdlog-" + version()); - } - - void spdlog::do_clean(clean c) - { - // delete the whole directory - if (is_set(c, clean::reclone)) - git_wrap::delete_directory(cx(), source_path()); - } - - void spdlog::do_fetch() - { - run_tool(make_git() - .url(make_git_url("gabime", "spdlog")) - .branch(version()) - .root(source_path())); - } - -} // namespace mob::tasks diff --git a/src/tasks/stylesheets.cpp b/src/tasks/stylesheets.cpp index 0cc7691..a9df301 100644 --- a/src/tasks/stylesheets.cpp +++ b/src/tasks/stylesheets.cpp @@ -77,7 +77,7 @@ namespace mob::tasks { fs::path stylesheets::release_build_path(const release& r) const { // something like build/paper-mono-v2.1 - return conf().path().build() / (r.repo + "-" + r.version); + return conf().path().build() / "stylesheets" / (r.repo + "-" + r.version); } downloader stylesheets::make_downloader_tool(const release& r, diff --git a/src/tasks/task.cpp b/src/tasks/task.cpp index 5f411ee..f8bd06c 100644 --- a/src/tasks/task.cpp +++ b/src/tasks/task.cpp @@ -391,13 +391,6 @@ namespace mob { do_fetch(); check_interrupted(); - - // auto patching if the task has a source path - if (!get_source_path().empty()) { - cx().debug(context::generic, "patching"); - - run_tool(patcher().task(name(), get_prebuilt()).root(get_source_path())); - } } void task::build_and_install() diff --git a/src/tasks/tasks.h b/src/tasks/tasks.h index 390258c..bf76107 100644 --- a/src/tasks/tasks.h +++ b/src/tasks/tasks.h @@ -11,86 +11,6 @@ namespace mob::tasks { // single header for all the tasks, not worth having a header per task - class boost : public basic_task { - public: - struct version_info { - std::string major, minor, patch, rest; - }; - - boost(); - - static version_info parsed_version(); - static std::string version(); - static std::string version_vs(); - static bool prebuilt(); - - static fs::path source_path(); - static fs::path lib_path(arch a); - static fs::path root_lib_path(arch a); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - void fetch_prebuilt(); - void build_and_install_prebuilt(); - - void fetch_from_source(); - void build_and_install_from_source(); - void write_config_jam(); - - void bootstrap(); - - void do_b2(const std::vector& components, const std::string& link, - const std::string& runtime_link, arch a); - }; - - // needed by bsapacker - // - class boost_di : public basic_task { - public: - boost_di(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - }; - - // needed by python - // - class bzip2 : public basic_task { - public: - bzip2(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - }; - - class directxtex : public basic_task { - public: - directxtex(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - }; - class explorerpp : public basic_task { public: explorerpp(); @@ -104,21 +24,6 @@ namespace mob::tasks { void do_fetch() override; }; - class gtest : public basic_task { - public: - gtest(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - static fs::path build_path(arch = arch::x64, config = config::release); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - }; - class installer : public basic_task { public: installer(); @@ -133,38 +38,6 @@ namespace mob::tasks { void do_build_and_install() override; }; - class libbsarch : public basic_task { - public: - libbsarch(); - - static std::string version(); - static config build_type(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - }; - - class libloot : public basic_task { - public: - libloot(); - - static std::string version(); - static std::string hash(); - static std::string branch(); - - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - }; - class licenses : public task { public: licenses(); @@ -173,29 +46,6 @@ namespace mob::tasks { void do_build_and_install() override; }; - class lz4 : public basic_task { - public: - lz4(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - void fetch_prebuilt(); - void build_and_install_prebuilt(); - - void fetch_from_source(); - void build_and_install_from_source(); - - msbuild create_msbuild_tool(msbuild::ops o = msbuild::build); - }; - // a task for all modorganizer projects except for the installer, which is // technically an MO project but is built differently // @@ -205,6 +55,10 @@ namespace mob::tasks { // class modorganizer : public task { public: + // build CMAKE_PREFIX_PATH for MO2 tasks + // + static std::string cmake_prefix_path(); + // path of the root modorganizer_super directory // static fs::path super_path(); @@ -252,240 +106,6 @@ namespace mob::tasks { std::string project_; }; - class ncc : public basic_task { - public: - ncc(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - msbuild create_msbuild_tool(msbuild::ops o); - }; - - class nmm : public basic_task { - public: - nmm(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - msbuild create_msbuild_tool(msbuild::ops o = msbuild::build, - msbuild::flags_t f = msbuild::noflags); - }; - - class openssl : public basic_task { - public: - struct version_info { - std::string major, minor, patch; - }; - - openssl(); - - static version_info parsed_version(); - static std::string version(); - static bool prebuilt(); - - static fs::path source_path(); - static fs::path include_path(); - static fs::path build_path(); - static fs::path bin_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - void fetch_prebuilt(); - void fetch_from_source(); - void build_and_install_prebuilt(); - void build_and_install_from_source(); - - void configure(); - void install_engines(); - void copy_files(); - void copy_dlls_to(const fs::path& dir); - void copy_pdbs_to(const fs::path& dir); - }; - - // when building from source, builds both pyqt5 and pyqt5-sip; when using the - // prebuilt, the downloaded zip contains both - // - class pyqt : public basic_task { - public: - pyqt(); - - static std::string version(); - static std::string builder_version(); - static bool prebuilt(); - - static fs::path source_path(); - static fs::path build_path(); - static config build_type(); - - // "PyQt5.sip", used both in pyqt and sip - // - static std::string pyqt_sip_module_name(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - void fetch_prebuilt(); - void fetch_from_source(); - void build_and_install_prebuilt(); - void build_and_install_from_source(); - - void sip_build(); - void install_sip_file(); - void copy_files(); - }; - - class python : public basic_task { - public: - struct version_info { - std::string major; - std::string minor; - std::string patch; - }; - - python(); - - static std::string version(); - static bool prebuilt(); - static version_info parsed_version(); - - // build/python-XX - // - static fs::path source_path(); - - // build/python-XX/PCBuild/amd64 - // - static fs::path build_path(); - - // build/python-XX/PCBuild/amd64/python.exe - // - static fs::path python_exe(); - - // build/python-XX/Include - // - static fs::path include_path(); - - // build/python-XX/Scripts - // - static fs::path scripts_path(); - - // build/python-XX/Lib/site-packages - // - static fs::path site_packages_path(); - - // configuration to build - // - static config build_type(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - void fetch_prebuilt(); - void fetch_from_source(); - void build_and_install_prebuilt(); - void build_and_install_from_source(); - - void prepare_dependencies(); - void package(); - void install_pip(); - void copy_files(); - - msbuild create_msbuild_tool(msbuild::ops o = msbuild::build); - }; - - class pybind11 : public basic_task { - public: - pybind11(); - - static bool prebuilt(); - static std::string version(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - }; - - class sevenz : public basic_task { - public: - sevenz(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - void build(); - }; - - class sip : public basic_task { - public: - sip(); - - static std::string version(); - static std::string version_for_pyqt(); - static bool prebuilt(); - - static fs::path source_path(); - static fs::path sip_module_exe(); - static fs::path sip_install_exe(); - static fs::path module_source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - void build(); - void generate_header(); - void convert_script_file_to_acp(const std::string& filename); - }; - - class spdlog : public basic_task { - public: - spdlog(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - }; - class stylesheets : public task { public: struct release { @@ -629,22 +249,4 @@ namespace mob::tasks { config = config::release) const; }; - class zlib : public basic_task { - public: - zlib(); - - static std::string version(); - static bool prebuilt(); - static fs::path source_path(); - - protected: - void do_clean(clean c) override; - void do_fetch() override; - void do_build_and_install() override; - - private: - cmake create_cmake_tool(cmake::ops o = cmake::generate); - msbuild create_msbuild_tool(msbuild::ops o = msbuild::build); - }; - } // namespace mob::tasks diff --git a/src/tasks/usvfs.cpp b/src/tasks/usvfs.cpp index e2bfe3d..2c3e7e9 100644 --- a/src/tasks/usvfs.cpp +++ b/src/tasks/usvfs.cpp @@ -76,8 +76,10 @@ namespace mob::tasks { { run_tool(create_cmake_tool(arch::x64)); run_tool(create_cmake_tool(arch::x86)); - run_tool(create_msbuild_tool(arch::x64)); - run_tool(create_msbuild_tool(arch::x86)); + run_tool(create_msbuild_tool(arch::x64, msbuild::build, + task_conf().configuration())); + run_tool(create_msbuild_tool(arch::x86, msbuild::build, + task_conf().configuration())); } cmake usvfs::create_cmake_tool(arch a, cmake::ops o) const diff --git a/src/tasks/zlib.cpp b/src/tasks/zlib.cpp deleted file mode 100644 index 1eeb8e4..0000000 --- a/src/tasks/zlib.cpp +++ /dev/null @@ -1,76 +0,0 @@ -#include "pch.h" -#include "tasks.h" - -namespace mob::tasks { - - zlib::zlib() : basic_task("zlib") {} - - std::string zlib::version() - { - return conf().version().get("zlib"); - } - - bool zlib::prebuilt() - { - return false; - } - - fs::path zlib::source_path() - { - return conf().path().build() / ("zlib-" + version()); - } - - void zlib::do_clean(clean c) - { - // delete the whole directory - if (is_set(c, clean::reclone)) { - git_wrap::delete_directory(cx(), source_path()); - - // no point in doing anything more - return; - } - - // cmake clean - if (is_set(c, clean::reconfigure)) - run_tool(create_cmake_tool(cmake::clean)); - - // msbuild clean - if (is_set(c, clean::rebuild)) - run_tool(create_msbuild_tool(msbuild::clean)); - } - - void zlib::do_fetch() - { - run_tool(make_git() - .url(make_git_url("madler", "zlib")) - .branch(version()) - .root(source_path())); - } - - void zlib::do_build_and_install() - { - const fs::path build_path = run_tool(create_cmake_tool()); - - run_tool(create_msbuild_tool()); - - // zconf.h needs to be copied to the root directory, it's where python - // looks for it - op::copy_file_to_dir_if_better(cx(), build_path / "zconf.h", source_path()); - } - - cmake zlib::create_cmake_tool(cmake::ops o) - { - return std::move(cmake(o) - .generator(cmake::vs) - .root(source_path()) - .arg("-Wno-deprecated") - .prefix(source_path())); - } - - msbuild zlib::create_msbuild_tool(msbuild::ops o) - { - const fs::path build_path = create_cmake_tool().build_path(); - return std::move(msbuild(o).solution(build_path / "INSTALL.vcxproj")); - } - -} // namespace mob::tasks diff --git a/src/tools/cmake.cpp b/src/tools/cmake.cpp index 3d8e629..3a89ff2 100644 --- a/src/tools/cmake.cpp +++ b/src/tools/cmake.cpp @@ -114,6 +114,12 @@ namespace mob { return *this; } + cmake& cmake::configuration_types(const std::vector& configs) + { + config_types_ = configs; + return *this; + } + cmake& cmake::cmd(const std::string& s) { cmd_ = s; @@ -181,6 +187,16 @@ namespace mob { p = p.arg("--preset").arg(preset_); } + if (!config_types_.empty()) { + std::string types; + for (const auto& c : config_types_) { + if (!types.empty()) + types += ";"; + types += config_to_string(c); + } + p = p.arg("-DCMAKE_CONFIGURATION_TYPES=" + types); + } + p = p.arg("-DCMAKE_INSTALL_MESSAGE=" + conf_cmake::to_string(conf().cmake().install_message())) .arg("--log-level=ERROR") diff --git a/src/tools/cmake.h b/src/tools/cmake.h index 9420fc5..11a289a 100644 --- a/src/tools/cmake.h +++ b/src/tools/cmake.h @@ -69,6 +69,9 @@ namespace mob { // cmake& configuration(mob::config config); + // set the configuration types available when generating + cmake& configuration_types(const std::vector& configs); + // overrides the directory in which cmake will write build files // // by default, this is a directory inside what was given in root() with a @@ -184,6 +187,9 @@ namespace mob { // configuration mob::config config_{mob::config::relwithdebinfo}; + // configuration types + std::vector config_types_; + // passed verbatim std::vector args_; diff --git a/src/tools/jom.cpp b/src/tools/jom.cpp deleted file mode 100644 index 66e6bc8..0000000 --- a/src/tools/jom.cpp +++ /dev/null @@ -1,93 +0,0 @@ -#include "pch.h" -#include "../core/conf.h" -#include "../core/process.h" -#include "tools.h" - -namespace mob { - - jom::jom() : basic_process_runner("jom"), arch_(arch::def) {} - - fs::path jom::binary() - { - return conf().tool().get("jom"); - } - - jom& jom::path(const fs::path& p) - { - cwd_ = p; - return *this; - } - - jom& jom::target(const std::string& s) - { - target_ = s; - return *this; - } - - jom& jom::def(const std::string& s) - { - def_.push_back(s); - return *this; - } - - jom& jom::flag(flags_t f) - { - flags_ = f; - return *this; - } - - jom& jom::architecture(arch a) - { - arch_ = a; - return *this; - } - - int jom::result() const - { - return exit_code(); - } - - void jom::do_run() - { - process p; - - // jom doesn't handle sigint well, it just continues, so kill it on - // interruption - auto pflags = process::terminate_on_interrupt; - - if (flags_ & allow_failure) { - // tasks will set allow_failure for the first couple of runs of jom, - // which often fails because of the /J multi-process flag, so don't log - // errors in that case - p.stderr_level(context::level::trace); - pflags |= process::allow_failure; - } - - p.binary(binary()) - .cwd(cwd_) - .stderr_filter([](process::filter& f) { - // initial log line, can't get rid of it, /L or /NOLOGO don't seem - // to work - if (f.line.find("empower your cores") != std::string::npos) - f.lv = context::level::trace; - }) - .arg("/C", process::log_quiet) // silent - .arg("/S", process::log_quiet) // silent - .arg("/L", process::log_quiet) // silent, jom likes to spew crap - .arg("/D", process::log_dump) // verbose stuff - .arg("/P", process::log_dump) // verbose stuff - .arg("/W", process::log_dump) // verbose stuff - .arg("/K"); // don't stop on errors - - if (flags_ & single_job) - p.arg("/J", "1"); // single-process - - for (auto&& def : def_) - p.arg(def); - - p.arg(target_).flags(pflags).env(env::vs(arch_)); - - execute_and_join(p); - } - -} // namespace mob diff --git a/src/tools/nmake.cpp b/src/tools/nmake.cpp deleted file mode 100644 index abd011e..0000000 --- a/src/tools/nmake.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "pch.h" -#include "../core/conf.h" -#include "../core/process.h" -#include "tools.h" - -namespace mob { - - nmake::nmake() : basic_process_runner("nmake"), arch_(arch::def) {} - - fs::path nmake::binary() - { - return conf().tool().get("nmake"); - } - - nmake& nmake::path(const fs::path& p) - { - cwd_ = p; - return *this; - } - - nmake& nmake::target(const std::string& s) - { - target_ = s; - return *this; - } - - nmake& nmake::def(const std::string& s) - { - def_.push_back(s); - return *this; - } - - nmake& nmake::architecture(arch a) - { - arch_ = a; - return *this; - } - - int nmake::result() const - { - return exit_code(); - } - - void nmake::do_run() - { - process p; - - p.binary(binary()) - .cwd(cwd_) - .stderr_filter([](process::filter& f) { - // initial log line, can't get rid of it, /L or /NOLOGO don't seem - // to work - if (f.line.find("Microsoft (R) Macro Assembler (x64)") != - std::string::npos) - f.lv = context::level::trace; - if (f.line.find("Copyright (C) Microsoft Corporation.") != - std::string::npos) - f.lv = context::level::trace; - if (f.line.find("Microsoft (R) Program Maintenance Utility") != - std::string::npos) - f.lv = context::level::trace; - }) - .arg("/C", process::log_quiet) // silent - .arg("/S", process::log_quiet) // silent - .arg("/L", process::log_quiet) // silent, nmake likes to spew crap - .arg("/D", process::log_dump) // verbose stuff - .arg("/P", process::log_dump) // verbose stuff - .arg("/W", process::log_dump) // verbose stuff - .arg("/K"); // don't stop on errors - - for (auto&& def : def_) - p.arg(def); - - p.arg(target_).env(env::vs(arch_)); - - execute_and_join(p); - } - -} // namespace mob diff --git a/src/tools/patcher.cpp b/src/tools/patcher.cpp deleted file mode 100644 index 7966297..0000000 --- a/src/tools/patcher.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "pch.h" -#include "../core/conf.h" -#include "../core/process.h" -#include "tools.h" - -namespace mob { - - patcher::patcher() : basic_process_runner("patch"), prebuilt_(false) {} - - fs::path patcher::binary() - { - return conf().tool().get("patch"); - } - - patcher& patcher::task(const std::string& name, bool prebuilt) - { - task_ = name; - prebuilt_ = prebuilt; - return *this; - } - - patcher& patcher::file(const fs::path& p) - { - file_ = p; - return *this; - } - - patcher& patcher::root(const fs::path& dir) - { - root_ = dir; - return *this; - } - - void patcher::do_run() - { - // patches should be in mob/patches/task-name, but not all tasks need - // patches - const fs::path root = conf().path().patches() / task_; - - if (!fs::exists(root)) { - cx().trace(context::generic, - "patch directory {} doesn't exist, assuming no patches", root); - - return; - } - - if (!file_.empty()) { - // patcher tool is being run for a manual patch - cx().trace(context::generic, "doing manual patch from {}", file_); - do_patch(root / file_); - return; - } - - // patcher tool is being by the task for auto patching, figure out the - // directory to use depending on whether it's a prebuilt - const fs::path patches = root / (prebuilt_ ? "prebuilt" : "sources"); - cx().trace(context::generic, "looking for patches in {}", patches); - - if (!fs::exists(patches)) { - cx().trace(context::generic, - "patch directory {} doesn't exist, assuming no patches", - patches); - - return; - } - - // for each path file - for (auto e : fs::directory_iterator(patches)) { - if (!e.is_regular_file()) { - cx().trace(context::generic, "skipping {}, not a file", e.path()); - continue; - } - - const auto p = e.path(); - - if (p.extension() == ".manual_patch") { - cx().trace(context::generic, "skipping manual patch {}", e.path()); - continue; - } - else if (p.extension() != ".patch") { - cx().warning(context::generic, "file with unknown extension {}", p); - continue; - } - - do_patch(p); - } - } - - void patcher::do_patch(const fs::path& patch) - { - // there's no way to figure out if patch failure is because 1) the patch - // file is incorrect, or 2) the patch has already been applied - // - // an incorrect patch file would probably mean that the source has changed - // and the patch must be updated or removed if it's not required anymore - // - // so patching is a two step process: check if the patch has already been - // applied, and apply it if it hasn't - - // use by both the check and apply processes - const auto base = process() - .binary(binary()) - .arg("--read-only", "ignore") - .arg("--strip", "0") - .arg("--directory", root_) - .arg("--quiet", process::log_quiet); - - // process to reverse the path: the only way to check if a patch has been - // applied is actually to try to reverse it and check if there was an error - // - // this uses --dry-run because if the file was already patched, it shouldn't - // actually be reversed - auto check = process(base) - .flags(process::allow_failure) - .arg("--dry-run") - .arg("--force") // no prompts - .arg("--reverse") // swaps old and new files - .arg("--input", patch); - - // process to apply the patch - auto apply = - process(base) - .arg("--forward") // don't try to reverse the patch if it fails - .arg("--batch") // no prompts - .arg("--input", patch); - - cx().trace(context::generic, "trying to patch using {}", patch); - - { - // check, returns 0 when the patch would have been reversed correctly, - // 1 if not, anything else on error - cx().trace(context::generic, "checking if already patched"); - const auto ret = execute_and_join(check); - - if (ret == 0) { - // reversing the patch would succeed, so the patch has already been - // applied - cx().trace(context::generic, "patch {} already applied", patch); - return; - } - - // anything other than 0 or 1 is a hard error - if (ret != 1) - cx().bail_out(context::generic, "patch returned {}", ret); - - cx().trace(context::generic, "looks like the patch is needed"); - } - - { - // apply - cx().trace(context::generic, "applying patch {}", patch); - execute_and_join(apply); - } - } - -} // namespace mob diff --git a/src/tools/python.cpp b/src/tools/python.cpp deleted file mode 100644 index 46c79bc..0000000 --- a/src/tools/python.cpp +++ /dev/null @@ -1,187 +0,0 @@ -#include "pch.h" -#include "../core/process.h" -#include "../tasks/tasks.h" -#include "tools.h" - -namespace mob { - - python::python() : basic_process_runner("python") {} - - python& python::root(const fs::path& p) - { - root_ = p; - return *this; - } - - python& python::arg(const std::string& s) - { - args_.push_back(s); - return *this; - } - - void python::do_run() - { - // python is a bit finicky for utf8, so: - // 1) chcp changes the codepage to utf8 - // 2) `-X utf8` and the PYTHONUTF8 environment variable are set, which - // is probably redundant - - auto p = process() - .binary(tasks::python::python_exe()) - .chcp(65001) - .stdout_encoding(encodings::utf8) - .stderr_encoding(encodings::utf8) - .stderr_filter([&](process::filter& f) { - // filter out crap from setuptools - if (f.line.find("zip_safe flag not set") != std::string::npos) - f.lv = context::level::trace; - else if (f.line.find("module references __file__") != - std::string::npos) - f.lv = context::level::trace; - }) - .arg("-X", "utf8"); // forces utf8 - - for (auto&& a : args_) - p.arg(a); - - p.cwd(root_).env(this_env::get().set("PYTHONUTF8", "1")); // forces utf8 - - execute_and_join(p); - } - - pip::pip(ops op) : basic_process_runner("pip"), op_(op), no_deps_{false} {} - - pip& pip::package(const std::string& s) - { - package_ = s; - return *this; - } - - pip& pip::version(const std::string& s) - { - version_ = s; - return *this; - } - - pip& pip::file(const fs::path& p) - { - file_ = p; - return *this; - } - - pip& pip::no_dependencies() - { - no_deps_ = true; - return *this; - } - - void pip::do_run() - { - switch (op_) { - case ensure: - do_ensure(); - break; - - case install: - do_install(); - break; - - case download: - do_download(); - break; - - default: - cx().bail_out(context::generic, "pip unknown op {}", op_); - } - } - - void pip::do_ensure() - { - // ensure - execute_and_join( - process() - .stderr_filter([](auto&& f) { - // this spits out two warnings about not being on PATH and suggests - // to add --no-warn-script-location, but that's not actually a valid - // parameter for `ensurepip` and it fails, unlike the `install` - // commands below - // - // so just filter it out - - if (f.line.find("which is not on PATH") != std::string::npos) - f.lv = context::level::debug; - else if (f.line.find("Consider adding this") != std::string::npos) - f.lv = context::level::debug; - }) - .binary(tasks::python::python_exe()) - .arg("-m", "ensurepip")); - - // upgrade - execute_and_join(process() - .binary(tasks::python::python_exe()) - .arg("-m pip") - .arg("install") - .arg("--no-warn-script-location") - .arg("--upgrade pip")); - - // ssl errors while downloading through python without certifi - execute_and_join(process() - .binary(tasks::python::python_exe()) - .arg("-m pip") - .arg("install") - .arg("--no-warn-script-location") - .arg("certifi")); - } - - void pip::do_install() - { - auto p = process() - .binary(tasks::python::python_exe()) - .chcp(65001) - .stdout_encoding(encodings::utf8) - .stderr_encoding(encodings::utf8) - .arg("-X", "utf8") - .arg("-m", "pip") - .arg("install") - .arg("--force") - .arg("--no-warn-script-location") - .arg("--disable-pip-version-check"); - - if (!package_.empty()) { - if (version_.empty()) { - p.arg(package_); - } - else { - p.arg(package_ + "==" + version_); - } - } - else if (!file_.empty()) - p.arg(file_); - - if (no_deps_) { - p.arg("--no-dependencies"); - } - - p.env(this_env::get().set("PYTHONUTF8", "1")); - - execute_and_join(p); - } - - void pip::do_download() - { - execute_and_join(process() - .binary(tasks::python::python_exe()) - .chcp(65001) - .stdout_encoding(encodings::utf8) - .stderr_encoding(encodings::utf8) - .arg("-X", "utf8") - .arg("-m", "pip") - .arg("download") - .arg("--no-binary=:all:") - .arg("--no-deps") - .arg("-d", conf().path().cache()) - .arg(package_ + "==" + version_) - .env(this_env::get().set("PYTHONUTF8", "1"))); - } - -} // namespace mob diff --git a/src/tools/tools.cpp b/src/tools/tools.cpp index 28549d0..a0779fe 100644 --- a/src/tools/tools.cpp +++ b/src/tools/tools.cpp @@ -73,16 +73,6 @@ namespace mob { return gcx(); } - fs::path perl::binary() - { - return conf().tool().get("perl"); - } - - fs::path nasm::binary() - { - return conf().tool().get("nasm"); - } - fs::path qt::installation_path() { return conf().path().qt_install(); @@ -201,19 +191,6 @@ namespace mob { return trim_copy(p.stdout_string()); } - nuget::nuget(fs::path sln) : basic_process_runner("nuget"), sln_(std::move(sln)) {} - - fs::path nuget::binary() - { - return conf().tool().get("nuget"); - } - - void nuget::do_run() - { - execute_and_join(process().binary(binary()).arg("restore").arg(sln_).cwd( - sln_.parent_path())); - } - transifex::transifex(ops o) : basic_process_runner("transifex"), op_(o), stdout_(context::level::trace), min_(100), force_(false) diff --git a/src/tools/tools.h b/src/tools/tools.h index 3d16fef..47d85eb 100644 --- a/src/tools/tools.h +++ b/src/tools/tools.h @@ -95,23 +95,6 @@ namespace mob { // these are not strictly tools, but they're a centralized place for perl, // nasm, qt and vswhere stuff - // only used when building openssl from source - // - struct perl { - // path to perl binary - // - static fs::path binary(); - }; - - // only used when building openssl from source, bundled with mob in - // third-party/bin - // - struct nasm { - // path to nasm binary - // - static fs::path binary(); - }; - struct qt { // path to qt's root directory, the one that contains bin, include, etc. // @@ -365,199 +348,6 @@ namespace mob { const fs::path& files_root); }; - // tool to apply patches available for a third-party task; patches live in - // mob/patches, in one directory per task - // - // patch.exe is bundled with mob in third-party/bin - // - // there are two ways of patching a task: - // - // 1) auto patching: each task runs the patcher tool as a step in - // task::fetch(), just after calling do_fetch(), which checks for a - // directory `patches/task-name/sources` or `patches/task-name/prebuilt` - // depending on whether this is a prebuilt or not - // - // any file ending with `.patch` will be applied, rooted in the path - // given to root() (defaults to the task's source directory) - // - // 2) manual patching: a task can run the patcher with a specific patch file - // given in file() at any time, which is useful for tasks that need to - // patch files generated later in the building process, such as pyqt - // - // manual patch files can be anywhere, but they're in patches/task-name with - // a `.manual_patch` by convention - // - class patcher : public basic_process_runner { - public: - // path to the patch binary - // - static fs::path binary(); - - patcher(); - - // sets the task name and whether it's a prebuilt, used in task::fetch() for - // auto patching - // - patcher& task(const std::string& name, bool prebuilt = false); - - // sets the patch's filename; if this is not set, uses the task name and - // prebuilt flag from task() - // - patcher& file(const fs::path& p); - - // directory in which the patches are applied; the filenames in the patch - // files must be relative to this directory - // - patcher& root(const fs::path& dir); - - protected: - // applies the patch file from the task directory or the patch file given - // in file() - // - void do_run() override; - - private: - // task name and prebuilt flag, used to generate the auto patching directory - std::string task_; - bool prebuilt_; - - // root directory, used to resolve the relative file names in the patch - // files - fs::path root_; - - // patch file, empty for auto patching - fs::path file_; - - // applies one patch file - // - void do_patch(const fs::path& patch_file); - }; - - // tool that runs `nmake` - // - class nmake : public basic_process_runner { - public: - // path to the job binary - // - static fs::path binary(); - - nmake(); - - // path in which to invoke nmake - // - nmake& path(const fs::path& p); - - // makefile target - // - nmake& target(const std::string& s); - - // adds a macro definition as "NAME=value" - // - nmake& def(const std::string& s); - - // sets the architecture used to run nmake, used to get the appropriate - // VS environment variables, defaults to arch::def - // - nmake& architecture(arch a); - - // nmake's exit code - // - int result() const; - - protected: - // runs nmake - // - void do_run() override; - - private: - // set in path() - fs::path cwd_; - - // set in def(), just plain arguments - std::vector def_; - - // set in target(), another plain argument - std::string target_; - - // set in architecture() - arch arch_; - }; - - // tool that runs `jom`, an alternative to `nmake` that supports parallel - // builds, bundled with mob in third-party/bin - // - // note that although jom reduces build times considerably, some third parties - // don't handle it very well, giving a bunch of errors about files in use, etc. - // - // so most tools that use jom use a loop that runs jom several times, allowing - // failure, and then run jom one last time in single_job mode to finish the - // build, which should be guaranteed to work - // - class jom : public basic_process_runner { - public: - // path to the job binary - // - static fs::path binary(); - - enum flags_t { - noflags = 0x00, - - // disables multi-process build - single_job = 0x01, - - // don't bail out on failure - allow_failure = 0x02 - }; - - jom(); - - // path in which to invoke jom - // - jom& path(const fs::path& p); - - // makefile target - // - jom& target(const std::string& s); - - // adds a macro definition as "NAME=value" - // - jom& def(const std::string& s); - - // sets flags - // - jom& flag(flags_t f); - - // sets the architecture used to run jom, used to get the appropriate - // VS environment variables, defaults to arch::def - // - jom& architecture(arch a); - - // jom's exit code - // - int result() const; - - protected: - // runs jom - // - void do_run() override; - - private: - // set in path() - fs::path cwd_; - - // set in def(), just plain arguments - std::vector def_; - - // set in target(), another plain argument - std::string target_; - - // set in flag() - flags_t flags_; - - // set in architecture() - arch arch_; - }; - // tool that runs devenv.exe, only invoked to upgrade projects for now // class vs : public basic_process_runner { @@ -619,125 +409,6 @@ namespace mob { void do_upgrade(); }; - // tool that runs `nuget restore` for the given solution, bundled with mob in - // third-party/bin - // - class nuget : public basic_process_runner { - public: - // nuget tool for the given solution - // - nuget(fs::path sln); - - // path to the nuget binary - // - static fs::path binary(); - - protected: - // runs nuget - void do_run() override; - - private: - // solution file set in constructor - fs::path sln_; - }; - - // tool that runs python - // - class python : public basic_process_runner { - public: - python(); - - // working directory - // - python& root(const fs::path& p); - - // arbitrary arguments given to python - // - python& arg(const std::string& s); - - protected: - // runs python - // - void do_run() override; - - private: - // working directory - fs::path root_; - - // arguments - std::vector args_; - }; - - // tool that runs pip for one of the operations in the enum - // - class pip : public basic_process_runner { - public: - // what run() should do - // - enum ops { - // installs pip if needed and updates it - ensure = 1, - - // runs `-m pip install` for the given package/version or file - // - install, - - // runs `-m pip download` for the given package and version, does not - // use file() - download - }; - - // a pip tool for the given operation - // - pip(ops o); - - // `pip install` can work with either a "package==version" string, which - // downloads the file if needed, or a local file - // - // so for the `install` operation, either package/version or file can be - // given, but the `download` operation requires the package/version only - // and puts the downloaded file in the "cache" path (defaults to the - // downloads/ directory) - // - pip& package(const std::string& s); - pip& version(const std::string& s); - pip& file(const fs::path& p); - - // do not install dependencies for the package - // - pip& no_dependencies(); - - protected: - // runs pip - // - void do_run() override; - - private: - // what pip command to run - ops op_; - - // set in package(), version() and file() - std::string package_; - std::string version_; - fs::path file_; - - // no depndencies - bool no_deps_; - - // runs `-m ensurepip`, then upgrades pip - // - void do_ensure(); - - // runs `-m pip install` with the given package/version or file - // - void do_install(); - - // runs `-m pip download` with the given package/version, saves the file - // in the cache directory - // - void do_download(); - }; - // tool that runs transifex, used for pulling translations before release, // bundled with mob in third-party/bin // diff --git a/src/utility.h b/src/utility.h index a0becf4..3b833e3 100644 --- a/src/utility.h +++ b/src/utility.h @@ -50,16 +50,6 @@ namespace mob { class url; - // returns a url for a prebuilt binary having the given filename; prebuilts are - // hosted on github, in the umbrella repo - // - url make_prebuilt_url(const std::string& filename); - - // returns a url for an appveyor artifact; this is used by usvfs for prebuilts - // - url make_appveyor_artifact_url(arch a, const std::string& project, - const std::string& filename); - // returns "mob x.y" // std::string mob_version();