Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove strict dependency on Node v6.10.x #1139

Merged
merged 5 commits into from
Apr 10, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 3 additions & 42 deletions build.proj
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
<RepoRootDirectory>$(MSBuildThisFileDirectory)</RepoRootDirectory>
<SdkDirectory>$(RepoRootDirectory)\sdk\</SdkDirectory>
<NodeJSSdkDirectory>$(SdkDirectory)\nodejs\</NodeJSSdkDirectory>
<NativeRuntimeModuleDirectory>$(NodeJSSdkDirectory)\runtime\native\</NativeRuntimeModuleDirectory>
<NodeVersion>6.10.2</NodeVersion>
<NodeArch>x64</NodeArch>
<TestParallelism>10</TestParallelism>
<MSVSVersion>2017</MSVSVersion>
<PulumiRoot Condition="'$(PulumiRoot)' == ''">C:\Pulumi\</PulumiRoot>
Expand All @@ -23,28 +20,6 @@
WorkingDirectory="$(NodeJSSdkDirectory)" />
</Target>

<Target Name="EnsureCustomNode"
Condition="!Exists('$(NodeJSSdkDirectory)\custom_node\node\node.exe')">
<Exec Command="&quot;$(NodeJSSdkDirectory)\scripts\download_node.cmd&quot;"
WorkingDirectory="$(NodeJSSdkDirectory)" />
</Target>

<Target Name="ConfigureNativeRuntimeModule"
DependsOnTargets="EnsureNodeDependencies">
<Exec Command="&quot;$(NativeRuntimeModuleDirectory)\ensure_node_v8.cmd&quot;"
WorkingDirectory="$(NativeRuntimeModuleDirectory)" />
<Exec Command="&quot;$(NodeJSSdkDirectory)\node_modules\.bin\node-gyp.cmd&quot; configure --msvs_version $(MSVSVersion) --devdir &quot;$(NativeRuntimeModuleDirectory)\node_dev&quot;"
WorkingDirectory="$(NativeRuntimeModuleDirectory)" />
<Copy SourceFiles="$(NodeJSSdkDirectory)\custom_node\node\node.lib"
DestinationFiles="$(NativeRuntimeModuleDirectory)\node_dev\$(NodeVersion)\$(NodeArch)\node.lib" />
</Target>

<Target Name="BuildNativeRuntimeModule"
DependsOnTargets="ConfigureNativeRuntimeModule">
<Exec Command="&quot;$(NodeJSSdkDirectory)\node_modules\.bin\node-gyp.cmd&quot; build --msvs_version $(MSVSVersion) --devdir &quot;$(NativeRuntimeModuleDirectory)\node_dev&quot;"
WorkingDirectory="$(NativeRuntimeModuleDirectory)" />
</Target>

<Target Name="TypeScriptCompileNodeSdk">
<Exec Command="&quot;$(MSBuildThisFileDirectory)\scripts\get-version.cmd&quot;" ConsoleToMSBuild="true" Condition="'$(Version)' == ''">
<Output TaskParameter="ConsoleOutput" PropertyName="Version" />
Expand Down Expand Up @@ -77,17 +52,6 @@
DestinationFolder="$(NodeJSSdkDirectory)\bin\proto" />
</Target>

<Target Name="BinPlaceNodeSdkNativeRuntimeModule"
DependsOnTargets="BuildNativeRuntimeModule">
<ItemGroup>
<NodeSdkNativeRuntimeModuleFiles Include="$(NativeRuntimeModuleDirectory)\build\Release\nativeruntime-v0.11.0.node" />
<NodeSdkNativeRuntimeModuleFiles Include="$(NativeRuntimeModuleDirectory)\build\Release\nativeruntime-v0.11.0.pdb" />
</ItemGroup>

<Copy SourceFiles="@(NodeSdkNativeRuntimeModuleFiles)"
DestinationFolder="$(NodeJSSdkDirectory)\bin\runtime\native\build\Release" />
</Target>

<Target Name="BinPlaceNodeSdkTestData">
<ItemGroup>
<NodeSdkTestDataFiles Include="$(NodeJSSdkDirectory)\tests\runtime\langhost\cases\**\*" />
Expand All @@ -103,16 +67,13 @@
</Target>

<Target Name="BinPlaceNodeSdk"
DependsOnTargets="BinPlaceNodeSdkProtos;BinPlaceNodeSdkNativeRuntimeModule;BinPlaceNodeSdkTestData;YarnLinkSdk">
<Copy SourceFiles="$(NodeJSSdkDirectory)\custom_node\node\node.exe" DestinationFiles="$(PulumiBin)\pulumi-language-nodejs-node.exe" />
DependsOnTargets="BinPlaceNodeSdkProtos;BinPlaceNodeSdkTestData;YarnLinkSdk">
<Copy SourceFiles="$(NodeJSSdkDirectory)\dist\pulumi-language-nodejs-exec.cmd" DestinationFolder="$(PulumiBin)" />
<Copy SourceFiles="$(NodeJSSdkDirectory)\dist\pulumi-resource-pulumi-nodejs.cmd" DestinationFolder="$(PulumiBin)" />
<Copy SourceFiles="$(NodeJSSdkDirectory)\bin\runtime\native\build\Release\nativeruntime-v0.11.0.node" DestinationFolder="$(PulumiBin)\v$(NodeVersion)" />
<Copy SourceFiles="$(NodeJSSdkDirectory)\bin\runtime\native\build\Release\nativeruntime-v0.11.0.pdb" DestinationFolder="$(PulumiBin)\v$(NodeVersion)" />
</Target>

<Target Name="BuildNodeSdk"
DependsOnTargets="BuildNativeRuntimeModule;TypeScriptCompileNodeSdk;GoCompileNodeSdk;BinPlaceNodeSdk">
DependsOnTargets="TypeScriptCompileNodeSdk;GoCompileNodeSdk;BinPlaceNodeSdk">
</Target>

<Target Name="BuildGoCmds">
Expand All @@ -129,7 +90,7 @@
</Target>

<Target Name="Build"
DependsOnTargets="EnsureGoDependencies;EnsureNodeDependencies;EnsureCustomNode;BuildNodeSdk;BuildGoCmds">
DependsOnTargets="EnsureGoDependencies;EnsureNodeDependencies;BuildNodeSdk;BuildGoCmds">
</Target>

<Target Name="IntegrationTest">
Expand Down
6 changes: 0 additions & 6 deletions scripts/make_release.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,6 @@ CopyPackage "$Root\sdk\nodejs\bin" "pulumi"

Copy-Item "$Root\sdk\nodejs\dist\pulumi-language-nodejs-exec.cmd" "$PublishDir\bin"
Copy-Item "$Root\sdk\nodejs\dist\pulumi-resource-pulumi-nodejs.cmd" "$PublishDir\bin"
Copy-Item "$Root\sdk\nodejs\custom_node\node\node.exe" "$PublishDir\bin\pulumi-language-nodejs-node.exe"

$NodeFolder = "$PublishDir\bin\$NodeVersion"
New-Item -ItemType Directory -Force -Path $NodeFolder | Out-Null
Copy-Item "$Root\sdk\nodejs\runtime\native\build\Release\nativeruntime-v0.11.0.node" $NodeFolder
Copy-Item "$Root\sdk\nodejs\runtime\native\build\Release\nativeruntime-v0.11.0.pdb" $NodeFolder

# By default, if the archive already exists, 7zip will just add files to it, so blow away the existing
# archive if it exists.
Expand Down
4 changes: 0 additions & 4 deletions scripts/make_release.sh
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ cp ${ROOT}/sdk/nodejs/dist/pulumi-language-nodejs-exec ${PUBDIR}/bin/
cp ${ROOT}/sdk/nodejs/dist/pulumi-resource-pulumi-nodejs ${PUBDIR}/bin/
cp ${ROOT}/sdk/python/cmd/pulumi-language-python-exec ${PUBDIR}/bin/

# Copy over our custom Node plugin
mkdir -p ${PUBDIR}/bin/$(node --version)
cp ${ROOT}/sdk/nodejs/runtime/native/build/Release/nativeruntime-v0.11.0.node ${PUBDIR}/bin/$(node --version)/nativeruntime-v0.11.0.node

# Copy packages
copy_package "${ROOT}/sdk/nodejs/bin/." "@pulumi/pulumi"

Expand Down
8 changes: 0 additions & 8 deletions sdk/nodejs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,13 @@ TEST_FAST_TIMEOUT := 2m
include ../../build/common.mk

export PATH:=$(shell yarn bin 2>/dev/null):$(PATH)
export NODE_PATH:=$(NODE_PATH):./runtime/native/build/Release

ensure::
cd runtime/native && ./ensure_node_v8.sh
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yaay,


lint::
$(GOMETALINTER) cmd/pulumi-language-nodejs/main.go | sort ; exit "$${PIPESTATUS[0]}"
tslint -c tslint.json -p tsconfig.json

build::
go install -ldflags "-X github.com/pulumi/pulumi/pkg/version.Version=${VERSION}" ${LANGUAGE_HOST}
cd runtime/native && node-gyp configure
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I expected to also see a boatload of red C++ code in this change? 😀

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

coming up in the next commit! 😆

cd runtime/native && node-gyp build
tsc
cp README.md ../../LICENSE package.json ./dist/* bin/
node ../../scripts/reversion.js bin/package.json ${VERSION}
Expand All @@ -39,8 +33,6 @@ install::
GOBIN=$(PULUMI_BIN) go install -ldflags "-X github.com/pulumi/pulumi/pkg/version.Version=${VERSION}" ${LANGUAGE_HOST}
cp dist/pulumi-language-nodejs-exec "$(PULUMI_BIN)"
cp dist/pulumi-resource-pulumi-nodejs "$(PULUMI_BIN)"
mkdir -p "$(PULUMI_BIN)/$(shell node --version)"
cp runtime/native/build/Release/nativeruntime-v0.11.0.node "$(PULUMI_BIN)/$(shell node --version)/nativeruntime-v0.11.0.node"
rm -rf "$(PULUMI_NODE_MODULES)/$(NODE_MODULE_NAME)/tests"

test_fast::
Expand Down
1 change: 0 additions & 1 deletion sdk/nodejs/dist/pulumi-language-nodejs-exec
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,4 @@
# we exploit the fact that the cwd when `pulumi-language-nodejs-exec` is
# run is the root of the node program we want to run and use a relative
# path here.
export NODE_PATH="$NODE_PATH:`dirname $0`/`node --version`"
node ./node_modules/@pulumi/pulumi/cmd/run $@
3 changes: 1 addition & 2 deletions sdk/nodejs/dist/pulumi-language-nodejs-exec.cmd
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
@set NODE_PATH=%NODE_PATH%;%~dp0\v6.10.2
@pulumi-language-nodejs-node.exe ./node_modules/@pulumi/pulumi/cmd/run %*
@node ./node_modules/@pulumi/pulumi/cmd/run %*
1 change: 0 additions & 1 deletion sdk/nodejs/dist/pulumi-resource-pulumi-nodejs
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
#!/bin/sh
export NODE_PATH="$NODE_PATH:`dirname $0`/`node --version`"
node ./node_modules/@pulumi/pulumi/cmd/dynamic-provider $@
3 changes: 1 addition & 2 deletions sdk/nodejs/dist/pulumi-resource-pulumi-nodejs.cmd
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
@set NODE_PATH=%NODE_PATH%;%~dp0\v6.10.2
@pulumi-language-nodejs-node.exe ./node_modules/@pulumi/pulumi/cmd/dynamic-provider %*
@node ./node_modules/@pulumi/pulumi/cmd/dynamic-provider %*
38 changes: 5 additions & 33 deletions sdk/nodejs/runtime/closure/createClosure.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,35 +7,7 @@ import { RunError } from "../../errors";
import * as resource from "../../resource";
import { CapturedPropertyInfo, CapturedVariableMap, parseFunction } from "./parseFunction";
import { rewriteSuperReferences } from "./rewriteSuper";

// Our closure serialization code links against v8 internals. On Windows, we can't dynamically link
// against v8 internals because their symbols are unexported. In order to address this problem,
// Pulumi programs run on a custom build of Node.
//
// On Linux and OSX, we can dynamically link against v8 internals, so we can run on stock Node.
// However, we only build nativeruntime.node against specific versions of Node, users running Pulumi
// programs must explicitly use a supported version of Node.
const supportedNodeVersions = ["v6.10.2"];
let nativeruntime: any;
try {
nativeruntime = require("nativeruntime-v0.11.0.node");
}
catch (err) {
// There are two reasons why this can happen:
// 1. We messed up when packaging Pulumi and failed to include nativeruntime.node,
// 2. A user is running their Pulumi program with a version of Node that we do not explicitly support.
const thisNodeVersion = process.version;
if (supportedNodeVersions.indexOf(thisNodeVersion) > -1) {
// This node version is explicitly supported, but the load still failed.
// This means that Pulumi messed up when installing itself.
throw new RunError(`Failed to locate custom Pulumi SDK Node.js extension. This is a bug! (${err.message})`);
}

throw new RunError(
`Failed to load custom Pulumi SDK Node.js extension; The version of Node.js that you are
using (${thisNodeVersion}) is not explicitly supported, you must use one of these
supported versions of Node.js: ${supportedNodeVersions}`);
}
import * as v8 from "./v8";

export interface ObjectInfo {
// information about the prototype of this object/function. If this is an object, we only store
Expand Down Expand Up @@ -320,9 +292,9 @@ function createFunctionInfo(
func: Function, context: Context,
serialize: (o: any) => boolean): FunctionInfo {

const file: string = nativeruntime.getFunctionFile(func);
const line: number = nativeruntime.getFunctionLine(func);
const column: number = nativeruntime.getFunctionColumn(func);
const file: string = v8.getFunctionFile(func);
const line: number = v8.getFunctionLine(func);
const column: number = v8.getFunctionColumn(func);
const functionString = func.toString();
const frame = { functionLocation: { func, file, line, column, functionString, isArrowFunction: false } };

Expand Down Expand Up @@ -469,7 +441,7 @@ function createFunctionInfo(
for (const name of Object.keys(capturedVariables)) {
let value: any;
try {
value = nativeruntime.lookupCapturedVariableValue(func, name, throwOnFailure);
value = v8.lookupCapturedVariableValue(func, name, throwOnFailure);
}
catch (err) {
throwSerializationError(func, context, err.message);
Expand Down
148 changes: 148 additions & 0 deletions sdk/nodejs/runtime/closure/v8.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
// Copyright 2016-2018, Pulumi Corporation. All rights reserved.

// This file provides a low-level interface to a few V8 runtime objects.
// We will use this low-level interface when serializing closures to walk the scope
// chain and find the value of free variables captured by closures, as well as getting
// source-level debug information so that we can present high-quality error messages.
//
// As a side-effect of importing this file, we must enable the --allow-natives-syntax V8
// flag. This is because we are using V8 intrinsics in order to implement this module.
import * as v8 from "v8";
v8.setFlagsFromString("--allow-natives-syntax");

// We use four V8 intrinsics in this file. The first, `FunctionGetScript`, gets
// a `Script` object given a JavaScript function. The `Script` object contains metadata
// about the function's source definition.
const getScript: (func: Function) => V8Script | undefined =
// The use of the Function constructor here and elsewhere in this file is because
// because V8 intrinsics are not valid JavaScript identifiers; they all begin with '%',
// which means that the TypeScript compiler issues errors for them.
new Function("func", "return %FunctionGetScript(func);") as any;

// The V8 script object contains the name of the file that defined a function and a function
// that convert a `V8SourcePosition` into a `V8SourceLocation`. (Conceptually - Positions are offsets
// into a resource stream, while locations are objects with line and column information.)
interface V8Script {
readonly name: string;
locationFromPosition(pos: V8SourcePosition): V8SourceLocation;
}

// The second intrinsic is `FunctionGetScriptSourcePosition`, which does about what you'd
// expect. It returns a `V8SourcePosition`, which can be passed to `V8Script::locationFromPosition`
// to produce a `V8SourceLocation`.
const getSourcePosition: (func: Function) => V8SourcePosition =
new Function("func", "return %FunctionGetScriptSourcePosition(func);") as any;

// V8SourcePosition is an opaque value that should be passed verbatim to `V8Script.locationFromPosition`
// in order to receive a V8SourceLocation.
interface V8SourcePosition {}

// V8SourceLocation contains metadata about a single location within a Script. For a function, it
// refers to the last character of that function's declaration.
interface V8SourceLocation {
readonly line: number;
readonly column: number;
}

// The last two intrinsics are `GetFunctionScopeCount` and `GetFunctionScopeDetails`.
// The former function returns the number of scopes in a given function's scope chain, while
// the latter function returns the i'th entry in a function's scope chain, given a function and
// index i.
const getFunctionScopeDetails: (func: Function, index: number) => any[] =
new Function("func", "index", "return %GetFunctionScopeDetails(func, index);") as any;
const getFunctionScopeCount: (func: Function) => number =
new Function("func", "return %GetFunctionScopeCount(func);") as any;

// `GetFunctionScopeDetails` returns a raw JavaScript array. This enum enumerates the objects that
// are at specific indices of the array. We only care about one of these.
enum V8ScopeDetailsFields {
kScopeDetailsTypeIndex = 0,
kScopeDetailsObjectIndex = 1, // <-- this one
kScopeDetailsNameIndex = 2,
kScopeDetailsStartPositionIndex = 3,
kScopeDetailsEndPositionIndex = 4,
kScopeDetailsFunctionIndex = 5,
}

// V8ScopeDetails contains a lot of information about a particular scope in the scope chain, but the
// only one we care about is `scopeObject`, which is a mapping of strings to values. The strings are variables
// declared within the given scope, and the values are the value of the captured variables.
interface V8ScopeDetails {
readonly scopeObject: Record<string, any>;
}

// getScopeForFunction extracts a V8ScopeDetails for the index'th element in the scope chain for the
// given function.
function getScopeForFunction(func: Function, index: number): V8ScopeDetails {
const scopeDetails = getFunctionScopeDetails(func, index);
return {
scopeObject: scopeDetails[V8ScopeDetailsFields.kScopeDetailsObjectIndex] as Record<string, any>,
};
}

/**
* Given a function and a free variable name, lookupCapturedVariableValue looks up the value of that free variable
* in the scope chain of the provided function. If the free variable is not found, `throwOnFailure` indicates
* whether or not this function should throw or return `undefined.
*
* @param func The function whose scope chain is to be analyzed
* @param freeVariable The name of the free variable to inspect
* @param throwOnFailure If true, throws if the free variable can't be found.
* @returns The value of the free variable. If `throwOnFailure` is false, returns `undefined` if not found.
*/
export function lookupCapturedVariableValue(func: Function, freeVariable: string, throwOnFailure: boolean): any {
// The implementation of this function is now very straightforward since the intrinsics do all of the
// difficult work.
const count = getFunctionScopeCount(func);
for (let i = 0; i < count; i++) {
const scope = getScopeForFunction(func, i);
if (freeVariable in scope.scopeObject) {
return scope.scopeObject[freeVariable];
}
}

if (throwOnFailure) {
throw new Error("Unexpected missing variable in closure environment: " + freeVariable);
}

return undefined;
}

/**
* Given a function, returns the name of the file where this function was defined.
* Returns the empty string if the given function has no Script. (e.g. a native function)
*/
export function getFunctionFile(func: Function): string {
const script = getScript(func);
return script ? script.name : "";
}

/**
* Given a function, returns the line number in the file where this function was defined.
* Returns 0 if the given function has no Script.
*/
export function getFunctionLine(func: Function): number {
const script = getScript(func);
if (!script) {
return 0;
}

const pos = getSourcePosition(func);
const location = script.locationFromPosition(pos);
return location.line;
}

/**
* Given a function, returns the column in the file where this function was defined.
* Returns 0 if the given function has no script.
*/
export function getFunctionColumn(func: Function): number {
const script = getScript(func);
if (!script) {
return 0;
}

const pos = getSourcePosition(func);
const location = script.locationFromPosition(pos);
return location.column;
}
3 changes: 0 additions & 3 deletions sdk/nodejs/runtime/native/.gitignore

This file was deleted.

24 changes: 0 additions & 24 deletions sdk/nodejs/runtime/native/binding.gyp

This file was deleted.

Loading