| // Copyright (c) 2020, Compiler Explorer Authors |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are met: |
| // |
| // * Redistributions of source code must retain the above copyright notice, |
| // this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above copyright |
| // notice, this list of conditions and the following disclaimer in the |
| // documentation and/or other materials provided with the distribution. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE |
| // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| // POSSIBILITY OF SUCH DAMAGE. |
| |
| import path from 'path'; |
| import zlib from 'zlib'; |
| |
| import fs from 'fs-extra'; |
| import request from 'request'; |
| import tar from 'tar-stream'; |
| import _ from 'underscore'; |
| |
| import { logger } from '../logger'; |
| |
| import { BuildEnvSetupBase } from './base'; |
| |
| export class BuildEnvSetupCeConanDirect extends BuildEnvSetupBase { |
| static get key() { return 'ceconan'; } |
| |
| constructor(compilerInfo, env, execCompilerCachedFunc) { |
| super(compilerInfo, env, execCompilerCachedFunc); |
| |
| this.host = compilerInfo.buildenvsetup.props('host', false); |
| this.onlyonstaticliblink = compilerInfo.buildenvsetup.props('onlyonstaticliblink', false); |
| |
| if (env.debug) request.debug = true; |
| } |
| |
| async getAllPossibleBuilds(libid, version) { |
| return new Promise((resolve, reject) => { |
| const encLibid = encodeURIComponent(libid); |
| const encVersion = encodeURIComponent(version); |
| const url = `${this.host}/v1/conans/${encLibid}/${encVersion}/${encLibid}/${encVersion}/search`; |
| const settings = { |
| method: 'GET', |
| json: true, |
| }; |
| |
| request(url, settings, (err, res, body) => { |
| if (res.statusCode === 404) { |
| reject(`Not found (${url})`); |
| } else if (err) { |
| reject(err); |
| } else { |
| resolve(body); |
| } |
| }); |
| }); |
| } |
| |
| async getPackageUrl(libid, version, hash) { |
| return new Promise((resolve, reject) => { |
| const encLibid = encodeURIComponent(libid); |
| const encVersion = encodeURIComponent(version); |
| const libUrl = `${this.host}/v1/conans/${encLibid}/${encVersion}/${encLibid}/${encVersion}`; |
| const url = `${libUrl}/packages/${hash}/download_urls`; |
| |
| const settings = { |
| method: 'GET', |
| json: true, |
| }; |
| |
| request(url, settings, (err, res, body) => { |
| if (err) { |
| reject(err); |
| return; |
| } |
| |
| resolve(body['conan_package.tgz']); |
| }); |
| }); |
| } |
| |
| async downloadAndExtractPackage(libId, version, downloadPath, packageUrl) { |
| return new Promise((resolve) => { |
| const startTime = process.hrtime.bigint(); |
| const extract = tar.extract(); |
| const gunzip = zlib.createGunzip(); |
| |
| extract.on('entry', (header, stream, next) => { |
| const filename = path.basename(header.name); |
| const filestream = fs.createWriteStream(path.join(downloadPath, filename)); |
| stream.pipe(filestream); |
| stream.on('end', next); |
| stream.resume(); |
| }); |
| |
| extract.on('finish', () => { |
| const endTime = process.hrtime.bigint(); |
| resolve({ |
| step: `Download of ${libId} ${version}`, |
| packageUrl: packageUrl, |
| time: ((endTime - startTime) / BigInt(1000000)).toString(), |
| }); |
| }); |
| |
| gunzip.pipe(extract); |
| |
| const settings = { |
| method: 'GET', |
| encoding: null, |
| }; |
| |
| request(packageUrl, settings).pipe(gunzip); |
| }); |
| } |
| |
| async getConanBuildProperties(key) { |
| const arch = this.getTarget(key); |
| const libcxx = this.getLibcxx(key); |
| const stdver = ''; |
| const flagcollection = ''; |
| |
| return { |
| os: 'Linux', |
| build_type: 'Debug', |
| compiler: this.compilerTypeOrGCC, |
| 'compiler.version': this.compiler.id, |
| 'compiler.libcxx': libcxx, |
| arch: arch, |
| stdver: stdver, |
| flagcollection: flagcollection, |
| }; |
| } |
| |
| async findMatchingHash(buildProperties, possibleBuilds) { |
| return _.findKey(possibleBuilds, (elem) => { |
| return _.all(buildProperties, (val, key) => { |
| return val === elem.settings[key]; |
| }); |
| }); |
| } |
| |
| async download(key, dirPath, libraryDetails) { |
| const allDownloads = []; |
| const allLibraryBuilds = []; |
| |
| _.each(libraryDetails, (details, libId) => { |
| if (this.hasBinariesToLink(details)) { |
| const lookupversion = details.lookupversion ? details.lookupversion : details.version; |
| allLibraryBuilds.push({ |
| id: libId, |
| version: details.version, |
| lookupversion: details.lookupversion, |
| possibleBuilds: this.getAllPossibleBuilds(libId, lookupversion).catch(() => false), |
| }); |
| } |
| }); |
| |
| const buildProperties = await this.getConanBuildProperties(key); |
| |
| for (const libVerBuilds of allLibraryBuilds) { |
| const lookupversion = libVerBuilds.lookupversion ? libVerBuilds.lookupversion : libVerBuilds.version; |
| const libVer = `${libVerBuilds.id}/${lookupversion}`; |
| const possibleBuilds = await libVerBuilds.possibleBuilds; |
| if (possibleBuilds) { |
| const hash = await this.findMatchingHash(buildProperties, possibleBuilds); |
| if (hash) { |
| logger.debug(`Found conan hash ${hash} for ${libVer}`); |
| allDownloads.push( |
| this.getPackageUrl(libVerBuilds.id, lookupversion, hash).then((downloadUrl) => { |
| return this.downloadAndExtractPackage(libVerBuilds.id, lookupversion, dirPath, downloadUrl); |
| }), |
| ); |
| } else { |
| logger.info(`No build found for ${libVer} matching ${JSON.stringify(buildProperties)}`); |
| } |
| } else { |
| logger.info(`Library ${libVer} not available`); |
| } |
| } |
| |
| return Promise.all(allDownloads); |
| } |
| |
| async setup(key, dirPath, libraryDetails) { |
| if (this.host && (!this.onlyonstaticliblink || this.hasAtLeastOneBinaryToLink(libraryDetails))) { |
| return this.download(key, dirPath, libraryDetails); |
| } else { |
| return Promise.resolve(); |
| } |
| } |
| |
| hasBinariesToLink(details) { |
| return (details.libpath.length === 0) && ((details.staticliblink.length > 0) || (details.liblink.length > 0)); |
| } |
| |
| hasAtLeastOneBinaryToLink(libraryDetails) { |
| return _.some(libraryDetails, (details) => this.hasBinariesToLink(details)); |
| } |
| } |