diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ebeb1a2..ab71837 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -330,19 +330,22 @@ jobs: state: 'closed' }); - feature-test: - name: Run ${{ github.event.pull_request.head.repo.full_name || github.repository }}@${{ github.head_ref || github.ref_name }} with latest Swift on ${{ matrix.os }} - if: always() && (needs.dependabot.result == 'success' || needs.dependabot.result == 'skipped') + e2e-test: + name: End-to-end test latest Swift on ${{ matrix.os }} + if: | + always() && + (needs.ci.result == 'success' || needs.ci.result == 'skipped') && + (needs.dependabot.result == 'success' || needs.dependabot.result == 'skipped') needs: [ci, dependabot] runs-on: ${{ matrix.os }} concurrency: - group: feature-test-${{ github.ref }}-${{ matrix.os }} + group: e2e-test-${{ github.ref }}-${{ matrix.os }} cancel-in-progress: ${{ needs.ci.outputs.run == 'true' }} env: COMPOSITE: ./.ref-download-test strategy: matrix: - os: [ubuntu-latest, macos-latest] + os: [ubuntu-latest, macos-latest, windows-latest] steps: - name: Setup wrapper composite action at ${{ env.COMPOSITE }} if: needs.ci.outputs.run == 'true' @@ -407,10 +410,17 @@ jobs: if: needs.ci.outputs.run == 'true' run: swift --version | grep ${{ steps.setup-swift.outputs.swift-version }} || exit 1 + - name: Test Swift package + if: needs.ci.outputs.run == 'true' + run: | + swift package init --type library --name SetupLib + swift build --build-tests + swift test + merge: name: Auto-merge submodule update PR if: needs.ci.outputs.run == 'true' && needs.dependabot.outputs.merge == 'true' - needs: [ci, analyze, unit-test, integration-test, dry-run, dependabot, feature-test] + needs: [ci, analyze, unit-test, integration-test, dry-run, dependabot, e2e-test] runs-on: ubuntu-latest concurrency: group: swiftorg-update @@ -427,8 +437,14 @@ jobs: cd: name: Create release - if: always() && needs.ci.outputs.release == 'true' && (needs.analyze.result == 'success' || needs.analyze.result == 'skipped') - needs: [ci, analyze, unit-test, integration-test, dry-run, feature-test] + if: | + always() && needs.ci.outputs.release == 'true' && + (needs.analyze.result == 'success' || needs.analyze.result == 'skipped') && + (needs.unit-test.result == 'success' || needs.unit-test.result == 'skipped') && + (needs.integration-test.result == 'success' || needs.integration-test.result == 'skipped') && + (needs.dry-run.result == 'success' || needs.dry-run.result == 'skipped') && + (needs.e2e-test.result == 'success' || needs.e2e-test.result == 'skipped') + needs: [ci, analyze, unit-test, integration-test, dry-run, e2e-test] runs-on: ubuntu-latest concurrency: group: cd-${{ github.ref }} diff --git a/__tests__/installer/windows.test.ts b/__tests__/installer/windows.test.ts index 51b7ba3..cf30aad 100644 --- a/__tests__/installer/windows.test.ts +++ b/__tests__/installer/windows.test.ts @@ -1,5 +1,6 @@ import * as path from 'path' import {promises as fs} from 'fs' +import * as os from 'os' import * as exec from '@actions/exec' import * as cache from '@actions/cache' import * as toolCache from '@actions/tool-cache' @@ -27,6 +28,11 @@ describe('windows toolchain installation verification', () => { setupEngineFilePath: path.join('C:', 'Visual Studio', 'setup.exe') } } + const vsEnvs = [ + `UniversalCRTSdkDir=${path.join('C:', 'Windows Kits')}`, + `UCRTVersion=10.0.17063`, + `VCToolsInstallDir=${path.join('C:', 'Visual Studio', 'Tools')}` + ] beforeEach(() => { process.env = {...env} @@ -97,7 +103,14 @@ describe('windows toolchain installation verification', () => { const installer = new WindowsToolchainInstaller(toolchain) installer['visualStudio'] = visualStudio const installation = path.resolve('tool', 'installed', 'path') + jest.spyOn(fs, 'access').mockRejectedValue(new Error()) + jest.spyOn(fs, 'copyFile').mockResolvedValue() jest.spyOn(exec, 'exec').mockResolvedValue(0) + jest.spyOn(exec, 'getExecOutput').mockResolvedValue({ + exitCode: 0, + stdout: vsEnvs.join(os.EOL), + stderr: '' + }) const toolPath = path.join( installation, 'Developer', diff --git a/__tests__/utils/visual_studio.test.ts b/__tests__/utils/visual_studio.test.ts index b0661ce..e946f41 100644 --- a/__tests__/utils/visual_studio.test.ts +++ b/__tests__/utils/visual_studio.test.ts @@ -89,24 +89,6 @@ describe('visual studio setup validation', () => { ) ) }) - - it('tests visual studio setup fails with exit code', async () => { - fsAccessMock() - process.env.VSWHERE_PATH = path.join('C:', 'Visual Studio') - jest.spyOn(exec, 'exec').mockResolvedValue(-1) - jest.spyOn(exec, 'getExecOutput').mockResolvedValue({ - exitCode: 0, - stdout: JSON.stringify([visualStudio]), - stderr: '' - }) - await expect( - vs.setupVisualStudioTools({version: '16', components: ['Component']}) - ).rejects.toMatchObject( - new Error( - `Visual Studio installer failed to install required components with exit code: -1.` - ) - ) - }) }) function fsAccessMock(value = true) { diff --git a/dist/index.js b/dist/index.js index 5eca62f..3a89dd0 100644 --- a/dist/index.js +++ b/dist/index.js @@ -460,12 +460,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.WindowsToolchainInstaller = void 0; -const os = __importStar(__nccwpck_require__(22037)); const path = __importStar(__nccwpck_require__(71017)); const fs_1 = __nccwpck_require__(57147); const core = __importStar(__nccwpck_require__(42186)); const exec_1 = __nccwpck_require__(71514); -const semver = __importStar(__nccwpck_require__(11383)); const verify_1 = __nccwpck_require__(38780); const utils_1 = __nccwpck_require__(11606); class WindowsToolchainInstaller extends verify_1.VerifyingToolchainInstaller { @@ -474,17 +472,13 @@ class WindowsToolchainInstaller extends verify_1.VerifyingToolchainInstaller { download: { get: () => super.download } }); return __awaiter(this, void 0, void 0, function* () { - const reccommended = '10.0.17763'; - const current = os.release(); - const version = semver.gte(current, reccommended) ? current : reccommended; - const winsdk = semver.patch(version); const vsRequirement = { version: '16', components: [ + 'Microsoft.VisualStudio.Component.VC.ATL', + 'Microsoft.VisualStudio.Component.VC.CMake.Project', 'Microsoft.VisualStudio.Component.VC.Tools.x86.x64', - `Microsoft.VisualStudio.Component.Windows10SDK.${winsdk}`, - 'Component.CPython.x64', - 'Microsoft.VisualStudio.Component.VC.CMake.Project' + 'Microsoft.VisualStudio.Component.Windows10SDK' ] }; core.debug(`Using Visual Studio requirement ${vsRequirement}`); @@ -532,9 +526,7 @@ class WindowsToolchainInstaller extends verify_1.VerifyingToolchainInstaller { if (!this.visualStudio) { throw new Error('No supported Visual Studio installation in installer'); } - if (this.version && semver.lt(this.version, '5.4.2')) { - yield (0, utils_1.setupSupportFiles)(this.visualStudio); - } + yield (0, utils_1.setupSupportFiles)(this.visualStudio, installation.sdkroot); const swiftFlags = `-sdk %SDKROOT% -I %SDKROOT%/usr/lib/swift -L %SDKROOT%/usr/lib/swift/windows`; core.exportVariable('SWIFTFLAGS', swiftFlags); }); @@ -558,7 +550,7 @@ class Installation { try { core.debug(`Checking for development snapshot installation`); toolchain = path.join(location, 'Toolchains', '0.0.0+Asserts'); - sdkroot = path.join(location, 'Toolchains', '0.0.0+Asserts'); + sdkroot = path.join(location, 'Platforms', 'Windows.platform', 'Developer', 'SDKs', 'Windows.sdk'); runtime = path.join(location, 'Runtimes', '0.0.0'); yield fs_1.promises.access(toolchain); } @@ -1848,27 +1840,69 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge }; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.getVsWherePath = exports.setupVisualStudioTools = exports.setupSupportFiles = void 0; -const fs_1 = __nccwpck_require__(57147); +const os = __importStar(__nccwpck_require__(22037)); const path = __importStar(__nccwpck_require__(71017)); +const fs_1 = __nccwpck_require__(57147); const io = __importStar(__nccwpck_require__(47351)); const core = __importStar(__nccwpck_require__(42186)); const exec_1 = __nccwpck_require__(71514); /// Do swift version based additional support files setup -function setupSupportFiles(visualStudio) { +function setupSupportFiles(visualStudio, sdkroot) { return __awaiter(this, void 0, void 0, function* () { /// https://docs.microsoft.com/en-us/cpp/build/building-on-the-command-line?view=msvc-170 - const nativeToolsScriptx86 = path.join(visualStudio.installationPath, 'VC\\Auxiliary\\Build\\vcvars32.bat'); - const copyCommands = [ - 'copy /Y %SDKROOT%\\usr\\share\\ucrt.modulemap "%UniversalCRTSdkDir%\\Include\\%UCRTVersion%\\ucrt\\module.modulemap"', - 'copy /Y %SDKROOT%\\usr\\share\\visualc.modulemap "%VCToolsInstallDir%\\include\\module.modulemap"', - 'copy /Y %SDKROOT%\\usr\\share\\visualc.apinotes "%VCToolsInstallDir%\\include\\visualc.apinotes"', - 'copy /Y %SDKROOT%\\usr\\share\\winsdk.modulemap "%UniversalCRTSdkDir%\\Include\\%UCRTVersion%\\um\\module.modulemap"' - ].join('&&'); - const code = yield (0, exec_1.exec)('cmd', ['/k', nativeToolsScriptx86], { - failOnStdErr: true, - input: Buffer.from(copyCommands, 'utf8') - }); - core.debug(`Tried Windows SDK accessible to Swift, exited with code: ${code}`); + const nativeToolsScriptx86 = path.join(visualStudio.installationPath, 'Common7', 'Tools', 'VsDevCmd.bat'); + const { stdout } = yield (0, exec_1.getExecOutput)('cmd', [ + '/k', + nativeToolsScriptx86, + `-arch=${os.arch()}`, + '&&', + 'set', + '&&', + 'exit' + ], { failOnStdErr: true }); + const vsEnvs = Object.fromEntries(stdout + .split(os.EOL) + .filter(s => s.indexOf('=')) + .map(s => s.trim()) + .map(s => s.split('=', 2)) + .filter(s => s.length === 2) + .map(s => [s[0].trim(), s[1].trim()])); + const universalCRTSdkDir = vsEnvs.UniversalCRTSdkDir; + const uCRTVersion = vsEnvs.UCRTVersion; + const vCToolsInstallDir = vsEnvs.VCToolsInstallDir; + if (!(universalCRTSdkDir && uCRTVersion && vCToolsInstallDir)) { + throw new Error(`Failed to find paths from "${JSON.stringify(vsEnvs)}"`); + } + const sdkshare = path.join(sdkroot, 'usr', 'share'); + const winsdk = path.join(universalCRTSdkDir, 'Include', uCRTVersion); + const vcToolsInclude = path.join(vCToolsInstallDir, 'include'); + const vcModulemap = path.join(vcToolsInclude, 'module.modulemap'); + const uCRTmap = path.join(sdkshare, 'ucrt.modulemap'); + const winsdkMap = path.join(sdkshare, 'winsdk.modulemap'); + yield fs_1.promises.copyFile(uCRTmap, path.join(winsdk, 'ucrt', 'module.modulemap')); + yield fs_1.promises.copyFile(winsdkMap, path.join(winsdk, 'um', 'module.modulemap')); + try { + const modulemap = path.join(sdkshare, 'vcruntime.modulemap'); + const runtimenotes = 'vcruntime.apinotes'; + const apinotes = path.join(sdkshare, runtimenotes); + yield fs_1.promises.access(modulemap); + yield fs_1.promises.copyFile(modulemap, vcModulemap); + yield fs_1.promises.copyFile(apinotes, path.join(vcToolsInclude, runtimenotes)); + } + catch (error) { + core.debug(`Using visualc files for copy due to "${error}"`); + const modulemap = path.join(sdkshare, 'visualc.modulemap'); + const runtimenotes = 'visualc.apinotes'; + const apinotes = path.join(sdkshare, runtimenotes); + yield fs_1.promises.copyFile(modulemap, vcModulemap); + yield fs_1.promises.copyFile(apinotes, path.join(vcToolsInclude, runtimenotes)); + } + for (const property in vsEnvs) { + if (vsEnvs[property] === process.env[property]) { + continue; + } + core.exportVariable(property, vsEnvs[property]); + } }); } exports.setupSupportFiles = setupSupportFiles; @@ -1897,16 +1931,15 @@ function setupVisualStudioTools(requirement) { /// https://docs.microsoft.com/en-us/visualstudio/install/use-command-line-parameters-to-install-visual-studio?view=vs-2022 /// install required visual studio components core.debug(`Installing Visual Studio components "${requirement.components}" at "${vs.installationPath}"`); - const code = yield (0, exec_1.exec)(`"${vs.properties.setupEngineFilePath}"`, [ + yield (0, exec_1.exec)(`"${vs.properties.setupEngineFilePath}"`, [ 'modify', '--installPath', vs.installationPath, ...requirement.components.flatMap(component => ['--add', component]), + '--installWhileDownloading', + '--force', '--quiet' ]); - if (code !== 0) { - throw new Error(`Visual Studio installer failed to install required components with exit code: ${code}.`); - } return vs; }); } diff --git a/src/installer/windows.ts b/src/installer/windows.ts index eb0b018..c7692d0 100644 --- a/src/installer/windows.ts +++ b/src/installer/windows.ts @@ -1,9 +1,7 @@ -import * as os from 'os' import * as path from 'path' import {promises as fs} from 'fs' import * as core from '@actions/core' import {exec} from '@actions/exec' -import * as semver from 'semver' import {VerifyingToolchainInstaller} from './verify' import {WindowsToolchainSnapshot} from '../snapshot' import { @@ -17,17 +15,13 @@ export class WindowsToolchainInstaller extends VerifyingToolchainInstaller s.indexOf('=')) + .map(s => s.trim()) + .map(s => s.split('=', 2)) + .filter(s => s.length === 2) + .map(s => [s[0].trim(), s[1].trim()] as const) + ) + const universalCRTSdkDir = vsEnvs.UniversalCRTSdkDir + const uCRTVersion = vsEnvs.UCRTVersion + const vCToolsInstallDir = vsEnvs.VCToolsInstallDir + if (!(universalCRTSdkDir && uCRTVersion && vCToolsInstallDir)) { + throw new Error(`Failed to find paths from "${JSON.stringify(vsEnvs)}"`) + } + + const sdkshare = path.join(sdkroot, 'usr', 'share') + const winsdk = path.join(universalCRTSdkDir, 'Include', uCRTVersion) + const vcToolsInclude = path.join(vCToolsInstallDir, 'include') + const vcModulemap = path.join(vcToolsInclude, 'module.modulemap') + const uCRTmap = path.join(sdkshare, 'ucrt.modulemap') + const winsdkMap = path.join(sdkshare, 'winsdk.modulemap') + await fs.copyFile(uCRTmap, path.join(winsdk, 'ucrt', 'module.modulemap')) + await fs.copyFile(winsdkMap, path.join(winsdk, 'um', 'module.modulemap')) + try { + const modulemap = path.join(sdkshare, 'vcruntime.modulemap') + const runtimenotes = 'vcruntime.apinotes' + const apinotes = path.join(sdkshare, runtimenotes) + await fs.access(modulemap) + await fs.copyFile(modulemap, vcModulemap) + await fs.copyFile(apinotes, path.join(vcToolsInclude, runtimenotes)) + } catch (error) { + core.debug(`Using visualc files for copy due to "${error}"`) + const modulemap = path.join(sdkshare, 'visualc.modulemap') + const runtimenotes = 'visualc.apinotes' + const apinotes = path.join(sdkshare, runtimenotes) + await fs.copyFile(modulemap, vcModulemap) + await fs.copyFile(apinotes, path.join(vcToolsInclude, runtimenotes)) + } + for (const property in vsEnvs) { + if (vsEnvs[property] === process.env[property]) { + continue + } + core.exportVariable(property, vsEnvs[property]) + } } /// set up required visual studio tools for swift on windows @@ -78,18 +131,15 @@ export async function setupVisualStudioTools( core.debug( `Installing Visual Studio components "${requirement.components}" at "${vs.installationPath}"` ) - const code = await exec(`"${vs.properties.setupEngineFilePath}"`, [ + await exec(`"${vs.properties.setupEngineFilePath}"`, [ 'modify', '--installPath', vs.installationPath, ...requirement.components.flatMap(component => ['--add', component]), + '--installWhileDownloading', + '--force', '--quiet' ]) - if (code !== 0) { - throw new Error( - `Visual Studio installer failed to install required components with exit code: ${code}.` - ) - } return vs }