Skip to content

Commit

Permalink
windows: runfiles resolution must handle both manifest and symlink cases
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeagle committed Sep 14, 2017
1 parent efc3a3f commit bf8c9f0
Show file tree
Hide file tree
Showing 2 changed files with 54 additions and 29 deletions.
56 changes: 33 additions & 23 deletions internal/node_launcher.sh
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,25 @@
set -e

# Launcher for NodeJS applications.
# Find our runfiles manifest. We need this to launch node with the correct
# Find our runfiles. We need this to launch node with the correct
# entry point.
#
# Call this program X. X was generated by a genrule and may be invoked
# in many ways:
# 1a) directly by a user, with $0 in the output tree
# 1b) via 'bazel run' (similar to case 1a)
# 2) directly by a user, with $0 in X's runfiles manifest
# 2) directly by a user, with $0 in X's runfiles
# 3) by another program Y which has a data dependency on X, with $0 in Y's
# runfiles manifest
# runfiles
# 4a) via 'bazel test'
# 4b) case 3 in the context of a test
# 5a) by a genrule cmd, with $0 in the output tree
# 6a) case 3 in the context of a genrule
#
# For case 1, $0 will be a regular file, and the runfiles manifest will be
# For case 1, $0 will be a regular file, and the runfiles will be
# at $0.runfiles.
# For case 2 or 3, $0 will be a symlink to the file seen in case 1.
# For case 4, $TEST_SRCDIR should already be set to the runfiles manifest by
# For case 4, $TEST_SRCDIR should already be set to the runfiles by
# blaze.
# Case 5a is handled like case 1.
# Case 6a is handled like case 3.
Expand All @@ -46,16 +46,16 @@ esac

if [[ -n "$TEST_SRCDIR" ]]; then
# Case 4, bazel has identified runfiles for us.
RUNFILES="${TEST_SRCDIR}_manifest"
RUNFILES="${TEST_SRCDIR}"
else
while true; do
if [[ -e "$self.runfiles" ]]; then
RUNFILES="$self.runfiles_manifest"
RUNFILES="$self.runfiles"
break
fi

if [[ $self == *.runfiles/* ]]; then
RUNFILES="${self%%.runfiles/*}.runfiles_manifest"
RUNFILES="${self%%.runfiles/*}.runfiles"
# don't break; this is a last resort for case 6b
fi

Expand Down Expand Up @@ -92,21 +92,31 @@ done
# Note: for debugging it is useful to see what files are actually present, eg:
# find . -name thingImLookingFor

# Lookup the real paths from the runfiles manifest with no dependency on posix
while read line; do
declare -a PARTS=($line)
if [ "${PARTS[0]}" == "TEMPLATED_node" ]; then
readonly node="${PARTS[1]}"
elif [ "${PARTS[0]}" == "TEMPLATED_script_path" ]; then
readonly script="${PARTS[1]}"
# On Windows, the runfiles symlink tree does not exist, so we must resolve paths
# using the mapping in the runfiles_manifest file.
# See https://github.com/bazelbuild/bazel/issues/3726
readonly MANIFEST="${RUNFILES}_manifest"
if [ -e "${MANIFEST}" ]; then
# Lookup the real paths from the runfiles manifest with no dependency on posix
while read line; do
declare -a PARTS=($line)
if [ "${PARTS[0]}" == "TEMPLATED_node" ]; then
readonly node="${PARTS[1]}"
elif [ "${PARTS[0]}" == "TEMPLATED_script_path" ]; then
readonly script="${PARTS[1]}"
fi
done < ${MANIFEST}
if [ -z "${node}" ]; then
echo "Failed to find node binary TEMPLATED_node in manifest ${MANIFEST}"
exit 1
fi
done < ${RUNFILES}
if [ -z "${node}" ]; then
echo "Failed to find node binary TEMPLATED_node in manifest ${RUNFILES}"
exit 1
fi
if [ -z "${script}" ]; then
echo "Failed to find script TEMPLATED_script_path in manifest ${RUNFILES}"
exit 1
if [ -z "${script}" ]; then
echo "Failed to find script TEMPLATED_script_path in manifest ${MANIFEST}"
exit 1
fi
else
readonly node="${RUNFILES}/TEMPLATED_node"
readonly script="${RUNFILES}/TEMPLATED_script_path"
fi

exec "${node}" "${NODE_OPTIONS[@]}" "${script}" "${ARGS[@]}"
27 changes: 21 additions & 6 deletions internal/node_loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,15 @@ function resolveToModuleRoot(path) {
* The runfiles manifest maps from short_path
* https://docs.bazel.build/versions/master/skylark/lib/File.html#short_path
* to the actual location on disk where the file can be read.
*
* In a sandboxed execution, it does not exist. In that case, runfiles must be
* resolved from a symlink tree under the runfiles dir.
* See https://github.com/bazelbuild/bazel/issues/3726
*/
function loadRunfilesManifest(manifestPath) {
if (!fs.existsSync(manifestPath)) {
return;
}
const result = Object.create(null);
const input = fs.readFileSync(manifestPath, {encoding: 'utf-8'});
for (const line of input.split("\n")) {
Expand All @@ -72,20 +79,28 @@ function loadRunfilesManifest(manifestPath) {
}
return result;
}
const runfilesManifest = loadRunfilesManifest(process.env.RUNFILES);
const runfilesManifest = loadRunfilesManifest(`${process.env.RUNFILES}_manifest`);

function resolveRunfiles(...pathSegments) {
if (runfilesManifest) {
// Join on forward slash, because even on Windows the runfiles_manifest
// file is written with forward slash.
return runfilesManifest[pathSegments.join('/')];
} else {
return path.join(process.env.RUNFILES, ...pathSegments);
}
}

var originalResolveFilename = module.constructor._resolveFilename;
module.constructor._resolveFilename =
function(request, parent) {
var failedResolutions = [];
var resolveLocations = [
request,
runfilesManifest[request],
// Join on forward slash, because even on Windows the runfiles_manifest
// file is written with forward slash.
runfilesManifest[[
resolveRunfiles(request),
resolveRunfiles(
'TEMPLATED_workspace_name', 'TEMPLATED_label_package',
'node_modules', request].join('/')],
'node_modules', request),
];
for (var location of resolveLocations) {
try {
Expand Down

0 comments on commit bf8c9f0

Please sign in to comment.