[vcpkg manifest] Manifest Implementation (#11757)

==== Changes Related to manifests ====

* Add the `manifests` feature flag
  * This only says whether we look for a `vcpkg.json` in the cwd, not
    whether we support parsing manifests (for ports, for example)
* Changes to the manifests RFC
  * `"authors"` -> `"maintainers"`
  * `--x-classic-mode` -> `-manifests` \in `vcpkg_feature_flags`
  * reserve `"core"` in addition to `"default"`, since that's already
    reserved for features
  * Add a small helper note about what identifiers must look like
  * `<license-string>`: SPDX v3.8 -> v3.9
  * `"feature"."description"` is allowed to be an array of strings as well
  * `"version"` -> `"version-string"` for forward-compat with versions
    RFC
* Add the `--feature-flags` option
* Add the ability to turn off feature flags via passing
  `-<feature-flag>` to `VCPKG_FEATURE_FLAGS` or `--feature-flags`
* Add CMake toolchain support for manifests
  * Requires either:
    * a feature flag of `manifests` in either `Env{VCPKG_FEATURE_FLAGS}`
      or `VCPKG_FEATURE_FLAGS`
    * Passing the `VCPKG_ENABLE_MANIFESTS` option
  * The toolchain will install your packages to
    `${VCPKG_MANIFEST_DIR}/vcpkg_installed`.
* Add MSBuild `vcpkg integrate install` support for manifests
  * Requires `VcpkgEnableManifest` to be true
* `vcpkg create` creates a port that has a `vcpkg.json` instead of a
  `CONTROL`
* argparse, abseil, 3fd, and avisynthplus ports switched to manifest
  from CONTROL
* Add support for `--x-manifest-root`, as well as code for finding it if
  not passed
* Add support for parsing manifests!
* Add a filesystem lock!

==== Important Changes which are somewhat unrelated to manifests ====

* Rename `logicexpression.{h,cpp}` to `platform-expression.{h,cpp}`
* Add `PlatformExpression` type which takes the place of the old logic
  expression
  * Split the parsing of platform expressions from checking whether
    they're true or not
  * Eagerly parse PlatformExpressions as opposed to leaving them as
    strings
* Add checking for feature flag consistency
  * i.e., if `-binarycaching` is passed, you shouldn't be passing
    `--binarysource`
* Add the `Json::Reader` type which, with the help of user-defined
  visitors, converts JSON to your internal type
* VcpkgArgParser: place the switch names into a constant as opposed to
  using magic constants
  * In general update the parsing code so that this ^ works
* Add `Port-Version` fields to CONTROL files
  * This replaces the existing practice of
    `Version: <my-version>-<port-version>`

==== Smaller changes ====
* small drive-by cleanups to some CMake
  * `${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}` ->
    `${CURRENT_INSTALLED_DIR}`
  * Remove `-analyze` when compiling with clang-cl, since that's not a
    supported flag (vcpkg's build system)
  * Add a message about which compiler is detected by vcpkg's build
    system machinery
* Fix `Expected::then`
* Convert `""` to `{}` for `std::string` and `fs::path`, to avoid a
  `strlen` (additionally, `.empty()` instead of `== ""`, and `.clear()`)
* Add `Strings::strto` which converts strings to numeric types
* Support built-in arrays and `StringView` for `Strings::join`
* Add `operator<` and friends to `StringView`
* Add `substr` to `StringView`
* SourceParagraphParser gets some new errors
This commit is contained in:
nicole mazzuca 2020-06-30 10:40:18 -07:00 committed by GitHub
parent 67ab6130b6
commit 1d8f0acc9c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
111 changed files with 6130 additions and 3359 deletions

2
.gitignore vendored
View File

@ -13,6 +13,8 @@
toolsrc/out*
toolsrc/CMakeSettings.json
# fuzzing
sync_dir*
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

View File

@ -41,7 +41,8 @@ We collect various telemetry events such as the command line used, the time of i
You can see the telemetry events any command by appending `--printmetrics` after the vcpkg command line.
In the source code (included in `toolsrc\`), you can search for calls to the functions `track_property()` and `track_metric()` to see every specific data point we collect.
In the source code (included in `toolsrc\`), you can search for calls to the functions `track_property()`, `track_feature()`, `track_metric()`, and `track_buildtime()`
to see every specific data point we collect.
## Avoid inadvertent disclosure information

View File

@ -34,12 +34,6 @@ rather than JSON5 or JSON with comments because JSON is the everywhere-supported
standard. That is not necessarily true of JSON with comments. Additionally, if one needs
to write a comment, they can do so via `"$reason"` or `"$comment"` fields.
### Why are `<platform-specification>`s so verbose?
In the initial implementation, we didn't want to do more parsing than is strictly necessary,
especially parsing languages which aren't defined anywhere. We may add a shorter way of
defining platform specifications in the future (more similar to those in control files).
## Specification
A manifest file shall have the name `vcpkg.json`, and shall be in the root directory of a package.
@ -54,26 +48,28 @@ to specify the shape of a value. Note that any object may contain any directives
a field key that starts with a `$`; these directive shall be ignored by `vcpkg`. Common
directives may include `"$schema"`, `"$comment"`, `"$reason"`.
A manifest must be a top-level object, and must have at least the following properties:
A manifest must be a top-level object, and must have at least:
* `"name"`: a `<package-name>`
* `"version"`: A `string`. This will be defined further later.
* [Semver](https://semver.org) is recommended but not required.
* One (and only one) of the following version fields:
* `"version-string"`: A `string`. Has no semantic meaning.
Equivalent to `CONTROL`'s `Version:` field.
* Other version fields will be defined by the Versions RFC
The simplest vcpkg.json looks like this:
```json
{
"name": "mypackage",
"version": "0.1.0-dev"
"version-string": "0.1.0-dev"
}
```
Additionally, it may contain the following properties:
* `"port-version"`: A non-negative integer. If this field doesn't exist, it's assumed to be `0`.
* Note that this is a change from existing CONTROL files, where versions were a part of the version string
* `"authors"`: An array of `string`s which contain the authors of a package
* `"authors": [ "Nicole Mazzuca <nicole@example.com>", "שלום עליכם <shalom@example.com>" ]`
* `"maintainers"`: An array of `string`s which contain the authors of a package
* `"maintainers": [ "Nicole Mazzuca <nicole@example.com>", "שלום עליכם <shalom@example.com>" ]`
* `"description"`: A string or array of strings containing the description of a package
* `"description": "mypackage is a package of mine"`
* `"homepage"`: A url which points to the homepage of a package
@ -86,8 +82,8 @@ Additionally, it may contain the following properties:
* `"dev-dependencies"`: An array of `<dependency>`s which are required only for developers (testing and the like)
* `"features"`: An array of `<feature>`s that the package supports
* `"default-features"`: An array of `<identifier>`s that correspond to features, which will be used by default.
* `"supports"`: A `<platform-specification>`
* `"supports": { "and": [ "win", { "not": "arm" } ] }`
* `"supports"`: A `<platform-expression>`
* `"supports": "windows & !arm"`
Any properties which are not listed, and which do not start with a `$`,
will be warned against and are reserved for future use.
@ -105,7 +101,7 @@ Build-Depends: glib, gettext, cairo, fontconfig, freetype, harfbuzz[glib] (!(win
```json
{
"name": "pango",
"version": "1.40.11",
"version-string": "1.40.11",
"port-version": 6,
"homepage": "https://ftp.gnome.org/pub/GNOME/sources/pango/",
"description": "Text and font handling library.",
@ -118,22 +114,15 @@ Build-Depends: glib, gettext, cairo, fontconfig, freetype, harfbuzz[glib] (!(win
{
"name": "harfbuzz",
"features": [ "glib" ],
"platform": {
"and": [
{ "not": { "and": [ "windows", "static" ] } },
{ "not": "osx" }
]
}
"platform": "!(windows & static) & !osx"
}
]
}
```
You may notice that the platform specification is fairly wordy. See [reasoning](#why-are-platform-specifications-so-verbose) for why.
## Behavior of the Tool
There will be two "modes" for vcpkg from this point forward: "classic", and "modern".
There will be two "modes" for vcpkg from this point forward: "classic", and "manifest".
The former will act exactly like the existing vcpkg workflow, so as to avoid breaking
anyone. The latter will be the mode only when the user either:
@ -146,11 +135,10 @@ anyone. The latter will be the mode only when the user either:
* The environment variable `VCPKG_FEATURE_FLAGS`
* The option `--feature-flags`
* (e.g., `--feature-flags=binarycaching,manifests`)
* If someone wants to use classic mode and silence the warning, they can add the
`-manifests` feature flag to disable the mode.
Additionally, we'll add the `--x-classic-mode` flag to allow someone to force classic
mode.
When in "modern" mode, the `installed` directory will be changed to
When in "manifest" mode, the `installed` directory will be changed to
`<manifest-root>/vcpkg_installed` (name up for bikeshedding).
The following commands will change behavior:
@ -158,49 +146,38 @@ The following commands will change behavior:
the manifest file, and will remove any dependencies
which are no longer in the dependency tree implied by the manifest file.
* `vcpkg install` with port arguments will give an error.
* `vcpkg x-clean` will be added, and will delete your `vcpkg_installed` directory.
The following commands will not work in modern mode, at least initially:
The following commands will not work in manifest mode, at least initially:
* `vcpkg x-set-installed`: `vcpkg install` serves the same function
* `vcpkg remove`
* `vcpkg export`
* `vcpkg import`
* `vcpkg create`
We may add these features back for modern mode once we understand how best to
We may add these features back for manifest mode once we understand how best to
implement them.
### Behavior of the Toolchain
Mostly, the toolchain file stays the same; however, we shall add one public cache variable:
Mostly, the toolchain file stays the same; however, we shall add
two public options:
```cmake
VCPKG_MANIFEST_ROOT:PATH=<path to the directory containing the vcpkg.json file>
VCPKG_MANIFEST_MODE:BOOL=<we found a manifest>
VCPKG_MANIFEST_INSTALL:BOOL=ON
```
and one function:
The first option either explicitly turns on, or off, manifest mode;
otherwise, we default to looking for a manifest file in the directory
tree upwards from the source directory.
```cmake
vcpkg_acquire_dependencies(
[TRIPLET <triplet>]
[MANIFEST <path to manifest>]
[INSTALL_DIRECTORY <install directory>])
```
The `VCPKG_MANIFEST_INSTALL` option tells the toolchain whether to
install the packages or not -- if you wish to install the manifest
dependencies manually, you can set this to off, and we also turn it
off for packages installed by vcpkg.
which installs the dependencies required by the manifest file.
The default for `TRIPLET` is `VCPKG_TARGET_TRIPLET`
(which is the default triplet for the configured system).
For example, on x64 Windows, it defaults to `x64-windows`.
The default for `INSTALL_DIRECTORY` is `${CMAKE_BINARY_DIR}/vcpkg_installed`.
Additionally, in the course of implementation, we would like to
look at adding the following function, but may not be able to:
It is almost certain that one should guard any use of this function
by `if(EXISTS CACHE{VCPKG_MANIFEST_FILE})`.
Additionally, if `-manifests` is set in the feature flags environment
variable, we turn off manifest mode in the toolchain, and we act like
the classic toolchain.
### Example - CMake Integration
@ -232,7 +209,7 @@ Therefore, in `vcpkg.json`, we'll need to depend on `fmt`:
```json
{
"name": "example",
"version": "0.0.1",
"version-string": "0.0.1",
"dependencies": [
"fmt"
]
@ -246,11 +223,6 @@ cmake_minimum_required(VERSION 3.14)
project(example CXX)
if(EXISTS CACHE{VCPKG_MANIFEST_FILE})
vcpkg_acquire_dependencies()
endif()
add_executable(example src/main.cxx)
find_package(fmt REQUIRED)
@ -285,7 +257,9 @@ Hello, world!
* Does not have multiple consecutive hyphens
* Does not begin nor end with a hyphen
* Is not a Windows filesystem reserved name
* Is not a vcpkg reserved name: "default".
* Is not a vcpkg reserved name: "default" or "core".
* In other words, it must follow the regex `[a-z0-9]+(-[a-z0-9]+)*`, and must not be any of:
* `{ prn, aux, nul, con, lpt[1-9], com[1-9], core, default }`
* `<package-name>`: A `string` consisting of a non-zero number of `<identifier>`s, separated by `.`.
* `a.b.c` is valid
* `a` is valid
@ -296,15 +270,29 @@ Hello, world!
* `"name"`: A `<package-name>`
* Optionally, `"features"`: an array of `<identifier>`s corresponding to features in the package.
* Optionally, `"default-features"`: a `boolean`. If this is false, then don't use the default features of the package; equivalent to core in existing CONTROL files. If this is true, do the default thing of including the default features.
* Optionally, `"platform"`: a `<platform-specification>`
* Optionally, `"platform"`: a `<platform-expression>`
* `<dependency.port>`: No extra fields are required.
* `<license-string>`: An SPDX license expression at version 3.8.
* `<platform-specification>`: A specification of a set of platforms; used in platform-specific dependencies and supports fields. One of:
* `<platform-specification.exact>`: A string denoting a triplet tag like “windows”, “osx”, etc.
* `<platform-specification.not>`: An object containing a member with key "not" and value `<platform-specification>`.
* `<platform-specification.and>`: An object containing a member with key "and" and value array of `<platform-specification>`s.
* `<platform-specification.or>`: An object containing a member with key "or" and value array of `<platform-specification>`s.
* `<license-string>`: An SPDX license expression at version 3.9.
* `<platform-expression>`: A specification of a set of platforms; used in platform-specific dependencies and supports fields. A string that is parsed as follows:
* `<platform-expression>`:
* `<platform-expression.not>`
* `<platform-expression.and>`
* `<platform-expression.or>`
* `<platform-expression.simple>`:
* `( <platform-expression> )`
* `<platform-expression.identifier>`
* `<platform-expression.identifier>`:
* regex: `/^[a-z0-9]+$/`
* `<platform-expression.not>`:
* `<platform-expression.simple>`
* `! <platform-expression.simple>`
* `<platform-expression.and>`
* `<platform-expression.not>`
* `<platform-expression.and> & <platform-expression.not>`
* `<platform-expression.or>`
* `<platform-expression.not>`
* `<platform-expression.or> | <platform-expression.not>`
* `<feature>`: An object containing the following:
* `"name"`: An `<identifier>`, the name of the feature
* `"description"`: A `string`, the description of the feature
* `"description"`: A `string` or array of `string`s, the description of the feature
* Optionally, `"dependencies"`: An array of `<dependency>`s, the dependencies used by this feature

View File

@ -15,6 +15,7 @@ subject to change without notice and should be considered highly unstable.
Non-exhaustive list of off-by-default features:
- `binarycaching`
- `manifest`
#### EDITOR

View File

@ -1,4 +0,0 @@
Source: 3fd
Version: 2.6.2-3
Description: C++ Framework For Fast Development
Build-Depends: boost-lockfree (windows), boost-regex (windows), poco (windows), sqlite3, rapidxml

22
ports/3fd/vcpkg.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "3fd",
"version-string": "2.6.2",
"port-version": 3,
"description": "C++ Framework For Fast Development",
"dependencies": [
{
"name": "boost-lockfree",
"platform": "windows"
},
{
"name": "boost-regex",
"platform": "windows"
},
{
"name": "poco",
"platform": "windows"
},
"sqlite3",
"rapidxml"
]
}

View File

@ -1,10 +0,0 @@
Source: abseil
Version: 2020-03-03-7
Homepage: https://github.com/abseil/abseil-cpp
Description: an open-source collection designed to augment the C++ standard library.
Abseil is an open-source collection of C++ library code designed to augment the C++ standard library. The Abseil library code is collected from Google's own C++ code base, has been extensively tested and used in production, and is the same code we depend on in our daily coding lives.
In some cases, Abseil provides pieces missing from the C++ standard; in others, Abseil provides alternatives to the standard for special needs we've found through usage in the Google code base. We denote those cases clearly within the library code we provide you.
Abseil is not meant to be a competitor to the standard library; we've just found that many of these utilities serve a purpose within our code base, and we now want to provide those resources to the C++ community as a whole.
Feature: cxx17
Description: Enable compiler C++17.

18
ports/abseil/vcpkg.json Normal file
View File

@ -0,0 +1,18 @@
{
"name": "abseil",
"version-string": "2020-03-03",
"port-version": 7,
"homepage": "https://github.com/abseil/abseil-cpp",
"description": [
"an open-source collection designed to augment the C++ standard library.",
"Abseil is an open-source collection of C++ library code designed to augment the C++ standard library. The Abseil library code is collected from Google's own C++ code base, has been extensively tested and used in production, and is the same code we depend on in our daily coding lives.",
"In some cases, Abseil provides pieces missing from the C++ standard; in others, Abseil provides alternatives to the standard for special needs we've found through usage in the Google code base. We denote those cases clearly within the library code we provide you.",
"Abseil is not meant to be a competitor to the standard library; we've just found that many of these utilities serve a purpose within our code base, and we now want to provide those resources to the C++ community as a whole."
],
"features": [
{
"name": "cxx17",
"description": "Enable compiler C++17."
}
]
}

View File

@ -1,4 +0,0 @@
Source: argparse
Version: 2.1
Description: Argument parser for modern C++
Homepage: https://github.com/p-ranav/argparse

View File

@ -0,0 +1,7 @@
{
"name": "argparse",
"version-string": "2.1",
"description": "Argument parser for modern C++",
"license": "MIT",
"homepage": "https://github.com/p-ranav/argparse"
}

View File

@ -1,5 +0,0 @@
Source: avisynthplus
Version: 3.6.0
Homepage: http://avs-plus.net/
Description: An improved version of the AviSynth frameserver, with improved features and developer friendliness
Supports: !(uwp|arm|static)

View File

@ -0,0 +1,7 @@
{
"name": "avisynthplus",
"version-string": "3.6.0",
"homepage": "http://avs-plus.net/",
"description": "An improved version of the AviSynth frameserver, with improved features and developer friendliness",
"supports": "!(uwp | arm | static)"
}

View File

@ -1,3 +1,4 @@
Source: cmocka
Version: 1.1.5-1
Version: 1.1.5
Port-Version: 2
Description: An elegant unit testing framework for C with support for mock objects

View File

@ -3,8 +3,8 @@ _find_package(${ARGS})
get_filename_component(_cmocka_lib_name ${CMOCKA_LIBRARY} NAME)
set(CMOCKA_LIBRARY
debug ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/lib/${_cmocka_lib_name}
optimized ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/${_cmocka_lib_name}
debug ${CURRENT_INSTALLED_DIR}/debug/lib/${_cmocka_lib_name}
optimized ${CURRENT_INSTALLED_DIR}/lib/${_cmocka_lib_name}
)
set(CMOCKA_LIBRARIES ${CMOCKA_LIBRARY})

View File

@ -1,5 +1,6 @@
Source: libarchive
Version: 3.4.1-3
Version: 3.4.1
Port-Version: 4
Homepage: https://github.com/libarchive/libarchive
Description: Library for reading and writing streaming archives
Build-Depends: zlib

View File

@ -22,8 +22,8 @@ if("@VCPKG_LIBRARY_LINKAGE@" STREQUAL "static")
list(APPEND LibArchive_LIBRARIES LibLZMA::LibLZMA)
endif()
if(@ENABLE_LZO@)
find_library(LZO_LIBRARY_DEBUG NAMES lzo2d lzo2 NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug" NO_DEFAULT_PATH)
find_library(LZO_LIBRARY_RELEASE NAMES lzo2 NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}" NO_DEFAULT_PATH)
find_library(LZO_LIBRARY_DEBUG NAMES lzo2d lzo2 NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}/debug" NO_DEFAULT_PATH)
find_library(LZO_LIBRARY_RELEASE NAMES lzo2 NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}" NO_DEFAULT_PATH)
if(LZO_LIBRARY_RELEASE)
list(APPEND LibArchive_LIBRARIES optimized ${LZO_LIBRARY_RELEASE})
endif()
@ -32,8 +32,8 @@ if("@VCPKG_LIBRARY_LINKAGE@" STREQUAL "static")
endif()
endif()
if(@ENABLE_ZSTD@)
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug" NO_DEFAULT_PATH)
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}" NO_DEFAULT_PATH)
find_library(ZSTD_LIBRARY_DEBUG NAMES zstdd zstd NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}/debug" NO_DEFAULT_PATH)
find_library(ZSTD_LIBRARY_RELEASE NAMES zstd NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}" NO_DEFAULT_PATH)
if(ZSTD_LIBRARY_RELEASE)
list(APPEND LibArchive_LIBRARIES optimized ${ZSTD_LIBRARY_RELEASE})
endif()

View File

@ -1,4 +1,5 @@
Source: libiconv
Version: 1.16-3
Version: 1.16
Port-Version: 4
Homepage: https://www.gnu.org/software/libiconv/
Description: GNU Unicode text conversion

View File

@ -2,8 +2,8 @@ include(SelectLibraryConfigurations)
_find_package(${ARGS})
if(Iconv_FOUND)
find_library(CHARSET_LIBRARY_DEBUG NAMES charsetd libcharsetd charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_RELEASE NAMES charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_DEBUG NAMES charsetd libcharsetd charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}/debug" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_RELEASE NAMES charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_RELEASE NAMES charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib)
select_library_configurations(CHARSET)
list(APPEND Iconv_LIBRARIES ${CHARSET_LIBRARIES})

View File

@ -2,7 +2,7 @@
find_library(PostgreSQL_LIBRARY_DEBUG
NAMES pq
PATHS
"${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/lib"
"${CURRENT_INSTALLED_DIR}/debug/lib"
NO_DEFAULT_PATH
)
_find_package(${ARGS})

View File

@ -1,5 +1,6 @@
Source: libxml2
Version: 2.9.9-6
Version: 2.9.9
Port-Version: 7
Homepage: https://xmlsoft.org/
Description: Libxml2 is the XML C parser and toolkit developed for the Gnome project (but usable outside of the Gnome platform)
Build-Depends: zlib, libiconv, liblzma

View File

@ -3,11 +3,11 @@ if(LibXml2_FOUND)
find_package(LibLZMA)
find_package(ZLIB)
include(SelectLibraryConfigurations)
find_library(ICONV_LIBRARY_DEBUG NAMES iconvd libiconvd iconv libiconv NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug" NO_DEFAULT_PATH)
find_library(ICONV_LIBRARY_RELEASE NAMES iconv libiconv NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}" NO_DEFAULT_PATH)
find_library(ICONV_LIBRARY_DEBUG NAMES iconvd libiconvd iconv libiconv NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}/debug" NO_DEFAULT_PATH)
find_library(ICONV_LIBRARY_RELEASE NAMES iconv libiconv NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}" NO_DEFAULT_PATH)
find_library(ICONV_LIBRARY_RELEASE NAMES iconv libiconv NAMES_PER_DIR PATH_SUFFIXES lib)
find_library(CHARSET_LIBRARY_DEBUG NAMES charsetd libcharsetd charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_RELEASE NAMES charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_DEBUG NAMES charsetd libcharsetd charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}/debug" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_RELEASE NAMES charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib PATHS "${CURRENT_INSTALLED_DIR}" NO_DEFAULT_PATH)
find_library(CHARSET_LIBRARY_RELEASE NAMES charset libcharset NAMES_PER_DIR PATH_SUFFIXES lib)
select_library_configurations(ICONV)
select_library_configurations(CHARSET)

View File

@ -77,10 +77,10 @@ function(find_qt_mkspec TARGET_PLATFORM_MKSPEC_OUT HOST_PLATFORM_MKSPEC_OUT EXT_
else()
endif()
foreach(_triplet ${_test_triplets})
find_program(QMAKE_PATH qmake PATHS ${_VCPKG_INSTALLED_DIR}/${_triplet}/tools/qt5/bin NO_DEFAULT_PATHS)
message(STATUS "Checking: ${_VCPKG_INSTALLED_DIR}/${_triplet}/tools/qt5/bin. ${QMAKE_PATH}")
find_program(QMAKE_PATH qmake PATHS ${VCPKG_INSTALLED_DIR}/${_triplet}/tools/qt5/bin NO_DEFAULT_PATHS)
message(STATUS "Checking: ${VCPKG_INSTALLED_DIR}/${_triplet}/tools/qt5/bin. ${QMAKE_PATH}")
if(QMAKE_PATH)
set(_tmp_host_root "${_VCPKG_INSTALLED_DIR}/${_triplet}/tools/qt5")
set(_tmp_host_root "${VCPKG_INSTALLED_DIR}/${_triplet}/tools/qt5")
set(_tmp_host_qmake ${QMAKE_PATH} PARENT_SCOPE)
message(STATUS "Qt host tools root dir within vcpkg: ${_tmp_host_root}")
break()

View File

@ -1,5 +1,6 @@
Source: raylib
Version: 3.0.0
Port-Version: 1
Description: A simple and easy-to-use library to enjoy videogames programming
Homepage: https://github.com/raysan5/raylib
Supports: !(arm|uwp)

View File

@ -6,8 +6,8 @@ if(raylib_FOUND)
get_filename_component(_raylib_lib_name ${raylib_LIBRARY} NAME)
set(raylib_LIBRARY
debug ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug/lib/${_raylib_lib_name}
optimized ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/${_raylib_lib_name}
debug ${CURRENT_INSTALLED_DIR}/debug/lib/${_raylib_lib_name}
optimized ${CURRENT_INSTALLED_DIR}/lib/${_raylib_lib_name}
)
set(raylib_LIBRARIES ${raylib_LIBRARY})

View File

@ -1,5 +1,6 @@
Source: spdk
Version: 19.01.1
Port-Version: 1
Description: Storage Performance Development Kit
Build-Depends: spdk-dpdk, spdk-ipsec, spdk-isal
Supports: linux

View File

@ -7,7 +7,7 @@ Add following to build examples/nvme/perf/perf.c
ADD_EXECUTABLE(SPDKTest perf.c)
TARGET_LINK_DIRECTORIES(SPDKTest PRIVATE ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/spdk)
TARGET_LINK_DIRECTORIES(SPDKTest PRIVATE ${VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/lib/spdk)
TARGET_LINK_LIBRARIES(SPDKTest PRIVATE
SPDK::spdk_app_rpc
SPDK::spdk_bdev

View File

@ -0,0 +1,63 @@
<#
#>
[CmdletBinding(PositionalBinding=$False)]
Param(
[Parameter(Mandatory=$True)]
[string]$Commit,
[Parameter()]
[string]$GithubRepository = "spdx/license-list-data",
[Parameter()]
[string]$LicensesOutFile = "$PSScriptRoot/../toolsrc/src/vcpkg/spdx-licenses.inc",
[Parameter()]
[string]$ExceptionsOutFile = "$PSScriptRoot/../toolsrc/src/vcpkg/spdx-exceptions.inc"
)
function Transform-JsonFile {
[CmdletBinding()]
Param(
[string]$Uri,
[string]$OutFile,
[string]$OuterName,
[string]$Id
)
$req = Invoke-WebRequest -Uri $Uri
if ($req.StatusCode -ne 200)
{
Write-Error "Failed to GET $Uri"
return
}
$json = $req.Content | ConvertFrom-Json -Depth 10
Write-Verbose "Writing output to $OutFile"
$fileContent = @(
"// Data downloaded from $Uri",
"// Generated by scripts/Generate-SpdxLicenseList.ps1",
"{")
$json.$OuterName | ForEach-Object {
$fileContent += " `"$($_.$Id)`","
}
$fileContent += "}"
$fileContent -join "`n" | Out-File -FilePath $OutFile -Encoding 'utf8'
}
$baseUrl = "https://raw.githubusercontent.com/$GithubRepository/$Commit/json"
Write-Verbose "Getting json files from $baseUrl"
Transform-JsonFile `
-Uri "$baseUrl/licenses.json" `
-OutFile $LicensesOutFile `
-OuterName 'licenses' `
-Id 'licenseId'
Transform-JsonFile `
-Uri "$baseUrl/exceptions.json" `
-OutFile $ExceptionsOutFile `
-OuterName 'exceptions' `
-Id 'licenseExceptionId'

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Rule
Name="VcpkgGeneral"
Name="VcpkgOptions"
DisplayName="Vcpkg"
PageTemplate="generic"
Description="Vcpkg"
@ -17,6 +17,18 @@
Category="General"
Default="true">
</BoolProperty>
<BoolProperty Name="VcpkgEnableManifest"
DisplayName="Use Vcpkg Manifest"
Description="Use the vcpkg manifest file to define your dependencies."
Category="General"
Default="false">
</BoolProperty>
<BoolProperty Name="VcpkgManifestInstall"
DisplayName="Install vcpkg dependencies"
Description="Install dependencies from the vcpkg manifest."
Category="General"
Default="true">
</BoolProperty>
<BoolProperty Name="VcpkgUseStatic"
DisplayName="Use static libraries"
Description="Vcpkg can build static libraries (e.g. x64-windows-static). This options changes the default triplet to use these static libraries by appending -static to $(VcpkgTriplet). This will not be shown in the evaluation of the Triplet within the UI."
@ -42,11 +54,19 @@
Subtype="folder"
Visible="false">
</StringProperty>
<StringProperty Name="VcpkgManifestRoot"
DisplayName="Vcpkg Manifest Root"
Description="The path to the directory which contains the manifest file, and the vcpkg_installed directory."
Category="General"
Subtype="folder"
Visible="false">
</StringProperty>
<StringProperty Name="VcpkgCurrentInstalledDir"
DisplayName="Package install directory"
Description="Defines the direct path to the installed Vcpkg packages. Only change this if you know what you are doing!"
Category="General"
Subtype="folder">
Subtype="folder"
Visible="false">
</StringProperty>
<EnumProperty Name="VcpkgConfiguration" DisplayName="Vcpkg Configuration" Description="Specifies if release or debug libraries build with vcpkg should be used." Category="General">
<EnumValue Name="Release" Switch="Release" DisplayName="Release" Description="Uses release libraries">

View File

@ -25,11 +25,17 @@
<VcpkgPlatformTarget Condition="'$(VcpkgPlatformTarget)' == ''">$(Platform)</VcpkgPlatformTarget>
</PropertyGroup>
<!-- Manifest files -->
<PropertyGroup>
<VcpkgEnableManifest Condition="'$(VcpkgEnableManifest)' == ''">false</VcpkgEnableManifest>
<VcpkgManifestInstall Condition="'$(VcpkgManifestInstall)' == ''">true</VcpkgManifestInstall>
<VcpkgManifestRoot>$([MSbuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), vcpkg.json))</VcpkgManifestRoot>
</PropertyGroup>
<!-- Set other defaults-->
<PropertyGroup>
<VcpkgUserTriplet Condition="'$(VcpkgUserTriplet)' == ''">$(VcpkgPlatformTarget)-$(VcpkgOSTarget)</VcpkgUserTriplet>
<VcpkgTriplet Condition="'$(VcpkgTriplet)' == ''">$(VcpkgUserTriplet)</VcpkgTriplet>
<VcpkgCurrentInstalledDir Condition="'$(VcpkgCurrentInstalledDir)' == ''">$(VcpkgRoot)\installed\$(VcpkgTriplet)\</VcpkgCurrentInstalledDir>
<VcpkgPageSchema>$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg-general.xml</VcpkgPageSchema>
</PropertyGroup>
</Project>

View File

@ -7,6 +7,9 @@
<VcpkgRoot Condition="'$(VcpkgRoot)' == ''">$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), .vcpkg-root))</VcpkgRoot>
<VcpkgConfiguration Condition="'$(VcpkgConfiguration)' == ''">$(Configuration)</VcpkgConfiguration>
<VcpkgPageSchema Condition="'$(VcpkgPageSchema)' == ''">$(VcpkgRoot)\scripts\buildsystems\msbuild\vcpkg-general.xml</VcpkgPageSchema>
<VcpkgEnableManifest Condition="'$(VcpkgEnableManifest)' == ''">false</VcpkgEnableManifest>
<VcpkgManifestInstall Condition="'$(VcpkgManifestInstall)' == ''">true</VcpkgManifestInstall>
<VcpkgManifestRoot>$([MSbuild]::GetDirectoryNameOfFileAbove($(MSBuildProjectDirectory), vcpkg.json))</VcpkgManifestRoot>
</PropertyGroup>
<!-- Set default OS Target-->
@ -31,19 +34,24 @@
</PropertyPageSchema>
</ItemGroup>
<!-- Update properties if using static libs-->
<PropertyGroup Condition="'$(VcpkgUseStatic)' == 'true'">
<VcpkgTripletTmp>$(VcpkgUserTriplet)</VcpkgTripletTmp>
<VcpkgTriplet>$(VcpkgTripletTmp)-static</VcpkgTriplet>
<VcpkgCurrentInstalledDir Condition="'$(VcpkgCurrentInstalledDir)' == '$(VcpkgRoot)\installed\$(VcpkgTripletTmp)\'">$(VcpkgRoot)\installed\$(VcpkgTriplet)\</VcpkgCurrentInstalledDir>
<VcpkgTripletTmp />
<!-- Update properties if manifests are enabled or disabled -->
<PropertyGroup Condition="'$(VcpkgEnableManifest)' == 'true'">
<VcpkgInstalledDir>$(VcpkgManifestRoot)\vcpkg_installed\</VcpkgInstalledDir>
</PropertyGroup>
<PropertyGroup Condition="'$(VcpkgEnableManifest)' != 'true'">
<VcpkgInstalledDir>$(VcpkgRoot)\installed\</VcpkgInstalledDir>
</PropertyGroup>
<!-- Update properties if using static libs-->
<PropertyGroup Condition="'$(VcpkgUseStatic)' == 'true'">
<VcpkgTriplet>$(VcpkgUserTriplet)-static</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Condition="'$(VcpkgUseStatic)' != 'true'">
<VcpkgTripletTmp>$(VcpkgTriplet)</VcpkgTripletTmp>
<VcpkgTriplet Condition="'$(VcpkgTriplet)'!='$(VcpkgUserTriplet)'">$(VcpkgUserTriplet)</VcpkgTriplet>
<VcpkgCurrentInstalledDir Condition="'$(VcpkgCurrentInstalledDir)' == '$(VcpkgRoot)\installed\$(VcpkgTripletTmp)\'">$(VcpkgRoot)\installed\$(VcpkgUserTriplet)\</VcpkgCurrentInstalledDir>
<VcpkgTripletTmp />
<VcpkgTriplet>$(VcpkgUserTriplet)</VcpkgTriplet>
</PropertyGroup>
<PropertyGroup Condition="'$(VcpkgCurrentInstalledDir)' == ''">
<VcpkgCurrentInstalledDir>$(VcpkgInstalledDir)$(VcpkgTriplet)</VcpkgCurrentInstalledDir>
</PropertyGroup>
<PropertyGroup Condition="'$(VcpkgEnabled)' == 'true'">
@ -51,6 +59,7 @@
<VcpkgNormalizedConfiguration Condition="$(VcpkgConfiguration.StartsWith('Release')) or '$(VcpkgConfiguration)' == 'RelWithDebInfo' or '$(VcpkgConfiguration)' == 'MinSizeRel'">Release</VcpkgNormalizedConfiguration>
<VcpkgRoot Condition="!$(VcpkgRoot.EndsWith('\'))">$(VcpkgRoot)\</VcpkgRoot>
<VcpkgCurrentInstalledDir Condition="!$(VcpkgCurrentInstalledDir.EndsWith('\'))">$(VcpkgCurrentInstalledDir)\</VcpkgCurrentInstalledDir>
<VcpkgManifestRoot Condition="!$(VcpkgManifestRoot.EndsWith('\'))">$(VcpkgManifestRoot)\</VcpkgManifestRoot>
<VcpkgApplocalDeps Condition="'$(VcpkgApplocalDeps)' == ''">true</VcpkgApplocalDeps>
<!-- Deactivate Autolinking if lld is used as a linker. (Until a better way to solve the problem is found!).
Tried to add /lib as a parameter to the linker call but was unable to find a way to pass it as the first parameter. -->
@ -72,12 +81,22 @@
</ResourceCompile>
</ItemDefinitionGroup>
<Target Name="VcpkgCheckManifestRoot" Condition="'$(VcpkgEnabled)' == 'true'" BeforeTargets="VcpkgInstallManifestDependencies">
<Error Text="The Vcpkg manifest was enabled, but we couldn't find a manifest file (vcpkg.json) in any directories above $(MSBuildProjectDirectory). Please add a manifest, disable manifests in your properties page, or pass /p:VcpkgEnableManifest=false." Condition="'$(VcpkgEnableManifest)' == 'true' and '$(VcpkgManifestRoot)' == ''" />
<Message Text="The Vcpkg manifest was disabled, but we found a manifest file in $(VcpkgManifestRoot). You may want to enable vcpkg manifests in your properties page or pass /p:VcpkgEnableManifest=true to the msbuild invocation." Importance="High" Condition="'$(VcpkgEnableManifest)' != 'true' and '$(VcpkgManifestRoot)' != ''" />
</Target>
<Target Name="VcpkgTripletSelection" BeforeTargets="ClCompile">
<Message Text="Using triplet &quot;$(VcpkgTriplet)&quot; from &quot;$(VcpkgCurrentInstalledDir)&quot;" Importance="High" Condition="'$(VcpkgEnabled)' == 'true'"/>
<Message Text="Not using Vcpkg because VcpkgEnabled is &quot;$(VcpkgEnabled)&quot;" Importance="High" Condition="'$(VcpkgEnabled)' != 'true'"/>
<Message Text="Vcpkg is unable to link because we cannot decide between Release and Debug libraries. Please define the property VcpkgConfiguration to be 'Release' or 'Debug' (currently '$(VcpkgConfiguration)')." Importance="High" Condition="'$(VcpkgEnabled)' == 'true' and '$(VcpkgNormalizedConfiguration)' == ''"/>
</Target>
<Target Name="VcpkgInstallManifestDependencies" Condition="'$(VcpkgEnabled)' == 'true' and '$(VcpkgEnableManifest)' == 'true' and '$(VcpkgManifestInstall)' == 'true'" BeforeTargets="ClCompile">
<Message Text="Installing vcpkg dependencies" Importance="High" />
<Exec Command="%22$(VcpkgRoot)vcpkg.exe%22 install --triplet %22$(VcpkgTriplet)%22 --vcpkg-root %22$(VcpkgRoot)\%22 %22--x-manifest-root=$(VcpkgManifestRoot)\%22 %22--x-install-root=$(VcpkgInstalledDir)\%22 --binarycaching" StandardOutputImportance="High" />
</Target>
<Target Name="AppLocalFromInstalled" AfterTargets="CopyFilesToOutputDirectory" BeforeTargets="CopyLocalFilesOutputGroup;RegisterOutput" Condition="'$(VcpkgEnabled)' == 'true' and '$(VcpkgApplocalDeps)' == 'true'">
<WriteLinesToFile
File="$(TLogLocation)$(ProjectName).write.1u.tlog"

View File

@ -5,6 +5,46 @@ mark_as_advanced(CMAKE_TOOLCHAIN_FILE)
option(VCPKG_VERBOSE "Enables messages from the VCPKG toolchain for debugging purposes." OFF)
mark_as_advanced(VCPKG_VERBOSE)
function(_vcpkg_get_directory_name_of_file_above OUT DIRECTORY FILENAME)
if(DEFINED ${OUT})
return()
endif()
set(_vcpkg_get_dir_candidate ${DIRECTORY})
while(IS_DIRECTORY ${_vcpkg_get_dir_candidate} AND NOT DEFINED _vcpkg_get_dir_out)
if(EXISTS ${_vcpkg_get_dir_candidate}/${FILENAME})
set(_vcpkg_get_dir_out ${_vcpkg_get_dir_candidate})
else()
get_filename_component(_vcpkg_get_dir_candidate_tmp ${_vcpkg_get_dir_candidate} DIRECTORY)
if(_vcpkg_get_dir_candidate STREQUAL _vcpkg_get_dir_candidate_tmp) # we've reached the root
set(_vcpkg_get_dir_out "${OUT}-NOTFOUND")
else()
set(_vcpkg_get_dir_candidate ${_vcpkg_get_dir_candidate_tmp})
endif()
endif()
endwhile()
set(${OUT} ${_vcpkg_get_dir_out} CACHE INTERNAL "_vcpkg_get_directory_name_of_file_above: ${OUT}")
endfunction()
_vcpkg_get_directory_name_of_file_above(_VCPKG_MANIFEST_DIR ${CMAKE_CURRENT_SOURCE_DIR} "vcpkg.json")
if(_VCPKG_MANIFEST_DIR)
set(_VCPKG_MANIFEST_MODE_DEFAULT ON)
else()
set(_VCPKG_MANIFEST_MODE_DEFAULT OFF)
endif()
option(VCPKG_MANIFEST_MODE "Set vcpkg to manifest mode" ${_VCPKG_MANIFEST_MODE_DEFAULT})
if(NOT _VCPKG_MANIFEST_DIR AND VCPKG_MANIFEST_MODE)
message(FATAL_ERROR
"vcpkg manifest mode was enabled, but we couldn't find a manifest file (vcpkg.json) "
"in any directories above ${CMAKE_CURRENT_SOURCE_DIR}. Please add a manifest, or "
"disable manifests by turning off VCPKG_MANIFEST_MODE.")
endif()
option(VCPKG_MANIFEST_INSTALL "Install packages from the manifest" ON)
# Determine whether the toolchain is loaded during a try-compile configuration
get_property(_CMAKE_IN_TRY_COMPILE GLOBAL PROPERTY IN_TRY_COMPILE)
@ -144,19 +184,28 @@ if(NOT DEFINED _VCPKG_ROOT_DIR)
while(IS_DIRECTORY ${_VCPKG_ROOT_DIR_CANDIDATE} AND NOT EXISTS "${_VCPKG_ROOT_DIR_CANDIDATE}/.vcpkg-root")
get_filename_component(_VCPKG_ROOT_DIR_TEMP ${_VCPKG_ROOT_DIR_CANDIDATE} DIRECTORY)
if (_VCPKG_ROOT_DIR_TEMP STREQUAL _VCPKG_ROOT_DIR_CANDIDATE) # If unchanged, we have reached the root of the drive
message(FATAL_ERROR "Could not find .vcpkg-root")
else()
SET(_VCPKG_ROOT_DIR_CANDIDATE ${_VCPKG_ROOT_DIR_TEMP})
endif()
endwhile()
set(_VCPKG_ROOT_DIR ${_VCPKG_ROOT_DIR_CANDIDATE} CACHE INTERNAL "Vcpkg root directory")
endif()
_vcpkg_get_directory_name_of_file_above(_VCPKG_ROOT_DIR ${CMAKE_CURRENT_LIST_DIR} ".vcpkg-root")
if(NOT _VCPKG_ROOT_DIR)
message(FATAL_ERROR "Could not find .vcpkg-root")
endif()
if (NOT DEFINED _VCPKG_INSTALLED_DIR)
if(_VCPKG_MANIFEST_DIR)
set(_VCPKG_INSTALLED_DIR ${_VCPKG_MANIFEST_DIR}/vcpkg_installed)
else()
set(_VCPKG_INSTALLED_DIR ${_VCPKG_ROOT_DIR}/installed)
endif()
if(NOT EXISTS "${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}" AND NOT _CMAKE_IN_TRY_COMPILE AND NOT VCPKG_SUPPRESS_INSTALLED_LIBRARIES_WARNING)
message(WARNING "There are no libraries installed for the Vcpkg triplet ${VCPKG_TARGET_TRIPLET}.")
set(_VCPKG_INSTALLED_DIR ${_VCPKG_INSTALLED_DIR}
CACHE PATH
"The directory which contains the installed libraries for each triplet")
endif()
if(CMAKE_BUILD_TYPE MATCHES "^[Dd][Ee][Bb][Uu][Gg]$" OR NOT DEFINED CMAKE_BUILD_TYPE) #Debug build: Put Debug paths before Release paths.
@ -218,6 +267,31 @@ foreach(_VCPKG_TOOLS_DIR ${_VCPKG_TOOLS_DIRS})
endif()
endforeach()
# CMAKE_EXECUTABLE_SUFFIX is not yet defined
if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows")
set(_VCPKG_EXECUTABLE_SUFFIX ".exe")
else()
set(_VCPKG_EXECUTABLE_SUFFIX "")
endif()
if(VCPKG_MANIFEST_MODE AND VCPKG_MANIFEST_INSTALL AND NOT _CMAKE_IN_TRY_COMPILE)
execute_process(
COMMAND "${_VCPKG_ROOT_DIR}/vcpkg${_VCPKG_EXECUTABLE_SUFFIX}" install
--triplet ${VCPKG_TARGET_TRIPLET}
--vcpkg-root ${_VCPKG_ROOT_DIR}
--x-manifest-root=${_VCPKG_MANIFEST_DIR}
--x-install-root=${_VCPKG_INSTALLED_DIR}
--binarycaching
RESULT_VARIABLE _VCPKG_INSTALL_RESULT)
if (NOT _VCPKG_INSTALL_RESULT EQUAL 0)
message(FATAL_ERROR "vcpkg install failed")
endif()
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
"${_VCPKG_MANIFEST_DIR}/vcpkg.json"
"${_VCPKG_INSTALLED_DIR}/vcpkg/status")
endif()
option(VCPKG_APPLOCAL_DEPS "Automatically copy dependencies into the output directory for executables." ON)
function(add_executable name)
_add_executable(${ARGV})

View File

@ -96,6 +96,7 @@ function(vcpkg_build_msbuild)
/p:VCPkgLocalAppDataDisabled=true
/p:UseIntelMKL=No
/p:WindowsTargetPlatformVersion=${_csc_TARGET_PLATFORM_VERSION}
/p:VcpkgManifestInstall=false
/m
)

View File

@ -238,6 +238,7 @@ function(vcpkg_configure_cmake)
"-DCMAKE_INSTALL_BINDIR:STRING=bin"
"-D_VCPKG_ROOT_DIR=${VCPKG_ROOT_DIR}"
"-D_VCPKG_INSTALLED_DIR=${_VCPKG_INSTALLED_DIR}"
"-DVCPKG_MANIFEST_INSTALL=OFF"
)
if(DEFINED ARCH)

View File

@ -138,6 +138,7 @@ function(vcpkg_install_msbuild)
/p:WindowsTargetPlatformVersion=${_csc_TARGET_PLATFORM_VERSION}
/p:VcpkgTriplet=${TARGET_TRIPLET}
"/p:VcpkgCurrentInstalledDir=${CURRENT_INSTALLED_DIR}"
/p:VcpkgManifestInstall=false
/m
)

View File

@ -43,6 +43,9 @@ if(CMD MATCHES "^BUILD$")
if(NOT EXISTS ${CURRENT_PORT_DIR}/portfile.cmake)
message(FATAL_ERROR "Port is missing portfile: ${CURRENT_PORT_DIR}/portfile.cmake")
endif()
if(NOT EXISTS ${CURRENT_PORT_DIR}/CONTROL AND NOT EXISTS ${CURRENT_PORT_DIR}/vcpkg.json)
message(FATAL_ERROR "Port is missing control or manifest file: ${CURRENT_PORT_DIR}/{CONTROL,vcpkg.json}")
endif()
unset(PACKAGES_DIR)
unset(BUILDTREES_DIR)
@ -77,33 +80,44 @@ if(CMD MATCHES "^BUILD$")
elseif(CMD MATCHES "^CREATE$")
file(TO_NATIVE_PATH ${VCPKG_ROOT_DIR} NATIVE_VCPKG_ROOT_DIR)
file(TO_NATIVE_PATH ${DOWNLOADS} NATIVE_DOWNLOADS)
if(EXISTS ${VCPKG_ROOT_DIR}/ports/${PORT}/portfile.cmake)
message(FATAL_ERROR "Portfile already exists: '${NATIVE_VCPKG_ROOT_DIR}\\ports\\${PORT}\\portfile.cmake'")
set(PORT_PATH "${VCPKG_ROOT_DIR}/ports/${PORT}")
file(TO_NATIVE_PATH ${PORT_PATH} NATIVE_PORT_PATH)
set(PORTFILE_PATH "${PORT_PATH}/portfile.cmake")
file(TO_NATIVE_PATH ${PORTFILE_PATH} NATIVE_PORTFILE_PATH)
set(MANIFEST_PATH "${PORT_PATH}/vcpkg.json")
file(TO_NATIVE_PATH ${MANIFEST_PATH} NATIVE_MANIFEST_PATH)
if(EXISTS "${PORTFILE_PATH}")
message(FATAL_ERROR "Portfile already exists: '${NATIVE_PORTFILE_PATH}'")
endif()
if(NOT FILENAME)
get_filename_component(FILENAME "${URL}" NAME)
endif()
string(REGEX REPLACE "(\\.(zip|gz|tar|tgz|bz2))+\$" "" ROOT_NAME ${FILENAME})
if(EXISTS ${DOWNLOADS}/${FILENAME})
message(STATUS "Using pre-downloaded: ${NATIVE_DOWNLOADS}\\${FILENAME}")
message(STATUS "If this is not desired, delete the file and ${NATIVE_VCPKG_ROOT_DIR}\\ports\\${PORT}")
set(DOWNLOAD_PATH "${DOWNLOADS}/${FILENAME}")
file(TO_NATIVE_PATH ${DOWNLOAD_PATH} NATIVE_DOWNLOAD_PATH)
if(EXISTS "${DOWNLOAD_PATH}")
message(STATUS "Using pre-downloaded: ${NATIVE_DOWNLOAD_PATH}")
message(STATUS "If this is not desired, delete the file and ${NATIVE_PORT_PATH}")
else()
include(vcpkg_download_distfile)
set(_VCPKG_INTERNAL_NO_HASH_CHECK "TRUE")
set(_VCPKG_INTERNAL_NO_HASH_CHECK ON)
vcpkg_download_distfile(ARCHIVE
URLS ${URL}
FILENAME ${FILENAME}
)
set(_VCPKG_INTERNAL_NO_HASH_CHECK "FALSE")
set(_VCPKG_INTERNAL_NO_HASH_CHECK OFF)
endif()
file(SHA512 ${DOWNLOADS}/${FILENAME} SHA512)
file(SHA512 ${DOWNLOAD_PATH} SHA512)
file(MAKE_DIRECTORY ${VCPKG_ROOT_DIR}/ports/${PORT})
configure_file(${SCRIPTS}/templates/portfile.in.cmake ${VCPKG_ROOT_DIR}/ports/${PORT}/portfile.cmake @ONLY)
configure_file(${SCRIPTS}/templates/CONTROL.in ${VCPKG_ROOT_DIR}/ports/${PORT}/CONTROL @ONLY)
file(MAKE_DIRECTORY ${PORT_PATH})
configure_file(${SCRIPTS}/templates/portfile.in.cmake ${PORTFILE_PATH} @ONLY)
configure_file(${SCRIPTS}/templates/vcpkg.json.in ${MANIFEST_PATH} @ONLY)
message(STATUS "Generated portfile: ${NATIVE_VCPKG_ROOT_DIR}\\ports\\${PORT}\\portfile.cmake")
message(STATUS "Generated CONTROL: ${NATIVE_VCPKG_ROOT_DIR}\\ports\\${PORT}\\CONTROL")
message(STATUS "Generated portfile: ${NATIVE_PORTFILE_PATH}")
message(STATUS "Generated manifest: ${NATIVE_MANIFEST_PATH}")
message(STATUS "To launch an editor for these new files, run")
message(STATUS " .\\vcpkg edit ${PORT}")
endif()

View File

@ -1,10 +0,0 @@
Source: @PORT@
Version:
Homepage:
Description:
Build-Depends:
Default-Features:
Feature:
Description:
Build-Depends:

View File

@ -0,0 +1,16 @@
{
"name": "@PORT@",
"version-string": "",
"homepage": "",
"description": "",
"dependencies": [],
"default-features": [],
"features": [
{
"name": "",
"description": "",
"dependencies": []
}
]
}

View File

@ -51,9 +51,11 @@ if(MSVC)
if(VCPKG_DEVELOPMENT_WARNINGS)
string(REGEX REPLACE "[-/]W[0-4]" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
add_compile_options(-W4 -analyze)
add_compile_options(-W4)
if(VCPKG_COMPILER STREQUAL "clang")
add_compile_options(-Wmissing-prototypes -Wno-missing-field-initializers)
else()
add_compile_options(-analyze)
endif()
endif()

View File

@ -1,6 +1,7 @@
# Outputs to Cache: VCPKG_COMPILER
function(vcpkg_detect_compiler)
if(NOT DEFINED CACHE{VCPKG_COMPILER})
message(STATUS "Detecting the C++ compiler in use")
if(CMAKE_COMPILER_IS_GNUXX OR CMAKE_CXX_COMPILER_ID MATCHES "GNU")
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 6.0)
message(FATAL_ERROR [[
@ -45,6 +46,7 @@ If you would like to try anyway, pass --allowAppleClang to bootstrap.sh.
set(VCPKG_COMPILER ${COMPILER}
CACHE STRING
"The compiler in use; one of gcc, clang, msvc")
message(STATUS "Detecting the C++ compiler in use - ${VCPKG_COMPILER}")
endif()
endfunction()

View File

@ -213,7 +213,7 @@ namespace vcpkg
}
else
{
return U{std::move(error()), expected_right_tag};
return U{std::move(*this).error(), expected_right_tag};
}
}

View File

@ -59,6 +59,17 @@ namespace fs
perms m_permissions;
};
struct SystemHandle
{
using type = intptr_t; // HANDLE
type system_handle = -1;
bool is_valid() const
{
return system_handle != -1;
}
};
#else
using stdfs::file_type;
@ -71,6 +82,17 @@ namespace fs
using stdfs::file_status::type;
};
struct SystemHandle
{
using type = int; // file descriptor
type system_handle = -1;
bool is_valid() const
{
return system_handle != -1;
}
};
#endif
inline bool is_symlink(file_status s) noexcept
@ -171,6 +193,9 @@ namespace vcpkg::Files
virtual void current_path(const fs::path& path, std::error_code&) = 0;
void current_path(const fs::path& path, LineInfo li);
virtual fs::SystemHandle try_take_exclusive_file_lock(const fs::path& path, std::error_code&) = 0;
virtual void unlock_file_lock(fs::SystemHandle handle, std::error_code&) = 0;
virtual std::vector<fs::path> find_from_PATH(const std::string& name) const = 0;
};

View File

@ -109,11 +109,13 @@ namespace vcpkg::Json
double number() const noexcept;
StringView string() const noexcept;
const Array& array() const noexcept;
Array& array() noexcept;
const Array& array() const& noexcept;
Array& array() & noexcept;
Array&& array() && noexcept;
const Object& object() const noexcept;
Object& object() noexcept;
const Object& object() const& noexcept;
Object& object() & noexcept;
Object&& object() && noexcept;
static Value null(std::nullptr_t) noexcept;
static Value boolean(bool) noexcept;
@ -268,11 +270,141 @@ namespace vcpkg::Json
underlying_t underlying_;
};
// currently, a hard assertion on file errors
struct ReaderError
{
virtual void add_missing_field(std::string&& type, std::string&& key) = 0;
virtual void add_expected_type(std::string&& key, std::string&& expected_type) = 0;
virtual void add_extra_fields(std::string&& type, std::vector<std::string>&& fields) = 0;
virtual void add_mutually_exclusive_fields(std::string&& type, std::vector<std::string>&& fields) = 0;
virtual ~ReaderError() = default;
};
struct Reader
{
explicit Reader(ReaderError* err) : err(err) { }
ReaderError& error() const { return *err; }
private:
ReaderError* err;
template<class Visitor>
using VisitorType = typename std::remove_reference_t<Visitor>::type;
template<class Visitor>
Optional<VisitorType<Visitor>> internal_visit(const Value& value, StringView key, Visitor& visitor)
{
switch (value.kind())
{
using VK = Json::ValueKind;
case VK::Null: return visitor.visit_null(*this, key);
case VK::Boolean: return visitor.visit_boolean(*this, key, value.boolean());
case VK::Integer: return visitor.visit_integer(*this, key, value.integer());
case VK::Number: return visitor.visit_number(*this, key, value.number());
case VK::String: return visitor.visit_string(*this, key, value.string());
case VK::Array: return visitor.visit_array(*this, key, value.array());
case VK::Object: return visitor.visit_object(*this, key, value.object());
}
vcpkg::Checks::exit_fail(VCPKG_LINE_INFO);
}
// returns whether the field was found, not whether it was valid
template<class Visitor>
bool internal_field(const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor& visitor)
{
auto value = obj.get(key);
if (!value)
{
return false;
}
Optional<VisitorType<Visitor>> opt = internal_visit(*value, key, visitor);
if (auto val = opt.get())
{
place = std::move(*val);
}
else
{
err->add_expected_type(key.to_string(), visitor.type_name().to_string());
}
return true;
}
public:
template<class Visitor>
void required_object_field(
StringView type, const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor&& visitor)
{
if (!internal_field(obj, key, place, visitor))
{
err->add_missing_field(type.to_string(), key.to_string());
}
}
template<class Visitor>
void optional_object_field(const Object& obj, StringView key, VisitorType<Visitor>& place, Visitor&& visitor)
{
internal_field(obj, key, place, visitor);
}
template<class Visitor>
Optional<std::vector<VisitorType<Visitor>>> array_elements(const Array& arr, StringView key, Visitor&& visitor)
{
std::vector<VisitorType<Visitor>> result;
for (const auto& el : arr)
{
auto opt = internal_visit(el, key, visitor);
if (auto p = opt.get())
{
result.push_back(std::move(*p));
}
else
{
return nullopt;
}
}
return std::move(result);
}
};
// Warning: NEVER use this type except as a CRTP base
template<class Underlying>
struct VisitorCrtpBase
{
// the following function must be defined by the Underlying class
// const char* type_name();
// We do this auto dance since function bodies are checked _after_ typedefs in the superclass,
// but function declarations are checked beforehand. Therefore, we can get C++ to use this typedef
// only once the function bodies are checked by returning `auto` and specifying the
// return type in the function body.
auto visit_null(Reader&, StringView) { return Optional<typename Underlying::type>(nullopt); }
auto visit_boolean(Reader&, StringView, bool) { return Optional<typename Underlying::type>(nullopt); }
auto visit_integer(Reader& r, StringView field_name, int64_t i)
{
return static_cast<Underlying&>(*this).visit_number(r, field_name, static_cast<double>(i));
}
auto visit_number(Reader&, StringView, double) { return Optional<typename Underlying::type>(nullopt); }
auto visit_string(Reader&, StringView, StringView) { return Optional<typename Underlying::type>(nullopt); }
auto visit_array(Reader&, StringView, const Json::Array&)
{
return Optional<typename Underlying::type>(nullopt);
}
auto visit_object(Reader&, StringView, const Json::Object&)
{
return Optional<typename Underlying::type>(nullopt);
}
// we can't make the SMFs protected because of an issue with /permissive mode
};
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse_file(
const Files::Filesystem&, const fs::path&, std::error_code& ec) noexcept;
ExpectedT<std::pair<Value, JsonStyle>, std::unique_ptr<Parse::IParseError>> parse(
StringView text, const fs::path& filepath = "") noexcept;
StringView text, const fs::path& filepath = {}) noexcept;
std::string stringify(const Value&, JsonStyle style);
std::string stringify(const Object&, JsonStyle style);

View File

@ -8,6 +8,10 @@
#include <vcpkg/base/stringview.h>
#include <vcpkg/base/view.h>
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
#include <vector>
namespace vcpkg::Strings::details
@ -48,6 +52,7 @@ namespace vcpkg::Strings::details
}
inline void append_internal(std::string& into, const char* v) { into.append(v); }
inline void append_internal(std::string& into, const std::string& s) { into.append(s); }
inline void append_internal(std::string& into, StringView s) { into.append(s.begin(), s.end()); }
template<class T, class = decltype(std::declval<const T&>().to_string(std::declval<std::string&>()))>
void append_internal(std::string& into, const T& t)
@ -60,6 +65,11 @@ namespace vcpkg::Strings::details
{
to_string(into, t);
}
struct tolower_char
{
char operator()(char c) const { return (c < 'A' || c > 'Z') ? c : c - 'A' + 'a'; }
};
}
namespace vcpkg::Strings
@ -117,6 +127,11 @@ namespace vcpkg::Strings
bool case_insensitive_ascii_equals(StringView left, StringView right);
template<class It>
void ascii_to_lowercase(It first, It last)
{
std::transform(first, last, first, details::tolower_char{});
}
std::string ascii_to_lowercase(std::string&& s);
std::string ascii_to_uppercase(std::string&& s);
@ -134,11 +149,11 @@ namespace vcpkg::Strings
}
std::string output;
output.append(transformer(*begin));
append(output, transformer(*begin));
for (auto it = std::next(begin); it != end; ++it)
{
output.append(delimiter);
output.append(transformer(*it));
append(output, transformer(*it));
}
return output;
@ -147,8 +162,8 @@ namespace vcpkg::Strings
template<class Container, class Transformer>
std::string join(const char* delimiter, const Container& v, Transformer transformer)
{
const auto begin = v.begin();
const auto end = v.end();
const auto begin = std::begin(v);
const auto end = std::end(v);
return join(delimiter, begin, end, transformer);
}
@ -163,7 +178,7 @@ namespace vcpkg::Strings
template<class Container>
std::string join(const char* delimiter, const Container& v)
{
using Element = decltype(*v.begin());
using Element = decltype(*std::begin(v));
return join(delimiter, v, [](const Element& x) -> const Element& { return x; });
}
@ -173,7 +188,7 @@ namespace vcpkg::Strings
void trim_all_and_remove_whitespace_strings(std::vector<std::string>* strings);
std::vector<std::string> split(const std::string& s, const char delimiter);
std::vector<std::string> split(StringView s, const char delimiter);
const char* find_first_of(StringView searched, StringView candidates);
@ -193,6 +208,77 @@ namespace vcpkg::Strings
return ret;
}
// Equivalent to one of the `::strto[T]` functions. Returns `nullopt` if there is an error.
template<class T>
Optional<T> strto(CStringView sv);
template<>
inline Optional<double> strto<double>(CStringView sv)
{
char* endptr = nullptr;
double res = strtod(sv.c_str(), &endptr);
if (endptr == sv.c_str())
{
// no digits
return nullopt;
}
// else, we may have HUGE_VAL but we expect the caller to deal with that
return res;
}
template<>
inline Optional<long> strto<long>(CStringView sv)
{
char* endptr = nullptr;
long res = strtol(sv.c_str(), &endptr, 10);
if (endptr == sv.c_str())
{
// no digits
return nullopt;
}
if (errno == ERANGE)
{
// out of bounds
return nullopt;
}
return res;
}
template<>
inline Optional<long long> strto<long long>(CStringView sv)
{
char* endptr = nullptr;
long long res = strtoll(sv.c_str(), &endptr, 10);
if (endptr == sv.c_str())
{
// no digits
return nullopt;
}
if (errno == ERANGE)
{
// out of bounds
return nullopt;
}
return res;
}
template<>
inline Optional<int> strto<int>(CStringView sv)
{
auto res = strto<long>(sv);
if (auto r = res.get())
{
if (*r < INT_MIN || *r > INT_MAX)
{
return nullopt;
}
return static_cast<int>(*r);
}
return nullopt;
}
const char* search(StringView haystack, StringView needle);
bool contains(StringView haystack, StringView needle);

View File

@ -2,6 +2,7 @@
#include <vcpkg/base/optional.h>
#include <limits>
#include <string>
#include <vector>
@ -42,6 +43,23 @@ namespace vcpkg
std::string to_string() const;
void to_string(std::string& out) const;
constexpr StringView substr(size_t pos, size_t count = std::numeric_limits<size_t>::max()) const
{
if (pos > m_size)
{
return StringView();
}
if (count > m_size - pos)
{
return StringView(m_ptr + pos, m_size - pos);
}
return StringView(m_ptr + pos, count);
}
constexpr char byte_at_index(size_t pos) const { return m_ptr[pos]; }
private:
const char* m_ptr = 0;
size_t m_size = 0;
@ -49,4 +67,8 @@ namespace vcpkg
bool operator==(StringView lhs, StringView rhs) noexcept;
bool operator!=(StringView lhs, StringView rhs) noexcept;
bool operator<(StringView lhs, StringView rhs) noexcept;
bool operator>(StringView lhs, StringView rhs) noexcept;
bool operator<=(StringView lhs, StringView rhs) noexcept;
bool operator>=(StringView lhs, StringView rhs) noexcept;
}

View File

@ -32,15 +32,19 @@ namespace vcpkg
PackageSpec spec;
std::string version;
std::string description;
std::string maintainer;
int port_version = 0;
std::vector<std::string> description;
std::vector<std::string> maintainers;
std::string feature;
std::vector<std::string> default_features;
std::vector<std::string> depends;
std::vector<std::string> dependencies;
std::string abi;
Type type;
};
bool operator==(const BinaryParagraph&, const BinaryParagraph&);
bool operator!=(const BinaryParagraph&, const BinaryParagraph&);
struct BinaryControlFile
{
BinaryParagraph core_paragraph;

View File

@ -16,6 +16,12 @@ namespace vcpkg::Commands
using CommandTypeB = void (*)(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
using CommandTypeC = void (*)(const VcpkgCmdArguments& args, Files::Filesystem& fs);
enum class DryRun : bool
{
No,
Yes,
};
namespace BuildExternal
{
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet);
@ -86,11 +92,6 @@ namespace vcpkg::Commands
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
namespace Import
{
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
namespace Integrate
{
extern const CommandStructure COMMAND_STRUCTURE;
@ -146,9 +147,23 @@ namespace vcpkg::Commands
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
namespace FormatManifest
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths);
}
namespace SetInstalled
{
extern const CommandStructure COMMAND_STRUCTURE;
void perform_and_exit_ex(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
const PortFileProvider::PathsPortFileProvider& provider,
IBinaryProvider& binary_provider,
const CMakeVars::CMakeVarProvider& cmake_vars,
const std::vector<FullPackageSpec>& specs,
const Build::BuildPackageOptions& install_plan_options,
DryRun dry_run);
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet);
}

View File

@ -162,5 +162,5 @@ namespace vcpkg::Dependencies
void print_plan(const ActionPlan& action_plan,
const bool is_recursive = true,
const fs::path& default_ports_dir = "");
const fs::path& default_ports_dir = {});
}

View File

@ -1,24 +0,0 @@
#pragma once
#include <string>
#include <unordered_map>
#include <vcpkg/base/expected.h>
namespace vcpkg
{
struct ExpressionContext
{
// map of cmake variables and their values.
const std::unordered_map<std::string, std::string>& cmake_context;
// The legacy context is a string (typically the name of the triplet).
// An identifier was considered 'true' if it is a substring of this.
// It is now used for backwards compatability diagnostic messages and
// will be eventually removed.
const std::string& legacy_context;
};
// Evaluate simple vcpkg logic expressions. An identifier in the expression is considered 'true'
// if it is a substring of the evaluation_context (typically the name of the triplet)
ExpectedT<bool, std::string> evaluate_expression(const std::string& expression, const ExpressionContext& context);
}

View File

@ -18,6 +18,7 @@ namespace vcpkg::Metrics
void track_metric(const std::string& name, double value);
void track_buildtime(const std::string& name, double value);
void track_property(const std::string& name, const std::string& value);
void track_feature(const std::string& feature, bool value);
bool metrics_enabled();

View File

@ -2,6 +2,7 @@
#include <vcpkg/base/expected.h>
#include <vcpkg/base/optional.h>
#include <vcpkg/platform-expression.h>
#include <vcpkg/triplet.h>
namespace vcpkg::Parse
@ -123,10 +124,9 @@ namespace vcpkg
struct Dependency
{
Features depend;
std::string qualifier;
static ExpectedS<Dependency> from_string(const std::string& input);
std::string name;
std::vector<std::string> features;
PlatformExpression::Expr platform;
};
struct ParsedQualifiedSpecifier
@ -134,7 +134,7 @@ namespace vcpkg
std::string name;
Optional<std::vector<std::string>> features;
Optional<std::string> triplet;
Optional<std::string> qualifier;
Optional<PlatformExpression::Expr> platform;
};
Optional<std::string> parse_feature_name(Parse::ParserBase& parser);

View File

@ -4,6 +4,7 @@
#include <vcpkg/packagespec.h>
#include <vcpkg/textrowcol.h>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
@ -14,9 +15,16 @@ namespace vcpkg::Parse
struct ParseControlErrorInfo
{
std::string name;
std::vector<std::string> missing_fields;
std::vector<std::string> extra_fields;
std::map<std::string, std::vector<std::string>> missing_fields;
std::map<std::string, std::vector<std::string>> extra_fields;
std::map<std::string, std::string> expected_types;
std::map<std::string, std::vector<std::string>> mutually_exclusive_fields;
std::string error;
bool has_error() const
{
return !missing_fields.empty() || !extra_fields.empty() || !expected_types.empty() || !error.empty();
}
};
template<class P>
@ -28,15 +36,21 @@ namespace vcpkg::Parse
{
ParagraphParser(Paragraph&& fields) : fields(std::move(fields)) { }
std::string required_field(const std::string& fieldname);
void required_field(const std::string& fieldname, std::string& out);
std::string optional_field(const std::string& fieldname);
void required_field(const std::string& fieldname, std::pair<std::string&, TextRowCol&> out);
std::string optional_field(const std::string& fieldname);
void optional_field(const std::string& fieldname, std::pair<std::string&, TextRowCol&> out);
void add_type_error(const std::string& fieldname, const char* type) { expected_types[fieldname] = type; }
std::unique_ptr<ParseControlErrorInfo> error_info(const std::string& name) const;
private:
Paragraph&& fields;
std::vector<std::string> missing_fields;
std::map<std::string, std::string> expected_types;
};
ExpectedS<std::vector<std::string>> parse_default_features_list(const std::string& str,

View File

@ -10,11 +10,16 @@ namespace vcpkg::Paragraphs
{
using Paragraph = Parse::Paragraph;
ExpectedS<Paragraph> parse_single_paragraph(const std::string& str, const std::string& origin);
ExpectedS<Paragraph> get_single_paragraph(const Files::Filesystem& fs, const fs::path& control_path);
ExpectedS<std::vector<Paragraph>> get_paragraphs(const Files::Filesystem& fs, const fs::path& control_path);
ExpectedS<std::vector<Paragraph>> parse_paragraphs(const std::string& str, const std::string& origin);
Parse::ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& control_path);
bool is_port_directory(const Files::Filesystem& fs, const fs::path& path);
Parse::ParseExpected<SourceControlFile> try_load_manifest(const Files::Filesystem& fs, const std::string& port_name, const fs::path& path_to_manifest, std::error_code& ec);
Parse::ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path);
ExpectedS<BinaryControlFile> try_load_cached_package(const VcpkgPaths& paths, const PackageSpec& spec);

View File

@ -0,0 +1,73 @@
#pragma once
#include <string>
#include <unordered_map>
#include <vcpkg/base/expected.h>
#include <vcpkg/base/stringview.h>
namespace vcpkg::PlatformExpression
{
// map of cmake variables and their values.
using Context = std::unordered_map<std::string, std::string>;
namespace detail
{
struct ExprImpl;
}
struct Expr
{
static Expr Identifier(StringView id);
static Expr Not(Expr&& e);
static Expr And(std::vector<Expr>&& exprs);
static Expr Or(std::vector<Expr>&& exprs);
// The empty expression is always true
static Expr Empty() { return Expr(); }
// since ExprImpl is not yet defined, we need to define the ctor and dtor in the C++ file
Expr();
Expr(const Expr&);
Expr(Expr&&);
Expr& operator=(const Expr& e);
Expr& operator=(Expr&&);
explicit Expr(std::unique_ptr<detail::ExprImpl>&& e);
~Expr();
bool evaluate(const Context& context) const;
bool is_empty() const { return !static_cast<bool>(underlying_); }
private:
std::unique_ptr<detail::ExprImpl> underlying_;
};
// Note: for backwards compatibility, in CONTROL files,
// multiple binary operators are allowed to be next to one another; i.e.
// (windows & arm) = (windows && arm) = (windows &&& arm), etc.
enum class MultipleBinaryOperators
{
Deny,
Allow,
};
// platform expression parses the following :
// <platform-expression>:
// <platform-expression.not>
// <platform-expression.and>
// <platform-expression.or>
// <platform-expression.simple>:
// ( <platform-expression> )
// <platform-expression.identifier>
// <platform-expression.identifier>:
// A lowercase alpha-numeric string
// <platform-expression.not>:
// <platform-expression.simple>
// ! <platform-expression.simple>
// <platform-expression.and>
// <platform-expression.not>
// <platform-expression.and> & <platform-expression.not>
// <platform-expression.or>
// <platform-expression.not>
// <platform-expression.or> | <platform-expression.not>
ExpectedS<Expr> parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators);
}

View File

@ -26,7 +26,7 @@ namespace vcpkg::PortFileProvider
struct PathsPortFileProvider : Util::ResourceBase, PortFileProvider
{
explicit PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
const std::vector<std::string>* ports_dirs_paths);
const std::vector<std::string>& ports_dirs_paths);
ExpectedS<const SourceControlFileLocation&> get_control_file(const std::string& src_name) const override;
std::vector<const SourceControlFileLocation*> load_all_control_files() const override;

View File

@ -1,13 +1,13 @@
#pragma once
#include <string>
#include <vcpkg/base/expected.h>
#include <vcpkg/base/json.h>
#include <vcpkg/base/span.h>
#include <vcpkg/base/system.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/platform-expression.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/paragraphparser.h>
#include <vector>
namespace vcpkg
{
@ -28,14 +28,17 @@ namespace vcpkg
static Type from_string(const std::string&);
};
bool operator==(const Type&, const Type&);
bool operator!=(const Type&, const Type&);
/// <summary>
/// Port metadata of additional feature in a package (part of CONTROL file)
/// </summary>
struct FeatureParagraph
{
std::string name;
std::string description;
std::vector<Dependency> depends;
std::vector<std::string> description;
std::vector<Dependency> dependencies;
};
/// <summary>
@ -45,13 +48,17 @@ namespace vcpkg
{
std::string name;
std::string version;
std::string description;
std::string maintainer;
int port_version = 0;
std::vector<std::string> description;
std::vector<std::string> maintainers;
std::string homepage;
std::vector<Dependency> depends;
std::string documentation;
std::vector<Dependency> dependencies;
std::vector<std::string> default_features;
std::string license; // SPDX license expression
Type type;
std::string supports_expression;
PlatformExpression::Expr supports_expression;
};
/// <summary>
@ -69,9 +76,13 @@ namespace vcpkg
}
}
static Parse::ParseExpected<SourceControlFile> parse_manifest_file(const fs::path& path_to_manifest,
const Json::Object& object);
static Parse::ParseExpected<SourceControlFile> parse_control_file(
const fs::path& path_to_control, std::vector<Parse::Paragraph>&& control_paragraphs);
// Always non-null in non-error cases
std::unique_ptr<SourceParagraph> core_paragraph;
std::vector<std::unique_ptr<FeatureParagraph>> feature_paragraphs;

View File

@ -35,8 +35,8 @@ namespace vcpkg
/// <param name="triplet">Triplet</param>
/// <param name="feature">Feature name</param>
/// <returns>Iterator for found spec</returns>
iterator find(const std::string& name, Triplet triplet, const std::string& feature = "");
const_iterator find(const std::string& name, Triplet triplet, const std::string& feature = "") const;
iterator find(const std::string& name, Triplet triplet, const std::string& feature = {});
const_iterator find(const std::string& name, Triplet triplet, const std::string& feature = {}) const;
std::vector<std::unique_ptr<StatusParagraph>*> find_all(const std::string& name, Triplet triplet);

View File

@ -106,32 +106,63 @@ namespace vcpkg
static void append_common_options(HelpTableFormatter& target);
constexpr static StringLiteral VCPKG_ROOT_DIR_ENV = "VCPKG_ROOT";
constexpr static StringLiteral VCPKG_ROOT_DIR_ARG = "vcpkg-root";
std::unique_ptr<std::string> vcpkg_root_dir;
constexpr static StringLiteral MANIFEST_ROOT_DIR_ARG = "x-manifest-root";
std::unique_ptr<std::string> manifest_root_dir;
constexpr static StringLiteral BUILDTREES_ROOT_DIR_ARG = "x-buildtrees-root";
std::unique_ptr<std::string> buildtrees_root_dir;
constexpr static StringLiteral DOWNLOADS_ROOT_DIR_ENV = "VCPKG_DOWNLOADS";
constexpr static StringLiteral DOWNLOADS_ROOT_DIR_ARG = "downloads-root";
std::unique_ptr<std::string> downloads_root_dir;
constexpr static StringLiteral INSTALL_ROOT_DIR_ARG = "x-install-root";
std::unique_ptr<std::string> install_root_dir;
constexpr static StringLiteral PACKAGES_ROOT_DIR_ARG = "x-packages-root";
std::unique_ptr<std::string> packages_root_dir;
constexpr static StringLiteral SCRIPTS_ROOT_DIR_ARG = "x-scripts-root";
std::unique_ptr<std::string> scripts_root_dir;
constexpr static StringLiteral DEFAULT_VISUAL_STUDIO_PATH_ENV = "VCPKG_VISUAL_STUDIO_PATH";
std::unique_ptr<std::string> default_visual_studio_path;
constexpr static StringLiteral TRIPLET_ENV = "VCPKG_DEFAULT_TRIPLET";
constexpr static StringLiteral TRIPLET_ARG = "triplet";
std::unique_ptr<std::string> triplet;
std::unique_ptr<std::vector<std::string>> overlay_ports;
std::unique_ptr<std::vector<std::string>> overlay_triplets;
constexpr static StringLiteral OVERLAY_PORTS_ARG = "overlay-ports";
std::vector<std::string> overlay_ports;
constexpr static StringLiteral OVERLAY_TRIPLETS_ARG = "overlay-triplets";
std::vector<std::string> overlay_triplets;
std::vector<std::string> binarysources;
constexpr static StringLiteral BINARY_SOURCES_ARG = "x-binarysource";
std::vector<std::string> binary_sources;
constexpr static StringLiteral DEBUG_SWITCH = "debug";
Optional<bool> debug = nullopt;
constexpr static StringLiteral SEND_METRICS_SWITCH = "sendmetrics";
Optional<bool> send_metrics = nullopt;
// fully disable metrics -- both printing and sending
constexpr static StringLiteral DISABLE_METRICS_ENV = "VCPKG_DISABLE_METRICS";
constexpr static StringLiteral DISABLE_METRICS_SWITCH = "disable-metrics";
Optional<bool> disable_metrics = nullopt;
constexpr static StringLiteral PRINT_METRICS_SWITCH = "printmetrics";
Optional<bool> print_metrics = nullopt;
// feature flags
constexpr static StringLiteral FEATURE_FLAGS_ENV = "VCPKG_FEATURE_FLAGS";
constexpr static StringLiteral FEATURE_FLAGS_ARG = "feature-flags";
constexpr static StringLiteral FEATURE_PACKAGES_SWITCH = "featurepackages";
Optional<bool> feature_packages = nullopt;
constexpr static StringLiteral BINARY_CACHING_FEATURE = "binarycaching";
constexpr static StringLiteral BINARY_CACHING_SWITCH = "binarycaching";
Optional<bool> binary_caching = nullopt;
constexpr static StringLiteral COMPILER_TRACKING_FEATURE = "compilertracking";
Optional<bool> compiler_tracking = nullopt;
constexpr static StringLiteral MANIFEST_MODE_FEATURE = "manifests";
Optional<bool> manifest_mode = nullopt;
bool binary_caching_enabled() const { return binary_caching.value_or(false); }
bool compiler_tracking_enabled() const { return compiler_tracking.value_or(false); }
@ -142,6 +173,10 @@ namespace vcpkg
void imbue_from_environment();
void check_feature_flag_consistency() const;
void track_feature_flag_metrics() const;
private:
std::unordered_map<std::string, Optional<std::vector<std::string>>> optional_command_arguments;
};

View File

@ -71,7 +71,7 @@ namespace vcpkg
};
VcpkgPaths(Files::Filesystem& filesystem, const VcpkgCmdArguments& args);
~VcpkgPaths() noexcept;
~VcpkgPaths();
fs::path package_dir(const PackageSpec& spec) const;
fs::path build_info_file_path(const PackageSpec& spec) const;
@ -84,7 +84,7 @@ namespace vcpkg
fs::path original_cwd;
fs::path root;
fs::path manifest_root_dir;
fs::path buildtrees;
fs::path downloads;
fs::path packages;
@ -120,6 +120,9 @@ namespace vcpkg
const System::Environment& get_action_env(const Build::AbiInfo& abi_info) const;
const std::string& get_triplet_info(const Build::AbiInfo& abi_info) const;
bool manifest_mode_enabled() const { return !manifest_root_dir.empty(); }
void track_feature_flag_metrics() const;
private:
std::unique_ptr<details::VcpkgPathsImpl> m_pimpl;

View File

@ -2,6 +2,9 @@
#include <vcpkg/base/json.h>
#include <vcpkg/base/stringview.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/unicode.h>
#include <vcpkg/platform-expression.h>
#include <iostream>
#include <sstream>
@ -17,6 +20,7 @@ namespace
None,
Utf8Decoder,
JsonParser,
PlatformExpr,
};
struct FuzzArgs
@ -57,10 +61,14 @@ namespace
{
kind = FuzzKind::Utf8Decoder;
}
else if (value == "platform-expr")
{
kind = FuzzKind::PlatformExpr;
}
else
{
System::print2(System::Color::error, "Invalid kind: ", value, "\n");
System::print2(System::Color::error, " Expected one of: utf-8, json\n\n");
System::print2(System::Color::error, " Expected one of: utf-8, json, platform-expr\n\n");
print_help_and_exit(true);
}
}
@ -120,6 +128,44 @@ Options:
return std::move(ss).str();
}
[[noreturn]] void fuzz_json_and_exit(StringView text)
{
auto res = Json::parse(text);
if (!res)
{
Checks::exit_with_message(VCPKG_LINE_INFO, res.error()->format());
}
Checks::exit_success(VCPKG_LINE_INFO);
}
[[noreturn]] void fuzz_utf8_and_exit(StringView text)
{
auto res = Unicode::Utf8Decoder(text.begin(), text.end());
for (auto ch : res)
{
(void)ch;
}
Checks::exit_success(VCPKG_LINE_INFO);
}
[[noreturn]] void fuzz_platform_expr_and_exit(StringView text)
{
auto res1 = PlatformExpression::parse_platform_expression(text, PlatformExpression::MultipleBinaryOperators::Deny);
auto res2 = PlatformExpression::parse_platform_expression(text, PlatformExpression::MultipleBinaryOperators::Allow);
if (!res1)
{
Checks::exit_with_message(VCPKG_LINE_INFO, res1.error());
}
if (!res2)
{
Checks::exit_with_message(VCPKG_LINE_INFO, res2.error());
}
Checks::exit_success(VCPKG_LINE_INFO);
}
}
int main(int argc, char** argv)
@ -132,13 +178,11 @@ int main(int argc, char** argv)
}
auto text = read_all_of_stdin();
auto res = Json::parse(text);
if (!res)
switch (args.kind)
{
System::print2(System::Color::error, res.error()->format());
}
else
{
System::print2(System::Color::success, "success!");
case FuzzKind::JsonParser: fuzz_json_and_exit(text);
case FuzzKind::Utf8Decoder: fuzz_utf8_and_exit(text);
case FuzzKind::PlatformExpr: fuzz_platform_expr_and_exit(text);
default: Checks::exit_fail(VCPKG_LINE_INFO);
}
}

View File

@ -32,13 +32,13 @@ TEST_CASE ("VcpkgCmdArguments from lowercase argument sequence", "[arguments]")
REQUIRE(v.print_metrics);
REQUIRE(*v.print_metrics.get());
REQUIRE(v.overlay_ports->size() == 2);
REQUIRE(v.overlay_ports->at(0) == "C:\\ports1");
REQUIRE(v.overlay_ports->at(1) == "C:\\ports2");
REQUIRE(v.overlay_ports.size() == 2);
REQUIRE(v.overlay_ports.at(0) == "C:\\ports1");
REQUIRE(v.overlay_ports.at(1) == "C:\\ports2");
REQUIRE(v.overlay_triplets->size() == 2);
REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA");
REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB");
REQUIRE(v.overlay_triplets.size() == 2);
REQUIRE(v.overlay_triplets.at(0) == "C:\\tripletsA");
REQUIRE(v.overlay_triplets.at(1) == "C:\\tripletsB");
}
TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]")
@ -64,13 +64,13 @@ TEST_CASE ("VcpkgCmdArguments from uppercase argument sequence", "[arguments]")
REQUIRE(v.print_metrics);
REQUIRE(*v.print_metrics.get());
REQUIRE(v.overlay_ports->size() == 2);
REQUIRE(v.overlay_ports->at(0) == "C:\\ports1");
REQUIRE(v.overlay_ports->at(1) == "C:\\ports2");
REQUIRE(v.overlay_ports.size() == 2);
REQUIRE(v.overlay_ports.at(0) == "C:\\ports1");
REQUIRE(v.overlay_ports.at(1) == "C:\\ports2");
REQUIRE(v.overlay_triplets->size() == 2);
REQUIRE(v.overlay_triplets->at(0) == "C:\\tripletsA");
REQUIRE(v.overlay_triplets->at(1) == "C:\\tripletsB");
REQUIRE(v.overlay_triplets.size() == 2);
REQUIRE(v.overlay_triplets.at(0) == "C:\\tripletsA");
REQUIRE(v.overlay_triplets.at(1) == "C:\\tripletsB");
}
TEST_CASE ("VcpkgCmdArguments from argument sequence with valued options", "[arguments]")

View File

@ -14,8 +14,10 @@ TEST_CASE ("parse depends", "[dependencies]")
REQUIRE(w);
auto& v = *w.get();
REQUIRE(v.size() == 1);
REQUIRE(v.at(0).depend.name == "liba");
REQUIRE(v.at(0).qualifier == "windows");
REQUIRE(v.at(0).name == "liba");
REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}}));
REQUIRE(v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}}));
REQUIRE(!v.at(0).platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}}));
}
TEST_CASE ("filter depends", "[dependencies]")
@ -48,14 +50,17 @@ TEST_CASE ("parse feature depends", "[dependencies]")
auto& v = *u_.get();
REQUIRE(v.size() == 2);
auto&& a0 = v.at(0);
REQUIRE(a0.depend.name == "libwebp");
REQUIRE(a0.depend.features.size() == 9);
REQUIRE(a0.qualifier.empty());
REQUIRE(a0.name == "libwebp");
REQUIRE(a0.features.size() == 9);
REQUIRE(a0.platform.is_empty());
auto&& a1 = v.at(1);
REQUIRE(a1.depend.name == "libwebp");
REQUIRE(a1.depend.features.size() == 2);
REQUIRE(a1.qualifier == "!osx");
REQUIRE(a1.name == "libwebp");
REQUIRE(a1.features.size() == 2);
REQUIRE(!a1.platform.is_empty());
REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}}));
REQUIRE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}}));
REQUIRE_FALSE(a1.platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}}));
}
TEST_CASE ("qualified dependency", "[dependencies]")

View File

@ -28,8 +28,8 @@ static std::string mystringify(const Value& val) { return Json::stringify(val, J
TEST_CASE ("JSON stringify weird strings", "[json]")
{
vcpkg::StringView str = U8_STR("😀 😁 😂 🤣 😃 😄 😅 😆 😉");
REQUIRE(mystringify(Value::string(str)) == ('"' + str.to_string() + '"'));
REQUIRE(mystringify(Value::string("\xED\xA0\x80")) == "\"\\ud800\""); // unpaired surrogate
REQUIRE(mystringify(Value::string(str)) == ('"' + str.to_string() + "\"\n"));
REQUIRE(mystringify(Value::string("\xED\xA0\x80")) == "\"\\ud800\"\n"); // unpaired surrogate
}
TEST_CASE ("JSON parse keywords", "[json]")

View File

@ -0,0 +1,235 @@
#include <catch2/catch.hpp>
#include <vcpkg-test/util.h>
#include <vcpkg/base/json.h>
#include <vcpkg/base/util.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/sourceparagraph.h>
using namespace vcpkg;
using namespace vcpkg::Paragraphs;
using namespace vcpkg::Test;
static Json::Object parse_json_object(StringView sv)
{
auto json = Json::parse(sv);
// we're not testing json parsing here, so just fail on errors
if (auto r = json.get())
{
return std::move(r->first.object());
}
else
{
Checks::exit_with_message(VCPKG_LINE_INFO, json.error()->format());
}
}
static Parse::ParseExpected<SourceControlFile> test_parse_manifest(StringView sv, bool expect_fail = false)
{
auto object = parse_json_object(sv);
auto res = SourceControlFile::parse_manifest_file(fs::u8path("<test manifest>"), object);
if (!res.has_value() && !expect_fail)
{
print_error_message(res.error());
}
return res;
}
TEST_CASE ("manifest construct minimum", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "1.2.8"
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->name == "zlib");
REQUIRE(pgh.core_paragraph->version == "1.2.8");
REQUIRE(pgh.core_paragraph->maintainers.empty());
REQUIRE(pgh.core_paragraph->description.empty());
REQUIRE(pgh.core_paragraph->dependencies.empty());
}
TEST_CASE ("manifest construct maximum", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "s",
"version-string": "v",
"maintainers": ["m"],
"description": "d",
"dependencies": ["bd"],
"default-features": ["df"],
"features": [
{
"name": "iroh",
"description": "zuko's uncle",
"dependencies": [
{
"name": "tea"
},
"firebending",
{
"name": "order.white-lotus",
"features": [ "the-ancient-ways" ],
"platform": "!(windows & arm)"
}
]
},
{
"name": "zuko",
"description": ["son of the fire lord", "firebending 師父"]
}
]
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->name == "s");
REQUIRE(pgh.core_paragraph->version == "v");
REQUIRE(pgh.core_paragraph->maintainers.size() == 1);
REQUIRE(pgh.core_paragraph->maintainers[0] == "m");
REQUIRE(pgh.core_paragraph->description.size() == 1);
REQUIRE(pgh.core_paragraph->description[0] == "d");
REQUIRE(pgh.core_paragraph->dependencies.size() == 1);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "bd");
REQUIRE(pgh.core_paragraph->default_features.size() == 1);
REQUIRE(pgh.core_paragraph->default_features[0] == "df");
REQUIRE(pgh.feature_paragraphs.size() == 2);
REQUIRE(pgh.feature_paragraphs[0]->name == "iroh");
REQUIRE(pgh.feature_paragraphs[0]->description.size() == 1);
REQUIRE(pgh.feature_paragraphs[0]->description[0] == "zuko's uncle");
REQUIRE(pgh.feature_paragraphs[0]->dependencies.size() == 3);
REQUIRE(pgh.feature_paragraphs[0]->dependencies[0].name == "tea");
REQUIRE(pgh.feature_paragraphs[0]->dependencies[1].name == "firebending");
REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].name == "order.white-lotus");
REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].features.size() == 1);
REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].features[0] == "the-ancient-ways");
REQUIRE_FALSE(pgh.feature_paragraphs[0]->dependencies[2].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}, {"VCPKG_TARGET_ARCHITECTURE", "arm"}}));
REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}, {"VCPKG_TARGET_ARCHITECTURE", "x86"}}));
REQUIRE(pgh.feature_paragraphs[0]->dependencies[2].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}, {"VCPKG_TARGET_ARCHITECTURE", "x86"}}));
REQUIRE(pgh.feature_paragraphs[1]->name == "zuko");
REQUIRE(pgh.feature_paragraphs[1]->description.size() == 2);
REQUIRE(pgh.feature_paragraphs[1]->description[0] == "son of the fire lord");
REQUIRE(pgh.feature_paragraphs[1]->description[1] == "firebending 師父");
}
TEST_CASE ("SourceParagraph manifest two dependencies", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "1.2.8",
"dependencies": ["z", "openssl"]
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->dependencies.size() == 2);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "z");
REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl");
}
TEST_CASE ("SourceParagraph manifest three dependencies", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "1.2.8",
"dependencies": ["z", "openssl", "xyz"]
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->dependencies.size() == 3);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "z");
REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl");
REQUIRE(pgh.core_paragraph->dependencies[2].name == "xyz");
}
TEST_CASE ("SourceParagraph manifest construct qualified dependencies", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "zlib",
"version-string": "1.2.8",
"dependencies": [
{
"name": "liba",
"platform": "windows"
},
{
"name": "libb",
"platform": "uwp"
}
]
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->name == "zlib");
REQUIRE(pgh.core_paragraph->version == "1.2.8");
REQUIRE(pgh.core_paragraph->maintainers.empty());
REQUIRE(pgh.core_paragraph->description.empty());
REQUIRE(pgh.core_paragraph->dependencies.size() == 2);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "liba");
REQUIRE(pgh.core_paragraph->dependencies[0].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}}));
REQUIRE(pgh.core_paragraph->dependencies[1].name == "libb");
REQUIRE(pgh.core_paragraph->dependencies[1].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}}));
}
TEST_CASE ("SourceParagraph manifest default features", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "a",
"version-string": "1.0",
"default-features": ["a1"]
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->default_features.size() == 1);
REQUIRE(pgh.core_paragraph->default_features[0] == "a1");
}
TEST_CASE ("SourceParagraph manifest description paragraph", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "a",
"version-string": "1.0",
"description": ["line 1", "line 2", "line 3"]
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->description.size() == 3);
REQUIRE(pgh.core_paragraph->description[0] == "line 1");
REQUIRE(pgh.core_paragraph->description[1] == "line 2");
REQUIRE(pgh.core_paragraph->description[2] == "line 3");
}
TEST_CASE ("SourceParagraph manifest supports", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "a",
"version-string": "1.0",
"supports": "!(windows | osx)"
})json");
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Linux"}}));
REQUIRE_FALSE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}}));
REQUIRE_FALSE(pgh.core_paragraph->supports_expression.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "Darwin"}}));
}
TEST_CASE ("SourceParagraph manifest empty supports", "[manifests]")
{
auto m_pgh = test_parse_manifest(R"json({
"name": "a",
"version-string": "1.0",
"supports": ""
})json", true);
REQUIRE_FALSE(m_pgh.has_value());
}

View File

@ -8,8 +8,8 @@
namespace Strings = vcpkg::Strings;
using vcpkg::Parse::Paragraph;
namespace {
namespace
{
auto test_parse_control_file(const std::vector<std::unordered_map<std::string, std::string>>& v)
{
std::vector<Paragraph> pghs;
@ -45,9 +45,9 @@ TEST_CASE ("SourceParagraph construct minimum", "[paragraph]")
REQUIRE(pgh.core_paragraph->name == "zlib");
REQUIRE(pgh.core_paragraph->version == "1.2.8");
REQUIRE(pgh.core_paragraph->maintainer == "");
REQUIRE(pgh.core_paragraph->description == "");
REQUIRE(pgh.core_paragraph->depends.size() == 0);
REQUIRE(pgh.core_paragraph->maintainers.empty());
REQUIRE(pgh.core_paragraph->description.empty());
REQUIRE(pgh.core_paragraph->dependencies.size() == 0);
}
TEST_CASE ("SourceParagraph construct maximum", "[paragraph]")
@ -65,15 +65,17 @@ TEST_CASE ("SourceParagraph construct maximum", "[paragraph]")
REQUIRE(pgh.core_paragraph->name == "s");
REQUIRE(pgh.core_paragraph->version == "v");
REQUIRE(pgh.core_paragraph->maintainer == "m");
REQUIRE(pgh.core_paragraph->description == "d");
REQUIRE(pgh.core_paragraph->depends.size() == 1);
REQUIRE(pgh.core_paragraph->depends[0].depend.name == "bd");
REQUIRE(pgh.core_paragraph->maintainers.size() == 1);
REQUIRE(pgh.core_paragraph->maintainers[0] == "m");
REQUIRE(pgh.core_paragraph->description.size() == 1);
REQUIRE(pgh.core_paragraph->description[0] == "d");
REQUIRE(pgh.core_paragraph->dependencies.size() == 1);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "bd");
REQUIRE(pgh.core_paragraph->default_features.size() == 1);
REQUIRE(pgh.core_paragraph->default_features[0] == "df");
}
TEST_CASE ("SourceParagraph two depends", "[paragraph]")
TEST_CASE ("SourceParagraph two dependencies", "[paragraph]")
{
auto m_pgh = test_parse_control_file({{
{"Source", "zlib"},
@ -83,12 +85,12 @@ TEST_CASE ("SourceParagraph two depends", "[paragraph]")
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->depends.size() == 2);
REQUIRE(pgh.core_paragraph->depends[0].depend.name == "z");
REQUIRE(pgh.core_paragraph->depends[1].depend.name == "openssl");
REQUIRE(pgh.core_paragraph->dependencies.size() == 2);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "z");
REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl");
}
TEST_CASE ("SourceParagraph three depends", "[paragraph]")
TEST_CASE ("SourceParagraph three dependencies", "[paragraph]")
{
auto m_pgh = test_parse_control_file({{
{"Source", "zlib"},
@ -98,13 +100,13 @@ TEST_CASE ("SourceParagraph three depends", "[paragraph]")
REQUIRE(m_pgh.has_value());
auto& pgh = **m_pgh.get();
REQUIRE(pgh.core_paragraph->depends.size() == 3);
REQUIRE(pgh.core_paragraph->depends[0].depend.name == "z");
REQUIRE(pgh.core_paragraph->depends[1].depend.name == "openssl");
REQUIRE(pgh.core_paragraph->depends[2].depend.name == "xyz");
REQUIRE(pgh.core_paragraph->dependencies.size() == 3);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "z");
REQUIRE(pgh.core_paragraph->dependencies[1].name == "openssl");
REQUIRE(pgh.core_paragraph->dependencies[2].name == "xyz");
}
TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]")
TEST_CASE ("SourceParagraph construct qualified dependencies", "[paragraph]")
{
auto m_pgh = test_parse_control_file({{
{"Source", "zlib"},
@ -116,13 +118,13 @@ TEST_CASE ("SourceParagraph construct qualified depends", "[paragraph]")
REQUIRE(pgh.core_paragraph->name == "zlib");
REQUIRE(pgh.core_paragraph->version == "1.2.8");
REQUIRE(pgh.core_paragraph->maintainer == "");
REQUIRE(pgh.core_paragraph->description == "");
REQUIRE(pgh.core_paragraph->depends.size() == 2);
REQUIRE(pgh.core_paragraph->depends[0].depend.name == "liba");
REQUIRE(pgh.core_paragraph->depends[0].qualifier == "windows");
REQUIRE(pgh.core_paragraph->depends[1].depend.name == "libb");
REQUIRE(pgh.core_paragraph->depends[1].qualifier == "uwp");
REQUIRE(pgh.core_paragraph->maintainers.empty());
REQUIRE(pgh.core_paragraph->description.empty());
REQUIRE(pgh.core_paragraph->dependencies.size() == 2);
REQUIRE(pgh.core_paragraph->dependencies[0].name == "liba");
REQUIRE(pgh.core_paragraph->dependencies[0].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", ""}}));
REQUIRE(pgh.core_paragraph->dependencies[1].name == "libb");
REQUIRE(pgh.core_paragraph->dependencies[1].platform.evaluate({{"VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore"}}));
}
TEST_CASE ("SourceParagraph default features", "[paragraph]")
@ -150,10 +152,10 @@ TEST_CASE ("BinaryParagraph construct minimum", "[paragraph]")
REQUIRE(pgh.spec.name() == "zlib");
REQUIRE(pgh.version == "1.2.8");
REQUIRE(pgh.maintainer == "");
REQUIRE(pgh.description == "");
REQUIRE(pgh.maintainers.empty());
REQUIRE(pgh.description.empty());
REQUIRE(pgh.spec.triplet().canonical_name() == "x86-windows");
REQUIRE(pgh.depends.size() == 0);
REQUIRE(pgh.dependencies.size() == 0);
}
TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]")
@ -170,13 +172,15 @@ TEST_CASE ("BinaryParagraph construct maximum", "[paragraph]")
REQUIRE(pgh.spec.name() == "s");
REQUIRE(pgh.version == "v");
REQUIRE(pgh.maintainer == "m");
REQUIRE(pgh.description == "d");
REQUIRE(pgh.depends.size() == 1);
REQUIRE(pgh.depends[0] == "bd");
REQUIRE(pgh.maintainers.size() == 1);
REQUIRE(pgh.maintainers[0] == "m");
REQUIRE(pgh.description.size() == 1);
REQUIRE(pgh.description[0] == "d");
REQUIRE(pgh.dependencies.size() == 1);
REQUIRE(pgh.dependencies[0] == "bd");
}
TEST_CASE ("BinaryParagraph three depends", "[paragraph]")
TEST_CASE ("BinaryParagraph three dependencies", "[paragraph]")
{
auto pgh = test_make_binary_paragraph({
{"Package", "zlib"},
@ -186,10 +190,10 @@ TEST_CASE ("BinaryParagraph three depends", "[paragraph]")
{"Depends", "a, b, c"},
});
REQUIRE(pgh.depends.size() == 3);
REQUIRE(pgh.depends[0] == "a");
REQUIRE(pgh.depends[1] == "b");
REQUIRE(pgh.depends[2] == "c");
REQUIRE(pgh.dependencies.size() == 3);
REQUIRE(pgh.dependencies[0] == "a");
REQUIRE(pgh.dependencies[1] == "b");
REQUIRE(pgh.dependencies[2] == "c");
}
TEST_CASE ("BinaryParagraph abi", "[paragraph]")
@ -202,7 +206,7 @@ TEST_CASE ("BinaryParagraph abi", "[paragraph]")
{"Abi", "abcd123"},
});
REQUIRE(pgh.depends.size() == 0);
REQUIRE(pgh.dependencies.size() == 0);
REQUIRE(pgh.abi == "abcd123");
}
@ -216,7 +220,7 @@ TEST_CASE ("BinaryParagraph default features", "[paragraph]")
{"Default-Features", "a1"},
});
REQUIRE(pgh.depends.size() == 0);
REQUIRE(pgh.dependencies.size() == 0);
REQUIRE(pgh.default_features.size() == 1);
REQUIRE(pgh.default_features[0] == "a1");
}
@ -300,8 +304,8 @@ TEST_CASE ("parse paragraphs empty fields", "[paragraph]")
REQUIRE(pghs.size() == 1);
REQUIRE(pghs[0].size() == 2);
REQUIRE(pghs[0]["f1"].first == "");
REQUIRE(pghs[0]["f2"].first == "");
REQUIRE(pghs[0]["f1"].first.empty());
REQUIRE(pghs[0]["f2"].first.empty());
REQUIRE(pghs[0].size() == 2);
}

View File

@ -37,7 +37,7 @@ static void features_check(Dependencies::InstallPlanAction& plan,
for (auto&& feature_name : expected_features)
{
// TODO: see if this can be simplified
if (feature_name == "core" || feature_name == "")
if (feature_name == "core" || feature_name.empty())
{
REQUIRE((Util::find(feature_list, "core") != feature_list.end() ||
Util::find(feature_list, "") != feature_list.end()));

View File

@ -72,6 +72,8 @@ static void inner(vcpkg::Files::Filesystem& fs, const VcpkgCmdArguments& args)
}
const VcpkgPaths paths(fs, args);
paths.track_feature_flag_metrics();
fs.current_path(paths.root, VCPKG_LINE_INFO);
if (args.command == "install" || args.command == "remove" || args.command == "export" || args.command == "update")
{
@ -231,10 +233,11 @@ int main(const int argc, const char* const* const argv)
VcpkgCmdArguments args = VcpkgCmdArguments::create_from_command_line(fs, argc, argv);
args.imbue_from_environment();
args.check_feature_flag_consistency();
if (const auto p = args.disable_metrics.get()) Metrics::g_metrics.lock()->set_disabled(*p);
if (const auto p = args.print_metrics.get()) Metrics::g_metrics.lock()->set_print_metrics(*p);
if (const auto p = args.send_metrics.get()) Metrics::g_metrics.lock()->set_send_metrics(*p);
if (const auto p = args.disable_metrics.get()) Metrics::g_metrics.lock()->set_disabled(*p);
if (const auto p = args.debug.get()) Debug::g_debugging = *p;
if (args.send_metrics.has_value() && !Metrics::g_metrics.lock()->metrics_enabled())
@ -248,6 +251,8 @@ int main(const int argc, const char* const* const argv)
"Warning: passed either --printmetrics or --no-printmetrics, but metrics are disabled.\n");
}
args.track_feature_flag_metrics();
if (Debug::g_debugging)
{
inner(fs, args);

View File

@ -10,6 +10,7 @@
#if !defined(_WIN32)
#include <fcntl.h>
#include <sys/file.h>
#include <sys/stat.h>
#endif
@ -839,6 +840,97 @@ namespace vcpkg::Files
fs::stdfs::current_path(path, ec);
}
virtual fs::SystemHandle try_take_exclusive_file_lock(const fs::path& path, std::error_code& ec) override
{
fs::SystemHandle res;
const auto system_file_name = path.native();
#if defined(WIN32)
constexpr static auto busy_error = ERROR_BUSY;
const auto system_try_take_file_lock = [&] {
auto handle = CreateFileW(
system_file_name.c_str(),
GENERIC_READ,
0 /* no sharing */,
nullptr /* no security attributes */,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
nullptr /* no template file */);
if (handle == INVALID_HANDLE_VALUE)
{
const auto err = GetLastError();
if (err != ERROR_SHARING_VIOLATION)
{
ec.assign(err, std::system_category());
}
return false;
}
res.system_handle = reinterpret_cast<intptr_t>(handle);
return true;
};
#else // ^^^ WIN32 / !WIN32 vvv
constexpr static auto busy_error = EBUSY;
int fd = open(system_file_name.c_str(), 0);
if (fd < 0)
{
ec.assign(errno, std::system_category());
return res;
}
const auto system_try_take_file_lock = [&] {
if (flock(fd, LOCK_EX | LOCK_NB) != 0)
{
if (errno != EWOULDBLOCK)
{
ec.assign(errno, std::system_category());
}
return false;
}
res.system_handle = fd;
return true;
};
#endif
if (system_try_take_file_lock() || ec)
{
return res;
}
System::printf("Waiting to take filesystem lock on %s...\n", path.u8string());
auto wait = std::chrono::milliseconds(100);
// waits, at most, a second and a half.
while (wait < std::chrono::milliseconds(1000))
{
std::this_thread::sleep_for(wait);
if (system_try_take_file_lock() || ec)
{
return res;
}
wait *= 2;
}
#if !defined(WIN32)
close(fd);
#endif
ec.assign(busy_error, std::system_category());
return res;
}
virtual void unlock_file_lock(fs::SystemHandle handle, std::error_code& ec) override
{
#if defined(WIN32)
if (CloseHandle(reinterpret_cast<HANDLE>(handle.system_handle)) == 0)
{
ec.assign(GetLastError(), std::system_category());
}
#else
if (flock(handle.system_handle, LOCK_UN) != 0 || close(handle.system_handle) != 0)
{
ec.assign(errno, std::system_category());
}
#endif
}
virtual std::vector<fs::path> find_from_PATH(const std::string& name) const override
{
#if defined(_WIN32)

View File

@ -144,27 +144,35 @@ namespace vcpkg::Json
return underlying_->string;
}
const Array& Value::array() const noexcept
const Array& Value::array() const& noexcept
{
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_array());
return underlying_->array;
}
Array& Value::array() noexcept
Array& Value::array() & noexcept
{
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_array());
return underlying_->array;
}
Array&& Value::array() && noexcept
{
return std::move(this->array());
}
const Object& Value::object() const noexcept
const Object& Value::object() const& noexcept
{
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_object());
return underlying_->object;
}
Object& Value::object() noexcept
Object& Value::object() & noexcept
{
vcpkg::Checks::check_exit(VCPKG_LINE_INFO, is_object());
return underlying_->object;
}
Object&& Value::object() && noexcept
{
return std::move(this->object());
}
Value::Value() noexcept = default;
Value::Value(Value&&) noexcept = default;
@ -601,35 +609,39 @@ namespace vcpkg::Json
}
}
#ifdef _MSC_VER
#define SCANF sscanf_s
#else
#define SCANF sscanf
#endif
// TODO: switch to `from_chars` once we are able to remove support for old compilers
if (floating)
{
double res;
if (SCANF(number_to_parse.c_str(), "%lf", &res) != 1)
auto opt = Strings::strto<double>(number_to_parse);
if (auto res = opt.get())
{
add_error(Strings::format("Invalid floating point constant: %s", number_to_parse));
return Value();
}
return Value::number(res);
if (std::abs(*res) < INFINITY)
{
return Value::number(*res);
}
else
{
int64_t res;
if (SCANF(number_to_parse.c_str(), "%" SCNd64, &res) != 1)
add_error(Strings::format("Floating point constant too big: %s", number_to_parse));
}
}
else
{
add_error(Strings::format("Invalid floating point constant: %s", number_to_parse));
}
}
else
{
auto opt = Strings::strto<int64_t>(number_to_parse);
if (auto res = opt.get())
{
return Value::integer(*res);
}
else
{
add_error(Strings::format("Invalid integer constant: %s", number_to_parse));
return Value();
}
return Value::integer(res);
}
#undef SCANF
return Value();
}
Value parse_keyword() noexcept
@ -960,12 +972,15 @@ namespace vcpkg::Json
for (auto code_point : Unicode::Utf8Decoder(sv.begin(), sv.end()))
{
// a. If C is listed in the "Code Point" column of Table 66, then
const auto match = std::find_if(begin(escape_sequences), end(escape_sequences), [code_point](const std::pair<char32_t, const char*>& attempt) {
const auto match = std::find_if(begin(escape_sequences),
end(escape_sequences),
[code_point](const std::pair<char32_t, const char*>& attempt) {
return attempt.first == code_point;
});
// i. Set product to the string-concatenation of product and the escape sequence for C as
// specified in the "Escape Sequence" column of the corresponding row.
if (match != end(escape_sequences)) {
if (match != end(escape_sequences))
{
buffer.append(match->second);
continue;
}
@ -1086,18 +1101,21 @@ namespace vcpkg::Json
{
std::string res;
Stringifier{style, res}.stringify(value, 0);
res.push_back('\n');
return res;
}
std::string stringify(const Object& obj, JsonStyle style)
{
std::string res;
Stringifier{style, res}.stringify_object(obj, 0);
res.push_back('\n');
return res;
}
std::string stringify(const Array& arr, JsonStyle style)
{
std::string res;
Stringifier{style, res}.stringify_array(arr, 0);
res.push_back('\n');
return res;
}
// } auto stringify()

View File

@ -144,6 +144,13 @@ namespace vcpkg::Parse
optional_field(fieldname, {out, ignore});
return out;
}
std::string ParagraphParser::required_field(const std::string& fieldname)
{
std::string out;
TextRowCol ignore;
optional_field(fieldname, {out, ignore});
return out;
}
std::unique_ptr<ParseControlErrorInfo> ParagraphParser::error_info(const std::string& name) const
{
@ -151,8 +158,9 @@ namespace vcpkg::Parse
{
auto err = std::make_unique<ParseControlErrorInfo>();
err->name = name;
err->extra_fields = Util::extract_keys(fields);
err->missing_fields = std::move(missing_fields);
err->extra_fields["CONTROL"] = Util::extract_keys(fields);
err->missing_fields["CONTROL"] = std::move(missing_fields);
err->expected_types = std::move(expected_types);
return err;
}
return nullptr;
@ -214,7 +222,7 @@ namespace vcpkg::Parse
parser.add_error("triplet specifier not allowed in this context", loc);
return nullopt;
}
return Dependency{{pqs.name, pqs.features.value_or({})}, pqs.qualifier.value_or({})};
return Dependency{pqs.name, pqs.features.value_or({}), pqs.platform.value_or({})};
});
});
if (!opt) return {parser.get_error()->format(), expected_right_tag};

View File

@ -10,10 +10,9 @@ namespace vcpkg::Strings::details
static bool is_space(const char c) { return std::isspace(static_cast<unsigned char>(c)) != 0; }
// Avoids C4244 warnings because of char<->int conversion that occur when using std::tolower()
static char tolower_char(const char c) { return (c < 'A' || c > 'Z') ? c : c - 'A' + 'a'; }
static char toupper_char(const char c) { return (c < 'a' || c > 'z') ? c : c - 'a' + 'A'; }
static bool icase_eq(char a, char b) { return tolower_char(a) == tolower_char(b); }
static bool icase_eq(char a, char b) { return tolower_char{}(a) == tolower_char{}(b); }
#if defined(_WIN32)
static _locale_t& c_locale()
@ -102,7 +101,7 @@ bool Strings::case_insensitive_ascii_equals(StringView left, StringView right)
std::string Strings::ascii_to_lowercase(std::string&& s)
{
std::transform(s.begin(), s.end(), s.begin(), &details::tolower_char);
Strings::ascii_to_lowercase(s.begin(), s.end());
return std::move(s);
}
@ -157,7 +156,7 @@ void Strings::trim_all_and_remove_whitespace_strings(std::vector<std::string>* s
Util::erase_remove_if(*strings, [](const std::string& s) { return s.empty(); });
}
std::vector<std::string> Strings::split(const std::string& s, const char delimiter)
std::vector<std::string> Strings::split(StringView s, const char delimiter)
{
std::vector<std::string> output;
auto first = s.begin();

View File

@ -82,4 +82,13 @@ namespace vcpkg
}
bool operator!=(StringView lhs, StringView rhs) noexcept { return !(lhs == rhs); }
bool operator<(StringView lhs, StringView rhs) noexcept
{
return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end());
}
bool operator>(StringView lhs, StringView rhs) noexcept { return rhs < lhs; }
bool operator<=(StringView lhs, StringView rhs) noexcept { return !(rhs < lhs); }
bool operator>=(StringView lhs, StringView rhs) noexcept { return !(lhs < rhs); }
}

View File

@ -1048,7 +1048,7 @@ std::string vcpkg::generate_nuspec(const VcpkgPaths& paths,
auto& version = scf.core_paragraph->version;
std::string description =
Strings::concat("NOT FOR DIRECT USE. Automatically generated cache package.\n\n",
scf.core_paragraph->description,
Strings::join("\n ", scf.core_paragraph->description),
"\n\nVersion: ",
version,
"\nTriplet/Compiler hash: ",

View File

@ -6,6 +6,7 @@
#include <vcpkg/binaryparagraph.h>
#include <vcpkg/paragraphparser.h>
#include <vcpkg/paragraphs.h>
namespace vcpkg
{
@ -13,6 +14,7 @@ namespace vcpkg
{
static const std::string PACKAGE = "Package";
static const std::string VERSION = "Version";
static const std::string PORT_VERSION = "Port-Version";
static const std::string ARCHITECTURE = "Architecture";
static const std::string MULTI_ARCH = "Multi-Arch";
}
@ -24,7 +26,7 @@ namespace vcpkg
static const std::string DESCRIPTION = "Description";
static const std::string MAINTAINER = "Maintainer";
static const std::string DEPENDS = "Depends";
static const std::string DEFAULTFEATURES = "Default-Features";
static const std::string DEFAULT_FEATURES = "Default-Features";
static const std::string TYPE = "Type";
}
@ -48,15 +50,38 @@ namespace vcpkg
this->version = parser.optional_field(Fields::VERSION);
this->feature = parser.optional_field(Fields::FEATURE);
this->description = parser.optional_field(Fields::DESCRIPTION);
this->maintainer = parser.optional_field(Fields::MAINTAINER);
auto pv_str = parser.optional_field(Fields::PORT_VERSION);
this->port_version = 0;
if (!pv_str.empty())
{
auto pv_opt = Strings::strto<int>(pv_str);
if (auto pv = pv_opt.get())
{
this->port_version = *pv;
}
else
{
parser.add_type_error(Fields::PORT_VERSION, "a non-negative integer");
}
}
this->description = Strings::split(parser.optional_field(Fields::DESCRIPTION), '\n');
for (auto& desc : this->description)
{
desc = Strings::trim(std::move(desc));
}
this->maintainers = Strings::split(parser.optional_field(Fields::MAINTAINER), '\n');
for (auto& maintainer : this->maintainers)
{
maintainer = Strings::trim(std::move(maintainer));
}
this->abi = parser.optional_field(Fields::ABI);
std::string multi_arch;
parser.required_field(Fields::MULTI_ARCH, multi_arch);
this->depends = Util::fmap(
this->dependencies = Util::fmap(
parse_qualified_specifier_list(parser.optional_field(Fields::DEPENDS)).value_or_exit(VCPKG_LINE_INFO),
[](const ParsedQualifiedSpecifier& dep) {
// for compatibility with previous vcpkg versions, we discard all irrelevant information
@ -64,7 +89,7 @@ namespace vcpkg
});
if (!this->is_feature())
{
this->default_features = parse_default_features_list(parser.optional_field(Fields::DEFAULTFEATURES))
this->default_features = parse_default_features_list(parser.optional_field(Fields::DEFAULT_FEATURES))
.value_or_exit(VCPKG_LINE_INFO);
}
@ -87,16 +112,17 @@ namespace vcpkg
const std::vector<FeatureSpec>& deps)
: spec(spgh.name, triplet)
, version(spgh.version)
, port_version(spgh.port_version)
, description(spgh.description)
, maintainer(spgh.maintainer)
, maintainers(spgh.maintainers)
, feature()
, default_features(spgh.default_features)
, depends()
, dependencies()
, abi(abi_tag)
, type(spgh.type)
{
this->depends = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); });
Util::sort_unique_erase(this->depends);
this->dependencies = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); });
Util::sort_unique_erase(this->dependencies);
}
BinaryParagraph::BinaryParagraph(const SourceParagraph& spgh,
@ -105,16 +131,17 @@ namespace vcpkg
const std ::vector<FeatureSpec>& deps)
: spec(spgh.name, triplet)
, version()
, port_version()
, description(fpgh.description)
, maintainer()
, maintainers()
, feature(fpgh.name)
, default_features()
, depends()
, dependencies()
, abi()
, type(spgh.type)
{
this->depends = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); });
Util::sort_unique_erase(this->depends);
this->dependencies = Util::fmap(deps, [](const FeatureSpec& spec) { return spec.spec().name(); });
Util::sort_unique_erase(this->dependencies);
}
std::string BinaryParagraph::displayname() const
@ -131,29 +158,157 @@ namespace vcpkg
return Strings::format("%s_%s_%s", this->spec.name(), this->version, this->spec.triplet());
}
void serialize(const BinaryParagraph& pgh, std::string& out_str)
bool operator==(const BinaryParagraph& lhs, const BinaryParagraph& rhs)
{
out_str.append("Package: ").append(pgh.spec.name()).push_back('\n');
if (!pgh.version.empty())
out_str.append("Version: ").append(pgh.version).push_back('\n');
else if (pgh.is_feature())
out_str.append("Feature: ").append(pgh.feature).push_back('\n');
if (!pgh.depends.empty())
if (lhs.spec != rhs.spec) return false;
if (lhs.version != rhs.version) return false;
if (lhs.port_version != rhs.port_version) return false;
if (lhs.description != rhs.description) return false;
if (lhs.maintainers != rhs.maintainers) return false;
if (lhs.feature != rhs.feature) return false;
if (lhs.default_features != rhs.default_features) return false;
if (lhs.dependencies != rhs.dependencies) return false;
if (lhs.abi != rhs.abi) return false;
if (lhs.type != rhs.type) return false;
return true;
}
bool operator!=(const BinaryParagraph& lhs, const BinaryParagraph& rhs) { return !(lhs == rhs); }
static void serialize_string(StringView name, const std::string& field, std::string& out_str)
{
out_str.append("Depends: ");
out_str.append(Strings::join(", ", pgh.depends));
if (field.empty())
{
return;
}
out_str.append(name.begin(), name.end()).append(": ").append(field).push_back('\n');
}
static void serialize_array(StringView name,
const std::vector<std::string>& array,
std::string& out_str,
const char* joiner = ", ")
{
if (array.empty())
{
return;
}
out_str.append(name.begin(), name.end()).append(": ");
out_str.append(Strings::join(joiner, array));
out_str.push_back('\n');
}
static void serialize_paragraph(StringView name, const std::vector<std::string>& array, std::string& out_str)
{
serialize_array(name, array, out_str, "\n ");
}
out_str.append("Architecture: ").append(pgh.spec.triplet().to_string()).push_back('\n');
out_str.append("Multi-Arch: same\n");
void serialize(const BinaryParagraph& pgh, std::string& out_str)
{
const size_t initial_end = out_str.size();
if (!pgh.maintainer.empty()) out_str.append("Maintainer: ").append(pgh.maintainer).push_back('\n');
if (!pgh.abi.empty()) out_str.append("Abi: ").append(pgh.abi).push_back('\n');
if (!pgh.description.empty()) out_str.append("Description: ").append(pgh.description).push_back('\n');
serialize_string(Fields::PACKAGE, pgh.spec.name(), out_str);
out_str.append("Type: ").append(Type::to_string(pgh.type)).push_back('\n');
if (!pgh.default_features.empty())
out_str.append("Default-Features: ").append(Strings::join(", ", pgh.default_features)).push_back('\n');
serialize_string(Fields::VERSION, pgh.version, out_str);
if (pgh.port_version != 0)
{
out_str.append(Fields::PORT_VERSION).append(": ").append(std::to_string(pgh.port_version)).push_back('\n');
}
if (pgh.is_feature())
{
serialize_string(Fields::FEATURE, pgh.feature, out_str);
}
if (!pgh.dependencies.empty())
{
serialize_array(Fields::DEPENDS, pgh.dependencies, out_str);
}
serialize_string(Fields::ARCHITECTURE, pgh.spec.triplet().to_string(), out_str);
serialize_string(Fields::MULTI_ARCH, "same", out_str);
serialize_paragraph(Fields::MAINTAINER, pgh.maintainers, out_str);
serialize_string(Fields::ABI, pgh.abi, out_str);
serialize_paragraph(Fields::DESCRIPTION, pgh.description, out_str);
serialize_string(Fields::TYPE, Type::to_string(pgh.type), out_str);
serialize_array(Fields::DEFAULT_FEATURES, pgh.default_features, out_str);
// sanity check the serialized data
const auto my_paragraph = out_str.substr(initial_end);
auto parsed_paragraph = Paragraphs::parse_single_paragraph(
out_str.substr(initial_end), "vcpkg::serialize(const BinaryParagraph&, std::string&)");
if (!parsed_paragraph.has_value())
{
Checks::exit_with_message(VCPKG_LINE_INFO,
R"([sanity check] Failed to parse a serialized binary paragraph.
Please open an issue at https://github.com/microsoft/vcpkg, with the following output:
Error: %s
=== Serialized BinaryParagraph ===
%s
)",
parsed_paragraph.error(),
my_paragraph);
}
auto binary_paragraph = BinaryParagraph(*parsed_paragraph.get());
if (binary_paragraph != pgh)
{
const auto& join_str = R"(", ")";
Checks::exit_with_message(
VCPKG_LINE_INFO,
R"([sanity check] The serialized binary paragraph was different from the original binary paragraph.
Please open an issue at https://github.com/microsoft/vcpkg, with the following output:
=== Original BinaryParagraph ===
spec: "%s"
version: "%s"
port_version: %d
description: ["%s"]
maintainers: ["%s"]
feature: "%s"
default_features: ["%s"]
dependencies: ["%s"]
abi: "%s"
type: %s
=== Serialized BinaryParagraph ===
spec: "%s"
version: "%s"
port_version: %d
description: ["%s"]
maintainers: ["%s"]
feature: "%s"
default_features: ["%s"]
dependencies: ["%s"]
abi: "%s"
type: %s
)",
pgh.spec.to_string(),
pgh.version,
pgh.port_version,
Strings::join(join_str, pgh.description),
Strings::join(join_str, pgh.maintainers),
pgh.feature,
Strings::join(join_str, pgh.default_features),
Strings::join(join_str, pgh.dependencies),
pgh.abi,
Type::to_string(pgh.type),
binary_paragraph.spec.to_string(),
binary_paragraph.version,
binary_paragraph.port_version,
Strings::join(join_str, binary_paragraph.description),
Strings::join(join_str, binary_paragraph.maintainers),
binary_paragraph.feature,
Strings::join(join_str, binary_paragraph.default_features),
Strings::join(join_str, binary_paragraph.dependencies),
binary_paragraph.abi,
Type::to_string(binary_paragraph.type));
}
}
}

View File

@ -153,14 +153,14 @@ namespace vcpkg::Build
std::string first_arg = args.command_arguments.at(0);
auto binaryprovider =
create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO);
create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO);
const FullPackageSpec spec = Input::check_and_get_full_package_spec(
std::move(first_arg), default_triplet, COMMAND_STRUCTURE.example_text);
Input::check_triplet(spec.package_spec.triplet(), paths);
PathsPortFileProvider provider(paths, args.overlay_ports.get());
PathsPortFileProvider provider(paths, args.overlay_ports);
const auto port_name = spec.package_spec.name();
const auto* scfl = provider.get_control_file(port_name).get();

View File

@ -14,6 +14,7 @@ namespace vcpkg
local_variables.emplace_back("BUILDTREES_DIR", paths.buildtrees);
local_variables.emplace_back("_VCPKG_INSTALLED_DIR", paths.installed);
local_variables.emplace_back("DOWNLOADS", paths.downloads);
local_variables.emplace_back("VCPKG_MANIFEST_INSTALL", "OFF");
return System::make_basic_cmake_cmd(paths.get_tool_exe(Tools::CMAKE), cmake_script, local_variables);
}
}

View File

@ -22,16 +22,16 @@ namespace vcpkg::Commands::BuildExternal
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
auto binaryprovider =
create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO);
create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO);
const FullPackageSpec spec = Input::check_and_get_full_package_spec(
std::string(args.command_arguments.at(0)), default_triplet, COMMAND_STRUCTURE.example_text);
Input::check_triplet(spec.package_spec.triplet(), paths);
auto overlays = args.overlay_ports ? *args.overlay_ports : std::vector<std::string>();
auto overlays = args.overlay_ports;
overlays.insert(overlays.begin(), args.command_arguments.at(1));
PortFileProvider::PathsPortFileProvider provider(paths, &overlays);
PortFileProvider::PathsPortFileProvider provider(paths, overlays);
auto maybe_scfl = provider.get_control_file(spec.package_spec.name());
Checks::check_exit(

View File

@ -14,7 +14,7 @@
#include <vcpkg/help.h>
#include <vcpkg/input.h>
#include <vcpkg/install.h>
#include <vcpkg/logicexpression.h>
#include <vcpkg/platform-expression.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/vcpkglib.h>
@ -171,7 +171,7 @@ namespace vcpkg::Commands::CI
}
std::string traits_block;
if (test.abi_tag != "")
if (!test.abi_tag.empty())
{
traits_block += Strings::format(R"(<trait name="abi_tag" value="%s" />)", test.abi_tag);
}
@ -226,26 +226,11 @@ namespace vcpkg::Commands::CI
const InstallPlanAction* install_plan)
{
auto&& scfl = install_plan->source_control_file_location.value_or_exit(VCPKG_LINE_INFO);
const std::string& supports_expression = scfl.source_control_file->core_paragraph->supports_expression;
if (supports_expression.empty())
{
return true; // default to 'supported'
}
const auto& supports_expression = scfl.source_control_file->core_paragraph->supports_expression;
PlatformExpression::Context context =
var_provider.get_tag_vars(install_plan->spec).value_or_exit(VCPKG_LINE_INFO);
ExpressionContext context = {var_provider.get_tag_vars(install_plan->spec).value_or_exit(VCPKG_LINE_INFO),
install_plan->spec.triplet().canonical_name()};
auto maybe_result = evaluate_expression(supports_expression, context);
if (auto b = maybe_result.get())
return *b;
else
{
System::print2(System::Color::error,
"Error: while processing ",
install_plan->spec.to_string(),
"\n",
maybe_result.error());
Checks::exit_fail(VCPKG_LINE_INFO);
}
return supports_expression.evaluate(context);
}
static std::unique_ptr<UnknownCIPortsResults> find_unknown_ports_for_ci(
@ -272,13 +257,13 @@ namespace vcpkg::Commands::CI
};
std::vector<PackageSpec> packages_with_qualified_deps;
auto has_qualifier = [](Dependency const& dep) { return !dep.qualifier.empty(); };
auto has_qualifier = [](Dependency const& dep) { return !dep.platform.is_empty(); };
for (auto&& spec : specs)
{
auto&& scfl = provider.get_control_file(spec.package_spec.name()).value_or_exit(VCPKG_LINE_INFO);
if (Util::any_of(scfl.source_control_file->core_paragraph->depends, has_qualifier) ||
if (Util::any_of(scfl.source_control_file->core_paragraph->dependencies, has_qualifier) ||
Util::any_of(scfl.source_control_file->feature_paragraphs,
[&](auto&& pgh) { return Util::any_of(pgh->depends, has_qualifier); }))
[&](auto&& pgh) { return Util::any_of(pgh->dependencies, has_qualifier); }))
{
packages_with_qualified_deps.push_back(spec.package_spec);
}
@ -385,7 +370,7 @@ namespace vcpkg::Commands::CI
if (args.binary_caching_enabled())
{
binaryproviderStorage =
create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO);
create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO);
}
IBinaryProvider& binaryprovider = binaryproviderStorage ? *binaryproviderStorage : null_binary_provider();
@ -412,7 +397,7 @@ namespace vcpkg::Commands::CI
StatusParagraphs status_db = database_load_check(paths);
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;

View File

@ -42,7 +42,6 @@ namespace vcpkg::Commands
{"update", &Update::perform_and_exit},
{"edit", &Edit::perform_and_exit},
{"create", &Create::perform_and_exit},
{"import", &Import::perform_and_exit},
{"cache", &Cache::perform_and_exit},
{"portsdiff", &PortsDiff::perform_and_exit},
{"autocomplete", &Autocomplete::perform_and_exit},
@ -51,6 +50,7 @@ namespace vcpkg::Commands
{"x-ci-clean", &CIClean::perform_and_exit},
{"x-history", &PortHistory::perform_and_exit},
{"x-vsinstances", &X_VSInstances::perform_and_exit},
{"x-format-manifest", &FormatManifest::perform_and_exit},
};
return t;
}
@ -89,8 +89,7 @@ namespace vcpkg::Commands::Fetch
namespace vcpkg::Commands::Hash
{
const CommandStructure COMMAND_STRUCTURE = {
Strings::format("The argument should be a file path\n%s",
create_example_string("hash boost_1_62_0.tar.bz2")),
Strings::format("The argument should be a file path\n%s", create_example_string("hash boost_1_62_0.tar.bz2")),
1,
2,
{},

View File

@ -251,7 +251,7 @@ namespace vcpkg::Commands::DependInfo
Input::check_triplet(spec.package_spec.triplet(), paths);
}
PathsPortFileProvider provider(paths, args.overlay_ports.get());
PathsPortFileProvider provider(paths, args.overlay_ports);
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;

View File

@ -39,7 +39,7 @@ namespace vcpkg::Commands::Env
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;

View File

@ -94,7 +94,7 @@ namespace vcpkg::Export::IFW
package_xml_file_path.generic_u8string());
auto deps = Strings::join(
",", binary_paragraph.depends, [](const std::string& dep) { return "packages." + dep + ":"; });
",", binary_paragraph.dependencies, [](const std::string& dep) { return "packages." + dep + ":"; });
if (!deps.empty()) deps = "\n <Dependencies>" + deps + "</Dependencies>";
@ -175,7 +175,7 @@ namespace vcpkg::Export::IFW
</Package>
)###",
action.spec.name(),
safe_rich_from_plain_text(binary_paragraph.description),
safe_rich_from_plain_text(Strings::join("\n", binary_paragraph.description)),
binary_paragraph.version,
create_release_date()),
VCPKG_LINE_INFO);

View File

@ -0,0 +1,100 @@
#include "pch.h"
#include <vcpkg/base/checks.h>
#include <vcpkg/base/files.h>
#include <vcpkg/base/json.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/commands.h>
#include <vcpkg/portfileprovider.h>
namespace vcpkg::Commands::FormatManifest
{
static constexpr StringLiteral OPTION_ALL = "--all";
const CommandSwitch FORMAT_SWITCHES[] = {
{ OPTION_ALL, "Format all ports' manifest files." }
};
const CommandStructure COMMAND_STRUCTURE = {
create_example_string(R"###(x-format-manifest --all)###"),
0,
SIZE_MAX,
{
FORMAT_SWITCHES,
{},
{}
},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
auto parsed_args = args.parse_arguments(COMMAND_STRUCTURE);
std::vector<fs::path> files_to_format;
auto& fs = paths.get_filesystem();
bool has_error = false;
if (Util::Sets::contains(parsed_args.switches, OPTION_ALL))
{
for (const auto& dir : fs::directory_iterator(paths.ports))
{
auto manifest_path = dir.path() / fs::u8path("vcpkg.json");
if (fs.exists(manifest_path))
{
files_to_format.push_back(std::move(manifest_path));
}
}
}
for (const auto& arg : args.command_arguments)
{
auto path = fs::u8path(arg);
if (path.is_relative())
{
path = paths.original_cwd / path;
}
files_to_format.push_back(std::move(path));
}
for (const auto& path : files_to_format)
{
std::error_code ec;
Debug::print("Formatting ", path.u8string(), "\n");
auto parsed_json_opt = Json::parse_file(fs, path, ec);
if (ec)
{
System::printf(System::Color::error, "Failed to read %s: %s\n", path.u8string(), ec.message());
has_error = true;
}
if (auto pr = parsed_json_opt.get())
{
fs.write_contents(path, Json::stringify(pr->first, Json::JsonStyle{}), ec);
}
else
{
System::printf(System::Color::error, "Failed to parse %s: %s\n", path.u8string(), parsed_json_opt.error()->format());
has_error = true;
}
if (ec)
{
System::printf(System::Color::error, "Failed to write %s: %s\n", path.u8string(), ec.message());
has_error = true;
}
}
if (has_error)
{
Checks::exit_fail(VCPKG_LINE_INFO);
}
else
{
System::print2("Succeeded in formatting the manifest files.\n");
Checks::exit_success(VCPKG_LINE_INFO);
}
}
}

View File

@ -1,126 +0,0 @@
#include "pch.h"
#include <vcpkg/base/files.h>
#include <vcpkg/commands.h>
#include <vcpkg/help.h>
#include <vcpkg/paragraphs.h>
#include <vcpkg/statusparagraph.h>
namespace vcpkg::Commands::Import
{
struct Binaries
{
std::vector<fs::path> dlls;
std::vector<fs::path> libs;
};
static void check_is_directory(const LineInfo& line_info, const Files::Filesystem& fs, const fs::path& dirpath)
{
Checks::check_exit(line_info, fs.is_directory(dirpath), "The path %s is not a directory", dirpath.string());
}
static Binaries find_binaries_in_dir(const Files::Filesystem& fs, const fs::path& path)
{
auto files = fs.get_files_recursive(path);
check_is_directory(VCPKG_LINE_INFO, fs, path);
Binaries binaries;
for (auto&& file : files)
{
if (fs.is_directory(file)) continue;
const auto ext = file.extension();
if (ext == ".dll")
binaries.dlls.push_back(std::move(file));
else if (ext == ".lib")
binaries.libs.push_back(std::move(file));
}
return binaries;
}
static void copy_files_into_directory(Files::Filesystem& fs,
const std::vector<fs::path>& files,
const fs::path& destination_folder)
{
std::error_code ec;
fs.create_directory(destination_folder, ec);
for (auto const& src_path : files)
{
const fs::path dest_path = destination_folder / src_path.filename();
fs.copy(src_path, dest_path, fs::copy_options::overwrite_existing);
}
}
static void place_library_files_in(Files::Filesystem& fs,
const fs::path& include_directory,
const fs::path& project_directory,
const fs::path& destination_path)
{
check_is_directory(VCPKG_LINE_INFO, fs, include_directory);
check_is_directory(VCPKG_LINE_INFO, fs, project_directory);
check_is_directory(VCPKG_LINE_INFO, fs, destination_path);
const Binaries debug_binaries = find_binaries_in_dir(fs, project_directory / "Debug");
const Binaries release_binaries = find_binaries_in_dir(fs, project_directory / "Release");
const fs::path destination_include_directory = destination_path / "include";
fs.copy(include_directory,
destination_include_directory,
fs::copy_options::recursive | fs::copy_options::overwrite_existing);
copy_files_into_directory(fs, release_binaries.dlls, destination_path / "bin");
copy_files_into_directory(fs, release_binaries.libs, destination_path / "lib");
std::error_code ec;
fs.create_directory(destination_path / "debug", ec);
copy_files_into_directory(fs, debug_binaries.dlls, destination_path / "debug" / "bin");
copy_files_into_directory(fs, debug_binaries.libs, destination_path / "debug" / "lib");
}
static void do_import(const VcpkgPaths& paths,
const fs::path& include_directory,
const fs::path& project_directory,
const BinaryParagraph& control_file_data)
{
auto& fs = paths.get_filesystem();
const fs::path library_destination_path = paths.package_dir(control_file_data.spec);
std::error_code ec;
fs.create_directory(library_destination_path, ec);
place_library_files_in(paths.get_filesystem(), include_directory, project_directory, library_destination_path);
const fs::path control_file_path = library_destination_path / "CONTROL";
fs.write_contents(control_file_path, Strings::serialize(control_file_data), VCPKG_LINE_INFO);
}
const CommandStructure COMMAND_STRUCTURE = {
create_example_string(R"(import C:\path\to\CONTROLfile C:\path\to\includedir C:\path\to\projectdir)"),
3,
3,
{},
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths)
{
Util::unused(args.parse_arguments(COMMAND_STRUCTURE));
const fs::path control_file_path(args.command_arguments[0]);
const fs::path include_directory(args.command_arguments[1]);
const fs::path project_directory(args.command_arguments[2]);
const auto pghs = Paragraphs::get_single_paragraph(paths.get_filesystem(), control_file_path);
Checks::check_exit(VCPKG_LINE_INFO,
pghs.get() != nullptr,
"Invalid control file %s for package",
control_file_path.generic_u8string(),
"\n",
pghs.error());
StatusParagraph spgh;
spgh.package = BinaryParagraph(*pghs.get());
auto& control_file_data = spgh.package;
do_import(paths, include_directory, project_directory, control_file_data);
Checks::exit_success(VCPKG_LINE_INFO);
}
}

View File

@ -14,14 +14,22 @@ namespace vcpkg::Commands::List
{
if (full_desc)
{
System::printf("%-50s %-16s %s\n", pgh.package.displayname(), pgh.package.version, pgh.package.description);
System::printf("%-50s %-16s %s\n",
pgh.package.displayname(),
pgh.package.version,
Strings::join("\n ", pgh.package.description));
}
else
{
std::string description;
if (!pgh.package.description.empty())
{
description = pgh.package.description[0];
}
System::printf("%-50s %-16s %s\n",
vcpkg::shorten_text(pgh.package.displayname(), 50),
vcpkg::shorten_text(pgh.package.version, 16),
vcpkg::shorten_text(pgh.package.description, 51));
vcpkg::shorten_text(description, 51));
}
}

View File

@ -20,15 +20,22 @@ namespace vcpkg::Commands::Search
{
if (full_desc)
{
System::printf(
"%-20s %-16s %s\n", source_paragraph.name, source_paragraph.version, source_paragraph.description);
System::printf("%-20s %-16s %s\n",
source_paragraph.name,
source_paragraph.version,
Strings::join("\n ", source_paragraph.description));
}
else
{
std::string description;
if (!source_paragraph.description.empty())
{
description = source_paragraph.description[0];
}
System::printf("%-20s %-16s %s\n",
vcpkg::shorten_text(source_paragraph.name, 20),
vcpkg::shorten_text(source_paragraph.version, 16),
vcpkg::shorten_text(source_paragraph.description, 81));
vcpkg::shorten_text(description, 81));
}
}
@ -37,13 +44,17 @@ namespace vcpkg::Commands::Search
auto full_feature_name = Strings::concat(name, "[", feature_paragraph.name, "]");
if (full_desc)
{
System::printf("%-37s %s\n", full_feature_name, feature_paragraph.description);
System::printf("%-37s %s\n", full_feature_name, Strings::join("\n ", feature_paragraph.description));
}
else
{
System::printf("%-37s %s\n",
vcpkg::shorten_text(full_feature_name, 37),
vcpkg::shorten_text(feature_paragraph.description, 81));
std::string description;
if (!feature_paragraph.description.empty())
{
description = feature_paragraph.description[0];
}
System::printf(
"%-37s %s\n", vcpkg::shorten_text(full_feature_name, 37), vcpkg::shorten_text(description, 81));
}
}
@ -66,7 +77,7 @@ namespace vcpkg::Commands::Search
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const bool full_description = Util::Sets::contains(options.switches, OPTION_FULLDESC);
PathsPortFileProvider provider(paths, args.overlay_ports.get());
PathsPortFileProvider provider(paths, args.overlay_ports);
auto source_paragraphs =
Util::fmap(provider.load_all_control_files(),
[](auto&& port) -> const SourceControlFile* { return port->source_control_file.get(); });
@ -84,24 +95,40 @@ namespace vcpkg::Commands::Search
}
else
{
const auto& icontains = Strings::case_insensitive_ascii_contains;
// At this point there is 1 argument
auto&& args_zero = args.command_arguments[0];
const auto contained_in = [&args_zero](const auto& s) {
return Strings::case_insensitive_ascii_contains(s, args_zero);
};
for (const auto& source_control_file : source_paragraphs)
{
auto&& sp = *source_control_file->core_paragraph;
const bool contains_name = icontains(sp.name, args_zero);
if (contains_name || icontains(sp.description, args_zero))
bool found_match = contained_in(sp.name);
if (!found_match)
{
found_match = std::any_of(sp.description.begin(), sp.description.end(), contained_in);
}
if (found_match)
{
do_print(sp, full_description);
}
for (auto&& feature_paragraph : source_control_file->feature_paragraphs)
{
if (contains_name || icontains(feature_paragraph->name, args_zero) ||
icontains(feature_paragraph->description, args_zero))
bool found_match_for_feature = found_match;
if (!found_match_for_feature)
{
found_match_for_feature = contained_in(feature_paragraph->name);
}
if (!found_match_for_feature)
{
found_match_for_feature = std::any_of(
feature_paragraph->description.begin(), feature_paragraph->description.end(), contained_in);
}
if (found_match_for_feature)
{
do_print(sp.name, *feature_paragraph, full_description);
}

View File

@ -27,52 +27,27 @@ namespace vcpkg::Commands::SetInstalled
nullptr,
};
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet)
void perform_and_exit_ex(const VcpkgCmdArguments& args,
const VcpkgPaths& paths,
const PortFileProvider::PathsPortFileProvider& provider,
IBinaryProvider& binary_provider,
const CMakeVars::CMakeVarProvider& cmake_vars,
const std::vector<FullPackageSpec>& specs,
const Build::BuildPackageOptions& install_plan_options,
DryRun dry_run)
{
// input sanitization
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_full_package_spec(
std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text);
});
for (auto&& spec : specs)
{
Input::check_triplet(spec.package_spec.triplet(), paths);
}
auto binaryprovider =
create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO);
const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
const Build::BuildPackageOptions install_plan_options = {
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
Build::OnlyDownloads::NO,
Build::CleanBuildtrees::YES,
Build::CleanPackages::YES,
Build::CleanDownloads::YES,
Build::DownloadTool::BUILT_IN,
Build::FailOnTombstone::NO,
};
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
auto cmake_vars = CMakeVars::make_triplet_cmake_var_provider(paths);
// We have a set of user-requested specs.
// We need to know all the specs which are required to fulfill dependencies for those specs.
// Therefore, we see what we would install into an empty installed tree, so we can use the existing code.
auto action_plan = Dependencies::create_feature_install_plan(provider, *cmake_vars, specs, {});
auto action_plan = Dependencies::create_feature_install_plan(provider, cmake_vars, specs, {});
for (auto&& action : action_plan.install_actions)
{
action.build_options = install_plan_options;
}
cmake_vars->load_tag_vars(action_plan, provider);
Build::compute_all_abis(paths, action_plan, *cmake_vars, {});
cmake_vars.load_tag_vars(action_plan, provider);
Build::compute_all_abis(paths, action_plan, cmake_vars, {});
std::set<std::string> all_abis;
@ -116,7 +91,7 @@ namespace vcpkg::Commands::SetInstalled
Dependencies::print_plan(action_plan, true, paths.ports);
if (dry_run)
if (dry_run == DryRun::Yes)
{
Checks::exit_success(VCPKG_LINE_INFO);
}
@ -125,12 +100,55 @@ namespace vcpkg::Commands::SetInstalled
Install::KeepGoing::NO,
paths,
status_db,
args.binary_caching_enabled() ? *binaryprovider : null_binary_provider(),
*cmake_vars);
args.binary_caching_enabled() ? binary_provider : null_binary_provider(),
cmake_vars);
System::print2("\nTotal elapsed time: ", summary.total_elapsed_time, "\n\n");
Checks::exit_success(VCPKG_LINE_INFO);
}
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet)
{
// input sanitization
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_full_package_spec(
std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text);
});
for (auto&& spec : specs)
{
Input::check_triplet(spec.package_spec.triplet(), paths);
}
auto binary_provider =
create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO);
const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
const Build::BuildPackageOptions install_plan_options = {
Build::UseHeadVersion::NO,
Build::AllowDownloads::YES,
Build::OnlyDownloads::NO,
Build::CleanBuildtrees::YES,
Build::CleanPackages::YES,
Build::CleanDownloads::YES,
Build::DownloadTool::BUILT_IN,
Build::FailOnTombstone::NO,
};
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
auto cmake_vars = CMakeVars::make_triplet_cmake_var_provider(paths);
perform_and_exit_ex(args,
paths,
provider,
*binary_provider,
*cmake_vars,
specs,
install_plan_options,
dry_run ? DryRun::Yes : DryRun::No);
}
}

View File

@ -43,12 +43,12 @@ namespace vcpkg::Commands::Upgrade
const KeepGoing keep_going = to_keep_going(Util::Sets::contains(options.switches, OPTION_KEEP_GOING));
auto binaryprovider =
create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO);
create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO);
StatusParagraphs status_db = database_load_check(paths);
// Load ports from ports dirs
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;

View File

@ -124,11 +124,10 @@ namespace vcpkg::Dependencies
bool requires_qualified_resolution = false;
for (const Dependency& dep : *qualified_deps)
{
if (dep.qualifier.empty())
if (dep.platform.is_empty())
{
Util::Vectors::append(
&dep_list,
FullPackageSpec({dep.depend.name, m_spec.triplet()}, dep.depend.features)
Util::Vectors::append(&dep_list,
FullPackageSpec({dep.name, m_spec.triplet()}, dep.features)
.to_feature_specs({"default"}, {"default"}));
}
else
@ -684,7 +683,7 @@ namespace vcpkg::Dependencies
const std::vector<Dependency>* paragraph_depends = nullptr;
if (spec.feature() == "core")
{
paragraph_depends = &clust.m_scfl.source_control_file->core_paragraph->depends;
paragraph_depends = &clust.m_scfl.source_control_file->core_paragraph->dependencies;
}
else if (spec.feature() == "default")
{
@ -697,12 +696,12 @@ namespace vcpkg::Dependencies
"Package %s does not have a %s feature",
spec.name(),
spec.feature());
paragraph_depends = &maybe_paragraph.value_or_exit(VCPKG_LINE_INFO).depends;
paragraph_depends = &maybe_paragraph.value_or_exit(VCPKG_LINE_INFO).dependencies;
}
// And it has at least one qualified dependency
if (paragraph_depends &&
Util::any_of(*paragraph_depends, [](auto&& dep) { return !dep.qualifier.empty(); }))
Util::any_of(*paragraph_depends, [](auto&& dep) { return !dep.platform.is_empty(); }))
{
// Add it to the next batch run
qualified_dependencies.emplace_back(spec);

View File

@ -19,7 +19,7 @@ namespace vcpkg::Export::Chocolatey
static constexpr auto CONTENT_TEMPLATE = R"(<dependency id="@PACKAGE_ID@" version="[@PACKAGE_VERSION@]" />)";
std::string nuspec_dependencies;
for (const std::string& depend : binary_paragraph.depends)
for (const std::string& depend : binary_paragraph.dependencies)
{
auto found = packages_version.find(depend);
if (found == packages_version.end())
@ -68,8 +68,8 @@ namespace vcpkg::Export::Chocolatey
Strings::replace_all(std::move(nuspec_file_content), "@PACKAGE_VERSION@", package_version->second);
nuspec_file_content = Strings::replace_all(
std::move(nuspec_file_content), "@PACKAGE_MAINTAINER@", chocolatey_options.maybe_maintainer.value_or(""));
nuspec_file_content =
Strings::replace_all(std::move(nuspec_file_content), "@PACKAGE_DESCRIPTION@", binary_paragraph.description);
nuspec_file_content = Strings::replace_all(
std::move(nuspec_file_content), "@PACKAGE_DESCRIPTION@", Strings::join("\n", binary_paragraph.description));
nuspec_file_content =
Strings::replace_all(std::move(nuspec_file_content), "@EXPORTED_ROOT_DIR@", exported_root_dir);
nuspec_file_content = Strings::replace_all(std::move(nuspec_file_content),

View File

@ -589,13 +589,17 @@ With a project open, go to Tools->NuGet Package Manager->Package Manager Console
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet)
{
if (paths.manifest_mode_enabled())
{
Checks::exit_with_message(VCPKG_LINE_INFO, "vcpkg export does not support manifest mode, in order to allow for future design considerations. You may use export in classic mode by running vcpkg outside of a manifest-based project.");
}
const StatusParagraphs status_db = database_load_check(paths);
const auto opts = handle_export_command_arguments(args, default_triplet, status_db);
for (auto&& spec : opts.specs)
Input::check_triplet(spec.triplet(), paths);
// Load ports from ports dirs
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
// create the plan
std::vector<ExportPlanAction> export_plan = Dependencies::create_export_plan(opts.specs, status_db);

View File

@ -84,9 +84,10 @@ namespace vcpkg::Install
const std::string filename = file.filename().generic_u8string();
if (fs::is_regular_file(status) && (Strings::case_insensitive_ascii_equals(filename, "CONTROL") ||
Strings::case_insensitive_ascii_equals(filename, "vcpkg.json") ||
Strings::case_insensitive_ascii_equals(filename, "BUILD_INFO")))
{
// Do not copy the control file
// Do not copy the control file or manifest file
continue;
}
@ -530,6 +531,14 @@ namespace vcpkg::Install
&get_all_port_names,
};
const CommandStructure MANIFEST_COMMAND_STRUCTURE = {
create_example_string("install --triplet x64-windows"),
0,
0,
{INSTALL_SWITCHES, INSTALL_SETTINGS},
nullptr,
};
static void print_cmake_information(const BinaryParagraph& bpgh, const VcpkgPaths& paths)
{
static const std::regex cmake_library_regex(R"(\badd_library\(([^\$\s\)]+)\s)",
@ -645,20 +654,10 @@ namespace vcpkg::Install
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet)
{
// input sanitization
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
const ParsedArguments options = args.parse_arguments(paths.manifest_mode_enabled() ? MANIFEST_COMMAND_STRUCTURE : COMMAND_STRUCTURE);
auto binaryprovider =
create_binary_provider_from_configs(paths, args.binarysources).value_or_exit(VCPKG_LINE_INFO);
const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_full_package_spec(
std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text);
});
for (auto&& spec : specs)
{
Input::check_triplet(spec.package_spec.triplet(), paths);
}
create_binary_provider_from_configs(paths, args.binary_sources).value_or_exit(VCPKG_LINE_INFO);
const bool dry_run = Util::Sets::contains(options.switches, OPTION_DRY_RUN);
const bool use_head_version = Util::Sets::contains(options.switches, (OPTION_USE_HEAD_VERSION));
@ -672,10 +671,6 @@ namespace vcpkg::Install
auto& fs = paths.get_filesystem();
// create the plan
System::print2("Computing installation plan...\n");
StatusParagraphs status_db = database_load_check(paths);
Build::DownloadTool download_tool = Build::DownloadTool::BUILT_IN;
if (use_aria2) download_tool = Build::DownloadTool::ARIA2;
@ -690,11 +685,54 @@ namespace vcpkg::Install
Build::FailOnTombstone::NO,
};
//// Load ports from ports dirs
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
auto var_provider_storage = CMakeVars::make_triplet_cmake_var_provider(paths);
auto& var_provider = *var_provider_storage;
if (paths.manifest_mode_enabled())
{
std::error_code ec;
const auto path_to_manifest = paths.manifest_root_dir / "vcpkg.json";
auto res = Paragraphs::try_load_manifest(paths.get_filesystem(), "user manifest", path_to_manifest, ec);
if (ec)
{
Checks::exit_with_message(VCPKG_LINE_INFO, "Failed to load manifest file (%s): %s\n",
path_to_manifest.u8string(), ec.message());
}
std::vector<FullPackageSpec> specs;
if (auto val = res.get())
{
for (auto& dep : (*val)->core_paragraph->dependencies)
{
specs.push_back(Input::check_and_get_full_package_spec(
std::move(dep.name), default_triplet, COMMAND_STRUCTURE.example_text));
}
}
else
{
print_error_message(res.error());
Checks::exit_fail(VCPKG_LINE_INFO);
}
Commands::SetInstalled::perform_and_exit_ex(args, paths, provider, *binaryprovider, var_provider, specs, install_plan_options, dry_run ? Commands::DryRun::Yes : Commands::DryRun::No);
}
const std::vector<FullPackageSpec> specs = Util::fmap(args.command_arguments, [&](auto&& arg) {
return Input::check_and_get_full_package_spec(
std::string(arg), default_triplet, COMMAND_STRUCTURE.example_text);
});
for (auto&& spec : specs)
{
Input::check_triplet(spec.package_spec.triplet(), paths);
}
// create the plan
System::print2("Computing installation plan...\n");
StatusParagraphs status_db = database_load_check(paths);
// Note: action_plan will hold raw pointers to SourceControlFileLocations from this map
auto action_plan = Dependencies::create_feature_install_plan(provider, var_provider, specs, status_db);

View File

@ -1,320 +0,0 @@
#include "pch.h"
#include <vcpkg/base/parse.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/logicexpression.h>
#include <string>
#include <vector>
namespace vcpkg
{
using vcpkg::Parse::ParseError;
enum class Identifier
{
invalid, // not a recognized identifier
x64,
x86,
arm,
arm64,
windows,
linux,
osx,
uwp,
android,
emscripten,
wasm32,
static_link,
};
// logic expression supports the following :
// primary-expression:
// ( logic-expression )
// identifier
// identifier:
// alpha-numeric string of characters
// logic-expression: <- this is the entry point
// not-expression
// not-expression | logic-expression
// not-expression & logic-expression
// not-expression:
// ! primary-expression
// primary-expression
//
// | and & have equal precidence and cannot be used together at the same nesting level
// for example a|b&c is not allowd but (a|b)&c and a|(b&c) are allowed.
class ExpressionParser : public Parse::ParserBase
{
public:
ExpressionParser(const std::string& str, const ExpressionContext& context) :
Parse::ParserBase(str, "CONTROL"), evaluation_context(context)
{
{
auto override_vars = evaluation_context.cmake_context.find("VCPKG_DEP_INFO_OVERRIDE_VARS");
if (override_vars != evaluation_context.cmake_context.end())
{
auto cmake_list = Strings::split(override_vars->second, ';');
for (auto& override_id : cmake_list)
{
if (!override_id.empty())
{
if (override_id[0] == '!')
{
context_override.insert({override_id.substr(1), false});
}
else
{
context_override.insert({override_id, true});
}
}
}
}
}
skip_whitespace();
final_result = logic_expression();
if (!at_eof())
{
add_error("invalid logic expression, unexpected character");
}
}
bool get_result() const { return final_result; }
private:
const ExpressionContext& evaluation_context;
std::map<std::string, bool> context_override;
bool final_result;
static bool is_identifier_char(char32_t ch)
{
return is_upper_alpha(ch) || is_lower_alpha(ch) || is_ascii_digit(ch) || ch == '-';
}
// Legacy evaluation only searches for substrings. Use this only for diagnostic purposes.
bool evaluate_identifier_legacy(const std::string name) const
{
return evaluation_context.legacy_context.find(name) != std::string::npos;
}
static Identifier string2identifier(const std::string& name)
{
static const std::map<std::string, Identifier> id_map = {
{"x64", Identifier::x64},
{"x86", Identifier::x86},
{"arm", Identifier::arm},
{"arm64", Identifier::arm64},
{"windows", Identifier::windows},
{"linux", Identifier::linux},
{"osx", Identifier::osx},
{"uwp", Identifier::uwp},
{"android", Identifier::android},
{"emscripten", Identifier::emscripten},
{"wasm32", Identifier::wasm32},
{"static", Identifier::static_link},
};
auto id_pair = id_map.find(name);
if (id_pair == id_map.end())
{
return Identifier::invalid;
}
return id_pair->second;
}
bool true_if_exists_and_equal(const std::string& variable_name, const std::string& value)
{
auto iter = evaluation_context.cmake_context.find(variable_name);
if (iter == evaluation_context.cmake_context.end())
{
return false;
}
return iter->second == value;
}
// If an identifier is on the explicit override list, return the override value
// Otherwise fall back to the built in logic to evaluate
// All unrecognized identifiers are an error
bool evaluate_identifier_cmake(const std::string name, const SourceLoc& loc)
{
auto id = string2identifier(name);
switch (id)
{
case Identifier::invalid:
// Point out in the diagnostic that they should add to the override list because that is what
// most users should do, however it is also valid to update the built in identifiers to recognize
// the name.
add_error("Unrecognized identifer name. Add to override list in triplet file.", loc);
break;
case Identifier::x64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x64");
case Identifier::x86: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x86");
case Identifier::arm:
// For backwards compatability arm is also true for arm64.
// This is because it previously was only checking for a substring.
return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm") ||
true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64");
case Identifier::arm64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64");
case Identifier::windows: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "") || true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore");
case Identifier::linux: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Linux");
case Identifier::osx: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Darwin");
case Identifier::uwp: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore");
case Identifier::android: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Android");
case Identifier::emscripten: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Emscripten");
case Identifier::wasm32: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "wasm32");
case Identifier::static_link: return true_if_exists_and_equal("VCPKG_LIBRARY_LINKAGE", "static");
}
return evaluation_context.legacy_context.find(name) != std::string::npos;
}
bool evaluate_identifier(const std::string name, const SourceLoc& loc)
{
if (!context_override.empty())
{
auto override_id = context_override.find(name);
if (override_id != context_override.end())
{
return override_id->second;
}
// Fall through to use the cmake logic if the id does not have an override
}
bool legacy = evaluate_identifier_legacy(name);
bool cmake = evaluate_identifier_cmake(name, loc);
if (legacy != cmake)
{
// Legacy evaluation only used the name of the triplet, now we use the actual
// cmake variables. This has the potential to break custom triplets.
// For now just print a message, this will need to change once we start introducing
// new variables that did not exist previously (such as host-*)
System::print2("Warning: qualifier has changed meaning recently:\n ", name, '\n');
}
return cmake;
}
// identifier:
// alpha-numeric string of characters
bool identifier_expression()
{
auto start_loc = cur_loc();
std::string name = match_zero_or_more(is_identifier_char).to_string();
if (name.empty())
{
add_error("unexpected character in logic expression");
return false;
}
bool result = evaluate_identifier(name, start_loc);
skip_whitespace();
return result;
}
// not-expression:
// ! primary-expression
// primary-expression
bool not_expression()
{
if (cur() == '!')
{
next();
skip_whitespace();
return !primary_expression();
}
return primary_expression();
}
template<char oper, char other, bool operation(bool, bool)>
bool logic_expression_helper(bool seed)
{
do
{
// Support chains of the operator to avoid breaking backwards compatability
while (next() == oper)
{
};
skip_whitespace();
seed = operation(not_expression(), seed);
} while (cur() == oper);
if (cur() == other)
{
add_error("mixing & and | is not allowed, use () to specify order of operations");
}
skip_whitespace();
return seed;
}
static bool and_helper(bool left, bool right) { return left && right; }
static bool or_helper(bool left, bool right) { return left || right; }
// logic-expression: <- entry point
// not-expression
// not-expression | logic-expression
// not-expression & logic-expression
bool logic_expression()
{
auto result = not_expression();
switch (cur())
{
case '|':
{
return logic_expression_helper<'|', '&', or_helper>(result);
}
case '&':
{
return logic_expression_helper<'&', '|', and_helper>(result);
}
default: return result;
}
}
// primary-expression:
// ( logic-expression )
// identifier
bool primary_expression()
{
if (cur() == '(')
{
next();
skip_whitespace();
bool result = logic_expression();
if (cur() != ')')
{
add_error("missing closing )");
return result;
}
next();
skip_whitespace();
return result;
}
return identifier_expression();
}
};
ExpectedT<bool, std::string> evaluate_expression(const std::string& expression, const ExpressionContext& context)
{
ExpressionParser parser(expression, context);
if (auto err = parser.get_error())
{
return err->format();
}
return parser.get_result();
}
}

View File

@ -153,6 +153,8 @@ namespace vcpkg::Metrics
Json::Array buildtime_names;
Json::Array buildtime_times;
Json::Object feature_flags;
void track_property(const std::string& name, const std::string& value)
{
properties.insert_or_replace(name, Json::Value::string(value));
@ -168,6 +170,10 @@ namespace vcpkg::Metrics
buildtime_names.push_back(Json::Value::string(name));
buildtime_times.push_back(Json::Value::number(value));
}
void track_feature(const std::string& name, bool value)
{
feature_flags.insert(name, Json::Value::boolean(value));
}
std::string format_event_data_template() const
{
@ -226,6 +232,7 @@ namespace vcpkg::Metrics
base_data.insert("name", Json::Value::string("commandline_test7"));
base_data.insert("properties", Json::Value::object(std::move(props_plus_buildtimes)));
base_data.insert("measurements", Json::Value::object(measurements.clone()));
base_data.insert("feature-flags", Json::Value::object(feature_flags.clone()));
}
return Json::stringify(arr, vcpkg::Json::JsonStyle());
@ -356,6 +363,15 @@ namespace vcpkg::Metrics
g_metricmessage.track_property(name, value);
}
void Metrics::track_feature(const std::string& name, bool value)
{
if (!metrics_enabled())
{
return;
}
g_metricmessage.track_feature(name, value);
}
void Metrics::upload(const std::string& payload)
{
if (!metrics_enabled())

View File

@ -64,7 +64,8 @@ namespace vcpkg
{
return parse_qualified_specifier(spec_as_string)
.then([&](ParsedQualifiedSpecifier&& p) -> ExpectedS<FullPackageSpec> {
if (p.qualifier) return "Error: qualifier not allowed in this context: " + spec_as_string + "\n";
if (p.platform)
return "Error: platform specifier not allowed in this context: " + spec_as_string + "\n";
auto triplet = p.triplet ? Triplet::from_canonical_name(std::move(*p.triplet.get())) : default_triplet;
return FullPackageSpec({p.name, triplet}, p.features.value_or({}));
});
@ -97,7 +98,7 @@ namespace vcpkg
{
return parse_qualified_specifier(name).then([&](ParsedQualifiedSpecifier&& pqs) -> ExpectedS<Features> {
if (pqs.triplet) return "Error: triplet not allowed in this context: " + name + "\n";
if (pqs.qualifier) return "Error: qualifier not allowed in this context: " + name + "\n";
if (pqs.platform) return "Error: platform specifier not allowed in this context: " + name + "\n";
return Features{pqs.name, pqs.features.value_or({})};
});
}
@ -107,8 +108,10 @@ namespace vcpkg
return Parse::ParserBase::is_lower_alpha(ch) || Parse::ParserBase::is_ascii_digit(ch) || ch == '-';
}
static bool is_feature_name_char(char32_t ch) {
// TODO: we do not intend underscores to be valid, however there is currently a feature using them (libwebp[vwebp_sdl]).
static bool is_feature_name_char(char32_t ch)
{
// TODO: we do not intend underscores to be valid, however there is currently a feature using them
// (libwebp[vwebp_sdl]).
// TODO: we need to rename this feature, then remove underscores from this list.
return is_package_name_char(ch) || ch == '_';
}
@ -127,11 +130,15 @@ namespace vcpkg
using Parse::ParserBase;
auto ret = parser.match_zero_or_more(is_feature_name_char).to_string();
auto ch = parser.cur();
if (ParserBase::is_upper_alpha(ch) || ch == '_')
// ignores the feature name vwebp_sdl as a back-compat thing
const bool has_underscore = std::find(ret.begin(), ret.end(), '_') != ret.end() && ret != "vwebp_sdl";
if (has_underscore || ParserBase::is_upper_alpha(ch))
{
parser.add_error("invalid character in feature name (must be lowercase, digits, '-')");
return nullopt;
}
if (ret.empty())
{
parser.add_error("expected feature name (must be lowercase, digits, '-')");
@ -224,6 +231,7 @@ namespace vcpkg
if (ch == '(')
{
auto loc = parser.cur_loc();
std::string platform_string;
int depth = 1;
while (depth > 0 && (ch = parser.next()) != 0)
{
@ -232,12 +240,19 @@ namespace vcpkg
}
if (depth > 0)
{
parser.add_error("unmatched open braces in qualifier", loc);
parser.add_error("unmatched open braces in platform specifier", loc);
return nullopt;
}
ret.qualifier = std::string(
(++loc.it).pointer_to_current(),
parser.it().pointer_to_current());
platform_string.append((++loc.it).pointer_to_current(), parser.it().pointer_to_current());
auto platform_opt = PlatformExpression::parse_platform_expression(platform_string, PlatformExpression::MultipleBinaryOperators::Allow);
if (auto platform = platform_opt.get())
{
ret.platform = std::move(*platform);
}
else
{
parser.add_error(platform_opt.error(), loc);
}
parser.next();
}
// This makes the behavior of the parser more consistent -- otherwise, it will skip tabs and spaces only if

View File

@ -86,7 +86,7 @@ namespace vcpkg::Paragraphs
}
};
static ExpectedS<Paragraph> parse_single_paragraph(const std::string& str, const std::string& origin)
ExpectedS<Paragraph> parse_single_paragraph(const std::string& str, const std::string& origin)
{
auto pghs = PghParser(str, origin).get_paragraphs();
@ -128,16 +128,67 @@ namespace vcpkg::Paragraphs
return PghParser(str, origin).get_paragraphs();
}
bool is_port_directory(const Files::Filesystem& fs, const fs::path& path)
{
return fs.exists(path / fs::u8path("CONTROL")) || fs.exists(path / fs::u8path("vcpkg.json"));
}
ParseExpected<SourceControlFile> try_load_manifest(const Files::Filesystem& fs, const std::string& port_name, const fs::path& path_to_manifest, std::error_code& ec)
{
auto error_info = std::make_unique<ParseControlErrorInfo>();
auto res = Json::parse_file(fs, path_to_manifest, ec);
if (ec) return error_info;
if (auto val = res.get())
{
if (val->first.is_object())
{
return SourceControlFile::parse_manifest_file(path_to_manifest, val->first.object());
}
else
{
error_info->name = port_name;
error_info->error = "Manifest files must have a top-level object";
return error_info;
}
}
else
{
error_info->name = port_name;
error_info->error = res.error()->format();
return error_info;
}
}
ParseExpected<SourceControlFile> try_load_port(const Files::Filesystem& fs, const fs::path& path)
{
const auto path_to_control = path / "CONTROL";
const auto path_to_manifest = path / fs::u8path("vcpkg.json");
const auto path_to_control = path / fs::u8path("CONTROL");
if (fs.exists(path_to_manifest)) {
vcpkg::Checks::check_exit(
VCPKG_LINE_INFO,
!fs.exists(path_to_control),
"Found both manifest and CONTROL file in port %s; please rename one or the other",
path.u8string());
std::error_code ec;
auto res = try_load_manifest(fs, path.filename().u8string(), path_to_manifest, ec);
if (ec)
{
auto error_info = std::make_unique<ParseControlErrorInfo>();
error_info->name = path.filename().u8string();
error_info->error = Strings::format("Failed to load manifest file for port: %s\n", path_to_manifest.u8string(), ec.message());
}
return res;
}
ExpectedS<std::vector<Paragraph>> pghs = get_paragraphs(fs, path_to_control);
if (auto vector_pghs = pghs.get())
{
return SourceControlFile::parse_control_file(path_to_control, std::move(*vector_pghs));
}
auto error_info = std::make_unique<ParseControlErrorInfo>();
error_info->name = path.filename().generic_u8string();
error_info->name = path.filename().u8string();
error_info->error = pghs.error();
return error_info;
}

View File

@ -0,0 +1,471 @@
#include "pch.h"
#include <vcpkg/base/parse.h>
#include <vcpkg/base/strings.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/platform-expression.h>
#include <string>
#include <variant>
#include <vector>
namespace vcpkg::PlatformExpression
{
using vcpkg::Parse::ParseError;
enum class Identifier
{
invalid = -1, // not a recognized identifier
x86,
x64,
arm,
arm64,
wasm32,
windows,
linux,
osx,
uwp,
android,
emscripten,
static_link,
};
static Identifier string2identifier(StringView name)
{
static const std::map<StringView, Identifier> id_map = {
{"x86", Identifier::x86},
{"x64", Identifier::x64},
{"arm", Identifier::arm},
{"arm64", Identifier::arm64},
{"wasm32", Identifier::wasm32},
{"windows", Identifier::windows},
{"linux", Identifier::linux},
{"osx", Identifier::osx},
{"uwp", Identifier::uwp},
{"android", Identifier::android},
{"emscripten", Identifier::emscripten},
{"static", Identifier::static_link},
};
auto id_pair = id_map.find(name);
if (id_pair == id_map.end())
{
return Identifier::invalid;
}
return id_pair->second;
}
namespace detail
{
struct ExprIdentifier
{
std::string identifier;
};
struct ExprNot
{
std::unique_ptr<ExprImpl> expr;
};
struct ExprAnd
{
std::vector<ExprImpl> exprs;
};
struct ExprOr
{
std::vector<ExprImpl> exprs;
};
struct ExprImpl
{
std::variant<ExprIdentifier, ExprNot, ExprAnd, ExprOr> underlying;
explicit ExprImpl(ExprIdentifier e) : underlying(std::move(e)) { }
explicit ExprImpl(ExprNot e) : underlying(std::move(e)) { }
explicit ExprImpl(ExprAnd e) : underlying(std::move(e)) { }
explicit ExprImpl(ExprOr e) : underlying(std::move(e)) { }
ExprImpl clone() const
{
struct Visitor
{
ExprImpl operator()(const ExprIdentifier& e) { return ExprImpl(e); }
ExprImpl operator()(const ExprNot& e)
{
return ExprImpl(ExprNot{std::make_unique<ExprImpl>(e.expr->clone())});
}
ExprImpl operator()(const ExprAnd& e)
{
ExprAnd res;
for (const auto& expr : e.exprs)
{
res.exprs.push_back(expr.clone());
}
return ExprImpl(std::move(res));
}
ExprImpl operator()(const ExprOr& e)
{
ExprOr res;
for (const auto& expr : e.exprs)
{
res.exprs.push_back(expr.clone());
}
return ExprImpl(std::move(res));
}
};
return std::visit(Visitor{}, underlying);
}
};
class ExpressionParser : public Parse::ParserBase
{
public:
ExpressionParser(StringView str, MultipleBinaryOperators multiple_binary_operators) : Parse::ParserBase(str, "CONTROL"), multiple_binary_operators(multiple_binary_operators) { }
MultipleBinaryOperators multiple_binary_operators;
bool allow_multiple_binary_operators() const
{
return multiple_binary_operators == MultipleBinaryOperators::Allow;
}
PlatformExpression::Expr parse()
{
skip_whitespace();
auto res = expr();
if (!at_eof())
{
add_error("invalid logic expression, unexpected character");
}
return Expr(std::make_unique<ExprImpl>(std::move(res)));
}
private:
// <platform-expression.and>
// <platform-expression.not>
// <platform-expression.and> & <platform-expression.not>
// <platform-expression.or>
// <platform-expression.not>
// <platform-expression.or> | <platform-expression.not>
static bool is_identifier_char(char32_t ch)
{
return is_lower_alpha(ch) || is_ascii_digit(ch);
}
// <platform-expression>:
// <platform-expression.not>
// <platform-expression.and>
// <platform-expression.or>
ExprImpl expr()
{
auto result = expr_not();
switch (cur())
{
case '|':
{
ExprOr e;
e.exprs.push_back(std::move(result));
return expr_binary<'|', '&'>(std::move(e));
}
case '&':
{
ExprAnd e;
e.exprs.push_back(std::move(result));
return expr_binary<'&', '|'>(std::move(e));
}
default: return result;
}
}
// <platform-expression.simple>:
// ( <platform-expression> )
// <platform-expression.identifier>
ExprImpl expr_simple()
{
if (cur() == '(')
{
next();
skip_whitespace();
auto result = expr();
if (cur() != ')')
{
add_error("missing closing )");
return result;
}
next();
skip_whitespace();
return result;
}
return expr_identifier();
}
// <platform-expression.identifier>:
// A lowercase alpha-numeric string
ExprImpl expr_identifier()
{
std::string name = match_zero_or_more(is_identifier_char).to_string();
if (name.empty())
{
add_error("unexpected character in logic expression");
}
skip_whitespace();
return ExprImpl{ExprIdentifier{name}};
}
// <platform-expression.not>:
// <platform-expression.simple>
// ! <platform-expression.simple>
ExprImpl expr_not()
{
if (cur() == '!')
{
next();
skip_whitespace();
return ExprImpl(ExprNot{std::make_unique<ExprImpl>(expr_simple())});
}
return expr_simple();
}
template<char oper, char other, class ExprKind>
ExprImpl expr_binary(ExprKind&& seed)
{
do
{
// Support chains of the operator to avoid breaking backwards compatibility
do
{
next();
} while (allow_multiple_binary_operators() && cur() == oper);
skip_whitespace();
seed.exprs.push_back(expr_not());
} while (cur() == oper);
if (cur() == other)
{
add_error("mixing & and | is not allowed; use () to specify order of operations");
}
skip_whitespace();
return ExprImpl(std::move(seed));
}
};
}
using namespace detail;
Expr::Expr() = default;
Expr::Expr(Expr&& other) = default;
Expr& Expr::operator=(Expr&& other) = default;
Expr::Expr(const Expr& other)
{
if (other.underlying_)
{
underlying_ = std::make_unique<ExprImpl>(other.underlying_->clone());
}
}
Expr& Expr::operator=(const Expr& other)
{
if (other.underlying_)
{
if (this->underlying_)
{
*this->underlying_ = other.underlying_->clone();
}
else
{
this->underlying_ = std::make_unique<ExprImpl>(other.underlying_->clone());
}
}
else
{
this->underlying_.reset();
}
return *this;
}
Expr::Expr(std::unique_ptr<ExprImpl>&& e) : underlying_(std::move(e)) { }
Expr::~Expr() = default;
Expr Expr::Identifier(StringView id)
{
return Expr(std::make_unique<ExprImpl>(ExprImpl{ExprIdentifier{id.to_string()}}));
}
Expr Expr::Not(Expr&& e) { return Expr(std::make_unique<ExprImpl>(ExprImpl{ExprNot{std::move(e.underlying_)}})); }
Expr Expr::And(std::vector<Expr>&& exprs)
{
std::vector<ExprImpl> impls;
for (auto& e : exprs)
{
impls.push_back(std::move(*e.underlying_));
}
return Expr(std::make_unique<ExprImpl>(ExprAnd{std::move(impls)}));
}
Expr Expr::Or(std::vector<Expr>&& exprs)
{
std::vector<ExprImpl> impls;
for (auto& e : exprs)
{
impls.push_back(std::move(*e.underlying_));
}
return Expr(std::make_unique<ExprImpl>(ExprOr{std::move(impls)}));
}
bool Expr::evaluate(const Context& context) const
{
if (!underlying_)
{
return true; // empty expression is always true
}
std::map<std::string, bool> override_ctxt;
{
auto override_vars = context.find("VCPKG_DEP_INFO_OVERRIDE_VARS");
if (override_vars != context.end())
{
auto cmake_list = Strings::split(override_vars->second, ';');
for (auto& override_id : cmake_list)
{
if (!override_id.empty())
{
if (override_id[0] == '!')
{
override_ctxt.insert({override_id.substr(1), false});
}
else
{
override_ctxt.insert({override_id, true});
}
}
}
}
}
struct Visitor
{
const Context& context;
const std::map<std::string, bool>& override_ctxt;
bool true_if_exists_and_equal(const std::string& variable_name, const std::string& value) const
{
auto iter = context.find(variable_name);
if (iter == context.end())
{
return false;
}
return iter->second == value;
}
bool visit(const ExprImpl& e) const { return std::visit(*this, e.underlying); }
bool operator()(const ExprIdentifier& expr) const
{
if (!override_ctxt.empty())
{
auto override_id = override_ctxt.find(expr.identifier);
if (override_id != override_ctxt.end())
{
return override_id->second;
}
// Fall through to use the cmake logic if the id does not have an override
}
auto id = string2identifier(expr.identifier);
switch (id)
{
case Identifier::invalid:
// Point out in the diagnostic that they should add to the override list because that is what
// most users should do, however it is also valid to update the built in identifiers to
// recognize the name.
System::printf(System::Color::error,
"Error: Unrecognized identifer name %s. Add to override list in triplet file.\n",
expr.identifier);
return false;
case Identifier::x64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x64");
case Identifier::x86: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "x86");
case Identifier::arm:
// For backwards compatability arm is also true for arm64.
// This is because it previously was only checking for a substring.
return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm") ||
true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64");
case Identifier::arm64: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "arm64");
case Identifier::windows:
return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "") ||
true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore");
case Identifier::linux: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Linux");
case Identifier::osx: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Darwin");
case Identifier::uwp: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "WindowsStore");
case Identifier::android: return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Android");
case Identifier::emscripten:
return true_if_exists_and_equal("VCPKG_CMAKE_SYSTEM_NAME", "Emscripten");
case Identifier::wasm32: return true_if_exists_and_equal("VCPKG_TARGET_ARCHITECTURE", "wasm32");
case Identifier::static_link: return true_if_exists_and_equal("VCPKG_LIBRARY_LINKAGE", "static");
default:
Checks::exit_with_message(
VCPKG_LINE_INFO,
"vcpkg bug: string2identifier returned a value that we don't recognize: %d\n",
static_cast<int>(id));
}
}
bool operator()(const ExprNot& expr) const {
bool res = visit(*expr.expr);
return !res;
}
bool operator()(const ExprAnd& expr) const
{
bool valid = true;
// we want to print errors in all expressions, so we check all of the expressions all the time
for (const auto& e : expr.exprs)
{
valid &= visit(e);
}
return valid;
}
bool operator()(const ExprOr& expr) const
{
bool valid = false;
// we want to print errors in all expressions, so we check all of the expressions all the time
for (const auto& e : expr.exprs)
{
valid |= visit(e);
}
return valid;
}
};
return Visitor{context, override_ctxt}.visit(*underlying_);
}
ExpectedS<Expr> parse_platform_expression(StringView expression, MultipleBinaryOperators multiple_binary_operators)
{
auto parser = ExpressionParser(expression, multiple_binary_operators);
auto res = parser.parse();
if (auto p = parser.extract_error())
{
return p->format();
}
else
{
return res;
}
}
}

View File

@ -25,13 +25,11 @@ namespace vcpkg::PortFileProvider
}
PathsPortFileProvider::PathsPortFileProvider(const vcpkg::VcpkgPaths& paths,
const std::vector<std::string>* ports_dirs_paths)
const std::vector<std::string>& ports_dirs_paths)
: filesystem(paths.get_filesystem())
{
auto& fs = paths.get_filesystem();
if (ports_dirs_paths)
{
for (auto&& overlay_path : *ports_dirs_paths)
for (auto&& overlay_path : ports_dirs_paths)
{
if (!overlay_path.empty())
{
@ -47,10 +45,8 @@ namespace vcpkg::PortFileProvider
Debug::print("Using overlay: ", overlay.u8string(), "\n");
Checks::check_exit(VCPKG_LINE_INFO,
filesystem.exists(overlay),
"Error: Path \"%s\" does not exist",
overlay.string());
Checks::check_exit(
VCPKG_LINE_INFO, filesystem.exists(overlay), "Error: Path \"%s\" does not exist", overlay.string());
Checks::check_exit(VCPKG_LINE_INFO,
fs::is_directory(fs.status(VCPKG_LINE_INFO, overlay)),
@ -60,7 +56,6 @@ namespace vcpkg::PortFileProvider
ports_dirs.emplace_back(overlay);
}
}
}
ports_dirs.emplace_back(paths.ports);
}
@ -75,7 +70,7 @@ namespace vcpkg::PortFileProvider
for (auto&& ports_dir : ports_dirs)
{
// Try loading individual port
if (filesystem.exists(ports_dir / "CONTROL"))
if (Paragraphs::is_port_directory(filesystem, ports_dir))
{
auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
if (auto scf = maybe_scf.get())
@ -94,10 +89,14 @@ namespace vcpkg::PortFileProvider
Checks::exit_with_message(
VCPKG_LINE_INFO, "Error: Failed to load port from %s", spec, ports_dir.u8string());
}
continue;
}
else if (filesystem.exists(ports_dir / spec / "CONTROL"))
auto ports_spec = ports_dir / spec;
if (Paragraphs::is_port_directory(filesystem, ports_spec))
{
auto found_scf = Paragraphs::try_load_port(filesystem, ports_dir / spec);
auto found_scf = Paragraphs::try_load_port(filesystem, ports_spec);
if (auto scf = found_scf.get())
{
if (scf->get()->core_paragraph->name == spec)
@ -133,7 +132,7 @@ namespace vcpkg::PortFileProvider
for (auto&& ports_dir : ports_dirs)
{
// Try loading individual port
if (filesystem.exists(ports_dir / "CONTROL"))
if (Paragraphs::is_port_directory(filesystem, ports_dir))
{
auto maybe_scf = Paragraphs::try_load_port(filesystem, ports_dir);
if (auto scf = maybe_scf.get())

View File

@ -215,6 +215,10 @@ namespace vcpkg::Remove
void perform_and_exit(const VcpkgCmdArguments& args, const VcpkgPaths& paths, Triplet default_triplet)
{
if (paths.manifest_mode_enabled())
{
Checks::exit_with_message(VCPKG_LINE_INFO, "vcpkg remove does not support manifest mode. In order to remove dependencies, you will need to edit your manifest (vcpkg.json).");
}
const ParsedArguments options = args.parse_arguments(COMMAND_STRUCTURE);
StatusParagraphs status_db = database_load_check(paths);
@ -228,7 +232,7 @@ namespace vcpkg::Remove
}
// Load ports from ports dirs
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports.get());
PortFileProvider::PathsPortFileProvider provider(paths, args.overlay_ports);
specs = Util::fmap(Update::find_outdated_packages(provider, status_db),
[](auto&& outdated) { return outdated.spec; });

View File

@ -1,6 +1,6 @@
#include "pch.h"
#include <vcpkg/logicexpression.h>
#include <vcpkg/platform-expression.h>
#include <vcpkg/packagespec.h>
#include <vcpkg/sourceparagraph.h>
#include <vcpkg/triplet.h>
@ -8,6 +8,7 @@
#include <vcpkg/base/checks.h>
#include <vcpkg/base/expected.h>
#include <vcpkg/base/span.h>
#include <vcpkg/base/system.debug.h>
#include <vcpkg/base/system.print.h>
#include <vcpkg/base/util.h>
@ -18,28 +19,71 @@ namespace vcpkg
namespace SourceParagraphFields
{
static const std::string BUILD_DEPENDS = "Build-Depends";
static const std::string DEFAULTFEATURES = "Default-Features";
static const std::string DEFAULT_FEATURES = "Default-Features";
static const std::string DESCRIPTION = "Description";
static const std::string FEATURE = "Feature";
static const std::string MAINTAINER = "Maintainer";
static const std::string SOURCE = "Source";
static const std::string MAINTAINERS = "Maintainer";
static const std::string NAME = "Source";
static const std::string VERSION = "Version";
static const std::string PORT_VERSION = "Port-Version";
static const std::string HOMEPAGE = "Homepage";
static const std::string TYPE = "Type";
static const std::string SUPPORTS = "Supports";
}
static Span<const std::string> get_list_of_valid_fields()
namespace ManifestFields
{
static const std::string valid_fields[] = {
SourceParagraphFields::SOURCE,
constexpr static StringLiteral NAME = "name";
constexpr static StringLiteral VERSION = "version-string";
constexpr static StringLiteral PORT_VERSION = "port-version";
constexpr static StringLiteral MAINTAINERS = "maintainers";
constexpr static StringLiteral DESCRIPTION = "description";
constexpr static StringLiteral HOMEPAGE = "homepage";
constexpr static StringLiteral DOCUMENTATION = "documentation";
constexpr static StringLiteral LICENSE = "license";
constexpr static StringLiteral DEPENDENCIES = "dependencies";
constexpr static StringLiteral DEV_DEPENDENCIES = "dev-dependencies";
constexpr static StringLiteral FEATURES = "features";
constexpr static StringLiteral DEFAULT_FEATURES = "default-features";
constexpr static StringLiteral SUPPORTS = "supports";
}
static Span<const StringView> get_list_of_valid_fields()
{
static const StringView valid_fields[] = {
SourceParagraphFields::NAME,
SourceParagraphFields::VERSION,
SourceParagraphFields::PORT_VERSION,
SourceParagraphFields::DESCRIPTION,
SourceParagraphFields::MAINTAINER,
SourceParagraphFields::MAINTAINERS,
SourceParagraphFields::BUILD_DEPENDS,
SourceParagraphFields::HOMEPAGE,
SourceParagraphFields::TYPE,
SourceParagraphFields::SUPPORTS,
SourceParagraphFields::DEFAULT_FEATURES,
};
return valid_fields;
}
static Span<const StringView> get_list_of_manifest_fields()
{
constexpr static StringView valid_fields[] = {
ManifestFields::NAME,
ManifestFields::VERSION,
ManifestFields::PORT_VERSION,
ManifestFields::MAINTAINERS,
ManifestFields::DESCRIPTION,
ManifestFields::HOMEPAGE,
ManifestFields::DOCUMENTATION,
ManifestFields::LICENSE,
ManifestFields::DEPENDENCIES,
ManifestFields::DEV_DEPENDENCIES,
ManifestFields::FEATURES,
ManifestFields::DEFAULT_FEATURES,
ManifestFields::SUPPORTS,
};
return valid_fields;
@ -65,22 +109,29 @@ namespace vcpkg
if (!error_info->extra_fields.empty())
{
System::print2(System::Color::error,
"Error: There are invalid fields in the control file of ",
"Error: There are invalid fields in the control or manifest file of ",
error_info->name,
'\n');
System::print2("The following fields were not expected:\n\n ",
Strings::join("\n ", error_info->extra_fields),
"\n\n");
System::print2("The following fields were not expected:\n");
for (const auto& pr : error_info->extra_fields)
{
System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n");
}
have_remaining_fields = true;
}
}
if (have_remaining_fields)
{
System::print2("This is the list of valid fields (case-sensitive): \n\n ",
System::print2("This is the list of valid fields for CONTROL files (case-sensitive): \n\n ",
Strings::join("\n ", get_list_of_valid_fields()),
"\n\n");
System::print2("Different source may be available for vcpkg. Use .\\bootstrap-vcpkg.bat to update.\n\n");
System::print2("And this is the list of valid fields for manifest files: \n\n ",
Strings::join("\n ", get_list_of_manifest_fields()),
"\n\n");
System::print2("You may need to update the vcpkg binary; try running bootstrap-vcpkg.bat or "
"bootstrap-vcpkg.sh to update.\n\n");
}
for (auto&& error_info : error_info_list)
@ -91,9 +142,29 @@ namespace vcpkg
"Error: There are missing fields in the control file of ",
error_info->name,
'\n');
System::print2("The following fields were missing:\n\n ",
Strings::join("\n ", error_info->missing_fields),
"\n\n");
System::print2("The following fields were missing:\n");
for (const auto& pr : error_info->missing_fields)
{
System::print2(" In ", pr.first, ": ", Strings::join(", ", pr.second), "\n");
}
}
}
for (auto&& error_info : error_info_list)
{
if (!error_info->expected_types.empty())
{
System::print2(System::Color::error,
"Error: There are invalid field types in the CONTROL or manifest file of ",
error_info->name,
'\n');
System::print2("The following fields had the wrong types:\n\n");
for (const auto& pr : error_info->expected_types)
{
System::printf(" %s was expected to be %s\n", pr.first, pr.second);
}
System::print2("\n");
}
}
}
@ -111,10 +182,27 @@ namespace vcpkg
Type Type::from_string(const std::string& t)
{
if (t == "Alias") return Type{Type::ALIAS};
if (t == "Port" || t == "") return Type{Type::PORT};
if (t == "Port" || t.empty()) return Type{Type::PORT};
return Type{Type::UNKNOWN};
}
bool operator==(const Type& lhs, const Type& rhs)
{
return lhs.type == rhs.type;
}
bool operator!=(const Type& lhs, const Type& rhs)
{
return !(lhs == rhs);
}
static void trim_all(std::vector<std::string>& arr)
{
for (auto& el : arr)
{
el = Strings::trim(std::move(el));
}
}
static ParseExpected<SourceParagraph> parse_source_paragraph(const fs::path& path_to_control, Paragraph&& fields)
{
auto origin = path_to_control.u8string();
@ -123,20 +211,52 @@ namespace vcpkg
auto spgh = std::make_unique<SourceParagraph>();
parser.required_field(SourceParagraphFields::SOURCE, spgh->name);
parser.required_field(SourceParagraphFields::NAME, spgh->name);
parser.required_field(SourceParagraphFields::VERSION, spgh->version);
spgh->description = parser.optional_field(SourceParagraphFields::DESCRIPTION);
spgh->maintainer = parser.optional_field(SourceParagraphFields::MAINTAINER);
auto pv_str = parser.optional_field(SourceParagraphFields::PORT_VERSION);
if (!pv_str.empty())
{
auto pv_opt = Strings::strto<int>(pv_str);
if (auto pv = pv_opt.get())
{
spgh->port_version = *pv;
}
else
{
parser.add_type_error(SourceParagraphFields::PORT_VERSION, "a non-negative integer");
}
}
spgh->description = Strings::split(parser.optional_field(SourceParagraphFields::DESCRIPTION), '\n');
trim_all(spgh->description);
spgh->maintainers = Strings::split(parser.optional_field(SourceParagraphFields::MAINTAINERS), '\n');
trim_all(spgh->maintainers);
spgh->homepage = parser.optional_field(SourceParagraphFields::HOMEPAGE);
TextRowCol textrowcol;
std::string buf;
parser.optional_field(SourceParagraphFields::BUILD_DEPENDS, {buf, textrowcol});
spgh->depends = parse_dependencies_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO);
spgh->dependencies = parse_dependencies_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO);
buf.clear();
parser.optional_field(SourceParagraphFields::DEFAULTFEATURES, {buf, textrowcol});
parser.optional_field(SourceParagraphFields::DEFAULT_FEATURES, {buf, textrowcol});
spgh->default_features = parse_default_features_list(buf, origin, textrowcol).value_or_exit(VCPKG_LINE_INFO);
spgh->supports_expression = parser.optional_field(SourceParagraphFields::SUPPORTS);
auto supports_expr = parser.optional_field(SourceParagraphFields::SUPPORTS);
if (!supports_expr.empty())
{
auto maybe_expr = PlatformExpression::parse_platform_expression(supports_expr, PlatformExpression::MultipleBinaryOperators::Allow);
if (auto expr = maybe_expr.get())
{
spgh->supports_expression = std::move(*expr);
}
else
{
parser.add_type_error(SourceParagraphFields::SUPPORTS, "a platform expression");
}
}
spgh->type = Type::from_string(parser.optional_field(SourceParagraphFields::TYPE));
auto err = parser.error_info(spgh->name.empty() ? origin : spgh->name);
if (err)
@ -153,9 +273,11 @@ namespace vcpkg
auto fpgh = std::make_unique<FeatureParagraph>();
parser.required_field(SourceParagraphFields::FEATURE, fpgh->name);
parser.required_field(SourceParagraphFields::DESCRIPTION, fpgh->description);
fpgh->description = Strings::split(parser.required_field(SourceParagraphFields::DESCRIPTION), '\n');
trim_all(fpgh->description);
fpgh->depends = parse_dependencies_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS), origin)
fpgh->dependencies =
parse_dependencies_list(parser.optional_field(SourceParagraphFields::BUILD_DEPENDS), origin)
.value_or_exit(VCPKG_LINE_INFO);
auto err = parser.error_info(fpgh->name.empty() ? origin : fpgh->name);
@ -197,6 +319,497 @@ namespace vcpkg
return control_file;
}
static std::vector<std::string> invalid_json_fields(const Json::Object& obj,
Span<const StringView> known_fields) noexcept
{
const auto field_is_unknown = [known_fields](StringView sv) {
// allow directives
if (sv.size() != 0 && *sv.begin() == '$')
{
return false;
}
return std::find(known_fields.begin(), known_fields.end(), sv) == known_fields.end();
};
std::vector<std::string> res;
for (const auto& kv : obj)
{
if (field_is_unknown(kv.first))
{
res.push_back(kv.first.to_string());
}
}
return res;
}
struct StringField : Json::VisitorCrtpBase<StringField>
{
using type = std::string;
StringView type_name() { return type_name_; }
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv) { return sv.to_string(); }
explicit StringField(StringView type_name_) : type_name_(type_name_) { }
private:
StringView type_name_;
};
struct BooleanField : Json::VisitorCrtpBase<BooleanField>
{
using type = bool;
StringView type_name() { return "a boolean"; }
Optional<bool> visit_bool(Json::Reader&, StringView, bool b) { return b; }
};
enum class AllowEmpty : bool
{
No,
Yes,
};
template<class T>
struct ArrayField : Json::VisitorCrtpBase<ArrayField<T>>
{
using type = std::vector<typename T::type>;
StringView type_name() { return type_name_; }
ArrayField(StringView type_name_, AllowEmpty allow_empty, T&& t = {}) : type_name_(type_name_), underlying_visitor_(static_cast<T&&>(t)), allow_empty_(allow_empty) { }
Optional<type> visit_array(Json::Reader& r, StringView key, const Json::Array& arr)
{
if (allow_empty_ == AllowEmpty::No && arr.size() == 0)
{
return nullopt;
}
return r.array_elements(arr, key, underlying_visitor_);
}
private:
StringView type_name_;
T underlying_visitor_;
AllowEmpty allow_empty_;
};
struct ParagraphField : Json::VisitorCrtpBase<ParagraphField>
{
using type = std::vector<std::string>;
StringView type_name() { return "a string or array of strings"; }
Optional<std::vector<std::string>> visit_string(Json::Reader&, StringView, StringView sv)
{
std::vector<std::string> out;
out.push_back(sv.to_string());
return out;
}
Optional<std::vector<std::string>> visit_array(Json::Reader& r, StringView key, const Json::Array& arr)
{
return r.array_elements(arr, key, StringField{"a string"});
}
};
struct IdentifierField : Json::VisitorCrtpBase<IdentifierField>
{
using type = std::string;
StringView type_name() { return "an identifier"; }
// [a-z0-9]+(-[a-z0-9]+)*, plus not any of {prn, aux, nul, con, lpt[1-9], com[1-9], core, default}
static bool is_ident(StringView sv)
{
static const std::regex BASIC_IDENTIFIER = std::regex(R"([a-z0-9]+(-[a-z0-9]+)*)");
// we only check for lowercase in RESERVED since we already remove all
// strings with uppercase letters from the basic check
static const std::regex RESERVED = std::regex(R"(prn|aux|nul|con|(lpt|com)[1-9]|core|default)");
if (!std::regex_match(sv.begin(), sv.end(), BASIC_IDENTIFIER))
{
return false; // we're not even in the shape of an identifier
}
if (std::regex_match(sv.begin(), sv.end(), RESERVED))
{
return false; // we're a reserved identifier
}
return true;
}
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
{
if (is_ident(sv))
{
return sv.to_string();
}
else
{
return nullopt;
}
}
};
struct PackageNameField : Json::VisitorCrtpBase<PackageNameField>
{
using type = std::string;
StringView type_name() { return "a package name"; }
static bool is_package_name(StringView sv)
{
if (sv.size() == 0)
{
return false;
}
for (const auto& ident : Strings::split(sv, '.'))
{
if (!IdentifierField::is_ident(ident))
{
return false;
}
}
return true;
}
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
{
if (!is_package_name(sv))
{
return nullopt;
}
return sv.to_string();
}
};
// We "parse" this so that we can add actual license parsing at some point in the future
// without breaking anyone
struct LicenseExpressionField : Json::VisitorCrtpBase<LicenseExpressionField>
{
using type = std::string;
StringView type_name() { return "an SPDX license expression"; }
enum class Mode
{
ExpectExpression,
ExpectContinue,
ExpectException,
};
constexpr static StringView EXPRESSION_WORDS[] = {
"WITH",
"AND",
"OR",
};
constexpr static StringView VALID_LICENSES[] =
#include "spdx-licenses.inc"
;
constexpr static StringView VALID_EXCEPTIONS[] =
#include "spdx-exceptions.inc"
;
Optional<std::string> visit_string(Json::Reader&, StringView, StringView sv)
{
Mode mode = Mode::ExpectExpression;
size_t open_parens = 0;
std::string current_word;
const auto check_current_word = [&current_word, &mode] {
if (current_word.empty())
{
return true;
}
Span<const StringView> valid_ids;
bool case_sensitive = false;
switch (mode)
{
case Mode::ExpectExpression:
valid_ids = VALID_LICENSES;
mode = Mode::ExpectContinue;
// a single + is allowed on the end of licenses
if (current_word.back() == '+')
{
current_word.pop_back();
}
break;
case Mode::ExpectContinue:
valid_ids = EXPRESSION_WORDS;
mode = Mode::ExpectExpression;
case_sensitive = true;
break;
case Mode::ExpectException:
valid_ids = VALID_EXCEPTIONS;
mode = Mode::ExpectContinue;
break;
}
const auto equal = [&](StringView sv) {
if (case_sensitive)
{
return sv == current_word;
}
else
{
return Strings::case_insensitive_ascii_equals(sv, current_word);
}
};
if (std::find_if(valid_ids.begin(), valid_ids.end(), equal) == valid_ids.end())
{
return false;
}
if (current_word == "WITH")
{
mode = Mode::ExpectException;
}
current_word.clear();
return true;
};
for (const auto& ch : sv)
{
if (ch == ' ' || ch == '\t')
{
if (!check_current_word())
{
return nullopt;
}
}
else if (ch == '(')
{
if (!check_current_word())
{
return nullopt;
}
if (mode != Mode::ExpectExpression)
{
return nullopt;
}
++open_parens;
}
else if (ch == ')')
{
if (!check_current_word())
{
return nullopt;
}
if (mode != Mode::ExpectContinue)
{
return nullopt;
}
if (open_parens == 0)
{
return nullopt;
}
--open_parens;
}
else
{
current_word.push_back(ch);
}
}
if (!check_current_word())
{
return nullopt;
}
else
{
return sv.to_string();
}
}
};
struct PlatformExprField : Json::VisitorCrtpBase<PlatformExprField>
{
using type = PlatformExpression::Expr;
StringView type_name() { return "a platform expression"; }
Optional<PlatformExpression::Expr> visit_string(Json::Reader&, StringView, StringView sv)
{
auto opt = PlatformExpression::parse_platform_expression(sv, PlatformExpression::MultipleBinaryOperators::Deny);
if (auto res = opt.get())
{
return std::move(*res);
}
else
{
Debug::print("Failed to parse platform expression: ", opt.error(), "\n");
return nullopt;
}
}
};
struct DependencyField : Json::VisitorCrtpBase<DependencyField>
{
using type = Dependency;
StringView type_name() { return "a dependency"; }
constexpr static StringView NAME = "name";
constexpr static StringView FEATURES = "features";
constexpr static StringView DEFAULT_FEATURES = "default-features";
constexpr static StringView PLATFORM = "platform";
constexpr static StringView KNOWN_FIELDS[] = {NAME, FEATURES, DEFAULT_FEATURES, PLATFORM};
Optional<Dependency> visit_string(Json::Reader&, StringView, StringView sv)
{
if (!PackageNameField::is_package_name(sv))
{
return nullopt;
}
Dependency dep;
dep.name = sv.to_string();
return dep;
}
Optional<Dependency> visit_object(Json::Reader& r, StringView, const Json::Object& obj)
{
{
auto extra_fields = invalid_json_fields(obj, KNOWN_FIELDS);
if (!extra_fields.empty())
{
r.error().add_extra_fields(type_name().to_string(), std::move(extra_fields));
}
}
Dependency dep;
r.required_object_field(type_name(), obj, NAME, dep.name, PackageNameField{});
r.optional_object_field(
obj, FEATURES, dep.features, ArrayField<IdentifierField>{"an array of identifiers", AllowEmpty::Yes});
bool default_features = true;
r.optional_object_field(obj, DEFAULT_FEATURES, default_features, BooleanField{});
if (!default_features)
{
dep.features.push_back("core");
}
r.optional_object_field(obj, PLATFORM, dep.platform, PlatformExprField{});
return dep;
}
};
struct FeatureField : Json::VisitorCrtpBase<FeatureField>
{
using type = std::unique_ptr<FeatureParagraph>;
StringView type_name() { return "a feature"; }
constexpr static StringView NAME = "name";
constexpr static StringView DESCRIPTION = "description";
constexpr static StringView DEPENDENCIES = "dependencies";
Optional<std::unique_ptr<FeatureParagraph>> visit_object(Json::Reader& r, StringView, const Json::Object& obj)
{
auto feature = std::make_unique<FeatureParagraph>();
r.required_object_field(type_name(), obj, NAME, feature->name, IdentifierField{});
r.required_object_field(type_name(), obj, DESCRIPTION, feature->description, ParagraphField{});
r.optional_object_field(obj,
DEPENDENCIES,
feature->dependencies,
ArrayField<DependencyField>{"an array of dependencies", AllowEmpty::Yes});
return std::move(feature);
}
};
Parse::ParseExpected<SourceControlFile> SourceControlFile::parse_manifest_file(const fs::path& path_to_manifest,
const Json::Object& manifest)
{
struct JsonErr final : Json::ReaderError
{
ParseControlErrorInfo pcei;
void add_missing_field(std::string&& type, std::string&& key) override
{
pcei.missing_fields[std::move(type)].push_back(std::move(key));
}
void add_expected_type(std::string&& key, std::string&& expected_type) override
{
pcei.expected_types.emplace(std::move(key), std::move(expected_type));
}
void add_extra_fields(std::string&& type, std::vector<std::string>&& fields) override
{
if (!fields.empty())
{
auto& fields_for_type = pcei.extra_fields[std::move(type)];
fields_for_type.insert(fields_for_type.end(), fields.begin(), fields.end());
}
}
void add_mutually_exclusive_fields(std::string&& type, std::vector<std::string>&& fields) override
{
if (!fields.empty())
{
auto& fields_for_type = pcei.mutually_exclusive_fields[std::move(type)];
fields_for_type.insert(fields_for_type.end(), fields.begin(), fields.end());
}
}
} err = {};
auto visit = Json::Reader{&err};
err.pcei.name = path_to_manifest.u8string();
{
auto extra_fields = invalid_json_fields(manifest, get_list_of_manifest_fields());
if (!extra_fields.empty())
{
err.pcei.extra_fields["manifest"] = std::move(extra_fields);
}
}
auto control_file = std::make_unique<SourceControlFile>();
control_file->core_paragraph = std::make_unique<SourceParagraph>();
auto& spgh = control_file->core_paragraph;
constexpr static StringView type_name = "vcpkg.json";
visit.required_object_field(type_name, manifest, ManifestFields::NAME, spgh->name, IdentifierField{});
visit.required_object_field(
type_name, manifest, ManifestFields::VERSION, spgh->version, StringField{"a version"});
visit.optional_object_field(manifest, ManifestFields::MAINTAINERS, spgh->maintainers, ParagraphField{});
visit.optional_object_field(manifest, ManifestFields::DESCRIPTION, spgh->description, ParagraphField{});
visit.optional_object_field(manifest, ManifestFields::HOMEPAGE, spgh->homepage, StringField{"a url"});
visit.optional_object_field(manifest, ManifestFields::DOCUMENTATION, spgh->documentation, StringField{"a url"});
visit.optional_object_field(manifest, ManifestFields::LICENSE, spgh->license, LicenseExpressionField{});
visit.optional_object_field(manifest,
ManifestFields::DEPENDENCIES,
spgh->dependencies,
ArrayField<DependencyField>{"an array of dependencies", AllowEmpty::Yes});
if (manifest.contains(ManifestFields::DEV_DEPENDENCIES))
{
System::print2(System::Color::error, "dev_dependencies are not yet supported");
Checks::exit_fail(VCPKG_LINE_INFO);
}
visit.optional_object_field(manifest, ManifestFields::SUPPORTS, spgh->supports_expression, PlatformExprField{});
visit.optional_object_field(manifest,
ManifestFields::DEFAULT_FEATURES,
spgh->default_features,
ArrayField<IdentifierField>{"an array of identifiers", AllowEmpty::Yes});
visit.optional_object_field(manifest,
ManifestFields::FEATURES,
control_file->feature_paragraphs,
ArrayField<FeatureField>{"an array of feature definitions", AllowEmpty::Yes});
if (err.pcei.has_error())
{
return std::make_unique<ParseControlErrorInfo>(std::move(err.pcei));
}
return std::move(control_file);
}
Optional<const FeatureParagraph&> SourceControlFile::find_feature(const std::string& featurename) const
{
auto it = Util::find_if(feature_paragraphs,
@ -211,10 +824,10 @@ namespace vcpkg
{
if (featurename == "core")
{
return core_paragraph->depends;
return core_paragraph->dependencies;
}
else if (auto p_feature = find_feature(featurename).get())
return p_feature->depends;
return p_feature->dependencies;
else
return nullopt;
}
@ -226,11 +839,9 @@ namespace vcpkg
std::vector<FullPackageSpec> ret;
for (auto&& dep : deps)
{
const auto& qualifier = dep.qualifier;
if (qualifier.empty() ||
evaluate_expression(qualifier, {cmake_vars, t.canonical_name()}).value_or_exit(VCPKG_LINE_INFO))
if (dep.platform.evaluate(cmake_vars))
{
ret.emplace_back(FullPackageSpec({dep.depend.name, t}, dep.depend.features));
ret.emplace_back(FullPackageSpec({dep.name, t}, dep.features));
}
}
return ret;

View File

@ -0,0 +1,45 @@
// Data downloaded from https://raw.githubusercontent.com/spdx/license-list-data/a3cab5c04eaf399ea8ee07ac69c749a9ad6a3f17/json/exceptions.json
// Generated by scripts/Generate-SpdxLicenseList.ps1
{
"GCC-exception-2.0",
"openvpn-openssl-exception",
"Nokia-Qt-exception-1.1",
"GPL-3.0-linking-exception",
"Fawkes-Runtime-exception",
"u-boot-exception-2.0",
"PS-or-PDF-font-exception-20170817",
"gnu-javamail-exception",
"LGPL-3.0-linking-exception",
"DigiRule-FOSS-exception",
"LLVM-exception",
"Linux-syscall-note",
"GPL-3.0-linking-source-exception",
"Qwt-exception-1.0",
"389-exception",
"mif-exception",
"eCos-exception-2.0",
"CLISP-exception-2.0",
"Bison-exception-2.2",
"Libtool-exception",
"LZMA-exception",
"OpenJDK-assembly-exception-1.0",
"Font-exception-2.0",
"OCaml-LGPL-linking-exception",
"GCC-exception-3.1",
"Bootloader-exception",
"SHL-2.0",
"Classpath-exception-2.0",
"Swift-exception",
"Autoconf-exception-2.0",
"FLTK-exception",
"freertos-exception-2.0",
"Universal-FOSS-exception-1.0",
"WxWindows-exception-3.1",
"OCCT-exception-1.0",
"Autoconf-exception-3.0",
"i2p-gpl-java-exception",
"GPL-CC-1.0",
"Qt-LGPL-exception-1.1",
"SHL-2.1",
"Qt-GPL-exception-1.0",
}

Some files were not shown because too many files have changed in this diff Show More