Skip to content

Commit

Permalink
feat(go): run go fmt and go build after code generation
Browse files Browse the repository at this point in the history
To ensure the generated go code is formatted and compiles by executing `go fmt`
and `go build` on the output directory.

In order to be able to do that within mono-repos (and in the jsii repo itself), we need to be able to resolve local dependencies (same as all other languages).

Resolves #2463
  • Loading branch information
Elad Ben-Israel committed Jan 26, 2021
1 parent 187b4bb commit 33cae0f
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 6 deletions.
110 changes: 105 additions & 5 deletions packages/jsii-pacmak/lib/targets/go.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,19 +5,29 @@ import { Rosetta } from 'jsii-rosetta';
import * as path from 'path';

import { IGenerator } from '../generator';
import * as logging from '../logging';
import { Target, TargetOptions } from '../target';
import { shell } from '../util';
import { Documentation } from './go/documentation';
import { RootPackage } from './go/package';
import { GOMOD_FILENAME, RootPackage } from './go/package';
import { JSII_INIT_PACKAGE } from './go/runtime';
import { JSII_RT_MODULE_NAME } from './go/runtime/constants';
import { goPackageName } from './go/util';

export class Golang extends Target {
public readonly generator: IGenerator;
private readonly goGenerator: GoGenerator;

private readonly goPackageName: string;

public constructor(options: TargetOptions) {
super(options);

this.generator = new GoGenerator(options.rosetta);
this.goPackageName = goPackageName(this.assembly.name);
this.goGenerator = new GoGenerator(options.rosetta);
}

public get generator(): IGenerator {
return this.goGenerator;
}

/**
Expand All @@ -27,7 +37,78 @@ export class Golang extends Target {
* @param outDir the directory where the publishable artifact should be placed.
*/
public async build(sourceDir: string, outDir: string): Promise<void> {
return this.copyFiles(sourceDir, outDir);
await this.copyFiles(sourceDir, outDir);

// resolve symlinks
outDir = await fs.realpath(outDir);

// append a "replace" directive for local dependencies
await this.replaceLocalDeps(outDir);

// run `go fmt` and `go build` in outdir to format and compile
const pkgout = path.resolve(outDir, this.goPackageName);
await shell('go', ['fmt'], { cwd: pkgout });
await shell('go', ['build'], { cwd: pkgout });
}

/**
* Appends a `replace` directive in the generated `go.mod` for local
* dependencies.
* @param outDir The dist output directory (e.g. `<packageRoot>/dist/go`).
*/
private async replaceLocalDeps(outDir: string) {
const rootPackage = this.goGenerator.rootPackage;
if (!rootPackage) {
throw new Error(`assertion failed: "rootPackage" is undefined`);
}

// find local deps by check if `<jsii.outdir>/go` exists for dependencies
// and also consider _out_ output directory in case pacmak is executed using
// `--outdir` (in which case all go code will be generated there).
const localDeps = [
outDir,
...(await this.findLocalDepsOutput(this.packageDir)),
];

const replace: { [from: string]: string } = {};

// try to resolve @jsii/go-runtime (only exists as a devDependency)
const localRuntime = tryFindLocalRuntime();
if (localRuntime) {
replace[JSII_RT_MODULE_NAME] = localRuntime;
}

// iterate over all local directories and try to match them with one this
// module's deps.
for (const localDep of localDeps) {
for (const dep of rootPackage.packageDependencies) {
const localpath = path.join(localDep, goPackageName(dep.packageName));
if (fs.existsSync(localpath)) {
replace[dep.goModuleName] = localpath;
}
}
}

// emit "replace" directive (if relevant)
if (Object.keys(replace).length > 0) {
const pkgOut = path.join(outDir, this.goPackageName);
const lines = new Array<string>();

lines.push('replace (');

for (const [from, to] of Object.entries(replace)) {
const rel = path.relative(pkgOut, to);
logging.debug(`Local dependency: ${from} => ${rel} (${to})`);
lines.push(`\t${from} => ${rel}`);
}

lines.push(')');

await fs.appendFile(
path.join(pkgOut, GOMOD_FILENAME),
`\n${lines.join('\n')}`,
);
}
}
}

Expand All @@ -39,6 +120,8 @@ class GoGenerator implements IGenerator {
});
private readonly documenter: Documentation;

public rootPackage: RootPackage | undefined;

public constructor(private readonly rosetta: Rosetta) {
this.documenter = new Documentation(this.code, this.rosetta);
}
Expand All @@ -53,7 +136,9 @@ class GoGenerator implements IGenerator {
}

public generate(): void {
new RootPackage(this.assembly).emit({
this.rootPackage = new RootPackage(this.assembly);

return this.rootPackage.emit({
code: this.code,
documenter: this.documenter,
});
Expand Down Expand Up @@ -94,3 +179,18 @@ class GoGenerator implements IGenerator {
this.code.closeFile(file);
}
}

/**
* Check if we are running from inside the jsii repository, and then we want to
* use the local runtime instead of download from a released version.
*/
function tryFindLocalRuntime() {
try {
const p = require.resolve('@jsii/go-runtime/jsii-experimental/go.mod');
const dir = path.dirname(p);
logging.debug(`Using @jsii/go-runtime from ${dir}`);
return dir;
} catch {
return undefined;
}
}
1 change: 1 addition & 0 deletions packages/jsii-pacmak/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
"devDependencies": {
"@jsii/dotnet-runtime": "^0.0.0",
"@jsii/java-runtime": "^0.0.0",
"@jsii/go-runtime": "^0.0.0",
"@scope/jsii-calc-lib": "^0.0.0",
"@types/clone": "^2.1.0",
"@types/commonmark": "^0.27.4",
Expand Down
16 changes: 15 additions & 1 deletion packages/jsii-pacmak/test/generated-code/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ export function checkTree(
expect({ [TARBALL]: relativeFile }).toMatchSnapshot(snapshotName);
} else {
expect({
[FILE]: fs.readFileSync(file, { encoding: 'utf-8' }),
[FILE]: normalizeLocalReferences(
fs.readFileSync(file, { encoding: 'utf-8' }),
),
}).toMatchSnapshot(snapshotName);
}
return path.basename(file);
Expand Down Expand Up @@ -232,3 +234,15 @@ function formatTree(tree: TreeStructure): string {
})
.join('\n');
}

/**
* Replaces any string that represents the absolute path of the
* jsii root directory with `<jsiiRoot>` to maintain deterministic
* output across environments.
*/
function normalizeLocalReferences(s: string) {
return s.replace(
path.resolve(__dirname, '..', '..', '..', '..'),
'<jsiiRoot>',
);
}

0 comments on commit 33cae0f

Please sign in to comment.