diff --git a/packages/mambajs/src/solver.ts b/packages/mambajs/src/solver.ts index 74a5cb6..b05182a 100644 --- a/packages/mambajs/src/solver.ts +++ b/packages/mambajs/src/solver.ts @@ -19,6 +19,29 @@ export interface ISolveOptions { nRetries?: number; } +const VIRTUAL_PACKAGES = { + __unix: makeVirtualPackage('__unix'), + __linux: makeVirtualPackage('__linux'), + __glibc: makeVirtualPackage('__glibc', '2.25'), + __osx: makeVirtualPackage('__osx'), + __win: makeVirtualPackage('__win'), + __archspec: makeVirtualPackage('__archspec') +}; + +function makeVirtualPackage(name: string, version='0') { + return { + filename: name, + packageName: name, + repoName: '@', + version, + build: '@', + subdir: '@', + md5: '@', + sha256: '@', + url: 'https://prefix.dev/@/@' + }; +} + export const solveConda = async (options: ISolveOptions): Promise => { const { ymlOrSpecs, currentLock, logger } = options; const platform = options.platform ?? DEFAULT_PLATFORM; @@ -48,6 +71,50 @@ export const solveConda = async (options: ISolveOptions): Promise => { logger.log('Solving environment...'); } + const installedPackages = Object.keys(installedCondaPackages).map( + (filename: string) => { + // Turn mambajs lock definition into what rattler expects + const installedPkg = installedCondaPackages[filename]; + return { + filename, + packageName: installedPkg.name, + repoName: installedPkg.channel, + version: installedPkg.version, + build: installedPkg.build, + subdir: installedPkg.subdir, + md5: installedPkg.hash?.md5, + sha256: installedPkg.hash?.sha256, + url: computePackageUrl( + installedPkg, + filename, + formattedChannels.channelInfo + ) + }; + } + ); + + // Inject virtual packages + if ( + platform.includes('wasm') || + platform.includes('linux') || + platform.includes('osx') || + platform.includes('zos') || + platform.includes('freebsd') + ) { + installedPackages.push(VIRTUAL_PACKAGES['__unix']); + } + if (platform.includes('linux')) { + installedPackages.push(VIRTUAL_PACKAGES['__linux']); + installedPackages.push(VIRTUAL_PACKAGES['__glibc']); + } + if (platform.includes('osx')) { + installedPackages.push(VIRTUAL_PACKAGES['__osx']); + } + if (platform.includes('win')) { + installedPackages.push(VIRTUAL_PACKAGES['__win']); + } + installedPackages.push(VIRTUAL_PACKAGES['__archspec']); + try { const startSolveTime = performance.now(); @@ -59,25 +126,7 @@ export const solveConda = async (options: ISolveOptions): Promise => { return formattedChannels.channelInfo[channelName][0].url; }), ['noarch', platform], - Object.keys(installedCondaPackages).map((filename: string) => { - // Turn mambajs lock definition into what rattler expects - const installedPkg = installedCondaPackages[filename]; - return { - filename, - packageName: installedPkg.name, - repoName: installedPkg.channel, - version: installedPkg.version, - build: installedPkg.build, - subdir: installedPkg.subdir, - md5: installedPkg.hash?.md5, - sha256: installedPkg.hash?.sha256, - url: computePackageUrl( - installedPkg, - filename, - formattedChannels.channelInfo - ) - }; - }) + installedPackages )) as SolvedPackage[]; const endSolveTime = performance.now(); @@ -99,6 +148,10 @@ export const solveConda = async (options: ISolveOptions): Promise => { sha256 } = item; + if (filename in VIRTUAL_PACKAGES) { + return; + } + const hash: ILock['packages'][string]['hash'] = {}; if (md5) hash.md5 = md5; if (sha256) hash.sha256 = sha256; diff --git a/unittests/tests/conda/test-virtual-packages.ts b/unittests/tests/conda/test-virtual-packages.ts new file mode 100644 index 0000000..93222c1 --- /dev/null +++ b/unittests/tests/conda/test-virtual-packages.ts @@ -0,0 +1,92 @@ +import { ISolvedPackage, solve } from "../../../packages/mambajs/src"; +import { TestLogger } from "../../helpers"; +import { expect } from 'earl'; + +const logger = new TestLogger(); + +let yml = ` +channels: + - https://prefix.dev/emscripten-forge-dev + - https://prefix.dev/conda-forge +dependencies: + # click requires __unix + - click>=8.3.1 +`; + +solve({ymlOrSpecs: yml, logger}).then(async result => { + const packageNames = Object.values(result.packages).map(pkg => pkg.name); + + // Index by package name for convenienve + const condaPackages: { [key: string]: ISolvedPackage } = {}; + Object.keys(result.packages).map(filename => { + condaPackages[result.packages[filename].name] = + result.packages[filename]; + }); + + expect(packageNames).toInclude('click'); +}); + +yml = ` +channels: + - https://prefix.dev/emscripten-forge-4x + - https://prefix.dev/conda-forge +dependencies: + # click requires __unix + - click>=8.3.1 +`; + +solve({ymlOrSpecs: yml, logger}).then(async result => { + const packageNames = Object.values(result.packages).map(pkg => pkg.name); + + // Index by package name for convenienve + const condaPackages: { [key: string]: ISolvedPackage } = {}; + Object.keys(result.packages).map(filename => { + condaPackages[result.packages[filename].name] = + result.packages[filename]; + }); + + expect(packageNames).toInclude('click'); +}); + +yml = ` +channels: + - https://prefix.dev/conda-forge +dependencies: + # click requires __unix + - click>=8.3.1 +`; + +solve({ymlOrSpecs: yml, logger, platform: "linux-64"}).then(async result => { + const packageNames = Object.values(result.packages).map(pkg => pkg.name); + + // Index by package name for convenienve + const condaPackages: { [key: string]: ISolvedPackage } = {}; + Object.keys(result.packages).map(filename => { + condaPackages[result.packages[filename].name] = + result.packages[filename]; + }); + + expect(packageNames).toInclude('click'); +}); + +yml = ` +channels: + - https://prefix.dev/conda-forge +dependencies: + # oracle-instant-client requires __glibc + - oracle-instant-client +`; + +solve({ymlOrSpecs: yml, logger, platform: "linux-64"}).then(async result => { + const packageNames = Object.values(result.packages).map(pkg => pkg.name); + + // Index by package name for convenienve + const condaPackages: { [key: string]: ISolvedPackage } = {}; + Object.keys(result.packages).map(filename => { + condaPackages[result.packages[filename].name] = + result.packages[filename]; + }); + + expect(packageNames).toInclude('oracle-instant-client'); +}); +