Add infrastructure for setting up GraalVM.

This commit is contained in:
Fabio Niephaus 2022-01-03 08:52:11 +01:00
parent db50d45bd5
commit a686e47055
No known key found for this signature in database
GPG Key ID: F21CF5275F31DFD6
5 changed files with 267 additions and 0 deletions

19
src/constants.ts Normal file
View File

@ -0,0 +1,19 @@
import * as otypes from '@octokit/types'
import {homedir} from 'os'
import {join} from 'path'
export const IS_MACOS = process.platform === 'darwin'
export const IS_WINDOWS = process.platform === 'win32'
export const VERSION_LATEST = 'latest'
export const VERSION_NIGHTLY = 'nightly'
export const VERSION_TRUNK = 'trunk'
export const GRAALVM_BASE = join(homedir(), '.graalvm')
export const GRAALVM_FILE_EXTENSION = IS_WINDOWS ? '.zip' : '.tar.gz'
export const GRAALVM_GH_USER = 'graalvm'
export const GRAALVM_PLATFORM = IS_WINDOWS ? 'windows' : process.platform
export const JDK_HOME_SUFFIX = IS_MACOS ? '/Contents/Home' : ''
export type LatestReleaseResponse =
otypes.Endpoints['GET /repos/{owner}/{repo}/releases/latest']['response']

103
src/graalvm-trunk.ts Normal file
View File

@ -0,0 +1,103 @@
import * as c from './constants'
import * as core from '@actions/core'
import * as tc from '@actions/tool-cache'
import {SpawnSyncOptionsWithStringEncoding, spawnSync} from 'child_process'
import {mkdirP, mv} from '@actions/io'
import {findJavaHomeInSubfolder} from './utils'
import {join} from 'path'
const GRAALVM_TRUNK_DL =
'https://github.com/oracle/graal/archive/refs/heads/master.zip'
const GRAALVM_MX_DL =
'https://github.com/graalvm/mx/archive/refs/heads/master.zip'
const DEFAULT_SUITES = '/compiler,/regex,/sdk,/tools,/truffle'
const GRAAL_REPO_DIR = join(c.GRAALVM_BASE, 'graal')
const VM_DIR = join(GRAAL_REPO_DIR, 'vm')
const MX_DIR = join(c.GRAALVM_BASE, 'mx')
const MX_EXEC = c.IS_WINDOWS ? 'mx.cmd' : 'mx'
const SPAWN_OPTIONS: SpawnSyncOptionsWithStringEncoding = {
cwd: VM_DIR,
encoding: 'utf8',
stdio: 'inherit'
}
const COMPONENTS_TO_SUITE_NAME = new Map<string, string>([
['espresso', '/espresso'],
['js', '/graal-js'],
['llvm-toolchain', '/sulong'],
['native-image', '/substratevm'],
['nodejs', '/graal-nodejs'],
['python', 'graalpython'],
['R', 'fastr'],
['ruby', 'truffleruby'],
['wasm', '/wasm']
])
export async function setUpGraalVMTrunk(
javaVersion: string,
components: string[]
): Promise<string> {
const jdkId = `labsjdk-ce-${javaVersion}`
core.startGroup(`Downloading GraalVM sources, mx, and ${jdkId}...`)
await tc.extractZip(await tc.downloadTool(GRAALVM_TRUNK_DL), c.GRAALVM_BASE)
await mv(join(c.GRAALVM_BASE, 'graal-master'), GRAAL_REPO_DIR)
await tc.extractZip(await tc.downloadTool(GRAALVM_MX_DL), c.GRAALVM_BASE)
await mv(join(c.GRAALVM_BASE, 'mx-master'), MX_DIR)
core.addPath(MX_DIR)
core.debug(`"${MX_DIR}" added to $PATH`)
const labsJDKDir = join(c.GRAALVM_BASE, 'labsjdk')
await mkdirP(labsJDKDir)
spawnSync(
MX_EXEC,
['--java-home=', 'fetch-jdk', '--jdk-id', jdkId, '--to', labsJDKDir],
SPAWN_OPTIONS
)
const labsJDKHome = findJavaHomeInSubfolder(labsJDKDir)
core.exportVariable('JAVA_HOME', labsJDKHome)
core.debug(`$JAVA_HOME set to "${labsJDKHome}"`)
core.endGroup()
const dynamicImports = toSuiteNames(components).join(',')
const mxArgs = [
'--no-download-progress', // avoid cluttering the build log
'--disable-installables=true', // installables not needed
'--force-bash-launchers=true', // disable native launchers
'--exclude-components=LibGraal', // avoid building libgraal to save time
'--dynamicimports',
dynamicImports
]
if (core.isDebug()) {
spawnSync(MX_EXEC, mxArgs.concat('graalvm-show'), SPAWN_OPTIONS)
}
const graalvmHome = spawnSync(MX_EXEC, mxArgs.concat(['graalvm-home']), {
...SPAWN_OPTIONS,
stdio: 'pipe'
})
core.startGroup('Building GraalVM CE from source...')
spawnSync(MX_EXEC, mxArgs.concat(['build']), SPAWN_OPTIONS)
core.endGroup()
const graalvmHomePath = graalvmHome.stdout.trim()
if (core.isDebug()) {
const cmd = c.IS_WINDOWS ? 'dir' : 'ls'
spawnSync(cmd, [graalvmHomePath], {stdio: 'inherit'})
}
return graalvmHomePath
}
function toSuiteNames(components: string[]): string[] {
const names = [DEFAULT_SUITES]
for (const component of components) {
const suiteName = COMPONENTS_TO_SUITE_NAME.get(component)
if (suiteName) {
names.push(suiteName)
} else {
throw new Error(`Unsupported component: ${component}`)
}
}
return names
}

51
src/graalvm.ts Normal file
View File

@ -0,0 +1,51 @@
import * as c from './constants'
import {downloadAndExtractJDK, getLatestRelease} from './utils'
const GRAALVM_CE_DL_BASE =
'https://github.com/graalvm/graalvm-ce-builds/releases/download'
const GRAALVM_REPO_NIGHTLY = 'graalvm-ce-dev-builds'
const GRAALVM_REPO_RELEASES = 'graalvm-ce-builds'
const GRAALVM_TAG_PREFIX = 'vm-'
export async function setUpGraalVMLatest(javaVersion: string): Promise<string> {
const latestRelease = await getLatestRelease(GRAALVM_REPO_RELEASES)
const tag_name = latestRelease.tag_name
if (tag_name.startsWith(GRAALVM_TAG_PREFIX)) {
const latestVersion = tag_name.substring(
GRAALVM_TAG_PREFIX.length,
tag_name.length
)
return setUpGraalVMRelease(latestVersion, javaVersion)
}
throw new Error(`Could not find latest GraalVM release: ${tag_name}`)
}
export async function setUpGraalVMNightly(
javaVersion: string
): Promise<string> {
const latestNightly = await getLatestRelease(GRAALVM_REPO_NIGHTLY)
const graalVMIdentifier = determineGraalVMIdentifier('dev', javaVersion)
const expectedFileName = `${graalVMIdentifier}${c.GRAALVM_FILE_EXTENSION}`
for (const asset of latestNightly.assets) {
if (asset.name === expectedFileName) {
return downloadAndExtractJDK(asset.browser_download_url)
}
}
throw new Error('Could not find GraalVM nightly build')
}
export async function setUpGraalVMRelease(
version: string,
javaVersion: string
): Promise<string> {
const graalVMIdentifier = determineGraalVMIdentifier(version, javaVersion)
const downloadUrl = `${GRAALVM_CE_DL_BASE}/${GRAALVM_TAG_PREFIX}${version}/${graalVMIdentifier}${c.GRAALVM_FILE_EXTENSION}`
return downloadAndExtractJDK(downloadUrl)
}
function determineGraalVMIdentifier(
version: string,
javaVersion: string
): string {
return `graalvm-ce-java${javaVersion}-${c.GRAALVM_PLATFORM}-amd64-${version}`
}

View File

@ -1,9 +1,48 @@
import * as c from './constants'
import * as core from '@actions/core' import * as core from '@actions/core'
import * as graalvm from './graalvm'
import {join} from 'path'
import {mkdirP} from '@actions/io'
import {setUpGraalVMTrunk} from './graalvm-trunk'
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const graalvmVersion: string = core.getInput('version', {required: true})
const javaVersion: string = core.getInput('java-version', {required: true})
const componentsString: string = core.getInput('components')
const components: string[] =
componentsString.length > 0 ? componentsString.split(',') : []
const setJavaHome = core.getInput('set-java-home') === 'true'
await mkdirP(c.GRAALVM_BASE)
// Download or build GraalVM
let graalVMHome let graalVMHome
switch (graalvmVersion) {
case c.VERSION_LATEST:
graalVMHome = await graalvm.setUpGraalVMLatest(javaVersion)
break
case c.VERSION_NIGHTLY:
graalVMHome = await graalvm.setUpGraalVMNightly(javaVersion)
break
case c.VERSION_TRUNK:
graalVMHome = await setUpGraalVMTrunk(javaVersion, components)
break
default:
graalVMHome = await graalvm.setUpGraalVMRelease(
graalvmVersion,
javaVersion
)
break
}
// Activate GraalVM
core.debug(`Activating GraalVM located at '${graalVMHome}'...`)
core.exportVariable('GRAALVM_HOME', graalVMHome) core.exportVariable('GRAALVM_HOME', graalVMHome)
core.addPath(join(graalVMHome, 'bin'))
if (setJavaHome) {
core.exportVariable('JAVA_HOME', graalVMHome)
}
} catch (error) { } catch (error) {
if (error instanceof Error) core.setFailed(error.message) if (error instanceof Error) core.setFailed(error.message)
} }

55
src/utils.ts Normal file
View File

@ -0,0 +1,55 @@
import * as c from './constants'
import * as core from '@actions/core'
import * as httpClient from '@actions/http-client'
import * as tc from '@actions/tool-cache'
import {Octokit} from '@octokit/core'
import {join} from 'path'
import {readdirSync} from 'fs'
// Set up Octokit in the same way as @actions/github (see https://git.io/Jy9YP)
const baseUrl = process.env['GITHUB_API_URL'] || 'https://api.github.com'
const GitHub = Octokit.defaults({
baseUrl,
request: {
agent: new httpClient.HttpClient().getAgent(baseUrl)
}
})
export async function getLatestRelease(
repo: string
): Promise<c.LatestReleaseResponse['data']> {
const githubToken = core.getInput('github-token')
const options = githubToken.length > 0 ? {auth: githubToken} : {}
const octokit = new GitHub(options)
return (
await octokit.request('GET /repos/{owner}/{repo}/releases/latest', {
owner: c.GRAALVM_GH_USER,
repo
})
).data
}
export async function downloadAndExtractJDK(
downloadUrl: string
): Promise<string> {
const downloadPath = await tc.downloadTool(downloadUrl)
if (downloadUrl.endsWith('.tar.gz')) {
await tc.extractTar(downloadPath, c.GRAALVM_BASE)
} else if (downloadUrl.endsWith('.zip')) {
await tc.extractZip(downloadPath, c.GRAALVM_BASE)
} else {
throw new Error(`Unexpected filetype downloaded: ${downloadUrl}`)
}
return findJavaHomeInSubfolder(c.GRAALVM_BASE)
}
export function findJavaHomeInSubfolder(searchPath: string): string {
const baseContents = readdirSync(searchPath)
if (baseContents.length === 1) {
return join(searchPath, baseContents[0], c.JDK_HOME_SUFFIX)
} else {
throw new Error(
`Unexpected amount of directory items found: ${baseContents.length}`
)
}
}