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

Bundle jsii modules as npm tarballs #52

Merged
merged 22 commits into from
Jul 11, 2018
Merged

Bundle jsii modules as npm tarballs #52

merged 22 commits into from
Jul 11, 2018

Conversation

eladb
Copy link
Contributor

@eladb eladb commented Jul 3, 2018

This change modifies the way we bundle jsii modules. Instead of webpacking them
and storing the code inside the assembly spec, we now simply use "npm pack" to
produce an npm tarball (.tgz) and include that in the generated library.

The kernel now simply accepts the path to the tarball (as oppose to the entire
code passed through STDIN), and untars it into a working directory (under node_modules).
This effectively allows dependencies to require(it).

Bundled dependencies are now simple npm bundled dependencies. If a module uses the old-
style configuration, we fail with instructions.

PERF: This change also improves load-time performance (again, we are not sending
a big JSON file over the wire, parse it, and then evaluate it into the VM).

.NET: Furthermore, in .NET we didn't even bundle the webpack and the jsii-runtime, so this change includes modifications to the .NET generator and runtime to actually bundle the runtime and the new tarballs. This removes the dependency on jsii-runtime for .NET, and leaves only node.js as an external dependency.

NOTE ABOUT VERSIONS: we currently do not support multiple versions of the same module loaded together into the same kernel space. This is a major and unacceptable limitation for production
environments, which we must address, or otherwise, people will constantly hit these
restrictions as jsii software stacks evolve.

BREAKING CHANGE

This change modifies the way we bundle jsii modules. Instead of webpacking them
and storing the code inside the assembly spec, we now simply use "npm pack" to
produce an npm tarball (.tgz) and include that in the generated library.

The kernel now simply accepts the path to the tarball (as oppose to the entire
code passed through STDIN), and untars it into a working directory (under node_modules).
This effectively allows dependencies to `require(it)`.

This change also dramatically improves load-time performance (again, we are not sending
a big JSON file over the wire, parse it, and then evaluate it into the VM).

Bundled dependencies are now simple npm bundled dependencies. If a module uses the old-
style configuration, we fail with instructions.

NOTE: we currently do not support multiple versions of the same module loaded together
into the same kernel space. This is a major and unacceptable limitation for production
environments, which we must address, or otherwise, people will constantly hit these
restrictions as jsii software stacks evolve.

BREAKING CHANGE.
@eladb eladb requested review from RomainMuller and mpiroc July 3, 2018 20:59
.gitignore Outdated
@@ -348,6 +348,7 @@ jspm_packages/

# Output of 'npm pack'
*.tgz
!*.jsii.tgz
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we really want to check in tarballs? Why not ignore them at the .npmignore level instead?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, thats obviously a mistake

@@ -27,7 +27,17 @@ export interface HelloResponse {
}

export interface LoadRequest {
assembly: Assembly;
/** @deprecated */
assembly?: Assembly;
Copy link
Contributor

Choose a reason for hiding this comment

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

Since we throw an error if assembly is present, is deprecated the right semantics? I think it would be better to remove the assembly property altogether (and continue to throw an error if it is present).

@@ -211,7 +274,9 @@ export class Kernel {
return this._wrapSandboxCode(() => fn.apply(obj, this._toSandboxValues(args)));
});

this._debug('method returned:', ret);
// console.error(ret);
Copy link
Contributor

Choose a reason for hiding this comment

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

Remove commented code

@@ -47,41 +47,64 @@ export interface IGenerator
*/
export abstract class Generator implements IGenerator {
private readonly options: GeneratorOptions;
private readonly mod: spec.Assembly
private mod: spec.Assembly
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: More readable to use the full word module. More broadly, it isn't clear what the difference is between modules and assemblies. If there is no difference, we should use the same terminology everywhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Got rid of .mod

/**
* Runs the generator (in-memory).
*/
generate() {
this.onBeginAssembly(this.mod);
this.visit(this.mod.nametree);
if (this.mod.nametree) {
this.visit(this.mod.nametree);
Copy link
Contributor

Choose a reason for hiding this comment

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

I thought we deprecated nametree. If not, we should update http://sapp.amazon.com/jsii/.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I need to update the spec :-)
[please try to avoid using non-public URLs here]

@@ -1,4 +1,5 @@
#!/bin/bash
set -e
Copy link
Contributor

Choose a reason for hiding this comment

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

Should this be set -euo pipefail?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Sure

Elad Ben-Israel added 21 commits July 3, 2018 14:59
Bundle jsii tarball modules into generated .NET packages and also
bundle the jsii-runtime program as an embedded resource into the
dotnet runtime library.

This removes the dependency on jsii-runtime, and leaves only node.js
as an external dependency. It also improves load-time performance since
the tarball is only passed as a path to the runtime/kernel and not
the entire .js code.
`set -euo` fails for unbound variables
Since we have version numbers in test expectations
every build will get a different version number and that
invariably causes tests to fail.
1. Add calc-lib
2. Create expected tarball on-the-fly to avoid binary diffs resulting from different platforms
@eladb eladb merged commit 0db8802 into master Jul 11, 2018
@eladb eladb deleted the benisrae/tarballs branch July 11, 2018 09:13

const generatorClass: any = await lookupGenerator(lang);
return new generatorClass(mod);
const tarball = await npmPack(packageDir);
Copy link
Contributor

Choose a reason for hiding this comment

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

Why do we always pack? Is this relevant, for example when we generate docs?

Copy link
Contributor Author

@eladb eladb Jul 12, 2018

Choose a reason for hiding this comment

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

KISS

await generator.save(outDir);
}
async function npmPack(packageDir: string) {
const child = spawn('npm', [ 'pack', '--ignore-scripts' ], { cwd: packageDir, stdio: [ 'ignore', 'pipe', 'pipe' ] });
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure this should --ignore-scripts here; as this no longer guarantees reproductible outcome.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. I need to think about it, also how this interplays with cross-language builds and publishing (#73)

const tarball = await new Promise<string>((ok, fail) => {
let stdout = '';
let stderr = '';
child.stdout.on('data', chunk => stdout += chunk.toString());
Copy link
Contributor

Choose a reason for hiding this comment

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

This is unsafe to do: the chunks may include incomplete multi-byte characters (the rest could be in the next chunk), which could render the stdout and stderr strings invalid.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mmm.. Good to know. I will change to Buffer.concat.

return ok(stdout.trim());
} else {
process.stderr.write(stderr);
return fail(new Error('Exit with status ' + status));
Copy link
Contributor

Choose a reason for hiding this comment

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

status might be null, if the child is killed by a signal.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants