diff --git a/CMakeLists.txt b/CMakeLists.txt index ef1d8e7ab5..712b09c03e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,6 +215,7 @@ target_include_directories(vcpkglib PUBLIC include) target_compile_definitions(vcpkglib PUBLIC VCPKG_VERSION=${VCPKG_VERSION} VCPKG_BASE_VERSION=${VCPKG_BASE_VERSION} + _FILE_OFFSET_BITS=64 ) if(NOT DEFINED VCPKG_STANDALONE_BUNDLE_SHA OR VCPKG_STANDALONE_BUNDLE_SHA STREQUAL "") diff --git a/azure-pipelines/e2e_assets/test-dll-port-template/build.cmd b/azure-pipelines/e2e_assets/test-dll-port-template/build.cmd new file mode 100644 index 0000000000..2f45d976d0 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-dll-port-template/build.cmd @@ -0,0 +1,4 @@ +mkdir "%~dp0debug" +mkdir "%~dp0release" +cl /MDd "%~dp0test.c" "%~dp0test.def" /Fo"%~dp0debug\test.obj" /Fe"%~dp0debug\test_dll.dll" /link /DLL +cl /MD "%~dp0test.c" "%~dp0test.def" /Fo"%~dp0release\test.obj" /Fe"%~dp0release\test_dll.dll" /link /DLL diff --git a/azure-pipelines/e2e_assets/test-dll-port-template/portfile.cmake b/azure-pipelines/e2e_assets/test-dll-port-template/portfile.cmake new file mode 100644 index 0000000000..a474519fd3 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-dll-port-template/portfile.cmake @@ -0,0 +1,14 @@ +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/bin") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/bin") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/include") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/debug/test_dll.dll" DESTINATION "${CURRENT_PACKAGES_DIR}/debug/bin") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/debug/test_dll.lib" DESTINATION "${CURRENT_PACKAGES_DIR}/debug/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/release/test_dll.dll" DESTINATION "${CURRENT_PACKAGES_DIR}/bin") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/release/test_dll.lib" DESTINATION "${CURRENT_PACKAGES_DIR}/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/test.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include") +file(TOUCH "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright") diff --git a/azure-pipelines/e2e_assets/test-dll-port-template/test.c b/azure-pipelines/e2e_assets/test-dll-port-template/test.c new file mode 100644 index 0000000000..69fc354ef0 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-dll-port-template/test.c @@ -0,0 +1,17 @@ +#include +#include + +BOOLEAN WINAPI DllMain(HINSTANCE hDllHandle, DWORD nReason, LPVOID Reserved) +{ + (void)Reserved; + if (nReason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls( hDllHandle ); + } + + return TRUE; +} + +int __cdecl export_me() { + puts("You called the exported function!"); + return 42; +} diff --git a/azure-pipelines/e2e_assets/test-dll-port-template/test.def b/azure-pipelines/e2e_assets/test-dll-port-template/test.def new file mode 100644 index 0000000000..aa9fb49c31 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-dll-port-template/test.def @@ -0,0 +1,4 @@ +LIBRARY test_dll + +EXPORTS +export_me diff --git a/azure-pipelines/e2e_assets/test-dll-port-template/test.h b/azure-pipelines/e2e_assets/test-dll-port-template/test.h new file mode 100644 index 0000000000..10d2052384 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-dll-port-template/test.h @@ -0,0 +1 @@ + // empty test header diff --git a/azure-pipelines/e2e_assets/test-dll-port-template/vcpkg.json b/azure-pipelines/e2e_assets/test-dll-port-template/vcpkg.json new file mode 100644 index 0000000000..e7b996246b --- /dev/null +++ b/azure-pipelines/e2e_assets/test-dll-port-template/vcpkg.json @@ -0,0 +1,4 @@ +{ + "name": "test-dll", + "version": "1.0.0" +} diff --git a/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/build.cmd b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/build.cmd new file mode 100644 index 0000000000..a50bd2aad8 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/build.cmd @@ -0,0 +1,8 @@ +mkdir "%~dp0debug" +mkdir "%~dp0release" +mkdir "%~dp0both" +cl /c /MDd "%~dp0test.c" /Fo"%~dp0debug\test.obj" +lib "%~dp0debug\test.obj" /OUT:"%~dp0debug\test_lib.lib" +cl /c /DNDEBUG /MD "%~dp0test.c" /Fo"%~dp0release\test.obj" +lib "%~dp0release\test.obj" /OUT:"%~dp0release\test_lib.lib" +lib "%~dp0debug\test.obj" "%~dp0release\test.obj" /OUT:"%~dp0both\both_lib.lib" diff --git a/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/portfile.cmake b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/portfile.cmake new file mode 100644 index 0000000000..d902620688 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/portfile.cmake @@ -0,0 +1,12 @@ +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/include") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/debug/test_lib.lib" DESTINATION "${CURRENT_PACKAGES_DIR}/debug/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/both/both_lib.lib" DESTINATION "${CURRENT_PACKAGES_DIR}/debug/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/release/test_lib.lib" DESTINATION "${CURRENT_PACKAGES_DIR}/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/both/both_lib.lib" DESTINATION "${CURRENT_PACKAGES_DIR}/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/test.h" DESTINATION "${CURRENT_PACKAGES_DIR}/include") +file(TOUCH "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright") diff --git a/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/test.c b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/test.c new file mode 100644 index 0000000000..e7a6cf2fe8 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/test.c @@ -0,0 +1,10 @@ +#include + +#ifdef NDEBUG +int __cdecl use_ndebug() { +#else +int __cdecl use_no_ndebug() { +#endif + puts("You called the static lib function!"); + return 42; +} diff --git a/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/test.h b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/test.h new file mode 100644 index 0000000000..10d2052384 --- /dev/null +++ b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/test.h @@ -0,0 +1 @@ + // empty test header diff --git a/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/vcpkg.json b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/vcpkg.json new file mode 100644 index 0000000000..aace75bf6b --- /dev/null +++ b/azure-pipelines/e2e_assets/test-lib-port-template-dynamic-crt/vcpkg.json @@ -0,0 +1,4 @@ +{ + "name": "test-lib", + "version": "1.0.0" +} diff --git a/azure-pipelines/e2e_ports/llvm-lto-lib/llvm-lto-charset.lib b/azure-pipelines/e2e_ports/llvm-lto-lib/llvm-lto-charset.lib new file mode 100644 index 0000000000..d7c616618a Binary files /dev/null and b/azure-pipelines/e2e_ports/llvm-lto-lib/llvm-lto-charset.lib differ diff --git a/azure-pipelines/e2e_ports/llvm-lto-lib/portfile.cmake b/azure-pipelines/e2e_ports/llvm-lto-lib/portfile.cmake new file mode 100644 index 0000000000..65cdf3ab9b --- /dev/null +++ b/azure-pipelines/e2e_ports/llvm-lto-lib/portfile.cmake @@ -0,0 +1,12 @@ +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/lib") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/debug/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/llvm-lto-charset.lib" + DESTINATION "${CURRENT_PACKAGES_DIR}/lib") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/llvm-lto-charset.lib" + DESTINATION "${CURRENT_PACKAGES_DIR}/debug/lib") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/include") +file(TOUCH "${CURRENT_PACKAGES_DIR}/include/llvm-lto-lib.h") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(TOUCH "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright") diff --git a/azure-pipelines/e2e_ports/llvm-lto-lib/vcpkg.json b/azure-pipelines/e2e_ports/llvm-lto-lib/vcpkg.json new file mode 100644 index 0000000000..eee03841d4 --- /dev/null +++ b/azure-pipelines/e2e_ports/llvm-lto-lib/vcpkg.json @@ -0,0 +1,5 @@ +{ + "name": "llvm-lto-lib", + "version": "1", + "supports": "windows" +} diff --git a/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/CMakeLists.txt b/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/CMakeLists.txt new file mode 100644 index 0000000000..3a63e6a73e --- /dev/null +++ b/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.24) +project(vcpkg-internal-dll-with-no-exports LANGUAGES C) +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test.c" [[ #include +BOOLEAN WINAPI DllMain(HINSTANCE hDllHandle, DWORD nReason, LPVOID Reserved) +{ + (void)Reserved; + if (nReason == DLL_PROCESS_ATTACH) { + DisableThreadLibraryCalls( hDllHandle ); + } + + return TRUE; +} +]]) + +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/test.h" [[ + // empty test header +]]) + +add_library(no_exports "${CMAKE_CURRENT_BINARY_DIR}/test.c") +include(GNUInstallDirs) +install(TARGETS no_exports) +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/test.c" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") diff --git a/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/portfile.cmake b/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/portfile.cmake new file mode 100644 index 0000000000..400e965ec8 --- /dev/null +++ b/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/portfile.cmake @@ -0,0 +1,12 @@ +SET(VCPKG_POLICY_DLLS_WITHOUT_LIBS enabled) +file(MAKE_DIRECTORY "${CURRENT_BUILDTREES_DIR}/src") +file(COPY "${CMAKE_CURRENT_LIST_DIR}/CMakeLists.txt" DESTINATION "${CURRENT_BUILDTREES_DIR}/src") +vcpkg_cmake_configure(SOURCE_PATH "${CURRENT_BUILDTREES_DIR}/src") +vcpkg_cmake_install() +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share") +file(MAKE_DIRECTORY "${CURRENT_PACKAGES_DIR}/share/${PORT}") +file(REMOVE_RECURSE + "${CURRENT_PACKAGES_DIR}/debug/include" + "${CURRENT_PACKAGES_DIR}/debug/lib" + "${CURRENT_PACKAGES_DIR}/lib") +file(TOUCH "${CURRENT_PACKAGES_DIR}/share/${PORT}/copyright") diff --git a/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/vcpkg.json b/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/vcpkg.json new file mode 100644 index 0000000000..721a3a90f1 --- /dev/null +++ b/azure-pipelines/e2e_ports/vcpkg-internal-dll-with-no-exports/vcpkg.json @@ -0,0 +1,10 @@ +{ + "name": "vcpkg-internal-dll-with-no-exports", + "version": "1.0.0", + "dependencies": [ + { + "name": "vcpkg-cmake", + "host": true + } + ] +} diff --git a/azure-pipelines/end-to-end-tests-dir/post-build-checks.ps1 b/azure-pipelines/end-to-end-tests-dir/post-build-checks.ps1 new file mode 100644 index 0000000000..5436696f11 --- /dev/null +++ b/azure-pipelines/end-to-end-tests-dir/post-build-checks.ps1 @@ -0,0 +1,81 @@ +. $PSScriptRoot/../end-to-end-tests-prelude.ps1 + +if (-not $IsWindows) { + Write-Host 'Skipping e2e post build checks on non-Windows' + return +} + +# DLLs with no exports +Refresh-TestRoot +[string]$buildOutput = Run-VcpkgAndCaptureOutput @commonArgs install --overlay-ports="$PSScriptRoot/../e2e_ports" vcpkg-internal-dll-with-no-exports --no-binarycaching +if (-not $buildOutput.Contains("$packagesRoot\vcpkg-internal-dll-with-no-exports_x86-windows\debug\bin\no_exports.dll") ` + -or -not $buildOutput.Contains("$packagesRoot\vcpkg-internal-dll-with-no-exports_x86-windows\bin\no_exports.dll") ` + -or -not $buildOutput.Contains('set(VCPKG_POLICY_DLLS_WITHOUT_EXPORTS enabled)')) { + throw 'Did not detect DLLs with no exports.' +} + +# DLLs with wrong architecture +Refresh-TestRoot +mkdir "$TestingRoot/wrong-architecture" +Copy-Item -Recurse "$PSScriptRoot/../e2e_assets/test-dll-port-template" "$TestingRoot/wrong-architecture/test-dll" +Run-Vcpkg env "$TestingRoot/wrong-architecture/test-dll/build.cmd" --Triplet x64-windows +Throw-IfFailed + +$buildOutput = Run-VcpkgAndCaptureOutput @commonArgs install --overlay-ports="$TestingRoot/wrong-architecture" test-dll --no-binarycaching +$expected = "warning: The following files were built for an incorrect architecture:`n" + ` +"warning: $packagesRoot\test-dll_x86-windows\debug\lib\test_dll.lib`n" + ` +" Expected: x86, but was x64`n" + ` +"warning: $packagesRoot\test-dll_x86-windows\lib\test_dll.lib`n" + ` +" Expected: x86, but was x64`n" + ` +"warning: The following files were built for an incorrect architecture:`n" + ` +"warning: $packagesRoot\test-dll_x86-windows\debug\bin\test_dll.dll`n" + ` +" Expected: x86, but was x64`n" + ` +"warning: $packagesRoot\test-dll_x86-windows\bin\test_dll.dll`n" + ` +" Expected: x86, but was x64`n" + +if (-not $buildOutput.Replace("`r`n", "`n").Contains($expected)) { + throw 'Did not detect DLL with wrong architecture.' +} + +# DLLs with no AppContainer bit +Refresh-TestRoot +mkdir "$TestingRoot/wrong-appcontainer" +Copy-Item -Recurse "$PSScriptRoot/../e2e_assets/test-dll-port-template" "$TestingRoot/wrong-appcontainer/test-dll" +Run-Vcpkg env "$TestingRoot/wrong-appcontainer/test-dll/build.cmd" --Triplet x64-windows +Throw-IfFailed + +$buildOutput = Run-VcpkgAndCaptureOutput --triplet x64-uwp "--x-buildtrees-root=$buildtreesRoot" "--x-install-root=$installRoot" "--x-packages-root=$packagesRoot" install --overlay-ports="$TestingRoot/wrong-appcontainer" test-dll --no-binarycaching +$expected = "warning: The App Container bit must be set for Windows Store apps. The following DLLs do not have the App Container bit set:`n" + ` +"`n" + ` +" $packagesRoot\test-dll_x64-uwp\debug\bin\test_dll.dll`n" + ` +" $packagesRoot\test-dll_x64-uwp\bin\test_dll.dll`n" + +if (-not $buildOutput.Replace("`r`n", "`n").Contains($expected)) { + throw 'Did not detect DLL with wrong appcontainer.' +} + +# Wrong CRT linkage +Refresh-TestRoot +mkdir "$TestingRoot/wrong-crt" +Copy-Item -Recurse "$PSScriptRoot/../e2e_assets/test-lib-port-template-dynamic-crt" "$TestingRoot/wrong-crt/test-lib" +Run-Vcpkg env "$TestingRoot/wrong-crt/test-lib/build.cmd" --Triplet x86-windows-static +Throw-IfFailed + +$buildOutput = Run-VcpkgAndCaptureOutput --triplet x86-windows-static "--x-buildtrees-root=$buildtreesRoot" "--x-install-root=$installRoot" "--x-packages-root=$packagesRoot" install --overlay-ports="$TestingRoot/wrong-crt" test-lib --no-binarycaching +$expected = "warning: The following binaries should use the Static Debug (/MTd) CRT.`n" + +" $packagesRoot\test-lib_x86-windows-static\debug\lib\both_lib.lib links with:`n" + +" Dynamic Debug (/MDd)`n" + +" Dynamic Release (/MD)`n" + +" $packagesRoot\test-lib_x86-windows-static\debug\lib\test_lib.lib links with: Dynamic Debug (/MDd)`n" + +"To inspect the lib files, use:`n" + +" dumpbin.exe /directives mylibfile.lib`n" + +"warning: The following binaries should use the Static Release (/MT) CRT.`n" + +" $packagesRoot\test-lib_x86-windows-static\lib\both_lib.lib links with:`n" + +" Dynamic Debug (/MDd)`n" + +" Dynamic Release (/MD)`n" + +" $packagesRoot\test-lib_x86-windows-static\lib\test_lib.lib links with: Dynamic Release (/MD)`n" + +"To inspect the lib files, use:`n" + +" dumpbin.exe /directives mylibfile.lib`n" +if (-not $buildOutput.Replace("`r`n", "`n").Contains($expected)) { + throw 'Did not detect lib with wrong CRT linkage.' +} diff --git a/azure-pipelines/end-to-end-tests-dir/regression-ports.ps1 b/azure-pipelines/end-to-end-tests-dir/regression-ports.ps1 new file mode 100644 index 0000000000..be08472c18 --- /dev/null +++ b/azure-pipelines/end-to-end-tests-dir/regression-ports.ps1 @@ -0,0 +1,6 @@ +. $PSScriptRoot/../end-to-end-tests-prelude.ps1 + +if ($IsWindows) { + Run-Vcpkg install --overlay-ports="$PSScriptRoot/../e2e_ports/llvm-lto-lib" llvm-lto-lib:x64-windows-static + Throw-IfFailed +} diff --git a/include/vcpkg/base/cofffilereader.h b/include/vcpkg/base/cofffilereader.h index e195713a6c..63af58278c 100644 --- a/include/vcpkg/base/cofffilereader.h +++ b/include/vcpkg/base/cofffilereader.h @@ -7,6 +7,8 @@ #include +#include +#include #include #include @@ -25,6 +27,21 @@ namespace vcpkg uint16_t characteristics; }; + struct CoffFileHeaderSignature + { + uint16_t machine; + uint16_t number_of_sections; + }; + + struct CoffFileHeaderAfterSignature + { + uint32_t date_time_stamp; + uint32_t pointer_to_symbol_table; + uint32_t number_of_symbols; + uint16_t size_of_optional_header; + uint16_t characteristics; + }; + struct CommonPEOptionalHeaders { uint16_t magic; @@ -37,6 +54,26 @@ namespace vcpkg uint32_t base_of_code; }; + enum class DllCharacteristics : uint16_t + { + HighEntropyVA = 0x0020, + DynamicBase = 0x0040, + ForceIntegrity = 0x0080, + NxCompat = 0x0100, + NoIsolation = 0x0200, + NoSeh = 0x0400, + NoBind = 0x0800, + AppContainer = 0x1000, + WdmDriver = 0x2000, + GuardCF = 0x4000, + TSAware = 0x8000 + }; + + constexpr bool operator&(DllCharacteristics lhs, DllCharacteristics rhs) noexcept + { + return (static_cast(lhs) & static_cast(rhs)) != 0; + } + struct UniquePEOptionalHeaders { uint32_t base_of_data; @@ -54,7 +91,7 @@ namespace vcpkg uint32_t size_of_headers; uint32_t checksum; uint16_t subsystem; - uint16_t dll_characteristics; + DllCharacteristics dll_characteristics; uint32_t size_of_stack_reserve; uint32_t size_of_stack_commit; uint32_t size_of_heap_reserve; @@ -79,7 +116,7 @@ namespace vcpkg uint32_t size_of_headers; uint32_t checksum; uint16_t subsystem; - uint16_t dll_characteristics; + DllCharacteristics dll_characteristics; uint64_t size_of_stack_reserve; uint64_t size_of_stack_commit; uint64_t size_of_heap_reserve; @@ -94,6 +131,50 @@ namespace vcpkg uint32_t size; }; + enum class SectionTableFlags : uint32_t + { + TypeNoPad = 0x00000008u, + CntCode = 0x00000020u, + CntInitializedData = 0x00000040u, + CntUniniitalizedData = 0x00000080u, + LinkOther = 0x00000100u, + LinkInfo = 0x00000200u, + LinkRemove = 0x00000400u, + LinkComdat = 0x00001000u, + GpRel = 0x00008000u, + MemPurgable = 0x00020000u, + // Mem16Bit has the same constant as MemPurgable?! + MemLocked = 0x00040000u, + MemPreload = 0x00080000u, + Align1Bytes = 0x00100000u, + Align2Bytes = 0x00200000u, + Align4Bytes = 0x00300000u, + Align8Bytes = 0x00400000u, + Align16Bytes = 0x00500000u, + Align32Bytes = 0x00600000u, + Align64Bytes = 0x00700000u, + Align128Bytes = 0x00800000u, + Align256Bytes = 0x00900000u, + Align512Bytes = 0x00A00000u, + Align1024Bytes = 0x00B00000u, + Align2048Bytes = 0x00C00000u, + Align4096Bytes = 0x00D00000u, + Align8192Bytes = 0x00E00000u, + LinkNumberOfRelocationsOverflow = 0x01000000u, + MemDiscardable = 0x02000000u, + MemNotCached = 0x04000000u, + MemNotPaged = 0x08000000u, + MemShared = 0x10000000u, + MemExecute = 0x20000000u, + MemRead = 0x40000000u, + MemWrite = 0x80000000u + }; + + constexpr bool operator&(SectionTableFlags lhs, SectionTableFlags rhs) noexcept + { + return (static_cast(lhs) & static_cast(rhs)) != 0; + } + struct SectionTableHeader { unsigned char name[8]; @@ -105,7 +186,22 @@ namespace vcpkg uint32_t pointer_to_line_numbers; uint16_t number_of_relocations; uint16_t number_of_line_numbers; - uint32_t characteristics; + SectionTableFlags characteristics; + }; + + struct ExportDirectoryTable + { + uint32_t export_flags; + uint32_t timestamp; + uint16_t major_version; + uint16_t minor_version; + uint32_t name_rva; + uint32_t ordinal_base; + uint32_t address_table_entries; + uint32_t number_of_name_pointers; + uint32_t export_address_table_rva; + uint32_t name_pointer_rva; + uint32_t ordinal_table_rva; }; struct ImportDirectoryTableEntry @@ -197,39 +293,39 @@ namespace vcpkg uint64_t CHPEMetadataPointer; }; + // It is expected that enumerators not in this list may be present. enum class MachineType : uint16_t { - UNKNOWN = 0x0, // The contents of this field are assumed to be applicable to any machine type - AM33 = 0x1d3, // Matsushita AM33 - AMD64 = 0x8664, // x64 - ARM = 0x1c0, // ARM little endian - ARM64 = 0xaa64, // ARM64 little endian - ARM64EC = 0xa641, // ARM64 "emulation compatible" - ARM64X = 0xa64e, // ARM64X - ARMNT = 0x1c4, // ARM Thumb-2 little endian - EBC = 0xebc, // EFI byte code - I386 = 0x14c, // Intel 386 or later processors and compatible processors - IA64 = 0x200, // Intel Itanium processor family - M32R = 0x9041, // Mitsubishi M32R little endian - MIPS16 = 0x266, // MIPS16 - MIPSFPU = 0x366, // MIPS with FPU - MIPSFPU16 = 0x466, // MIPS16 with FPU - POWERPC = 0x1f0, // Power PC little endian - POWERPCFP = 0x1f1, // Power PC with floating point support - R4000 = 0x166, // MIPS little endian - RISCV32 = 0x5032, // RISC-V 32-bit address space - RISCV64 = 0x5064, // RISC-V 64-bit address space - RISCV128 = 0x5128, // RISC-V 128-bit address space - SH3 = 0x1a2, // Hitachi SH3 - SH3DSP = 0x1a3, // Hitachi SH3 DSP - SH4 = 0x1a6, // Hitachi SH4 - SH5 = 0x1a8, // Hitachi SH5 - THUMB = 0x1c2, // Thumb - WCEMIPSV2 = 0x169, // MIPS little-endian WCE v2 + UNKNOWN = 0x0, // The contents of this field are assumed to be applicable to any machine type + AM33 = 0x1d3, // Matsushita AM33 + AMD64 = 0x8664, // x64 + ARM = 0x1c0, // ARM little endian + ARM64 = 0xaa64, // ARM64 little endian + ARM64EC = 0xa641, // ARM64 "emulation compatible" + ARM64X = 0xa64e, // ARM64X + ARMNT = 0x1c4, // ARM Thumb-2 little endian + EBC = 0xebc, // EFI byte code + I386 = 0x14c, // Intel 386 or later processors and compatible processors + IA64 = 0x200, // Intel Itanium processor family + M32R = 0x9041, // Mitsubishi M32R little endian + MIPS16 = 0x266, // MIPS16 + MIPSFPU = 0x366, // MIPS with FPU + MIPSFPU16 = 0x466, // MIPS16 with FPU + POWERPC = 0x1f0, // Power PC little endian + POWERPCFP = 0x1f1, // Power PC with floating point support + R4000 = 0x166, // MIPS little endian + RISCV32 = 0x5032, // RISC-V 32-bit address space + RISCV64 = 0x5064, // RISC-V 64-bit address space + RISCV128 = 0x5128, // RISC-V 128-bit address space + SH3 = 0x1a2, // Hitachi SH3 + SH3DSP = 0x1a3, // Hitachi SH3 DSP + SH4 = 0x1a6, // Hitachi SH4 + SH5 = 0x1a8, // Hitachi SH5 + THUMB = 0x1c2, // Thumb + WCEMIPSV2 = 0x169, // MIPS little-endian WCE v2 + LLVM_BITCODE = 0x4342, // LLVM bitcode https://www.llvm.org/docs/BitCodeFormat.html#llvm-ir-magic-number }; - MachineType to_machine_type(const uint16_t value); - enum class PEType { Unset, @@ -279,11 +375,49 @@ namespace vcpkg char size[10]; char end_of_header[2]; - void check_end_of_header() const; uint64_t decoded_size() const; }; + struct ImportHeader + { + uint16_t sig1; // must be IMAGE_FILE_MACHINE_UNKNOWN + uint16_t sig2; // must be 0xFFFF + uint16_t version; + uint16_t machine; + uint32_t date_time_stamp; + uint32_t size_of_data; + uint16_t ordinal_hint; + uint16_t type_and_name_type; + // 2 bits: type + // 3 bits: name type + // 11 bits: reserved and must be 0 + }; + + constexpr uint32_t ImportHeaderSignature = 0xFFFF0000u; + constexpr uint32_t LlvmBitcodeSignature = 0xDEC04342u; + + struct ImportHeaderAfterSignature + { + uint16_t version; + uint16_t machine; + uint32_t date_time_stamp; + uint32_t size_of_data; + uint16_t ordinal_hint; + uint16_t type_and_name_type; + // 2 bits: type + // 3 bits: name type + // 11 bits: reserved and must be 0 + }; + + struct LibInformation + { + std::vector machine_types; // used as set because n is tiny + std::set> linker_directives; + }; + + std::vector tokenize_command_line(StringView cmd_line); ExpectedL try_read_dll_metadata(ReadFilePointer& f); + ExpectedL try_read_if_dll_has_exports(const DllMetadata& dll, ReadFilePointer& f); ExpectedL> try_read_dll_imported_dll_names(const DllMetadata& dll, ReadFilePointer& f); - std::vector read_lib_machine_types(const ReadFilePointer& f); + ExpectedL read_lib_information(ReadFilePointer& f); } diff --git a/include/vcpkg/base/files.h b/include/vcpkg/base/files.h index c95ffd258a..bd852295d1 100644 --- a/include/vcpkg/base/files.h +++ b/include/vcpkg/base/files.h @@ -117,13 +117,13 @@ namespace vcpkg FilePointer& operator=(const FilePointer&) = delete; explicit operator bool() const noexcept; - int seek(long long offset, int origin) const noexcept; long long tell() const noexcept; int eof() const noexcept; std::error_code error() const noexcept; const Path& path() const; ExpectedL try_seek_to(long long offset); + ExpectedL try_seek_to(long long offset, int origin); ~FilePointer(); }; @@ -137,6 +137,7 @@ namespace vcpkg size_t read(void* buffer, size_t element_size, size_t element_count) const noexcept; ExpectedL try_read_all(void* buffer, std::uint32_t size); ExpectedL try_getc(); + ExpectedL try_read_all_from(long long offset, void* buffer, std::uint32_t size); }; struct WriteFilePointer : FilePointer diff --git a/include/vcpkg/base/messages.h b/include/vcpkg/base/messages.h index f92dd90cff..723463f236 100644 --- a/include/vcpkg/base/messages.h +++ b/include/vcpkg/base/messages.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -27,91 +28,55 @@ namespace vcpkg struct LocalizedString { LocalizedString() = default; - operator StringView() const noexcept { return m_data; } - const std::string& data() const noexcept { return m_data; } - const std::string& to_string() const noexcept { return m_data; } - std::string extract_data() { return std::exchange(m_data, ""); } + operator StringView() const noexcept; + const std::string& data() const noexcept; + const std::string& to_string() const noexcept; + std::string extract_data(); - static LocalizedString from_raw(std::string&& s) { return LocalizedString(std::move(s)); } + static LocalizedString from_raw(std::string&& s) noexcept; template, int> = 0> static LocalizedString from_raw(const StringLike& s) { return LocalizedString(StringView(s)); } - LocalizedString& append_raw(char c) - { - m_data.push_back(c); - return *this; - } - LocalizedString& append_raw(StringView s) - { - m_data.append(s.begin(), s.size()); - return *this; - } + + LocalizedString& append_raw(char c); + LocalizedString& append_raw(StringView s); template LocalizedString& append_fmt_raw(fmt::format_string s, Args&&... args) { m_data.append(fmt::format(s, std::forward(args)...)); return *this; } - LocalizedString& append(const LocalizedString& s) - { - m_data.append(s.m_data); - return *this; - } + LocalizedString& append(const LocalizedString& s); template LocalizedString& append(Message m, const Args&... args) { return append(msg::format(m, args...)); } - LocalizedString& append_indent(size_t indent = 1) - { - m_data.append(indent * 4, ' '); - return *this; - } - - friend const char* to_printf_arg(const LocalizedString& s) { return s.data().c_str(); } - - friend bool operator==(const LocalizedString& lhs, const LocalizedString& rhs) - { - return lhs.data() == rhs.data(); - } - - friend bool operator!=(const LocalizedString& lhs, const LocalizedString& rhs) - { - return lhs.data() != rhs.data(); - } - - friend bool operator<(const LocalizedString& lhs, const LocalizedString& rhs) - { - return lhs.data() < rhs.data(); - } - - friend bool operator<=(const LocalizedString& lhs, const LocalizedString& rhs) - { - return lhs.data() <= rhs.data(); - } - - friend bool operator>(const LocalizedString& lhs, const LocalizedString& rhs) - { - return lhs.data() > rhs.data(); - } - - friend bool operator>=(const LocalizedString& lhs, const LocalizedString& rhs) - { - return lhs.data() >= rhs.data(); - } + LocalizedString& append_indent(size_t indent = 1); - bool empty() const { return m_data.empty(); } - void clear() { m_data.clear(); } + // 0 items - Does nothing + // 1 item - .append_raw(' ').append(item) + // 2+ items - foreach: .append_raw('\n').append_indent(indent).append(item) + LocalizedString& append_floating_list(int indent, View items); + friend const char* to_printf_arg(const LocalizedString& s) noexcept; + friend bool operator==(const LocalizedString& lhs, const LocalizedString& rhs) noexcept; + friend bool operator!=(const LocalizedString& lhs, const LocalizedString& rhs) noexcept; + friend bool operator<(const LocalizedString& lhs, const LocalizedString& rhs) noexcept; + friend bool operator<=(const LocalizedString& lhs, const LocalizedString& rhs) noexcept; + friend bool operator>(const LocalizedString& lhs, const LocalizedString& rhs) noexcept; + friend bool operator>=(const LocalizedString& lhs, const LocalizedString& rhs) noexcept; + bool empty() const noexcept; + void clear() noexcept; private: std::string m_data; - explicit LocalizedString(StringView data) : m_data(data.data(), data.size()) { } - explicit LocalizedString(std::string&& data) : m_data(std::move(data)) { } + explicit LocalizedString(StringView data); + explicit LocalizedString(std::string&& data) noexcept; }; } @@ -1223,10 +1188,6 @@ namespace vcpkg "{expected} is a locale-invariant delimiter; for example, the ':' or '=' in 'zlib:x64-windows=skip'", "expected '{expected}' here"); DECLARE_MESSAGE(ExpectedDigitsAfterDecimal, (), "", "Expected digits after the decimal point"); - DECLARE_MESSAGE(ExpectedExtension, - (msg::extension, msg::path), - "", - "The file extension was not {extension}: {path}"); DECLARE_MESSAGE(ExpectedFailOrSkip, (), "", "expected 'fail', 'skip', or 'pass' here"); DECLARE_MESSAGE(ExpectedOneSetOfTags, (msg::count, msg::old_value, msg::new_value, msg::value), @@ -1593,7 +1554,6 @@ namespace vcpkg "{value} is a sha.", "SHA512's must be 128 hex characters: {value}"); DECLARE_MESSAGE(IncorrectArchiveFileSignature, (), "", "Incorrect archive file signature"); - DECLARE_MESSAGE(IncorrectLibHeaderEnd, (), "", "Incorrect lib header end"); DECLARE_MESSAGE(IncorrectNumberOfArgs, (msg::command_name, msg::expected, msg::actual), "'{expected}' is the required number of arguments. '{actual}' is the number of arguments provided.", @@ -1734,6 +1694,8 @@ namespace vcpkg "invalid format string: {actual}"); DECLARE_MESSAGE(InvalidHexDigit, (), "", "Invalid hex digit in unicode escape"); DECLARE_MESSAGE(InvalidIntegerConst, (msg::count), "", "Invalid integer constant: {count}"); + DECLARE_MESSAGE(InvalidLibraryMissingLinkerMembers, (), "", "Library was invalid: could not find a linker member."); + DECLARE_MESSAGE(InvalidLibraryMissingSignature, (), "", "Library was invalid: did not find ! signature."); DECLARE_MESSAGE( InvalidLinkage, (msg::system_name, msg::value), @@ -1760,6 +1722,11 @@ namespace vcpkg (msg::tool_name), "A platform API call failure message is appended after this", "Launching {tool_name}:"); + DECLARE_MESSAGE(LibraryArchiveMemberTooSmall, + (), + "", + "A library archive member was too small to contain the expected data type."); + DECLARE_MESSAGE(LibraryFirstLinkerMemberMissing, (), "", "Could not find first linker member name."); DECLARE_MESSAGE(LicenseExpressionContainsExtraPlus, (), "", @@ -1829,6 +1796,10 @@ namespace vcpkg (msg::value), "Example of {value} is 'unknownlicense'", "Unknown license identifier '{value}'. Known values are listed at https://spdx.org/licenses/"); + DECLARE_MESSAGE(LinkageDynamicDebug, (), "", "Dynamic Debug (/MDd)"); + DECLARE_MESSAGE(LinkageDynamicRelease, (), "", "Dynamic Release (/MD)"); + DECLARE_MESSAGE(LinkageStaticDebug, (), "", "Static Debug (/MTd)"); + DECLARE_MESSAGE(LinkageStaticRelease, (), "", "Static Release (/MT)"); DECLARE_MESSAGE(ListOfValidFieldsForControlFiles, (), "", @@ -2102,10 +2073,29 @@ namespace vcpkg "The folder /include exists in a cmake helper port; this is incorrect, since only cmake " "files should be installed"); DECLARE_MESSAGE(PortBugInspectFiles, (msg::extension), "", "To inspect the {extension} files, use:"); - DECLARE_MESSAGE(PortBugInvalidCrtLinkage, - (msg::expected), - "{expected} is the expected build type", - "Invalid crt linkage. Expected {expected}, but the following libs had:"); + DECLARE_MESSAGE( + PortBugInvalidCrtLinkage, + (msg::expected), + "{expected} is one of LinkageDynamicDebug/LinkageDynamicRelease/LinkageStaticDebug/LinkageStaticRelease. " + "Immediately after this message is a file by file list with what linkages they contain. 'CRT' is an acronym " + "meaning C Runtime. See also: " + "https://learn.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-170. This is " + "complicated because a binary can link with more than one CRT.\n" + "Example fully formatted message:\n" + "The following binaries should use the Dynamic Debug (/MDd) CRT.\n" + " C:\\some\\path\\to\\sane\\lib links with: Dynamic Release (/MD)\n" + " C:\\some\\path\\to\\lib links with:\n" + " Static Debug (/MTd)\n" + " Dynamic Release (/MD)\n" + " C:\\some\\different\\path\\to\\a\\dll links with:\n" + " Static Debug (/MTd)\n" + " Dynamic Debug (/MDd)\n", + "The following binaries should use the {expected} CRT."); + DECLARE_MESSAGE(PortBugInvalidCrtLinkageEntry, + (msg::path), + "See explanation in PortBugInvalidCrtLinkage", + "{path} links with:"); + DECLARE_MESSAGE( PortBugMergeLibCMakeDir, (msg::package_name), @@ -2384,11 +2374,6 @@ namespace vcpkg "unknown binary provider type: valid providers are 'clear', 'default', 'nuget', " "'nugetconfig','nugettimeout', 'interactive', 'x-azblob', 'x-gcs', 'x-aws', " "'x-aws-config', 'http', and 'files'"); - DECLARE_MESSAGE(UnknownMachineCode, - (msg::value), - "{value} is machine type code, see " - "https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types", - "Unknown machine type code {value}"); DECLARE_MESSAGE(UnknownOptions, (msg::command_name), "", "Unknown option(s) for command '{command_name}':"); DECLARE_MESSAGE(UnknownParameterForIntegrate, (msg::value), diff --git a/include/vcpkg/base/util.h b/include/vcpkg/base/util.h index 6a86dcf646..34505b0a32 100644 --- a/include/vcpkg/base/util.h +++ b/include/vcpkg/base/util.h @@ -24,12 +24,23 @@ namespace vcpkg::Util { augend->insert(augend->end(), addend.begin(), addend.end()); } + template + bool contains(const Vec& container, const Key& item, KeyEqual key_equal) + { + for (auto&& citem : container) + { + if (key_equal(citem, item)) + { + return true; + } + } + + return false; + } template bool contains(const Vec& container, const Key& item) { - using std::begin; - using std::end; - return std::find(begin(container), end(container), item) != end(container); + return std::find(container.begin(), container.end(), item) != container.end(); } template std::vector concat(View r1, View r2) diff --git a/include/vcpkg/postbuildlint.buildtype.h b/include/vcpkg/postbuildlint.buildtype.h deleted file mode 100644 index 8404305590..0000000000 --- a/include/vcpkg/postbuildlint.buildtype.h +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include - -#include - -#include - -namespace vcpkg::PostBuildLint -{ - struct BuildType - { - BuildType() = delete; - - constexpr BuildType(ConfigurationType c, LinkageType l) : config(c), linkage(l) { } - - bool has_crt_linker_option(StringView sv) const; - StringLiteral to_string() const; - - ConfigurationType config; - LinkageType linkage; - - friend bool operator==(const BuildType& lhs, const BuildType& rhs) - { - return lhs.config == rhs.config && lhs.linkage == rhs.linkage; - } - friend bool operator!=(const BuildType& lhs, const BuildType& rhs) { return !(lhs == rhs); } - }; -} diff --git a/include/vcpkg/postbuildlint.h b/include/vcpkg/postbuildlint.h index aadec0f3a1..b1b4b4b707 100644 --- a/include/vcpkg/postbuildlint.h +++ b/include/vcpkg/postbuildlint.h @@ -1,20 +1,16 @@ #pragma once #include +#include #include #include namespace vcpkg { - struct PackageSpec; -} - -namespace vcpkg::PostBuildLint -{ - size_t perform_all_checks(const PackageSpec& spec, - const VcpkgPaths& paths, - const PreBuildInfo& pre_build_info, - const BuildInfo& build_info, - const Path& port_dir); + size_t perform_post_build_lint_checks(const PackageSpec& spec, + const VcpkgPaths& paths, + const PreBuildInfo& pre_build_info, + const BuildInfo& build_info, + const Path& port_dir); } diff --git a/include/vcpkg/vcpkgpaths.h b/include/vcpkg/vcpkgpaths.h index 9e2c7b2ed3..56352f2dda 100644 --- a/include/vcpkg/vcpkgpaths.h +++ b/include/vcpkg/vcpkgpaths.h @@ -36,7 +36,6 @@ namespace vcpkg struct Toolset { Path visual_studio_root_path; - Path dumpbin; Path vcvarsall; std::vector vcvarsall_options; ZStringView version; diff --git a/locales/messages.json b/locales/messages.json index 3d8c71a9cb..243df37812 100644 --- a/locales/messages.json +++ b/locales/messages.json @@ -437,8 +437,6 @@ "ExpectedCharacterHere": "expected '{expected}' here", "_ExpectedCharacterHere.comment": "{expected} is a locale-invariant delimiter; for example, the ':' or '=' in 'zlib:x64-windows=skip'", "ExpectedDigitsAfterDecimal": "Expected digits after the decimal point", - "ExpectedExtension": "The file extension was not {extension}: {path}", - "_ExpectedExtension.comment": "An example of {extension} is .exe. An example of {path} is /foo/bar.", "ExpectedFailOrSkip": "expected 'fail', 'skip', or 'pass' here", "ExpectedOneSetOfTags": "Found {count} sets of {old_value}.*{new_value} but expected exactly 1, in block:\n{value}", "_ExpectedOneSetOfTags.comment": "{old_value} is a left tag and {new_value} is the right tag. {value} is the input. An example of {count} is 42.", @@ -655,7 +653,6 @@ "ImproperShaLength": "SHA512's must be 128 hex characters: {value}", "_ImproperShaLength.comment": "{value} is a sha.", "IncorrectArchiveFileSignature": "Incorrect archive file signature", - "IncorrectLibHeaderEnd": "Incorrect lib header end", "IncorrectNumberOfArgs": "'{command_name}' requires '{expected}' arguments, but '{actual}' were provided.", "_IncorrectNumberOfArgs.comment": "'{expected}' is the required number of arguments. '{actual}' is the number of arguments provided. An example of {command_name} is install.", "IncorrectPESignature": "Incorrect PE signature", @@ -739,6 +736,7 @@ "InvalidHexDigit": "Invalid hex digit in unicode escape", "InvalidIntegerConst": "Invalid integer constant: {count}", "_InvalidIntegerConst.comment": "An example of {count} is 42.", + "InvalidLibraryMissingLinkerMembers": "Library was invalid: could not find a linker member.", "InvalidLinkage": "Invalid {system_name} linkage type: [{value}]", "_InvalidLinkage.comment": "'{value}' is the linkage type vcpkg would did not understand. (Correct values would be static ofr dynamic) An example of {system_name} is Darwin.", "InvalidOptionForRemove": "'remove' accepts either libraries or '--outdated'", @@ -762,6 +760,8 @@ "JsonValueNotString": "json value is not a string", "LaunchingProgramFailed": "Launching {tool_name}:", "_LaunchingProgramFailed.comment": "A platform API call failure message is appended after this An example of {tool_name} is aria2.", + "LibraryArchiveMemberTooSmall": "A library archive member was too small to contain the expected data type.", + "LibraryFirstLinkerMemberMissing": "Could not find first linker member name.", "LicenseExpressionContainsExtraPlus": "SPDX license expression contains an extra '+'. These are only allowed directly after a license identifier.", "LicenseExpressionContainsInvalidCharacter": "SPDX license expression contains an invalid character (0x{value:02X} '{value}').", "_LicenseExpressionContainsInvalidCharacter.comment": "example of {value:02X} is '7B'\nexample of {value} is '{'", @@ -788,6 +788,10 @@ "_LicenseExpressionUnknownException.comment": "Example of {value} is 'unknownexception'", "LicenseExpressionUnknownLicense": "Unknown license identifier '{value}'. Known values are listed at https://spdx.org/licenses/", "_LicenseExpressionUnknownLicense.comment": "Example of {value} is 'unknownlicense'", + "LinkageDynamicDebug": "Dynamic Debug (/MDd)", + "LinkageDynamicRelease": "Dynamic Release (/MD)", + "LinkageStaticDebug": "Static Debug (/MTd)", + "LinkageStaticRelease": "Static Release (/MT)", "ListOfValidFieldsForControlFiles": "This is the list of valid fields for CONTROL files (case-sensitive):", "LoadingCommunityTriplet": "-- [COMMUNITY] Loading triplet configuration from: {path}", "_LoadingCommunityTriplet.comment": "'-- [COMMUNITY]' at the beginning must be preserved An example of {path} is /foo/bar.", @@ -932,8 +936,10 @@ "PortBugIncludeDirInCMakeHelperPort": "The folder /include exists in a cmake helper port; this is incorrect, since only cmake files should be installed", "PortBugInspectFiles": "To inspect the {extension} files, use:", "_PortBugInspectFiles.comment": "An example of {extension} is .exe.", - "PortBugInvalidCrtLinkage": "Invalid crt linkage. Expected {expected}, but the following libs had:", - "_PortBugInvalidCrtLinkage.comment": "{expected} is the expected build type", + "PortBugInvalidCrtLinkage": "The following binaries should use the {expected} CRT.", + "_PortBugInvalidCrtLinkage.comment": "{expected} is one of LinkageDynamicDebug/LinkageDynamicRelease/LinkageStaticDebug/LinkageStaticRelease. Immediately after this message is a file by file list with what linkages they contain. 'CRT' is an acronym meaning C Runtime. See also: https://learn.microsoft.com/en-us/cpp/build/reference/md-mt-ld-use-run-time-library?view=msvc-170. This is complicated because a binary can link with more than one CRT.\nExample fully formatted message:\nThe following binaries should use the Dynamic Debug (/MDd) CRT.\n C:\\some\\path\\to\\sane\\lib links with: Dynamic Release (/MD)\n C:\\some\\path\\to\\lib links with:\n Static Debug (/MTd)\n Dynamic Release (/MD)\n C:\\some\\different\\path\\to\\a\\dll links with:\n Static Debug (/MTd)\n Dynamic Debug (/MDd)\n", + "PortBugInvalidCrtLinkageEntry": "{path} links with:", + "_PortBugInvalidCrtLinkageEntry.comment": "See explanation in PortBugInvalidCrtLinkage An example of {path} is /foo/bar.", "PortBugMergeLibCMakeDir": "The /lib/cmake folder should be merged with /debug/lib/cmake and moved to /share/{package_name}/cmake. Please use the helper function `vcpkg_cmake_config_fixup()` from the port vcpkg-cmake-config.`", "_PortBugMergeLibCMakeDir.comment": "An example of {package_name} is zlib.", "PortBugMismatchedNumberOfBinaries": "Mismatching number of debug and release binaries.", @@ -1093,8 +1099,6 @@ "_UnexpectedToolOutput.comment": "The actual command line output will be appended after this message. An example of {tool_name} is aria2. An example of {path} is /foo/bar.", "UnknownBaselineFileContent": "unrecognizable baseline entry; expected 'port:triplet=(fail|skip|pass)'", "UnknownBinaryProviderType": "unknown binary provider type: valid providers are 'clear', 'default', 'nuget', 'nugetconfig','nugettimeout', 'interactive', 'x-azblob', 'x-gcs', 'x-aws', 'x-aws-config', 'http', and 'files'", - "UnknownMachineCode": "Unknown machine type code {value}", - "_UnknownMachineCode.comment": "{value} is machine type code, see https://learn.microsoft.com/en-us/windows/win32/debug/pe-format#machine-types", "UnknownOptions": "Unknown option(s) for command '{command_name}':", "_UnknownOptions.comment": "An example of {command_name} is install.", "UnknownParameterForIntegrate": "Unknown parameter '{value}' for integrate.", diff --git a/src/vcpkg-test/coffilereader.cpp b/src/vcpkg-test/coffilereader.cpp new file mode 100644 index 0000000000..4dfda982ad --- /dev/null +++ b/src/vcpkg-test/coffilereader.cpp @@ -0,0 +1,36 @@ +#include + +#include + +using namespace vcpkg; + +TEST_CASE ("tokenize-command-line", "[cofffilereader]") +{ + using Vec = std::vector; + CHECK(tokenize_command_line("") == Vec{}); + CHECK(tokenize_command_line("a b c") == Vec{"a", "b", "c"}); + CHECK(tokenize_command_line("a b c ") == Vec{"a", "b", "c"}); + CHECK(tokenize_command_line(" a b c ") == Vec{"a", "b", "c"}); + CHECK(tokenize_command_line(" a b c") == Vec{"a", "b", "c"}); + CHECK(tokenize_command_line("a\"embedded quotes\"") == Vec{"aembedded quotes"}); + CHECK(tokenize_command_line("a\\slash\\b") == Vec{"a\\slash\\b"}); + // n backslashes not followed by a quotation mark produce n backslashes + CHECK(tokenize_command_line("a\\\\\\slash\\b") == Vec{"a\\\\\\slash\\b"}); + CHECK(tokenize_command_line("an arg with \\\"quotes") == Vec{"an", "arg", "with", "\"quotes"}); + CHECK(tokenize_command_line("an arg with \"\\\"quotes\"") == Vec{"an", "arg", "with", "\"quotes"}); + CHECK(tokenize_command_line("arg \"quoted\" suffix") == Vec{"arg", "quoted", "suffix"}); + // 2n + 1 backslashes followed by a quotation mark produce n backslashes followed by a (escaped) quotation mark + CHECK(tokenize_command_line("arg \"quoted\\\" suffix") == Vec{"arg", "quoted\" suffix"}); + // 2n backslashes followed by a quotation mark produce n backslashes followed by a (terminal) quotation mark + CHECK(tokenize_command_line("arg \"quoted\\\\\" suffix") == Vec{"arg", "quoted\\", "suffix"}); + CHECK(tokenize_command_line("arg \"quoted\\\\\\\" suffix") == Vec{"arg", "quoted\\\" suffix"}); + CHECK(tokenize_command_line("arg \"quoted\\\\\\\\\" suffix") == Vec{"arg", "quoted\\\\", "suffix"}); + // The above cases but at the end + CHECK(tokenize_command_line("\\") == Vec{"\\"}); + CHECK(tokenize_command_line("\\\\") == Vec{"\\\\"}); + CHECK(tokenize_command_line("\\\\\\") == Vec{"\\\\\\"}); + CHECK(tokenize_command_line("arg \"quoted\\\"") == Vec{"arg", "quoted\""}); + CHECK(tokenize_command_line("arg \"quoted\\\\\"") == Vec{"arg", "quoted\\"}); + CHECK(tokenize_command_line("arg \"quoted\\\\\\\"") == Vec{"arg", "quoted\\\""}); + CHECK(tokenize_command_line("arg \"quoted\\\\\\\\\"") == Vec{"arg", "quoted\\\\"}); +} diff --git a/src/vcpkg-test/messages.cpp b/src/vcpkg-test/messages.cpp index f8e0a13dac..2298e1e9ea 100644 --- a/src/vcpkg-test/messages.cpp +++ b/src/vcpkg-test/messages.cpp @@ -8,6 +8,18 @@ using namespace vcpkg; using namespace vcpkg::Commands; +TEST_CASE ("append floating list", "[LocalizedString]") +{ + const auto a = LocalizedString::from_raw("a"); + const auto b = LocalizedString::from_raw("b"); + CHECK(LocalizedString().append_floating_list(2, std::vector{}) == LocalizedString()); + CHECK(LocalizedString().append_floating_list(2, std::vector{a}) == + LocalizedString::from_raw(" a")); + const auto expected = LocalizedString::from_raw(" heading\n a\n b"); + CHECK(LocalizedString::from_raw(" heading").append_floating_list(2, std::vector{a, b}) == + expected); +} + TEST_CASE ("get path to locale from LCID", "[messages]") { // valid LCID; Chinese diff --git a/src/vcpkg/base/cofffilereader.cpp b/src/vcpkg/base/cofffilereader.cpp index ecd88a8685..0234dd7d09 100644 --- a/src/vcpkg/base/cofffilereader.cpp +++ b/src/vcpkg/base/cofffilereader.cpp @@ -1,8 +1,10 @@ #include #include #include +#include #include #include +#include #include @@ -26,34 +28,18 @@ namespace static constexpr long OFFSET_TO_PE_SIGNATURE_OFFSET = 0x3c; static constexpr StringLiteral PE_SIGNATURE = "PE\0\0"; static constexpr auto PE_SIGNATURE_SIZE = static_cast(PE_SIGNATURE.size()); - { - auto seek_to_signature_offset = f.try_seek_to(OFFSET_TO_PE_SIGNATURE_OFFSET); - if (!seek_to_signature_offset.has_value()) - { - return std::move(seek_to_signature_offset).error(); - } - } - uint32_t offset; { - auto read_offset = f.try_read_all(&offset, sizeof(offset)); + auto read_offset = f.try_read_all_from(OFFSET_TO_PE_SIGNATURE_OFFSET, &offset, sizeof(offset)); if (!read_offset.has_value()) { return std::move(read_offset).error(); } } - { - auto seek_to_signature = f.try_seek_to(offset); - if (!seek_to_signature.has_value()) - { - return std::move(seek_to_signature).error(); - } - } - char signature[PE_SIGNATURE_SIZE]; { - auto read_signature = f.try_read_all(signature, PE_SIGNATURE_SIZE); + auto read_signature = f.try_read_all_from(offset, signature, PE_SIGNATURE_SIZE); if (!read_signature.has_value()) { return std::move(read_signature).error(); @@ -129,7 +115,7 @@ namespace // seeks the file `f` to the location in the file denoted by `rva`; // returns the remaining size of data in the section - ExpectedL try_seek_to_rva(const DllMetadata& metadata, ReadFilePointer& f, uint32_t rva) + ExpectedL try_seek_to_rva(const DllMetadata& metadata, ReadFilePointer& f, uint32_t rva) { // The PE spec says that the sections have to be sorted by virtual_address and // contiguous, but this does not assume that for paranoia reasons. @@ -143,7 +129,7 @@ namespace const auto start_offset_within_section = rva - section.virtual_address; const auto file_pointer = start_offset_within_section + section.pointer_to_raw_data; - const size_t leftover = section.size_of_raw_data - start_offset_within_section; + const uint32_t leftover = section.size_of_raw_data - start_offset_within_section; return f.try_seek_to(file_pointer).map([=](Unit) { return leftover; }); } @@ -204,11 +190,27 @@ namespace return std::move(maybe_remaining_size).error(); } + ExpectedL try_read_struct_from_rva( + const DllMetadata& metadata, ReadFilePointer& f, void* target, uint32_t rva, uint32_t size) + { + return try_seek_to_rva(metadata, f, rva).then([&](uint32_t maximum_size) -> ExpectedL { + if (maximum_size >= size) + { + return f.try_read_all(target, size); + } + + return f.try_read_all(target, maximum_size).map([&](Unit) { + ::memset(static_cast(target) + maximum_size, 0, size - maximum_size); + return Unit{}; + }); + }); + } + ExpectedL try_read_ntbs_from_rva(const DllMetadata& metadata, ReadFilePointer& f, uint32_t rva) { // Note that maximum_size handles the case that size_of_raw_data < virtual_size, where the loader // inserts the null(s). - return try_seek_to_rva(metadata, f, rva).then([&](size_t maximum_size) -> ExpectedL { + return try_seek_to_rva(metadata, f, rva).then([&](uint32_t maximum_size) -> ExpectedL { std::string result; for (;;) { @@ -239,113 +241,278 @@ namespace static_assert(sizeof(ArchiveMemberHeader) == 60, "The ArchiveMemberHeader struct must match its on-disk representation"); - MachineType read_import_machine_type_after_sig1(const ReadFilePointer& f) + ExpectedL read_and_verify_archive_file_signature(ReadFilePointer& f) { - struct ImportHeaderPrefixAfterSig1 + static constexpr StringLiteral FILE_START = "!\n"; + static constexpr auto FILE_START_SIZE = FILE_START.size(); + return f.try_seek_to(0).then([&f](Unit) -> ExpectedL { + char file_start[FILE_START_SIZE]; + auto result = f.try_read_all(file_start, FILE_START_SIZE); + if (result.has_value() && FILE_START != StringView{file_start, FILE_START_SIZE}) + { + return msg::format_error(msgIncorrectArchiveFileSignature); + } + + return result; + }); + } + + uint32_t bswap(uint32_t value) + { + return (value >> 24) | ((value & 0x00FF0000u) >> 8) | ((value & 0x0000FF00u) << 8) | (value << 24); + } + + ExpectedL> try_read_first_linker_member_offsets(ReadFilePointer& f) + { + ArchiveMemberHeader first_linker_member_header; + Checks::check_exit(VCPKG_LINE_INFO, + f.read(&first_linker_member_header, sizeof(first_linker_member_header), 1) == 1); + if (memcmp(first_linker_member_header.name, "/ ", 2) != 0) { - uint16_t sig2; - uint16_t version; - uint16_t machine; - } tmp; + return msg::format_error(msgLibraryFirstLinkerMemberMissing); + } - Checks::check_exit(VCPKG_LINE_INFO, f.read(&tmp, sizeof(tmp), 1) == 1); - if (tmp.sig2 == 0xFFFF) + auto first_size = first_linker_member_header.decoded_size(); + uint32_t archive_symbol_count; + if (first_size < sizeof(archive_symbol_count)) { - return to_machine_type(tmp.machine); + return msg::format_error(msgLibraryArchiveMemberTooSmall); } - // This can happen, for example, if this is a .drectve member - return MachineType::UNKNOWN; - } + { + auto read = f.try_read_all(&archive_symbol_count, sizeof(archive_symbol_count)); + if (!read.has_value()) + { + return std::move(read).error(); + } + } - void read_and_verify_archive_file_signature(const ReadFilePointer& f) - { - static constexpr StringLiteral FILE_START = "!\n"; - Checks::check_exit(VCPKG_LINE_INFO, f.seek(0, SEEK_SET) == 0); + archive_symbol_count = bswap(archive_symbol_count); + const auto maximum_possible_archive_members = (first_size / sizeof(uint32_t)) - 1; + if (archive_symbol_count > maximum_possible_archive_members) + { + return msg::format_error(msgLibraryArchiveMemberTooSmall); + } - char file_start[FILE_START.size()]; - Checks::check_exit(VCPKG_LINE_INFO, f.read(&file_start, FILE_START.size(), 1) == 1); + std::vector offsets(archive_symbol_count); + { + auto read = f.try_read_all(offsets.data(), sizeof(uint32_t) * archive_symbol_count); + if (!read.has_value()) + { + return std::move(read).error(); + } + } - if (FILE_START != StringView{file_start, FILE_START.size()}) + // convert (big endian) offsets to little endian + for (auto&& offset : offsets) { - msg::println_error(msgIncorrectArchiveFileSignature); - Checks::exit_fail(VCPKG_LINE_INFO); + offset = bswap(offset); } - } - void read_and_skip_first_linker_member(const vcpkg::ReadFilePointer& f) - { - ArchiveMemberHeader first_linker_member_header; - Checks::check_exit(VCPKG_LINE_INFO, - f.read(&first_linker_member_header, sizeof(first_linker_member_header), 1) == 1); - Checks::check_exit(VCPKG_LINE_INFO, - memcmp(first_linker_member_header.name, "/ ", 2) == 0, - "Could not find proper first linker member"); - Checks::check_exit(VCPKG_LINE_INFO, f.seek(first_linker_member_header.decoded_size(), SEEK_CUR) == 0); + offsets.erase(std::remove(offsets.begin(), offsets.end(), 0u), offsets.end()); + Util::sort_unique_erase(offsets); + uint64_t leftover = first_size - sizeof(uint32_t) - (archive_symbol_count * sizeof(uint32_t)); + { + auto seek = f.try_seek_to(leftover, SEEK_CUR); + if (!seek.has_value()) + { + return std::move(seek).error(); + } + } + + return offsets; } - std::vector read_second_linker_member_offsets(const vcpkg::ReadFilePointer& f) + ExpectedL>> try_read_second_linker_member_offsets(ReadFilePointer& f) { ArchiveMemberHeader second_linker_member_header; - Checks::check_exit(VCPKG_LINE_INFO, - f.read(&second_linker_member_header, sizeof(second_linker_member_header), 1) == 1); - Checks::check_exit(VCPKG_LINE_INFO, - memcmp(second_linker_member_header.name, "/ ", 2) == 0, - "Could not find proper second linker member"); + { + auto read = f.try_read_all(&second_linker_member_header, sizeof(second_linker_member_header)); + if (!read.has_value()) + { + return std::move(read).error(); + } + } + + if (memcmp(second_linker_member_header.name, "/ ", 2) != 0) + { + return nullopt; + } const auto second_size = second_linker_member_header.decoded_size(); // The first 4 bytes contains the number of archive members uint32_t archive_member_count; - Checks::check_exit(VCPKG_LINE_INFO, - second_size >= sizeof(archive_member_count), - "Second linker member was too small to contain a single uint32_t"); - Checks::check_exit(VCPKG_LINE_INFO, f.read(&archive_member_count, sizeof(archive_member_count), 1) == 1); + + if (second_size < sizeof(archive_member_count)) + { + return msg::format_error(msgLibraryArchiveMemberTooSmall); + } + + { + auto read = f.try_read_all(&archive_member_count, sizeof(archive_member_count)); + if (!read.has_value()) + { + return std::move(read).error(); + } + } + const auto maximum_possible_archive_members = (second_size / sizeof(uint32_t)) - 1; - Checks::check_exit(VCPKG_LINE_INFO, - archive_member_count <= maximum_possible_archive_members, - "Second linker member was too small to contain the expected number of archive members"); + if (archive_member_count > maximum_possible_archive_members) + { + return msg::format_error(msgLibraryArchiveMemberTooSmall); + } + std::vector offsets(archive_member_count); - Checks::check_exit(VCPKG_LINE_INFO, - f.read(offsets.data(), sizeof(uint32_t), archive_member_count) == archive_member_count); + { + auto read = f.try_read_all(offsets.data(), sizeof(uint32_t) * archive_member_count); + if (!read.has_value()) + { + return std::move(read).error(); + } + } // Ignore offsets that point to offset 0. See vcpkg github #223 #288 #292 offsets.erase(std::remove(offsets.begin(), offsets.end(), 0u), offsets.end()); // Sort the offsets, because it is possible for them to be unsorted. See vcpkg github #292 std::sort(offsets.begin(), offsets.end()); uint64_t leftover = second_size - sizeof(uint32_t) - (archive_member_count * sizeof(uint32_t)); - Checks::check_exit(VCPKG_LINE_INFO, f.seek(leftover, SEEK_CUR) == 0); + { + auto seek = f.try_seek_to(leftover, SEEK_CUR); + if (!seek.has_value()) + { + return std::move(seek).error(); + } + } + return offsets; } - std::vector read_machine_types_from_archive_members(const vcpkg::ReadFilePointer& f, - const std::vector& member_offsets) + static void add_machine_type(std::vector& machine_types, MachineType machine_type) + { + if (machine_type == MachineType::UNKNOWN || + std::find(machine_types.begin(), machine_types.end(), machine_type) != machine_types.end()) + { + return; + } + + machine_types.push_back(machine_type); + } + + ExpectedL read_lib_information_from_archive_members(ReadFilePointer& f, + const std::vector& member_offsets) { - std::vector machine_types; // used as a set because n is tiny + std::vector machine_types; // used as set because n is tiny + std::set> directives; // Next we have the obj and pseudo-object files for (unsigned int offset : member_offsets) { // Skip the header, no need to read it - Checks::check_exit(VCPKG_LINE_INFO, f.seek(offset + sizeof(ArchiveMemberHeader), SEEK_SET) == 0); - uint16_t machine_type_raw; - Checks::check_exit(VCPKG_LINE_INFO, f.read(&machine_type_raw, sizeof(machine_type_raw), 1) == 1); - - auto result_machine_type = to_machine_type(machine_type_raw); - if (result_machine_type == MachineType::UNKNOWN) + const auto coff_base = offset + sizeof(ArchiveMemberHeader); + uint32_t tag_sniffer; { - result_machine_type = read_import_machine_type_after_sig1(f); + auto read = f.try_read_all_from(coff_base, &tag_sniffer, sizeof(tag_sniffer)); + if (!read.has_value()) + { + return std::move(read).error(); + } } - if (result_machine_type == MachineType::UNKNOWN || - std::find(machine_types.begin(), machine_types.end(), result_machine_type) != machine_types.end()) + if (tag_sniffer == LlvmBitcodeSignature) { - continue; + // obj is LLVM bitcode + add_machine_type(machine_types, MachineType::LLVM_BITCODE); + } + else if (tag_sniffer == ImportHeaderSignature) + { + // obj is an import obj + ImportHeaderAfterSignature import_header; + { + auto read = f.try_read_all(&import_header, sizeof(import_header)); + if (!read.has_value()) + { + return std::move(read).error(); + } + } + + add_machine_type(machine_types, static_cast(import_header.machine)); } + else + { + // obj is traditional COFF + CoffFileHeaderSignature coff_signature; + static_assert(sizeof(tag_sniffer) == sizeof(coff_signature), "BOOM"); + memcpy(&coff_signature, &tag_sniffer, sizeof(tag_sniffer)); + add_machine_type(machine_types, static_cast(coff_signature.machine)); + + CoffFileHeaderAfterSignature coff_header; + { + auto read = f.try_read_all(&coff_header, sizeof(coff_header)); + if (!read.has_value()) + { + return std::move(read).error(); + } + } + + // Object files shouldn't have optional headers, but the spec says we should skip over one if any + { + auto seek = f.try_seek_to(coff_header.size_of_optional_header, SEEK_CUR); + if (!seek.has_value()) + { + return std::move(seek).error(); + } + } - machine_types.push_back(result_machine_type); + // Read section headers + std::vector sections; + sections.resize(coff_signature.number_of_sections); + { + auto read = + f.try_read_all(sections.data(), sizeof(SectionTableHeader) * coff_signature.number_of_sections); + if (!read.has_value()) + { + return std::move(read).error(); + } + } + + // Look for linker directive sections + for (auto&& section : sections) + { + if (!(section.characteristics & SectionTableFlags::LinkInfo) || + memcmp(".drectve", §ion.name, 8) != 0 || section.number_of_relocations != 0 || + section.number_of_line_numbers != 0) + { + continue; + } + + // read the actual directive + std::string directive_command_line; + const auto section_offset = coff_base + section.pointer_to_raw_data; + directive_command_line.resize(section.size_of_raw_data); + { + auto read = f.try_read_all_from( + section_offset, directive_command_line.data(), section.size_of_raw_data); + if (!read.has_value()) + { + return std::move(read).error(); + } + } + + if (Strings::starts_with(directive_command_line, StringLiteral{"\xEF\xBB\xBF"})) + { + // chop off the BOM + directive_command_line.erase(0, 3); + } + + for (auto&& directive : tokenize_command_line(directive_command_line)) + { + directives.insert(std::move(directive)); + } + } + } } std::sort(machine_types.begin(), machine_types.end()); - return machine_types; + return LibInformation{std::move(machine_types), std::move(directives)}; } } // unnamed namespace @@ -362,15 +529,7 @@ namespace vcpkg } } - MachineType DllMetadata::get_machine_type() const noexcept { return to_machine_type(coff_header.machine); } - - void ArchiveMemberHeader::check_end_of_header() const - { - // The name[0] == '\0' check is for freeglut, see GitHub issue #223 - Checks::msg_check_exit(VCPKG_LINE_INFO, - name[0] == '\0' || (end_of_header[0] == '`' && end_of_header[1] == '\n'), - msgIncorrectLibHeaderEnd); - } + MachineType DllMetadata::get_machine_type() const noexcept { return static_cast(coff_header.machine); } uint64_t ArchiveMemberHeader::decoded_size() const { @@ -386,6 +545,87 @@ namespace vcpkg return value; } + static void handle_uninteresting_command_line_ch(char ch, size_t& slash_count, std::string& this_arg) + { + // n backslashes not followed by a quotation mark produce n backslashes + if (slash_count) + { + this_arg.append(slash_count, '\\'); + slash_count = 0; + } + + this_arg.push_back(ch); + } + + static void handle_maybe_adding_argument(std::vector& result, std::string& this_arg) + { + if (!this_arg.empty()) + { + result.push_back(std::move(this_arg)); + this_arg.clear(); + } + } + + std::vector tokenize_command_line(StringView cmd_line) + { + std::vector result; + std::string this_arg; + bool in_quoted_argument = false; + std::size_t slash_count = 0; + for (auto&& ch : cmd_line) + { + if (ch == '\\') + { + ++slash_count; + } + else if (ch == '"') + { + // 2n backslashes followed by a quotation mark produce n backslashes followed by an (ending) quotation + // mark + // + // 2n + 1 backslashes followed by a quotation mark produce n backslashes followed by an (escaped) + // quotation mark + if (slash_count) + { + this_arg.append(slash_count >> 1, '\\'); + if (std::exchange(slash_count, std::size_t{}) & 0x1u) + { + // escaped + handle_uninteresting_command_line_ch('"', slash_count, this_arg); + } + else + { + // not escaped + in_quoted_argument = !in_quoted_argument; + } + } + else + { + in_quoted_argument = !in_quoted_argument; + } + } + else if (ParserBase::is_whitespace(ch)) + { + if (in_quoted_argument) + { + handle_uninteresting_command_line_ch(ch, slash_count, this_arg); + } + else + { + handle_maybe_adding_argument(result, this_arg); + } + } + else + { + handle_uninteresting_command_line_ch(ch, slash_count, this_arg); + } + } + + this_arg.append(slash_count, '\\'); + handle_maybe_adding_argument(result, this_arg); + return result; + } + ExpectedL try_read_dll_metadata(ReadFilePointer& f) { { @@ -433,6 +673,32 @@ namespace vcpkg return result; } + ExpectedL try_read_if_dll_has_exports(const DllMetadata& dll, ReadFilePointer& f) + { + if (dll.data_directories.size() < 1) + { + Debug::print("No export directory\n"); + return false; + } + + const auto& export_data_directory = dll.data_directories[0]; + if (export_data_directory.virtual_address == 0) + { + Debug::print("Null export directory.\n"); + return false; + } + + ExportDirectoryTable export_directory_table; + auto export_read_result = try_read_struct_from_rva( + dll, f, &export_directory_table, export_data_directory.virtual_address, sizeof(export_directory_table)); + if (!export_read_result.has_value()) + { + return std::move(export_read_result).error(); + } + + return export_directory_table.address_table_entries != 0; + } + ExpectedL> try_read_dll_imported_dll_names(const DllMetadata& dll, ReadFilePointer& f) { if (dll.data_directories.size() < 2) @@ -502,47 +768,42 @@ namespace vcpkg }); } - std::vector read_lib_machine_types(const ReadFilePointer& f) + ExpectedL read_lib_information(ReadFilePointer& f) { - read_and_verify_archive_file_signature(f); - read_and_skip_first_linker_member(f); - const auto offsets = read_second_linker_member_offsets(f); - return read_machine_types_from_archive_members(f, offsets); - } + auto read_signature = read_and_verify_archive_file_signature(f); + if (!read_signature.has_value()) + { + return std::move(read_signature).error(); + } - MachineType to_machine_type(const uint16_t value) - { - const MachineType t = static_cast(value); - switch (t) - { - case MachineType::UNKNOWN: - case MachineType::AM33: - case MachineType::AMD64: - case MachineType::ARM: - case MachineType::ARM64: - case MachineType::ARM64EC: - case MachineType::ARM64X: - case MachineType::ARMNT: - case MachineType::EBC: - case MachineType::I386: - case MachineType::IA64: - case MachineType::M32R: - case MachineType::MIPS16: - case MachineType::MIPSFPU: - case MachineType::MIPSFPU16: - case MachineType::POWERPC: - case MachineType::POWERPCFP: - case MachineType::R4000: - case MachineType::RISCV32: - case MachineType::RISCV64: - case MachineType::RISCV128: - case MachineType::SH3: - case MachineType::SH3DSP: - case MachineType::SH4: - case MachineType::SH5: - case MachineType::THUMB: - case MachineType::WCEMIPSV2: return t; - default: Checks::msg_exit_maybe_upgrade(VCPKG_LINE_INFO, msgUnknownMachineCode, msg::value = value); + auto first_offsets = try_read_first_linker_member_offsets(f); + if (!first_offsets.has_value()) + { + return std::move(first_offsets).error(); + } + + auto second_offsets = try_read_second_linker_member_offsets(f); + if (!second_offsets.has_value()) + { + return std::move(second_offsets).error(); + } + + const std::vector* offsets; + // "Although both linker members provide a directory of symbols and archive members that + // contain them, the second linker member is used in preference to the first by all current linkers." + if (auto maybe_offsets2 = second_offsets.get()->get()) + { + offsets = maybe_offsets2; } + else if (auto maybe_offsets = first_offsets.get()) + { + offsets = maybe_offsets; + } + else + { + return msg::format_error(msgInvalidLibraryMissingLinkerMembers); + } + + return read_lib_information_from_archive_members(f, *offsets); } } diff --git a/src/vcpkg/base/files.cpp b/src/vcpkg/base/files.cpp index 1fc2e187e1..005469a807 100644 --- a/src/vcpkg/base/files.cpp +++ b/src/vcpkg/base/files.cpp @@ -1365,15 +1365,6 @@ namespace vcpkg FilePointer::operator bool() const noexcept { return m_fs != nullptr; } - int FilePointer::seek(long long offset, int origin) const noexcept - { -#if defined(_WIN32) - return ::_fseeki64(m_fs, offset, origin); -#else // ^^^ _WIN32 / !_WIN32 vvv - return ::fseeko(m_fs, offset, origin); -#endif // ^^^ !_WIN32 - } - long long FilePointer::tell() const noexcept { #if defined(_WIN32) @@ -1391,9 +1382,17 @@ namespace vcpkg const Path& FilePointer::path() const { return m_path; } - ExpectedL FilePointer::try_seek_to(long long offset) + ExpectedL FilePointer::try_seek_to(long long offset) { return try_seek_to(offset, SEEK_SET); } + + ExpectedL FilePointer::try_seek_to(long long offset, int origin) { - if (this->seek(offset, SEEK_SET)) +#if defined(_WIN32) + const int result = ::_fseeki64(m_fs, offset, origin); +#else // ^^^ _WIN32 / !_WIN32 vvv + const int result = ::fseeko(m_fs, offset, origin); +#endif // ^^^ !_WIN32 + + if (result) { return msg::format(msgFileSeekFailed, msg::path = m_path, msg::byte_offset = offset); } @@ -1465,6 +1464,11 @@ namespace vcpkg return static_cast(result); } + ExpectedL ReadFilePointer::try_read_all_from(long long offset, void* buffer, std::uint32_t size) + { + return try_seek_to(offset).then([&](Unit) { return try_read_all(buffer, size); }); + } + WriteFilePointer::WriteFilePointer() noexcept = default; WriteFilePointer::WriteFilePointer(WriteFilePointer&&) noexcept = default; @@ -3579,13 +3583,17 @@ namespace vcpkg void print_paths(const std::vector& paths) { - std::string message = "\n"; + LocalizedString ls; + ls.append_raw('\n'); for (const Path& p : paths) { - Strings::append(message, " ", p.generic_u8string(), '\n'); + auto as_preferred = p; + as_preferred.make_preferred(); + ls.append_indent().append_raw(as_preferred).append_raw('\n'); } - message.push_back('\n'); - msg::write_unlocalized_text_to_stdout(Color::none, message); + + ls.append_raw('\n'); + msg::print(ls); } IExclusiveFileLock::~IExclusiveFileLock() = default; diff --git a/src/vcpkg/base/messages.cpp b/src/vcpkg/base/messages.cpp index b277c04093..063f140c02 100644 --- a/src/vcpkg/base/messages.cpp +++ b/src/vcpkg/base/messages.cpp @@ -13,6 +13,90 @@ CMRC_DECLARE(cmakerc); using namespace vcpkg; +namespace vcpkg +{ + LocalizedString::operator StringView() const noexcept { return m_data; } + const std::string& LocalizedString::data() const noexcept { return m_data; } + const std::string& LocalizedString::to_string() const noexcept { return m_data; } + std::string LocalizedString::extract_data() { return std::exchange(m_data, std::string{}); } + + LocalizedString LocalizedString::from_raw(std::string&& s) noexcept { return LocalizedString(std::move(s)); } + + LocalizedString& LocalizedString::append_raw(char c) + { + m_data.push_back(c); + return *this; + } + + LocalizedString& LocalizedString::append_raw(StringView s) + { + m_data.append(s.begin(), s.size()); + return *this; + } + + LocalizedString& LocalizedString::append(const LocalizedString& s) + { + m_data.append(s.m_data); + return *this; + } + + LocalizedString& LocalizedString::append_indent(size_t indent) + { + m_data.append(indent * 4, ' '); + return *this; + } + + LocalizedString& LocalizedString::append_floating_list(int indent, View items) + { + switch (items.size()) + { + case 0: break; + case 1: append_raw(' ').append(items[0]); break; + default: + for (auto&& item : items) + { + append_raw('\n').append_indent(indent).append(item); + } + + break; + } + + return *this; + } + + const char* to_printf_arg(const LocalizedString& s) noexcept { return s.data().c_str(); } + + bool operator==(const LocalizedString& lhs, const LocalizedString& rhs) noexcept + { + return lhs.data() == rhs.data(); + } + + bool operator!=(const LocalizedString& lhs, const LocalizedString& rhs) noexcept + { + return lhs.data() != rhs.data(); + } + + bool operator<(const LocalizedString& lhs, const LocalizedString& rhs) noexcept { return lhs.data() < rhs.data(); } + + bool operator<=(const LocalizedString& lhs, const LocalizedString& rhs) noexcept + { + return lhs.data() <= rhs.data(); + } + + bool operator>(const LocalizedString& lhs, const LocalizedString& rhs) noexcept { return lhs.data() > rhs.data(); } + + bool operator>=(const LocalizedString& lhs, const LocalizedString& rhs) noexcept + { + return lhs.data() >= rhs.data(); + } + + bool LocalizedString::empty() const noexcept { return m_data.empty(); } + void LocalizedString::clear() noexcept { m_data.clear(); } + + LocalizedString::LocalizedString(StringView data) : m_data(data.data(), data.size()) { } + LocalizedString::LocalizedString(std::string&& data) noexcept : m_data(std::move(data)) { } +} + namespace vcpkg::msg { REGISTER_MESSAGE(SeeURL); @@ -734,7 +818,6 @@ namespace vcpkg REGISTER_MESSAGE(IllegalPlatformSpec); REGISTER_MESSAGE(ImproperShaLength); REGISTER_MESSAGE(IncorrectArchiveFileSignature); - REGISTER_MESSAGE(IncorrectLibHeaderEnd); REGISTER_MESSAGE(IncorrectPESignature); REGISTER_MESSAGE(IncorrectNumberOfArgs); REGISTER_MESSAGE(IncrementedUtf8Decoder); @@ -780,6 +863,7 @@ namespace vcpkg REGISTER_MESSAGE(InvalidFloatingPointConst); REGISTER_MESSAGE(InvalidHexDigit); REGISTER_MESSAGE(InvalidIntegerConst); + REGISTER_MESSAGE(InvalidLibraryMissingLinkerMembers); REGISTER_MESSAGE(InvalidPortVersonName); REGISTER_MESSAGE(InvalidString); REGISTER_MESSAGE(InvalidFileType); @@ -795,6 +879,8 @@ namespace vcpkg REGISTER_MESSAGE(JsonValueNotObject); REGISTER_MESSAGE(JsonValueNotString); REGISTER_MESSAGE(LaunchingProgramFailed); + REGISTER_MESSAGE(LibraryArchiveMemberTooSmall); + REGISTER_MESSAGE(LibraryFirstLinkerMemberMissing); REGISTER_MESSAGE(LicenseExpressionContainsExtraPlus); REGISTER_MESSAGE(LicenseExpressionContainsInvalidCharacter); REGISTER_MESSAGE(LicenseExpressionContainsUnicode); @@ -812,6 +898,10 @@ namespace vcpkg REGISTER_MESSAGE(LicenseExpressionImbalancedParens); REGISTER_MESSAGE(LicenseExpressionUnknownException); REGISTER_MESSAGE(LicenseExpressionUnknownLicense); + REGISTER_MESSAGE(LinkageDynamicDebug); + REGISTER_MESSAGE(LinkageDynamicRelease); + REGISTER_MESSAGE(LinkageStaticDebug); + REGISTER_MESSAGE(LinkageStaticRelease); REGISTER_MESSAGE(ListOfValidFieldsForControlFiles); REGISTER_MESSAGE(LoadingCommunityTriplet); REGISTER_MESSAGE(LoadingDependencyInformation); @@ -963,7 +1053,6 @@ namespace vcpkg REGISTER_MESSAGE(UnexpectedToolOutput); REGISTER_MESSAGE(UnknownBaselineFileContent); REGISTER_MESSAGE(UnknownBinaryProviderType); - REGISTER_MESSAGE(UnknownMachineCode); REGISTER_MESSAGE(UnknownOptions); REGISTER_MESSAGE(UnknownParameterForIntegrate); REGISTER_MESSAGE(UnknownPolicySetting); @@ -1067,7 +1156,6 @@ namespace vcpkg REGISTER_MESSAGE(PortBugDllAppContainerBitNotSet); REGISTER_MESSAGE(BuiltWithIncorrectArchitecture); REGISTER_MESSAGE(BinaryWithInvalidArchitecture); - REGISTER_MESSAGE(ExpectedExtension); REGISTER_MESSAGE(FailedToDetermineArchitecture); REGISTER_MESSAGE(PortBugFoundDllInStaticBuild); REGISTER_MESSAGE(PortBugMismatchedNumberOfBinaries); @@ -1085,6 +1173,7 @@ namespace vcpkg REGISTER_MESSAGE(PortBugMovePkgConfigFiles); REGISTER_MESSAGE(PortBugRemoveEmptyDirs); REGISTER_MESSAGE(PortBugInvalidCrtLinkage); + REGISTER_MESSAGE(PortBugInvalidCrtLinkageEntry); REGISTER_MESSAGE(PortBugInspectFiles); REGISTER_MESSAGE(PortBugOutdatedCRT); REGISTER_MESSAGE(PortBugMisplacedFiles); diff --git a/src/vcpkg/binarycaching.cpp b/src/vcpkg/binarycaching.cpp index 5dc4143ad9..a63eae4dea 100644 --- a/src/vcpkg/binarycaching.cpp +++ b/src/vcpkg/binarycaching.cpp @@ -1255,7 +1255,7 @@ namespace vcpkg { std::vector invalid_keys; auto result = api_stable_format(url_template, [&](std::string&, StringView key) { - StringView valid_keys[] = {"name", "version", "sha", "triplet"}; + static constexpr std::array valid_keys = {"name", "version", "sha", "triplet"}; if (!Util::Vectors::contains(valid_keys, key)) { invalid_keys.push_back(key.to_string()); diff --git a/src/vcpkg/build.cpp b/src/vcpkg/build.cpp index a921833b86..e68e9f5d1b 100644 --- a/src/vcpkg/build.cpp +++ b/src/vcpkg/build.cpp @@ -971,7 +971,7 @@ namespace vcpkg const BuildInfo build_info = read_build_info(fs, paths.build_info_file_path(action.spec)); const size_t error_count = - PostBuildLint::perform_all_checks(action.spec, paths, pre_build_info, build_info, scfl.source_location); + perform_post_build_lint_checks(action.spec, paths, pre_build_info, build_info, scfl.source_location); auto find_itr = action.feature_dependencies.find("core"); Checks::check_exit(VCPKG_LINE_INFO, find_itr != action.feature_dependencies.end()); diff --git a/src/vcpkg/commands.add-version.cpp b/src/vcpkg/commands.add-version.cpp index 19ae6508ae..74e7883b36 100644 --- a/src/vcpkg/commands.add-version.cpp +++ b/src/vcpkg/commands.add-version.cpp @@ -209,7 +209,7 @@ namespace msg::format(msgAddVersionAddedVersionToFile, msg::version = port_version.version, msg::path = version_db_file_path) - .append_raw(" ") + .append_raw(' ') .append(msgAddVersionNewFile)); } return UpdateResult::Updated; diff --git a/src/vcpkg/configuration.cpp b/src/vcpkg/configuration.cpp index 9756ad8110..d3fcb68c5b 100644 --- a/src/vcpkg/configuration.cpp +++ b/src/vcpkg/configuration.cpp @@ -389,10 +389,10 @@ namespace { return msg.append_indent(indent_level) .append(msgDuplicatePackagePatternLocation, msg::path = location) - .append_raw("\n") + .append_raw('\n') .append_indent(indent_level) .append(msgDuplicatePackagePatternRegistry, msg::url = registry) - .append_raw("\n"); + .append_raw('\n'); } std::vector collect_package_pattern_warnings(const std::vector& registries) @@ -436,20 +436,20 @@ namespace auto first = locations.begin(); const auto last = locations.end(); auto warning = msg::format_warning(msgDuplicatePackagePattern, msg::package_name = pattern) - .append_raw("\n") + .append_raw('\n') .append_indent() .append(msgDuplicatePackagePatternFirstOcurrence) - .append_raw("\n"); + .append_raw('\n'); append_declaration_warning(warning, first->location, first->registry, 2) - .append_raw("\n") + .append_raw('\n') .append_indent() .append(msgDuplicatePackagePatternIgnoredLocations) - .append_raw("\n"); + .append_raw('\n'); ++first; append_declaration_warning(warning, first->location, first->registry, 2); while (++first != last) { - warning.append_raw("\n"); + warning.append_raw('\n'); append_declaration_warning(warning, first->location, first->registry, 2); } warnings.emplace_back(warning); diff --git a/src/vcpkg/postbuildlint.buildtype.cpp b/src/vcpkg/postbuildlint.buildtype.cpp deleted file mode 100644 index c4507e69d3..0000000000 --- a/src/vcpkg/postbuildlint.buildtype.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include - -#include - -namespace vcpkg::PostBuildLint -{ - bool BuildType::has_crt_linker_option(StringView sv) const - { - // "/DEFAULTLIB:LIBCMTD"; - // "/DEFAULTLIB:MSVCRTD"; - // "/DEFAULTLIB:LIBCMT[^D]"; - // "/DEFAULTLIB:LIBCMT[^D]"; - - constexpr static const StringLiteral static_crt = "/DEFAULTLIB:LIBCMT"; - constexpr static const StringLiteral dynamic_crt = "/DEFAULTLIB:MSVCRT"; - - StringView option = linkage == LinkageType::STATIC ? static_crt : dynamic_crt; - - auto found = Strings::case_insensitive_ascii_search(sv, option); - if (found == sv.end()) - { - return false; - } - - auto option_end = found + option.size(); - if (config == ConfigurationType::DEBUG) - { - if (option_end == sv.end() || !Strings::icase_eq(*option_end, 'd')) - { - return false; - } - ++option_end; - } - - return option_end == sv.end() || ParserBase::is_whitespace(*option_end); - } - - StringLiteral BuildType::to_string() const - { - if (config == ConfigurationType::DEBUG) - { - if (linkage == LinkageType::STATIC) - { - return "Debug,Static"; - } - else - { - return "Debug,Dynamic"; - } - } - else - { - if (linkage == LinkageType::STATIC) - { - return "Release,Static"; - } - else - { - return "Release,Dynamic"; - } - } - } -} diff --git a/src/vcpkg/postbuildlint.cpp b/src/vcpkg/postbuildlint.cpp index 09f5c6d4e0..ac542873da 100644 --- a/src/vcpkg/postbuildlint.cpp +++ b/src/vcpkg/postbuildlint.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -8,13 +9,12 @@ #include #include #include -#include #include #include -namespace vcpkg::PostBuildLint +namespace vcpkg { - constexpr static const StringLiteral windows_system_names[] = { + constexpr static std::array windows_system_names = { "", "Windows", "WindowsStore", @@ -27,39 +27,37 @@ namespace vcpkg::PostBuildLint PROBLEM_DETECTED = 1 }; - struct OutdatedDynamicCrt + // clang-format off +#define OUTDATED_V_NO_120 \ + StringLiteral{"msvcp100.dll"}, \ + StringLiteral{"msvcp100d.dll"}, \ + StringLiteral{"msvcp110.dll"}, \ + StringLiteral{"msvcp110_win.dll"}, \ + StringLiteral{"msvcp60.dll"}, \ + StringLiteral{"msvcp60.dll"}, \ + \ + StringLiteral{"msvcrt.dll"}, \ + StringLiteral{"msvcr100.dll"}, \ + StringLiteral{"msvcr100d.dll"}, \ + StringLiteral{"msvcr100_clr0400.dll"}, \ + StringLiteral{"msvcr110.dll"}, \ + StringLiteral{"msvcrt20.dll"}, \ + StringLiteral{"msvcrt40.dll"} + + // clang-format on + + static View get_outdated_dynamic_crts(const Optional& toolset_version) { - std::string name; - }; - - static Span get_outdated_dynamic_crts(const Optional& toolset_version) - { - static const std::vector V_NO_120 = { - {"msvcp100.dll"}, - {"msvcp100d.dll"}, - {"msvcp110.dll"}, - {"msvcp110_win.dll"}, - {"msvcp60.dll"}, - {"msvcp60.dll"}, - - {"msvcrt.dll"}, - {"msvcr100.dll"}, - {"msvcr100d.dll"}, - {"msvcr100_clr0400.dll"}, - {"msvcr110.dll"}, - {"msvcrt20.dll"}, - {"msvcrt40.dll"}, + static constexpr std::array V_NO_120 = {OUTDATED_V_NO_120}; + + static constexpr std::array V_NO_MSVCRT = { + OUTDATED_V_NO_120, + StringLiteral{"msvcp120.dll"}, + StringLiteral{"msvcp120_clr0400.dll"}, + StringLiteral{"msvcr120.dll"}, + StringLiteral{"msvcr120_clr0400.dll"}, }; - static const std::vector V_NO_MSVCRT = [&]() { - auto ret = V_NO_120; - ret.push_back({"msvcp120.dll"}); - ret.push_back({"msvcp120_clr0400.dll"}); - ret.push_back({"msvcr120.dll"}); - ret.push_back({"msvcr120_clr0400.dll"}); - return ret; - }(); - const auto tsv = toolset_version.get(); if (tsv && (*tsv) == "v120") { @@ -70,6 +68,8 @@ namespace vcpkg::PostBuildLint return V_NO_MSVCRT; } +#undef OUTDATED_V_NO_120 + static LintStatus check_for_files_in_include_directory(const Filesystem& fs, const BuildPolicies& policies, const Path& package_dir) @@ -408,30 +408,78 @@ namespace vcpkg::PostBuildLint return LintStatus::SUCCESS; } + struct PostBuildCheckDllData + { + Path path; + MachineType machine_type; + bool is_arm64_ec; + bool has_exports; + bool has_appcontainer; + std::vector dependencies; + }; + + static ExpectedL try_load_dll_data(const Filesystem& fs, const Path& path) + { + auto maybe_file = fs.try_open_for_read(path); + auto file = maybe_file.get(); + if (!file) + { + return std::move(maybe_file).error(); + } + + auto maybe_metadata = try_read_dll_metadata(*file); + auto metadata = maybe_metadata.get(); + if (!metadata) + { + return std::move(maybe_metadata).error(); + } + + auto maybe_has_exports = try_read_if_dll_has_exports(*metadata, *file); + auto phas_exports = maybe_has_exports.get(); + if (!phas_exports) + { + return std::move(maybe_has_exports).error(); + } + + bool has_exports = *phas_exports; + bool has_appcontainer; + switch (metadata->pe_type) + { + case PEType::PE32: + has_appcontainer = metadata->pe_headers.dll_characteristics & DllCharacteristics::AppContainer; + break; + case PEType::PE32Plus: + has_appcontainer = metadata->pe_plus_headers.dll_characteristics & DllCharacteristics::AppContainer; + break; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + + auto maybe_dependencies = try_read_dll_imported_dll_names(*metadata, *file); + auto dependencies = maybe_dependencies.get(); + if (!dependencies) + { + return std::move(maybe_dependencies).error(); + } + + return PostBuildCheckDllData{path, + metadata->get_machine_type(), + metadata->is_arm64_ec(), + has_exports, + has_appcontainer, + std::move(*dependencies)}; + } + static LintStatus check_exports_of_dlls(const BuildPolicies& policies, - const std::vector& dlls, - const Path& dumpbin_exe) + const std::vector& dlls) { if (policies.is_enabled(BuildPolicy::DLLS_WITHOUT_EXPORTS)) return LintStatus::SUCCESS; std::vector dlls_with_no_exports; - for (const Path& dll : dlls) + for (const PostBuildCheckDllData& dll_data : dlls) { - auto cmd_line = Command(dumpbin_exe).string_arg("/exports").string_arg(dll); - const auto maybe_output = flatten_out(cmd_execute_and_capture_output(cmd_line), "dumpbin"); - if (const auto output = maybe_output.get()) - { - if (output->find("ordinal hint RVA name") == std::string::npos) - { - dlls_with_no_exports.push_back(dll); - } - } - else + if (!dll_data.has_exports) { - Checks::msg_exit_with_message(VCPKG_LINE_INFO, - msg::format(msgCommandFailed, msg::command_line = cmd_line.command_line()) - .append_raw('\n') - .append(maybe_output.error())); + dlls_with_no_exports.push_back(dll_data.path); } } @@ -446,8 +494,7 @@ namespace vcpkg::PostBuildLint } static LintStatus check_uwp_bit_of_dlls(const std::string& expected_system_name, - const std::vector& dlls, - const Path dumpbin_exe) + const std::vector& dlls) { if (expected_system_name != "WindowsStore") { @@ -455,23 +502,11 @@ namespace vcpkg::PostBuildLint } std::vector dlls_with_improper_uwp_bit; - for (const Path& dll : dlls) + for (const PostBuildCheckDllData& dll_data : dlls) { - auto cmd_line = Command(dumpbin_exe).string_arg("/headers").string_arg(dll); - const auto maybe_output = flatten_out(cmd_execute_and_capture_output(cmd_line), "dumpbin"); - if (const auto output = maybe_output.get()) + if (!dll_data.has_appcontainer) { - if (output->find("App Container") == std::string::npos) - { - dlls_with_improper_uwp_bit.push_back(dll); - } - } - else - { - Checks::msg_exit_with_message(VCPKG_LINE_INFO, - msg::format(msgCommandFailed, msg::command_line = cmd_line.command_line()) - .append_raw('\n') - .append(maybe_output.error())); + dlls_with_improper_uwp_bit.push_back(dll_data.path); } } @@ -479,7 +514,6 @@ namespace vcpkg::PostBuildLint { msg::println_warning(msgPortBugDllAppContainerBitNotSet); print_paths(dlls_with_improper_uwp_bit); - msg::println_warning(msgPortBugDllAppContainerBitNotSet); return LintStatus::PROBLEM_DETECTED; } @@ -492,18 +526,39 @@ namespace vcpkg::PostBuildLint std::string actual_arch; }; - static std::string get_actual_architecture(const MachineType& machine_type) + static std::string get_printable_architecture(const MachineType& machine_type) { switch (machine_type) { - case MachineType::AMD64: - case MachineType::IA64: return "x64"; - case MachineType::I386: return "x86"; - case MachineType::ARM: - case MachineType::ARMNT: return "arm"; + case MachineType::UNKNOWN: return "unknown"; + case MachineType::AM33: return "matsushita-am33"; + case MachineType::AMD64: return "x64"; + case MachineType::ARM: return "arm"; case MachineType::ARM64: return "arm64"; case MachineType::ARM64EC: return "arm64ec"; - default: return "Machine Type Code = " + std::to_string(static_cast(machine_type)); + case MachineType::ARM64X: return "arm64x"; + case MachineType::ARMNT: return "arm"; + case MachineType::EBC: return "efi-byte-code"; + case MachineType::I386: return "x86"; + case MachineType::IA64: return "ia64"; + case MachineType::M32R: return "mitsubishi-m32r-le"; + case MachineType::MIPS16: return "mips16"; + case MachineType::MIPSFPU: return "mipsfpu"; + case MachineType::MIPSFPU16: return "mipsfpu16"; + case MachineType::POWERPC: return "ppc"; + case MachineType::POWERPCFP: return "ppcfp"; + case MachineType::R4000: return "mips-le"; + case MachineType::RISCV32: return "riscv-32"; + case MachineType::RISCV64: return "riscv-64"; + case MachineType::RISCV128: return "riscv-128"; + case MachineType::SH3: return "hitachi-sh3"; + case MachineType::SH3DSP: return "hitachi-sh3-dsp"; + case MachineType::SH4: return "hitachi-sh4"; + case MachineType::SH5: return "hitachi-sh5"; + case MachineType::THUMB: return "thumb"; + case MachineType::WCEMIPSV2: return "mips-le-wce-v2"; + case MachineType::LLVM_BITCODE: return "llvm-bitcode"; + default: return "unknown-" + std::to_string(static_cast(machine_type)); } } @@ -520,41 +575,25 @@ namespace vcpkg::PostBuildLint } } -#if defined(_WIN32) static LintStatus check_dll_architecture(const std::string& expected_architecture, - const std::vector& files, - const Filesystem& fs) + const std::vector& dlls) { std::vector binaries_with_invalid_architecture; - for (const Path& file : files) + for (const PostBuildCheckDllData& dll_data : dlls) { - if (!Strings::case_insensitive_ascii_equals(file.extension(), ".dll")) - { - continue; - } - - auto maybe_opened = fs.try_open_for_read(file); - if (const auto opened = maybe_opened.get()) + const std::string actual_architecture = get_printable_architecture(dll_data.machine_type); + if (expected_architecture == "arm64ec") { - auto maybe_metadata = try_read_dll_metadata(*opened); - if (auto* metadata = maybe_metadata.get()) + if (dll_data.machine_type != MachineType::AMD64 || !dll_data.is_arm64_ec) { - const auto machine_type = metadata->get_machine_type(); - const std::string actual_architecture = get_actual_architecture(machine_type); - if (expected_architecture == "arm64ec") - { - if (actual_architecture != "x64" || !metadata->is_arm64_ec()) - { - binaries_with_invalid_architecture.push_back({file, actual_architecture}); - } - } - else if (expected_architecture != actual_architecture) - { - binaries_with_invalid_architecture.push_back({file, actual_architecture}); - } + binaries_with_invalid_architecture.push_back({dll_data.path, actual_architecture}); } } + else if (expected_architecture != actual_architecture) + { + binaries_with_invalid_architecture.push_back({dll_data.path, actual_architecture}); + } } if (!binaries_with_invalid_architecture.empty()) @@ -565,7 +604,6 @@ namespace vcpkg::PostBuildLint return LintStatus::SUCCESS; } -#endif static LintStatus check_lib_architecture(const std::string& expected_architecture, const std::string& cmake_system_name, @@ -577,21 +615,39 @@ namespace vcpkg::PostBuildLint { for (const Path& file : files) { - Checks::msg_check_exit(VCPKG_LINE_INFO, - Strings::case_insensitive_ascii_equals(file.extension(), ".lib"), - msgExpectedExtension, - msg::extension = ".lib", - msg::path = file); - - const auto machine_types = Util::fmap(read_lib_machine_types(fs.open_for_read(file, VCPKG_LINE_INFO)), - [](MachineType mt) { return get_actual_architecture(mt); }); + if (!Strings::case_insensitive_ascii_equals(file.extension(), ".lib")) + { + continue; + } + + auto maybe_lib_information = fs.try_open_for_read(file).then( + [](ReadFilePointer&& file_handle) { return read_lib_information(file_handle); }); + + if (!maybe_lib_information.has_value()) + { + continue; + } + + auto&& machine_types = maybe_lib_information.value_or_exit(VCPKG_LINE_INFO).machine_types; + { + auto llvm_bitcode = + std::find(machine_types.begin(), machine_types.end(), MachineType::LLVM_BITCODE); + if (llvm_bitcode != machine_types.end()) + { + machine_types.erase(llvm_bitcode); + } + } + + auto printable_machine_types = + Util::fmap(machine_types, [](MachineType mt) { return get_printable_architecture(mt); }); // Either machine_types is empty (meaning this lib is architecture independent), or // we need at least one of the machine types to match. - // Agnostic example: Folly's debug library + // Agnostic example: Folly's debug library, LLVM LTO libraries // Multiple example: arm64x libraries - if (!machine_types.empty() && !Util::Vectors::contains(machine_types, expected_architecture)) + if (!printable_machine_types.empty() && + !Util::Vectors::contains(printable_machine_types, expected_architecture)) { - binaries_with_invalid_architecture.push_back({file, Strings::join(",", machine_types)}); + binaries_with_invalid_architecture.push_back({file, Strings::join(",", printable_machine_types)}); } } } @@ -650,19 +706,26 @@ namespace vcpkg::PostBuildLint } msg::println_warning(msgPortBugMismatchedNumberOfBinaries); - msg::println_warning(msgPortBugFoundDebugBinaries, msg::count = debug_count); - print_paths(debug_binaries); - msg::println_warning(msgPortBugFoundDebugBinaries, msg::count = release_count); - print_paths(release_binaries); - if (debug_count == 0) { - msg::println_warning(msgPortBugMissingDebugBinaries); + msg::println(msgPortBugMissingDebugBinaries); + } + else + { + msg::println(msgPortBugFoundDebugBinaries, msg::count = debug_count); + print_paths(debug_binaries); } + if (release_count == 0) { - msg::println_warning(msgPortBugMissingReleaseBinaries); + msg::println(msgPortBugMissingReleaseBinaries); } + else + { + msg::println(msgPortBugFoundReleaseBinaries, msg::count = release_count); + print_paths(release_binaries); + } + return LintStatus::PROBLEM_DETECTED; } @@ -862,59 +925,153 @@ namespace vcpkg::PostBuildLint struct BuildTypeAndFile { Path file; - BuildType build_type; + bool has_static_release = false; + bool has_static_debug = false; + bool has_dynamic_release = false; + bool has_dynamic_debug = false; }; - static LintStatus check_crt_linkage_of_libs(const BuildType& expected_build_type, - const std::vector& libs, - const Path& dumpbin_exe) + static LocalizedString format_linkage(LinkageType linkage, bool release) { - auto bad_build_types = std::vector({ - {ConfigurationType::DEBUG, LinkageType::STATIC}, - {ConfigurationType::DEBUG, LinkageType::DYNAMIC}, - {ConfigurationType::RELEASE, LinkageType::STATIC}, - {ConfigurationType::RELEASE, LinkageType::DYNAMIC}, - }); - Util::erase_remove(bad_build_types, expected_build_type); + switch (linkage) + { + case LinkageType::DYNAMIC: + if (release) + { + return msg::format(msgLinkageDynamicRelease); + } + else + { + return msg::format(msgLinkageDynamicDebug); + } + break; + case LinkageType::STATIC: + if (release) + { + return msg::format(msgLinkageStaticRelease); + } + else + { + return msg::format(msgLinkageStaticDebug); + } + break; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + } + static LintStatus check_crt_linkage_of_libs(const Filesystem& fs, + const BuildInfo& build_info, + bool expect_release, + const std::vector& libs) + { std::vector libs_with_invalid_crt; - for (const Path& lib : libs) { - auto cmd_line = Command(dumpbin_exe).string_arg("/directives").string_arg(lib); - const auto maybe_output = flatten_out(cmd_execute_and_capture_output(cmd_line), "dumpbin"); - if (const auto output = maybe_output.get()) + auto maybe_lib_info = fs.try_open_for_read(lib).then( + [](ReadFilePointer&& lib_file) { return read_lib_information(lib_file); }); + + if (!maybe_lib_info.has_value()) { - for (const BuildType& bad_build_type : bad_build_types) + continue; + } + + auto&& lib_info = maybe_lib_info.value_or_exit(VCPKG_LINE_INFO); + Debug::println( + "The lib ", lib.native(), " has directives: ", Strings::join(" ", lib_info.linker_directives)); + + BuildTypeAndFile this_lib{lib}; + constexpr static const StringLiteral static_release_crt = "/DEFAULTLIB:LIBCMT"; + constexpr static const StringLiteral static_debug_crt = "/DEFAULTLIB:LIBCMTd"; + constexpr static const StringLiteral dynamic_release_crt = "/DEFAULTLIB:MSVCRT"; + constexpr static const StringLiteral dynamic_debug_crt = "/DEFAULTLIB:MSVCRTd"; + + for (auto&& directive : lib_info.linker_directives) + { + if (Strings::case_insensitive_ascii_equals(directive, static_release_crt)) { - if (bad_build_type.has_crt_linker_option(*output)) - { - libs_with_invalid_crt.push_back({lib, bad_build_type}); - break; - } + this_lib.has_static_release = true; + } + else if (Strings::case_insensitive_ascii_equals(directive, static_debug_crt)) + { + this_lib.has_static_debug = true; + } + else if (Strings::case_insensitive_ascii_equals(directive, dynamic_release_crt)) + { + this_lib.has_dynamic_release = true; } + else if (Strings::case_insensitive_ascii_equals(directive, dynamic_debug_crt)) + { + this_lib.has_dynamic_debug = true; + } + } + + bool fail = false; + if (expect_release) + { + fail |= this_lib.has_static_debug; + fail |= this_lib.has_dynamic_debug; } else { - Checks::msg_exit_with_message(VCPKG_LINE_INFO, - msg::format(msgCommandFailed, msg::command_line = cmd_line.command_line()) - .append_raw('\n') - .append_raw(maybe_output.error())); + fail |= this_lib.has_static_release; + fail |= this_lib.has_dynamic_release; + } + + switch (build_info.crt_linkage) + { + case LinkageType::DYNAMIC: + fail |= this_lib.has_static_debug; + fail |= this_lib.has_static_release; + break; + case LinkageType::STATIC: + fail |= this_lib.has_dynamic_debug; + fail |= this_lib.has_dynamic_release; + break; + default: Checks::unreachable(VCPKG_LINE_INFO); + } + + if (fail) + { + libs_with_invalid_crt.push_back(std::move(this_lib)); } } if (!libs_with_invalid_crt.empty()) { - msg::println_warning(msgPortBugInvalidCrtLinkage, msg::expected = expected_build_type.to_string()); - + msg::println_warning(msgPortBugInvalidCrtLinkage, + msg::expected = format_linkage(build_info.crt_linkage, expect_release)); + std::vector printed_linkages; for (const BuildTypeAndFile& btf : libs_with_invalid_crt) { - msg::write_unlocalized_text_to_stdout( - Color::warning, fmt::format(" {}: {}\n", btf.file, btf.build_type.to_string())); + printed_linkages.clear(); + LocalizedString this_entry; + this_entry.append_indent().append(msgPortBugInvalidCrtLinkageEntry, msg::path = btf.file); + if (btf.has_dynamic_debug) + { + printed_linkages.push_back(msg::format(msgLinkageDynamicDebug)); + } + + if (btf.has_dynamic_release) + { + printed_linkages.push_back(msg::format(msgLinkageDynamicRelease)); + } + + if (btf.has_static_debug) + { + printed_linkages.push_back(msg::format(msgLinkageStaticDebug)); + } + + if (btf.has_static_release) + { + printed_linkages.push_back(msg::format(msgLinkageStaticRelease)); + } + + this_entry.append_floating_list(2, printed_linkages); + msg::println(this_entry); } - msg::println_warning(msg::format(msgPortBugInspectFiles, msg::extension = "lib") - .append_raw("\n dumpbin.exe /directives mylibfile.lib")); + msg::println(msg::format(msgPortBugInspectFiles, msg::extension = "lib") + .append_raw("\n dumpbin.exe /directives mylibfile.lib")); return LintStatus::PROBLEM_DETECTED; } @@ -924,41 +1081,29 @@ namespace vcpkg::PostBuildLint struct OutdatedDynamicCrtAndFile { Path file; - OutdatedDynamicCrt outdated_crt; + StringLiteral outdated_crt; }; - static LintStatus check_outdated_crt_linkage_of_dlls(const std::vector& dlls, - const Path& dumpbin_exe, + static LintStatus check_outdated_crt_linkage_of_dlls(const std::vector& dlls, const BuildInfo& build_info, const PreBuildInfo& pre_build_info) { if (build_info.policies.is_enabled(BuildPolicy::ALLOW_OBSOLETE_MSVCRT)) return LintStatus::SUCCESS; + const auto outdated_crts = get_outdated_dynamic_crts(pre_build_info.platform_toolset); std::vector dlls_with_outdated_crt; - for (const Path& dll : dlls) + for (const PostBuildCheckDllData& dll_data : dlls) { - auto cmd_line = Command(dumpbin_exe).string_arg("/dependents").string_arg(dll); - const auto maybe_output = flatten_out(cmd_execute_and_capture_output(cmd_line), "dumpbin"); - if (const auto output = maybe_output.get()) + for (const StringLiteral& outdated_crt : outdated_crts) { - for (const OutdatedDynamicCrt& outdated_crt : - get_outdated_dynamic_crts(pre_build_info.platform_toolset)) + if (Util::Vectors::contains( + dll_data.dependencies, outdated_crt, Strings::case_insensitive_ascii_equals)) { - if (Strings::case_insensitive_ascii_contains(*output, outdated_crt.name)) - { - dlls_with_outdated_crt.push_back({dll, outdated_crt}); - break; - } + dlls_with_outdated_crt.push_back({dll_data.path, outdated_crt}); + break; } } - else - { - Checks::msg_exit_with_message(VCPKG_LINE_INFO, - msg::format(msgCommandFailed, msg::command_line = cmd_line.command_line()) - .append_raw('\n') - .append_raw(maybe_output.error())); - } } if (!dlls_with_outdated_crt.empty()) @@ -967,10 +1112,10 @@ namespace vcpkg::PostBuildLint for (const OutdatedDynamicCrtAndFile& btf : dlls_with_outdated_crt) { msg::write_unlocalized_text_to_stdout(Color::warning, - fmt::format(" {}:{}\n", btf.file, btf.outdated_crt.name)); + fmt::format(" {}:{}\n", btf.file, btf.outdated_crt)); } - msg::println_warning(msg::format(msgPortBugInspectFiles, msg::extension = "dll") - .append_raw("\n dumpbin.exe /dependents mylibfile.dll")); + msg::println(msg::format(msgPortBugInspectFiles, msg::extension = "dll") + .append_raw("\n dumpbin.exe /dependents mylibfile.dll")); return LintStatus::PROBLEM_DETECTED; } @@ -1090,6 +1235,28 @@ namespace vcpkg::PostBuildLint static void operator+=(size_t& left, const LintStatus& right) { left += static_cast(right); } + static size_t perform_post_build_checks_dll_loads(const Filesystem& fs, + std::vector& dlls, + const std::vector& dll_files) + { + size_t error_count = 0; + for (const Path& dll : dll_files) + { + auto maybe_dll_data = try_load_dll_data(fs, dll); + if (const auto dll_data = maybe_dll_data.get()) + { + dlls.emplace_back(std::move(*dll_data)); + } + else + { + ++error_count; + msg::println(Color::warning, maybe_dll_data.error()); + } + } + + return error_count; + } + static size_t perform_all_checks_and_return_error_count(const PackageSpec& spec, const VcpkgPaths& paths, const PreBuildInfo& pre_build_info, @@ -1097,9 +1264,6 @@ namespace vcpkg::PostBuildLint const Path& port_dir) { const auto& fs = paths.get_filesystem(); - - // for dumpbin - const Toolset& toolset = paths.get_toolset(pre_build_info); const auto package_dir = paths.package_dir(spec); size_t error_count = 0; @@ -1130,7 +1294,8 @@ namespace vcpkg::PostBuildLint const auto release_bin_dir = package_dir / "bin"; NotExtensionsCaseInsensitive lib_filter; - if (Util::Vectors::contains(windows_system_names, pre_build_info.cmake_system_name)) + const bool windows_target = Util::Vectors::contains(windows_system_names, pre_build_info.cmake_system_name); + if (windows_target) { lib_filter = NotExtensionsCaseInsensitive{{".lib"}}; } @@ -1145,73 +1310,72 @@ namespace vcpkg::PostBuildLint Util::erase_remove_if(release_libs, lib_filter); if (!pre_build_info.build_type && !build_info.policies.is_enabled(BuildPolicy::MISMATCHED_NUMBER_OF_BINARIES)) - error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs); - - if (!build_info.policies.is_enabled(BuildPolicy::SKIP_ARCHITECTURE_CHECK)) { - std::vector libs; - libs.insert(libs.cend(), debug_libs.cbegin(), debug_libs.cend()); - libs.insert(libs.cend(), release_libs.cbegin(), release_libs.cend()); - error_count += - check_lib_architecture(pre_build_info.target_architecture, pre_build_info.cmake_system_name, libs, fs); + error_count += check_matching_debug_and_release_binaries(debug_libs, release_libs); } - std::vector debug_dlls = fs.get_regular_files_recursive(debug_bin_dir, IgnoreErrors{}); - Util::erase_remove_if(debug_dlls, NotExtensionCaseInsensitive{".dll"}); - std::vector release_dlls = fs.get_regular_files_recursive(release_bin_dir, IgnoreErrors{}); - Util::erase_remove_if(release_dlls, NotExtensionCaseInsensitive{".dll"}); - - switch (build_info.library_linkage) + if (windows_target) { - case LinkageType::DYNAMIC: + Debug::println("Running windows targeting post-build checks"); + if (!build_info.policies.is_enabled(BuildPolicy::SKIP_ARCHITECTURE_CHECK)) { - if (!pre_build_info.build_type && - !build_info.policies.is_enabled(BuildPolicy::MISMATCHED_NUMBER_OF_BINARIES)) - error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls); - - error_count += check_lib_files_are_available_if_dlls_are_available( - build_info.policies, debug_libs.size(), debug_dlls.size(), debug_lib_dir); - error_count += check_lib_files_are_available_if_dlls_are_available( - build_info.policies, release_libs.size(), release_dlls.size(), release_lib_dir); + std::vector libs; + libs.insert(libs.cend(), debug_libs.cbegin(), debug_libs.cend()); + libs.insert(libs.cend(), release_libs.cbegin(), release_libs.cend()); + error_count += check_lib_architecture( + pre_build_info.target_architecture, pre_build_info.cmake_system_name, libs, fs); + } - std::vector dlls; - dlls.insert(dlls.cend(), debug_dlls.cbegin(), debug_dlls.cend()); - dlls.insert(dlls.cend(), release_dlls.cbegin(), release_dlls.cend()); + std::vector debug_dlls = fs.get_regular_files_recursive(debug_bin_dir, IgnoreErrors{}); + Util::erase_remove_if(debug_dlls, NotExtensionCaseInsensitive{".dll"}); + std::vector release_dlls = fs.get_regular_files_recursive(release_bin_dir, IgnoreErrors{}); + Util::erase_remove_if(release_dlls, NotExtensionCaseInsensitive{".dll"}); - if (!toolset.dumpbin.empty() && !build_info.policies.is_enabled(BuildPolicy::SKIP_DUMPBIN_CHECKS)) + switch (build_info.library_linkage) + { + case LinkageType::DYNAMIC: { - error_count += check_exports_of_dlls(build_info.policies, dlls, toolset.dumpbin); - error_count += check_uwp_bit_of_dlls(pre_build_info.cmake_system_name, dlls, toolset.dumpbin); - error_count += - check_outdated_crt_linkage_of_dlls(dlls, toolset.dumpbin, build_info, pre_build_info); + if (!pre_build_info.build_type && + !build_info.policies.is_enabled(BuildPolicy::MISMATCHED_NUMBER_OF_BINARIES)) + error_count += check_matching_debug_and_release_binaries(debug_dlls, release_dlls); + + error_count += check_lib_files_are_available_if_dlls_are_available( + build_info.policies, debug_libs.size(), debug_dlls.size(), debug_lib_dir); + error_count += check_lib_files_are_available_if_dlls_are_available( + build_info.policies, release_libs.size(), release_dlls.size(), release_lib_dir); + + std::vector dlls; + dlls.reserve(debug_dlls.size() + release_dlls.size()); + error_count += perform_post_build_checks_dll_loads(fs, dlls, debug_dlls); + error_count += perform_post_build_checks_dll_loads(fs, dlls, release_dlls); + error_count += check_exports_of_dlls(build_info.policies, dlls); + error_count += check_uwp_bit_of_dlls(pre_build_info.cmake_system_name, dlls); + error_count += check_outdated_crt_linkage_of_dlls(dlls, build_info, pre_build_info); + if (!build_info.policies.is_enabled(BuildPolicy::SKIP_ARCHITECTURE_CHECK)) + { + error_count += check_dll_architecture(pre_build_info.target_architecture, dlls); + } } - -#if defined(_WIN32) - error_count += check_dll_architecture(pre_build_info.target_architecture, dlls, fs); -#endif break; - } - case LinkageType::STATIC: - { - auto dlls = release_dlls; - dlls.insert(dlls.end(), debug_dlls.begin(), debug_dlls.end()); - error_count += check_no_dlls_present(build_info.policies, dlls); - - error_count += check_bin_folders_are_not_present_in_static_build(build_info.policies, fs, package_dir); - - if (!toolset.dumpbin.empty() && !build_info.policies.is_enabled(BuildPolicy::SKIP_DUMPBIN_CHECKS)) + case LinkageType::STATIC: { + auto& dlls = debug_dlls; + dlls.insert(dlls.end(), + std::make_move_iterator(release_dlls.begin()), + std::make_move_iterator(release_dlls.end())); + error_count += check_no_dlls_present(build_info.policies, dlls); + error_count += + check_bin_folders_are_not_present_in_static_build(build_info.policies, fs, package_dir); if (!build_info.policies.is_enabled(BuildPolicy::ONLY_RELEASE_CRT)) { - error_count += check_crt_linkage_of_libs( - BuildType(ConfigurationType::DEBUG, build_info.crt_linkage), debug_libs, toolset.dumpbin); + error_count += check_crt_linkage_of_libs(fs, build_info, false, debug_libs); } - error_count += check_crt_linkage_of_libs( - BuildType(ConfigurationType::RELEASE, build_info.crt_linkage), release_libs, toolset.dumpbin); + + error_count += check_crt_linkage_of_libs(fs, build_info, true, release_libs); + break; } - break; + default: Checks::unreachable(VCPKG_LINE_INFO); } - default: Checks::unreachable(VCPKG_LINE_INFO); } error_count += check_no_empty_folders(fs, package_dir); @@ -1227,11 +1391,11 @@ namespace vcpkg::PostBuildLint return error_count; } - size_t perform_all_checks(const PackageSpec& spec, - const VcpkgPaths& paths, - const PreBuildInfo& pre_build_info, - const BuildInfo& build_info, - const Path& port_dir) + size_t perform_post_build_lint_checks(const PackageSpec& spec, + const VcpkgPaths& paths, + const PreBuildInfo& pre_build_info, + const BuildInfo& build_info, + const Path& port_dir) { msg::println(msgPerformingPostBuildValidation); const size_t error_count = diff --git a/src/vcpkg/sourceparagraph.cpp b/src/vcpkg/sourceparagraph.cpp index bb19873103..dba878254d 100644 --- a/src/vcpkg/sourceparagraph.cpp +++ b/src/vcpkg/sourceparagraph.cpp @@ -1210,16 +1210,16 @@ namespace vcpkg { LocalizedString ret; ret.append(msgFailedToParseConfig, msg::path = origin); - ret.append_raw("\n"); + ret.append_raw('\n'); for (auto&& err : reader.errors()) { ret.append_indent(); ret.append_fmt_raw("{}\n", err); } ret.append(msgExtendedDocumentationAtUrl, msg::url = docs::registries_url); - ret.append_raw("\n"); + ret.append_raw('\n'); ret.append(msgExtendedDocumentationAtUrl, msg::url = docs::manifests_url); - ret.append_raw("\n"); + ret.append_raw('\n'); return std::move(ret); } diff --git a/src/vcpkg/tools.cpp b/src/vcpkg/tools.cpp index 587d915976..5d14aa9f0d 100644 --- a/src/vcpkg/tools.cpp +++ b/src/vcpkg/tools.cpp @@ -212,7 +212,7 @@ namespace vcpkg virtual void add_system_package_info(LocalizedString& out) const { - out.append_raw(" ").append(msgInstallWithSystemManager); + out.append_raw(' ').append(msgInstallWithSystemManager); } }; @@ -419,11 +419,11 @@ namespace vcpkg virtual void add_system_package_info(LocalizedString& out) const override { #if defined(__APPLE__) - out.append_raw(" ").append(msgInstallWithSystemManagerPkg, msg::command_line = "brew install mono"); + out.append_raw(' ').append(msgInstallWithSystemManagerPkg, msg::command_line = "brew install mono"); #else - out.append_raw(" ").append(msgInstallWithSystemManagerPkg, + out.append_raw(' ').append(msgInstallWithSystemManagerPkg, msg::command_line = "sudo apt install mono-complete"); - out.append_raw(" ").append(msgInstallWithSystemManagerMono, + out.append_raw(' ').append(msgInstallWithSystemManagerMono, msg::url = "https://www.mono-project.com/download/stable/"); #endif } @@ -573,9 +573,9 @@ namespace vcpkg virtual void add_system_package_info(LocalizedString& out) const override { #if defined(__APPLE__) - out.append_raw(" ").append(msgInstallWithSystemManagerPkg, msg::command_line = "brew install python3"); + out.append_raw(' ').append(msgInstallWithSystemManagerPkg, msg::command_line = "brew install python3"); #else - out.append_raw(" ").append(msgInstallWithSystemManagerPkg, msg::command_line = "sudo apt install python3"); + out.append_raw(' ').append(msgInstallWithSystemManagerPkg, msg::command_line = "sudo apt install python3"); #endif } }; @@ -595,9 +595,9 @@ namespace vcpkg virtual void add_system_package_info(LocalizedString& out) const override { #if defined(__APPLE__) - out.append_raw(" ").append(msgInstallWithSystemManagerPkg, msg::command_line = "brew install python3"); + out.append_raw(' ').append(msgInstallWithSystemManagerPkg, msg::command_line = "brew install python3"); #else - out.append_raw(" ").append(msgInstallWithSystemManagerPkg, + out.append_raw(' ').append(msgInstallWithSystemManagerPkg, msg::command_line = "sudo apt install python3-virtualenv"); #endif } @@ -831,7 +831,7 @@ namespace vcpkg s.append(msgToolFetchFailed, msg::tool_name = tool.tool_data_name()); if (env_force_system_binaries && download_available) { - s.append_raw(" ").append(msgDownloadAvailable, msg::env_var = s_env_vcpkg_force_system_binaries); + s.append_raw(' ').append(msgDownloadAvailable, msg::env_var = s_env_vcpkg_force_system_binaries); } if (consider_system) { @@ -839,7 +839,7 @@ namespace vcpkg } else if (!download_available) { - s.append_raw(" ").append(msgUnknownTool); + s.append_raw(' ').append(msgUnknownTool); } Checks::msg_exit_maybe_upgrade(VCPKG_LINE_INFO, s); } diff --git a/src/vcpkg/vcpkgpaths.cpp b/src/vcpkg/vcpkgpaths.cpp index 1431b54b7b..2aafb3209f 100644 --- a/src/vcpkg/vcpkgpaths.cpp +++ b/src/vcpkg/vcpkgpaths.cpp @@ -1333,16 +1333,13 @@ namespace vcpkg { if (!prebuildinfo.using_vcvars()) { - static Toolset external_toolset = []() -> Toolset { - Toolset ret; - ret.dumpbin.clear(); - ret.supported_architectures = {ToolsetArchOption{"", get_host_processor(), get_host_processor()}}; - ret.vcvarsall.clear(); - ret.vcvarsall_options = {}; - ret.version = "external"; - ret.visual_studio_root_path.clear(); - return ret; - }(); + static const Toolset external_toolset{ + Path{}, + Path{}, + std::vector{}, + "external", + std::string{}, + std::vector{ToolsetArchOption{"", get_host_processor(), get_host_processor()}}}; return external_toolset; } diff --git a/src/vcpkg/visualstudio.cpp b/src/vcpkg/visualstudio.cpp index b2cba33cfd..dd316fbfae 100644 --- a/src/vcpkg/visualstudio.cpp +++ b/src/vcpkg/visualstudio.cpp @@ -279,33 +279,26 @@ namespace vcpkg::VisualStudio // unknown toolset minor version continue; } - const auto dumpbin_dir = subdir / "bin/HostX86/x86"; - const auto dumpbin_path = dumpbin_dir / "dumpbin.exe"; - paths_examined.push_back(dumpbin_path); - if (fs.exists(dumpbin_path, IgnoreErrors{})) - { - Toolset toolset{vs_instance.root_path, - dumpbin_path, - vcvarsall_bat, - {vcvars_option}, - toolset_version, - toolset_version_full.to_string(), - supported_architectures}; - - found_toolsets.push_back(std::move(toolset)); - if (v140_is_available) - { - found_toolsets.push_back({vs_instance.root_path, - dumpbin_path, - vcvarsall_bat, - {"-vcvars_ver=14.0"}, - V_140, - "14", - supported_architectures}); - } - continue; + Toolset toolset{vs_instance.root_path, + vcvarsall_bat, + {vcvars_option}, + toolset_version, + toolset_version_full.to_string(), + supported_architectures}; + + found_toolsets.push_back(std::move(toolset)); + if (v140_is_available) + { + found_toolsets.push_back({vs_instance.root_path, + vcvarsall_bat, + {"-vcvars_ver=14.0"}, + V_140, + "14", + supported_architectures}); } + + continue; } continue; @@ -314,41 +307,47 @@ namespace vcpkg::VisualStudio if (major_version == "14" || major_version == "12") { const auto vcvarsall_bat = vs_instance.root_path / "VC/vcvarsall.bat"; - paths_examined.push_back(vcvarsall_bat); if (fs.exists(vcvarsall_bat, IgnoreErrors{})) { - const auto vs_dumpbin_dir = vs_instance.root_path / "VC/bin"; - const auto vs_dumpbin_exe = vs_dumpbin_dir / "dumpbin.exe"; - paths_examined.push_back(vs_dumpbin_exe); - const auto vs_bin_dir = Path(vcvarsall_bat.parent_path()) / "bin"; std::vector supported_architectures; if (fs.exists(vs_bin_dir / "vcvars32.bat", IgnoreErrors{})) + { supported_architectures.push_back({"x86", CPU::X86, CPU::X86}); + } + if (fs.exists(vs_bin_dir / "amd64/vcvars64.bat", IgnoreErrors{})) + { supported_architectures.push_back({"x64", CPU::X64, CPU::X64}); + } + if (fs.exists(vs_bin_dir / "x86_amd64/vcvarsx86_amd64.bat", IgnoreErrors{})) + { supported_architectures.push_back({"x86_amd64", CPU::X86, CPU::X64}); + } + if (fs.exists(vs_bin_dir / "x86_arm/vcvarsx86_arm.bat", IgnoreErrors{})) + { supported_architectures.push_back({"x86_arm", CPU::X86, CPU::ARM}); + } + if (fs.exists(vs_bin_dir / "amd64_x86/vcvarsamd64_x86.bat", IgnoreErrors{})) + { supported_architectures.push_back({"amd64_x86", CPU::X64, CPU::X86}); - if (fs.exists(vs_bin_dir / "amd64_arm/vcvarsamd64_arm.bat", IgnoreErrors{})) - supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM}); + } - if (fs.exists(vs_dumpbin_exe, IgnoreErrors{})) + if (fs.exists(vs_bin_dir / "amd64_arm/vcvarsamd64_arm.bat", IgnoreErrors{})) { - const Toolset toolset = {vs_instance.root_path, - vs_dumpbin_exe, - vcvarsall_bat, - {}, - major_version == "14" ? V_140 : V_120, - major_version, - supported_architectures}; - - found_toolsets.push_back(toolset); + supported_architectures.push_back({"amd64_arm", CPU::X64, CPU::ARM}); } + + found_toolsets.push_back(Toolset{vs_instance.root_path, + vcvarsall_bat, + {}, + major_version == "14" ? V_140 : V_120, + major_version, + supported_architectures}); } } }