Skip to content

Commit

Permalink
Merge pull request #1900 from posit-dev/test/windows
Browse files Browse the repository at this point in the history
Barebones work for R discovery on Windows
  • Loading branch information
DavisVaughan authored Dec 7, 2023
2 parents 5906752 + ca20255 commit 93bf393
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 44 deletions.
1 change: 1 addition & 0 deletions build/filters.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,7 @@ module.exports.indentationFilter = [

// --- Start Positron ---
'!**/amalthea/**/*',
'!extensions/positron-r/resources/scripts/*.R'
// --- End Positron ---
];

Expand Down
13 changes: 6 additions & 7 deletions extensions/positron-r/scripts/install-kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,13 +228,6 @@ async function downloadAndReplaceArk(version: string,
}

async function main() {

// Temporarily skip downloading ARK on Windows until we have a Windows build.
if (platform() === 'win32') {
console.warn('ARK is not yet supported on Windows. Skipping download.');
return;
}

// Before we do any work, check to see if there is a locally built copy of Amalthea in the
// `amalthea / target` directory. If so, we'll assume that the user is a kernel developer
// and skip the download; this version will take precedence over any downloaded version.
Expand Down Expand Up @@ -276,6 +269,12 @@ async function main() {
`checking downloaded version.`);
}

// TODO: Remove once we have ark releases
if (platform() === 'win32') {
console.warn('On Windows, only locally built versions of Ark are currently supported.');
return;
}

const packageJsonVersion = await getVersionFromPackageJson();
const localArkVersion = await getLocalArkVersion();

Expand Down
11 changes: 7 additions & 4 deletions extensions/positron-r/src/kernel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (C) 2023 Posit Software, PBC. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import * as os from 'os';
import * as vscode from 'vscode';

/**
Expand All @@ -25,11 +26,13 @@ export function getArkKernelPath(context: vscode.ExtensionContext): string | und
return kernelPath;
}

const kernelName = os.platform() === 'win32' ? 'ark.exe' : 'ark';

// No kernel path specified; try the default (embedded) kernel. This is where the kernel
// is placed in release builds.
const path = require('path');
const fs = require('fs');
const embeddedKernel = path.join(context.extensionPath, 'dist', 'bin', 'ark');
const embeddedKernel = path.join(context.extensionPath, 'dist', 'bin', kernelName);
if (fs.existsSync(embeddedKernel)) {
return embeddedKernel;
}
Expand All @@ -39,8 +42,8 @@ export function getArkKernelPath(context: vscode.ExtensionContext): string | und
// by developers, who have `positron` and `amalthea` directories side-by-side.
let devKernel = undefined;
const positronParent = path.dirname(path.dirname(path.dirname(context.extensionPath)));
const devDebugKernel = path.join(positronParent, 'amalthea', 'target', 'debug', 'ark');
const devReleaseKernel = path.join(positronParent, 'amalthea', 'target', 'release', 'ark');
const devDebugKernel = path.join(positronParent, 'amalthea', 'target', 'debug', kernelName);
const devReleaseKernel = path.join(positronParent, 'amalthea', 'target', 'release', kernelName);
const debugModified = fs.statSync(devDebugKernel, { throwIfNoEntry: false })?.mtime;
const releaseModified = fs.statSync(devReleaseKernel, { throwIfNoEntry: false })?.mtime;

Expand All @@ -55,7 +58,7 @@ export function getArkKernelPath(context: vscode.ExtensionContext): string | und

// Finally, look for a local copy of the kernel in our `resources` directory. This is
// where the kernel is placed by the `install-kernel` script in development builds.
const localKernel = path.join(context.extensionPath, 'resources', 'ark', 'ark');
const localKernel = path.join(context.extensionPath, 'resources', 'ark', kernelName);
if (fs.existsSync(localKernel)) {
return localKernel;
}
Expand Down
54 changes: 40 additions & 14 deletions extensions/positron-r/src/provider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,21 @@ export async function* rRuntimeDiscoverer(
binaries.add(b);
}

// make sure we include R executable found on the PATH
// we've probably already discovered it, but we still need to single it out, so that we mark
// that particular R installation as the current one
const whichR = await which('R', { nothrow: true }) as string;
if (whichR) {
const whichRCanonical = fs.realpathSync(whichR);
rInstallations.push(new RInstallation(whichRCanonical, true));
binaries.delete(whichRCanonical);
// TODO: Windows
// On Windows this finds `C:\Program Files\R\bin\R.BAT`, a batch file that starts
// the underlying version of R, i.e. try right clicking and editing the batch file to see the
// underlying path it ends up using. We will need some way to associate this with the `R.exe`
// file it ends up starting.
if (os.platform() !== 'win32') {
// make sure we include R executable found on the PATH
// we've probably already discovered it, but we still need to single it out, so that we mark
// that particular R installation as the current one
const whichR = await which('R', { nothrow: true }) as string;
if (whichR) {
const whichRCanonical = fs.realpathSync(whichR);
rInstallations.push(new RInstallation(whichRCanonical, true));
binaries.delete(whichRCanonical);
}
}

binaries.forEach((b: string) => {
Expand Down Expand Up @@ -159,6 +166,22 @@ export async function* rRuntimeDiscoverer(

}

if (process.platform === 'win32') {
// On Windows, we must place the `bin/` path for the current R version on the PATH
// so that the DLLs in that same folder can be resolved properly when ark starts up
// (like `R.dll`, `Rblas.dll`, `Rgraphapp.dll`, `Riconv.dll`, and `Rlapack.dll`).
// https://learn.microsoft.com/en-us/windows/win32/dlls/dynamic-link-library-search-order#standard-search-order-for-unpackaged-apps
const binpath = path.join(rHome.homepath, 'bin', 'x64');

const processPath = process.env['PATH'];

const subprocessPath = processPath === undefined ?
binpath :
processPath + ';' + binpath;

env['PATH'] = subprocessPath;
}

// Is the runtime path within the user's home directory?
const homedir = os.homedir();
const isUserInstallation = rHome.homepath.startsWith(homedir);
Expand Down Expand Up @@ -297,23 +320,25 @@ export async function getRunningRRuntime(runtimes: Map<string, RRuntime>): Promi
function rHeadquarters(): string {
switch (process.platform) {
case 'darwin':
return '/Library/Frameworks/R.framework/Versions';
return path.join('/Library', 'Frameworks', 'R.framework', 'Versions');
case 'linux':
return '/opt/R';
return path.join('/opt', 'R');
case 'win32':
return path.join('C:\\', 'Program Files', 'R');
default:
// TODO: handle Windows
throw new Error('Unsupported platform');
}
}

function binFragment(version: string): string {
switch (process.platform) {
case 'darwin':
return `${version}/Resources/bin/R`;
return path.join(version, 'Resources', 'bin', 'R');
case 'linux':
return `${version}/bin/R`;
return path.join(version, 'bin', 'R');
case 'win32':
return path.join(version, 'bin', 'R.exe');
default:
// TODO: handle Windows
throw new Error('Unsupported platform');
}
}
Expand Down Expand Up @@ -379,6 +404,7 @@ function isRStudioUser(): boolean {
* @returns The path to RStudio's state folder directory.
*/
function rstudioStateFolderPath(pathToAppend = ''): string {
// TODO: Windows
const newPath = path.join(process.env.HOME!, '.local/share/rstudio', pathToAppend);
return newPath;
}
45 changes: 26 additions & 19 deletions extensions/positron-r/src/r-installation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
* Copyright (C) 2023 Posit Software, PBC. All rights reserved.
*--------------------------------------------------------------------------------------------*/

import * as os from 'os';
import * as semver from 'semver';
import * as path from 'path';
import * as fs from 'fs';
Expand Down Expand Up @@ -42,25 +43,30 @@ export class RInstallation {
this.binpath = pth;
this.current = current;

const binLines = readLines(this.binpath);
const re = new RegExp('Shell wrapper for R executable');
if (!binLines.some(x => re.test(x))) {
Logger.info('Binary is not a shell script wrapping the executable');
return;
}
const targetLine = binLines.find(line => line.match('R_HOME_DIR'));
if (!targetLine) {
Logger.info('Can\'t determine R_HOME_DIR from the binary');
return;
}
// macOS: R_HOME_DIR=/Library/Frameworks/R.framework/Versions/4.3-arm64/Resources
// macOS non-orthogonal: R_HOME_DIR=/Library/Frameworks/R.framework/Resources
// linux: R_HOME_DIR=/opt/R/4.2.3/lib/R
const R_HOME_DIR = extractValue(targetLine, 'R_HOME_DIR');
this.homepath = R_HOME_DIR;
if (this.homepath === '') {
Logger.info('Can\'t determine R_HOME_DIR from the binary');
return;
if (os.platform() === 'win32') {
// TODO: Windows - do we want something more robust here?
this.homepath = path.join(pth, '..', '..');
} else {
const binLines = readLines(this.binpath);
const re = new RegExp('Shell wrapper for R executable');
if (!binLines.some(x => re.test(x))) {
Logger.info('Binary is not a shell script wrapping the executable');
return;
}
const targetLine = binLines.find(line => line.match('R_HOME_DIR'));
if (!targetLine) {
Logger.info('Can\'t determine R_HOME_DIR from the binary');
return;
}
// macOS: R_HOME_DIR=/Library/Frameworks/R.framework/Versions/4.3-arm64/Resources
// macOS non-orthogonal: R_HOME_DIR=/Library/Frameworks/R.framework/Resources
// linux: R_HOME_DIR=/opt/R/4.2.3/lib/R
const R_HOME_DIR = extractValue(targetLine, 'R_HOME_DIR');
this.homepath = R_HOME_DIR;
if (this.homepath === '') {
Logger.info('Can\'t determine R_HOME_DIR from the binary');
return;
}
}

// orthogonality is a concern specific to macOS
Expand Down Expand Up @@ -89,6 +95,7 @@ export class RInstallation {
// macOS arm64: Built: R 4.3.1; aarch64-apple-darwin20; 2023-06-16 21:52:54 UTC; unix
// macOS intel: Built: R 4.3.1; x86_64-apple-darwin20; 2023-06-16 21:51:34 UTC; unix
// linux: Built: R 4.2.3; x86_64-pc-linux-gnu; 2023-03-15 09:03:13 UTC; unix
// windows: Built: R 4.3.2; x86_64-w64-mingw32; 2023-10-31 13:57:45 UTC; windows
const builtField = extractValue(targetLine2, 'Built', ':');
const builtParts = builtField.split(new RegExp(';\\s+'));

Expand Down

0 comments on commit 93bf393

Please sign in to comment.