Skip to content

Commit

Permalink
Merge pull request #66 from /issues/65
Browse files Browse the repository at this point in the history
Add support for user-passed compiler settings
  • Loading branch information
cd1m0 authored Sep 20, 2021
2 parents bf51021 + 2689cf4 commit 28bebbe
Show file tree
Hide file tree
Showing 5 changed files with 250 additions and 49 deletions.
45 changes: 38 additions & 7 deletions src/bin/compile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ const cli = {
"source"
],
number: ["depth"],
string: ["mode", "compiler-version", "path-remapping", "xpath"],
string: ["mode", "compiler-version", "path-remapping", "xpath", "compiler-settings"],
default: {
depth: Number.MAX_SAFE_INTEGER,
mode: modes[0],
Expand Down Expand Up @@ -87,6 +87,9 @@ OPTIONS:
- auto (try to detect suitable compiler version)
Default value: ${cli.default["compiler-version"]}
--path-remapping Path remapping input for Solc.
--compiler-settings Additional settings passed to the solc compiler in the form of a
JSON string (e.g. '{"optimizer": {"enabled": true, "runs": 200}}').
Note the double quotes. For more details see https://docs.soliditylang.org/en/latest/using-the-compiler.html#input-description.
--raw Print raw Solc compilation output.
--with-sources When used with "raw", adds "source" property with
source files content to the compiler artifact.
Expand Down Expand Up @@ -119,6 +122,17 @@ OPTIONS:
}

const pathRemapping: string[] = args["path-remapping"] ? args["path-remapping"].split(";") : [];
let compilerSettings: any = undefined;

if (args["compiler-settings"]) {
try {
compilerSettings = JSON.parse(args["compiler-settings"]);
} catch (e) {
throw new Error(
`Invalid compiler settings '${args["compiler-settings"]}'. Compiler settings must be a valid JSON object.(${e})`
);
}
}

let fileName = args._[0];
let result: CompileResult;
Expand All @@ -137,26 +151,43 @@ OPTIONS:

result =
mode === "json"
? compileJsonData(fileName, JSON.parse(content), compilerVersion, pathRemapping)
: compileSourceString(fileName, content, compilerVersion, pathRemapping);
? compileJsonData(
fileName,
JSON.parse(content),
compilerVersion,
pathRemapping,
compilerSettings
)
: compileSourceString(
fileName,
content,
compilerVersion,
pathRemapping,
compilerSettings
);
} else {
fileName = path.resolve(process.cwd(), args._[0]);

if (mode === "auto") {
const iFileName = fileName.toLowerCase();

if (iFileName.endsWith(".sol")) {
result = compileSol(fileName, compilerVersion, pathRemapping);
result = compileSol(fileName, compilerVersion, pathRemapping, compilerSettings);
} else if (iFileName.endsWith(".json")) {
result = compileJson(fileName, compilerVersion, pathRemapping);
result = compileJson(
fileName,
compilerVersion,
pathRemapping,
compilerSettings
);
} else {
throw new Error("Unable to auto-detect mode for the file name: " + fileName);
}
} else {
result =
mode === "json"
? compileJson(fileName, compilerVersion, pathRemapping)
: compileSol(fileName, compilerVersion, pathRemapping);
? compileJson(fileName, compilerVersion, pathRemapping, compilerSettings)
: compileSol(fileName, compilerVersion, pathRemapping, compilerSettings);
}
}
} catch (e) {
Expand Down
146 changes: 104 additions & 42 deletions src/compile/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,41 +55,90 @@ export function getCompilerForVersion(version: string): any {
);
}

type CompilerInputCreator = (fileName: string, content: string, remappings?: string[]) => any;

const createCompiler04Input: CompilerInputCreator = (fileName, content, remappings?) => ({
language: "Solidity",
sources: {
[fileName]: content
},
settings: {
remappings,
outputSelection: {
"*": {
"*": ["*"],
"": ["*"]
type Solc04Input = {
language: "Solidity";
sources: { [fileName: string]: string };
settings: { remappings: string[]; outputSelection: any; [otherKeys: string]: any };
};

type Solc05Input = {
language: "Solidity";
sources: { [fileName: string]: { content: string } };
settings: { remappings: string[]; outputSelection: any; [otherKeys: string]: any };
};

function mergeCompilerSettings<T extends Solc04Input | Solc05Input>(input: T, settings: any): T {
if (settings !== undefined) {
for (const key in settings) {
if (key === "remappings" || key === "outputSelection") {
continue;
}

input.settings[key] = settings[key];
}
}
});

const createCompiler05Input: CompilerInputCreator = (fileName, content, remappings?) => ({
language: "Solidity",
sources: {
[fileName]: {
content
}
},
settings: {
remappings,
outputSelection: {
"*": {
"*": ["*"],
"": ["*"]
return input;
}

type CompilerInputCreator = (
fileName: string,
content: string,
remappings: string[],
compilerSettings: any
) => Solc04Input | Solc05Input;

const createCompiler04Input: CompilerInputCreator = (
fileName,
content,
remappings,
compilerSettings
) =>
mergeCompilerSettings(
{
language: "Solidity",
sources: {
[fileName]: content
},
settings: {
remappings,
outputSelection: {
"*": {
"*": ["*"],
"": ["*"]
}
}
}
}
}
});
},
compilerSettings
);

const createCompiler05Input: CompilerInputCreator = (
fileName,
content,
remappings,
compilerSettings
) =>
mergeCompilerSettings(
{
language: "Solidity",
sources: {
[fileName]: {
content
}
},
settings: {
remappings,
outputSelection: {
"*": {
"*": ["*"],
"": ["*"]
}
}
}
},
compilerSettings
);

function consistentlyContainsOneOf(
sources: { [key: string]: any },
Expand Down Expand Up @@ -234,26 +283,27 @@ export function compile(
content: string,
version: string,
finder: ImportFinder,
remapping: string[]
remapping: string[],
compilerSettings?: any
): any {
const compiler = getCompilerForVersion(version);

if (satisfies(version, "0.4")) {
const input = createCompiler04Input(fileName, content, remapping);
const input = createCompiler04Input(fileName, content, remapping, compilerSettings);
const output = compiler.compile(input, 1, finder);

return output;
}

if (satisfies(version, "0.5")) {
const input = createCompiler05Input(fileName, content, remapping);
const input = createCompiler05Input(fileName, content, remapping, compilerSettings);
const output = compiler.compile(JSON.stringify(input), finder);

return JSON.parse(output);
}

const callbacks = { import: finder };
const input = createCompiler05Input(fileName, content, remapping);
const input = createCompiler05Input(fileName, content, remapping, compilerSettings);
const output = compiler.compile(JSON.stringify(input), callbacks);

return JSON.parse(output);
Expand Down Expand Up @@ -291,7 +341,8 @@ export function compileSourceString(
fileName: string,
sourceCode: string,
version: string | CompilerVersionSelectionStrategy,
remapping: string[]
remapping: string[],
compilerSettings?: any
): CompileResult {
const compilerVersionStrategy = getCompilerVersionStrategy(sourceCode, version);
const files = new Map([[fileName, sourceCode]]);
Expand All @@ -304,7 +355,14 @@ export function compileSourceString(
satisfies(compilerVersion, "0.4") ? parsePathRemapping(remapping) : []
);

const data = compile(fileName, sourceCode, compilerVersion, finder, remapping);
const data = compile(
fileName,
sourceCode,
compilerVersion,
finder,
remapping,
compilerSettings
);
const errors = detectCompileErrors(data);

if (errors.length === 0) {
Expand All @@ -320,18 +378,20 @@ export function compileSourceString(
export function compileSol(
fileName: string,
version: string | CompilerVersionSelectionStrategy,
remapping: string[]
remapping: string[],
compilerSettings?: any
): CompileResult {
const source = fse.readFileSync(fileName, { encoding: "utf-8" });

return compileSourceString(fileName, source, version, remapping);
return compileSourceString(fileName, source, version, remapping, compilerSettings);
}

export function compileJsonData(
fileName: string,
data: any,
version: string | CompilerVersionSelectionStrategy,
remapping: string[]
remapping: string[],
compilerSettings?: any
): CompileResult {
const files = new Map<string, string>();

Expand Down Expand Up @@ -377,7 +437,8 @@ export function compileJsonData(
sourceCode,
compilerVersion,
finder,
remapping
remapping,
compilerSettings
);

const errors = detectCompileErrors(compileData);
Expand All @@ -400,9 +461,10 @@ export function compileJsonData(
export function compileJson(
fileName: string,
version: string | CompilerVersionSelectionStrategy,
remapping: string[]
remapping: string[],
compilerSettings?: any
): CompileResult {
const data = fse.readJSONSync(fileName);

return compileJsonData(fileName, data, version, remapping);
return compileJsonData(fileName, data, version, remapping, compilerSettings);
}
1 change: 1 addition & 0 deletions test/integration/sol-ast-compile/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export const options = [
"mode",
"compiler-version",
"path-remapping",
"compiler-settings",
"raw",
"with-sources",
"tree",
Expand Down
42 changes: 42 additions & 0 deletions test/integration/sol-ast-compile/compiler-settings/invalid.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import expect from "expect";
import { SolAstCompileCommand, SolAstCompileExec } from "../common";

const sample = "test/samples/solidity/missing_pragma.sol";
const badArgsSamples = [
[
[sample, "--compiler-settings", "{blahblah}"],
`Error: Invalid compiler settings '{blahblah}'. Compiler settings must be a valid JSON object.`
]
];

for (const [args, expectedError] of badArgsSamples) {
const command = SolAstCompileCommand(...args);

describe(command, () => {
let exitCode: number | null;
let outData = "";
let errData = "";

before((done) => {
const result = SolAstCompileExec(...args);

outData = result.stdout;
errData = result.stderr;
exitCode = result.status;

done();
});

it("Exit code is valid", () => {
expect(exitCode).toEqual(1);
});

it("STDERR is correct", () => {
expect(errData).toContain(expectedError);
});

it("STDOUT is empty", () => {
expect(outData).toEqual("");
});
});
}
Loading

0 comments on commit 28bebbe

Please sign in to comment.