Skip to content

Commit 250c85b

Browse files
authored
Merge pull request #533 from ethereum/solcjs-base-path-stripping-matching-solc
[solcjs] Base path stripping matching `solc`
2 parents 05e3564 + eadcfdd commit 250c85b

File tree

2 files changed

+84
-18
lines changed

2 files changed

+84
-18
lines changed

solcjs

Lines changed: 36 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
var originalUncaughtExceptionListeners = process.listeners("uncaughtException");
55

66
var fs = require('fs-extra');
7+
var os = require('os');
78
var path = require('path');
89
var solc = require('./index.js');
910
var smtchecker = require('./smtchecker.js');
@@ -34,26 +35,48 @@ function abort (msg) {
3435
process.exit(1);
3536
}
3637

37-
function readFileCallback(path) {
38-
path = program.basePath + '/' + path;
39-
if (fs.existsSync(path)) {
38+
function readFileCallback(sourcePath) {
39+
if (program.basePath)
40+
sourcePath = program.basePath + '/' + sourcePath;
41+
if (fs.existsSync(sourcePath)) {
4042
try {
41-
return { 'contents': fs.readFileSync(path).toString('utf8') }
43+
return { 'contents': fs.readFileSync(sourcePath).toString('utf8') }
4244
} catch (e) {
43-
return { error: 'Error reading ' + path + ': ' + e };
45+
return { error: 'Error reading ' + sourcePath + ': ' + e };
4446
}
4547
} else
46-
return { error: 'File not found at ' + path}
48+
return { error: 'File not found at ' + sourcePath}
4749
}
48-
function stripBasePath(path) {
49-
if (program.basePath && path.startsWith(program.basePath))
50-
return path.slice(program.basePath.length);
51-
else
52-
return path;
50+
51+
function withUnixPathSeparators(filePath) {
52+
if (os.platform !== 'win32')
53+
// On UNIX-like systems forward slashes in paths are just a part of the file name.
54+
return filePath;
55+
56+
return filePath.replace(/\\/g, "/");
57+
}
58+
59+
function stripBasePath(sourcePath) {
60+
const absoluteBasePath = (program.basePath ? path.resolve(program.basePath) : path.resolve('.'));
61+
62+
// Compared to base path stripping logic in solc this is much simpler because path.resolve()
63+
// handles symlinks correctly (does not resolve them except in work dir) and strips .. segments
64+
// from paths going beyond root (e.g. `/../../a/b/c` -> `/a/b/c/`). It's simpler also because it
65+
// ignores less important corner cases: drive letters are not stripped from absolute paths on
66+
// Windows and UNC paths are not handled in a special way (at least on Linux). Finally, it has
67+
// very little test coverage so there might be more differences that we are just not aware of.
68+
const absoluteSourcePath = path.resolve(sourcePath);
69+
const relativeSourcePath = path.relative(absoluteBasePath, absoluteSourcePath);
70+
71+
if (relativeSourcePath.startsWith('../'))
72+
// Path can't be made relative without stepping outside of base path so return absolute one.
73+
return withUnixPathSeparators(absoluteSourcePath);
74+
75+
return withUnixPathSeparators(relativeSourcePath);
5376
}
5477

5578
var callbacks = undefined
56-
if (program.basePath)
79+
if (program.basePath || !program.standardJson)
5780
callbacks = {'import': readFileCallback};
5881

5982
if (program.standardJson) {
@@ -167,4 +190,4 @@ originalUncaughtExceptionListeners.forEach(function (listener) {
167190

168191
if (hasError) {
169192
process.exit(1);
170-
}
193+
}

test/cli.js

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const tape = require('tape');
22
const spawn = require('tape-spawn');
3+
const path = require('path');
34
const pkg = require('../package.json');
45

56
tape('CLI', function (t) {
@@ -62,14 +63,56 @@ tape('CLI', function (t) {
6263
spt.end();
6364
});
6465

65-
t.test('no-base-path', function (st) {
66-
var spt = spawn(st, './solcjs --bin test/resources/importA.sol');
67-
spt.stderr.match(/not found: File import callback not supported/);
66+
t.test('no base path', function (st) {
67+
var spt = spawn(
68+
st,
69+
'./solcjs --bin ' +
70+
'test/resources/importA.sol ' +
71+
'./test/resources//importA.sol ' +
72+
path.resolve('test/resources/importA.sol')
73+
);
74+
spt.stderr.empty();
75+
spt.succeeds();
76+
spt.end();
77+
});
78+
79+
t.test('relative base path', function (st) {
80+
// NOTE: This and other base path tests rely on the relative ./importB.sol import in importA.sol.
81+
// If base path is not stripped correctly from all source paths below, they will not be found
82+
// by the import callback when it appends the base path back.
83+
var spt = spawn(
84+
st,
85+
'./solcjs --bin --base-path test/resources ' +
86+
'test/resources/importA.sol ' +
87+
'./test/resources//importA.sol ' +
88+
path.resolve('test/resources/importA.sol')
89+
);
90+
spt.stderr.empty();
91+
spt.succeeds();
92+
spt.end();
93+
});
94+
95+
t.test('relative non canonical base path', function (st) {
96+
var spt = spawn(
97+
st,
98+
'./solcjs --bin --base-path ./test/resources ' +
99+
'test/resources/importA.sol ' +
100+
'./test/resources//importA.sol ' +
101+
path.resolve('test/resources/importA.sol')
102+
);
103+
spt.stderr.empty();
104+
spt.succeeds();
68105
spt.end();
69106
});
70107

71-
t.test('base-path', function (st) {
72-
var spt = spawn(st, './solcjs --bin --base-path test/resources test/resources/importA.sol');
108+
t.test('absolute base path', function (st) {
109+
var spt = spawn(
110+
st,
111+
'./solcjs --bin --base-path ' + path.resolve('test/resources') + ' ' +
112+
'test/resources/importA.sol ' +
113+
'./test/resources//importA.sol ' +
114+
path.resolve('test/resources/importA.sol')
115+
);
73116
spt.stderr.empty();
74117
spt.succeeds();
75118
spt.end();

0 commit comments

Comments
 (0)