/** * Generates a command line to run for the test action, taking into account coverage and {@code * --run_under} settings. * * @param testScript the setup script that invokes the test * @param coverageScript a script interjected between setup script and rest of command line to * collect coverage data. If this is an empty string, it is ignored. * @param testAction The test action. * @return the command line as string list. */ protected List<String> getArgs( String testScript, String coverageScript, TestRunnerAction testAction) { List<String> args = Lists.newArrayList(); if (OS.getCurrent() == OS.WINDOWS) { args.add(testAction.getShExecutable().getPathString()); args.add("-c"); args.add("$0 $*"); } args.add(testScript); TestTargetExecutionSettings execSettings = testAction.getExecutionSettings(); List<String> execArgs = new ArrayList<>(); if (!coverageScript.isEmpty() && isCoverageMode(testAction)) { execArgs.add(coverageScript); } // Execute the test using the alias in the runfiles tree, as mandated by // the Test Encyclopedia. execArgs.add(execSettings.getExecutable().getRootRelativePath().getPathString()); execArgs.addAll(execSettings.getArgs()); // Insert the command prefix specified by the "--run_under=<command-prefix>" option, // if any. if (execSettings.getRunUnder() == null) { args.addAll(execArgs); } else if (execSettings.getRunUnderExecutable() != null) { args.add(execSettings.getRunUnderExecutable().getRootRelativePath().getPathString()); args.addAll(execSettings.getRunUnder().getOptions()); args.addAll(execArgs); } else { args.add(testAction.getConfiguration().getShellExecutable().getPathString()); args.add("-c"); String runUnderCommand = ShellEscaper.escapeString(execSettings.getRunUnder().getCommand()); Path fullySpecified = SearchPath.which( SearchPath.parse( testAction.getTestLog().getPath().getFileSystem(), clientEnv.get("PATH")), runUnderCommand); if (fullySpecified != null) { runUnderCommand = fullySpecified.toString(); } args.add( runUnderCommand + ' ' + ShellEscaper.escapeJoinAll( Iterables.concat(execSettings.getRunUnder().getOptions(), execArgs))); } return args; }
private void actuallyClean( CommandEnvironment env, Path outputBase, Options cleanOptions, String symlinkPrefix) throws IOException, ShutdownBlazeServerException, CommandException, ExecException, InterruptedException { BlazeRuntime runtime = env.getRuntime(); if (env.getOutputService() != null) { env.getOutputService().clean(); } if (cleanOptions.expunge) { LOG.info("Expunging..."); // Delete the big subdirectories with the important content first--this // will take the most time. Then quickly delete the little locks, logs // and links right before we exit. Once the lock file is gone there will // be a small possibility of a server race if a client is waiting, but // all significant files will be gone by then. FileSystemUtils.deleteTreesBelow(outputBase); FileSystemUtils.deleteTree(outputBase); } else if (cleanOptions.expunge_async) { LOG.info("Expunging asynchronously..."); String tempBaseName = outputBase.getBaseName() + "_tmp_" + ProcessUtils.getpid(); // Keeping tempOutputBase in the same directory ensures it remains in the // same file system, and therefore the mv will be atomic and fast. Path tempOutputBase = outputBase.getParentDirectory().getChild(tempBaseName); outputBase.renameTo(tempOutputBase); env.getReporter() .handle(Event.info(null, "Output base moved to " + tempOutputBase + " for deletion")); // Daemonize the shell and use the double-fork idiom to ensure that the shell // exits even while the "rm -rf" command continues. String command = String.format( "exec >&- 2>&- <&- && (/usr/bin/setsid /bin/rm -rf %s &)&", ShellEscaper.escapeString(tempOutputBase.getPathString())); LOG.info("Executing shell commmand " + ShellEscaper.escapeString(command)); // Doesn't throw iff command exited and was successful. new CommandBuilder() .addArg(command) .useShell(true) .setWorkingDir(tempOutputBase.getParentDirectory()) .build() .execute(); } else { LOG.info("Output cleaning..."); runtime.clearCaches(); // In order to be sure that we delete everything, delete the workspace directory both for // --deep_execroot and for --nodeep_execroot. for (String directory : new String[] {runtime.getWorkspaceName(), "execroot/" + runtime.getWorkspaceName()}) { Path child = outputBase.getRelative(directory); if (child.exists()) { LOG.finest("Cleaning " + child); FileSystemUtils.deleteTreesBelow(child); } } } // remove convenience links OutputDirectoryLinksUtils.removeOutputDirectoryLinks( runtime.getWorkspaceName(), runtime.getWorkspace(), env.getReporter(), symlinkPrefix); // shutdown on expunge cleans if (cleanOptions.expunge || cleanOptions.expunge_async) { throw new ShutdownBlazeServerException(0); } }
/** * Computes, for each C++ source file in {@link #getLinkstamps}, the command necessary to compile * that file such that the output is correctly fed into the link command. * * <p>As these options (as well as all others) are taken into account when computing the action * key, they do not directly contain volatile build information to avoid unnecessary relinking. * Instead this information is passed as an additional header generated by {@link * com.google.devtools.build.lib.rules.cpp.WriteBuildInfoHeaderAction}. * * @param outputPrefix prefix to add before the linkstamp outputs' exec paths * @return a list of shell-escaped compiler commmands, one for each entry in {@link * #getLinkstamps} */ public List<String> getLinkstampCompileCommands(String outputPrefix) { if (linkstamps.isEmpty()) { return ImmutableList.of(); } String compilerCommand = cppConfiguration.getCppExecutable().getPathString(); List<String> commands = Lists.newArrayListWithCapacity(linkstamps.size()); for (Map.Entry<Artifact, Artifact> linkstamp : linkstamps.entrySet()) { List<String> optionList = new ArrayList<>(); // Defines related to the build info are read from generated headers. for (Artifact header : buildInfoHeaderArtifacts) { optionList.add("-include"); optionList.add(header.getExecPathString()); } String labelReplacement = Matcher.quoteReplacement( isSharedNativeLibrary() ? output.getExecPathString() : Label.print(owner.getLabel())); String outputPathReplacement = Matcher.quoteReplacement(output.getExecPathString()); for (String option : linkstampCompileOptions) { optionList.add( option .replaceAll(Pattern.quote("${LABEL}"), labelReplacement) .replaceAll(Pattern.quote("${OUTPUT_PATH}"), outputPathReplacement)); } optionList.add("-DGPLATFORM=\"" + cppConfiguration + "\""); // Needed to find headers included from linkstamps. optionList.add("-I."); // Add sysroot. PathFragment sysroot = cppConfiguration.getSysroot(); if (sysroot != null) { optionList.add("--sysroot=" + sysroot.getPathString()); } // Add toolchain compiler options. optionList.addAll(cppConfiguration.getCompilerOptions(features)); optionList.addAll(cppConfiguration.getCOptions()); optionList.addAll(cppConfiguration.getUnfilteredCompilerOptions(features)); if (CppFileTypes.CPP_SOURCE.matches(linkstamp.getKey().getExecPath())) { optionList.addAll(cppConfiguration.getCxxOptions(features)); } // For dynamic libraries, produce position independent code. if (linkTargetType == LinkTargetType.DYNAMIC_LIBRARY && cppConfiguration.toolchainNeedsPic()) { optionList.add("-fPIC"); } // Stamp FDO builds with FDO subtype string if (fdoBuildStamp != null) { optionList.add("-D" + CppConfiguration.FDO_STAMP_MACRO + "=\"" + fdoBuildStamp + "\""); } // Add the compilation target. optionList.add("-c"); optionList.add(linkstamp.getKey().getExecPathString()); // Assemble the final command, exempting outputPrefix from shell escaping. commands.add( compilerCommand + " " + ShellEscaper.escapeJoinAll(optionList) + " -o " + outputPrefix + ShellEscaper.escapeString(linkstamp.getValue().getExecPathString())); } return commands; }