Revert "Replace skslc worklist files with -- delimited command lines."

This reverts commit 3e1b771ce4.

Reason for revert: Not working on Windows.

Original change's description:
> Replace skslc worklist files with -- delimited command lines.
>
> Command lines with delimiters are a simpler approach; they don't require
> a scratch file to be created and parsed. (I didn't consider this
> approach until after implementing worklists.)
>
> This also fixes a minor issue with result codes when processing multiple
> files at once; in particular, unit tests can ignore compile errors, but
> regular fragment processor compilation should treat compile errors as
> fatal and stop the build.
>
> Change-Id: I3f153e7670d757c6b021bf60a260a2cd3f2090aa
> Bug: skia:10919
> Reviewed-on: https://skia-review.googlesource.com/c/skia/+/334428
> Reviewed-by: Ethan Nicholas <ethannicholas@google.com>
> Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
> Auto-Submit: John Stiles <johnstiles@google.com>

TBR=brianosman@google.com,ethannicholas@google.com,johnstiles@google.com

# Not skipping CQ checks because original CL landed > 1 day ago.

Bug: skia:10919
Change-Id: I0e4bae8a8e09c61eac4e79453fd38e5e81b29e89
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/335858
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
This commit is contained in:
Brian Osman 2020-11-18 17:04:33 +00:00 committed by Skia Commit-Bot
parent c59e4486d5
commit 7b239054d9
3 changed files with 119 additions and 100 deletions

View File

@ -8,6 +8,7 @@
import os
import subprocess
import sys
import tempfile
skslc = sys.argv[1]
clangFormat = sys.argv[2]
@ -15,14 +16,14 @@ fetchClangFormat = sys.argv[3]
processors = sys.argv[4:]
exeSuffix = '.exe' if sys.platform.startswith('win') else '';
skslcArgs = [skslc]
clangFormatArgs = [clangFormat, "--sort-includes=false", "-i"]
targets = []
worklist = tempfile.NamedTemporaryFile(suffix='.worklist')
# Fetch clang-format if it's not present already.
if not os.path.isfile(clangFormat + exeSuffix):
subprocess.check_call([sys.executable, fetchClangFormat]);
# Build argument lists for all the fragment processors that we want to compile.
# Build a worklist of all the fragment processors that we want to compile.
for p in processors:
noExt, _ = os.path.splitext(p)
head, tail = os.path.split(noExt)
@ -30,27 +31,26 @@ for p in processors:
if not os.path.isdir(targetDir):
os.mkdir(targetDir)
target = os.path.join(targetDir, tail)
clangFormatArgs.append(target + ".h")
clangFormatArgs.append(target + ".cpp")
skslcArgs.append("--");
skslcArgs.append(p);
skslcArgs.append(target + ".h");
skslcArgs.append("--");
skslcArgs.append(p);
skslcArgs.append(target + ".cpp");
targets.append(target + ".h")
targets.append(target + ".cpp")
# Invoke skslc on every target that needs to be compiled.
worklist.write(p + "\n")
worklist.write(target + ".h\n\n")
worklist.write(p + "\n")
worklist.write(target + ".cpp\n\n")
# Invoke skslc, passing in the worklist.
worklist.close()
try:
output = subprocess.check_output(skslcArgs, stderr=subprocess.STDOUT)
output = subprocess.check_output([skslc, worklist.name], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as err:
print("### skslc error:\n")
print("\n".join(err.output.splitlines()))
sys.exit(err.returncode)
# Invoke clang-format on every generated target.
try:
output = subprocess.check_output(clangFormatArgs, stderr=subprocess.STDOUT)
output = subprocess.check_output([clangFormat, "--sort-includes=false", "-i"] + targets,
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as err:
print("### clang-format error:\n")
print("\n".join(err.output.splitlines()))
sys.exit(err.returncode)

View File

@ -8,6 +8,7 @@
import os
import subprocess
import sys
import tempfile
skslc = sys.argv[1]
lang = sys.argv[2]
@ -23,8 +24,8 @@ def makeEmptyFile(path):
if settings != "--settings" and settings != "--nosettings":
sys.exit("### Expected --settings or --nosettings, got " + settings)
skslcArgs = [skslc]
targets = []
worklist = tempfile.NamedTemporaryFile(suffix='.worklist')
# Convert the list of command-line inputs into a worklist file sfor skslc.
for input in inputs:
@ -41,36 +42,31 @@ for input in inputs:
targets.append(target)
if lang == "--fp":
skslcArgs.append("--")
skslcArgs.append(input)
skslcArgs.append(target + ".cpp")
skslcArgs.append(settings)
skslcArgs.append("--")
skslcArgs.append(input)
skslcArgs.append(target + ".h")
skslcArgs.append(settings)
worklist.write(input + "\n")
worklist.write(target + ".cpp\n")
worklist.write(settings + "\n\n")
worklist.write(input + "\n")
worklist.write(target + ".h\n")
worklist.write(settings + "\n\n")
elif lang == "--glsl":
skslcArgs.append("--")
skslcArgs.append(input)
skslcArgs.append(target + ".glsl")
skslcArgs.append(settings)
worklist.write(input + "\n")
worklist.write(target + ".glsl\n")
worklist.write(settings + "\n\n")
elif lang == "--metal":
skslcArgs.append("--")
skslcArgs.append(input)
skslcArgs.append(target + ".metal")
skslcArgs.append(settings)
worklist.write(input + "\n")
worklist.write(target + ".metal\n")
worklist.write(settings + "\n\n")
else:
sys.exit("### Expected one of: --fp --glsl --metal, got " + lang)
# Invoke skslc on every target that needs to be compiled.
# Invoke skslc, passing in the worklist.
worklist.close()
try:
output = subprocess.check_output(skslcArgs, stderr=subprocess.STDOUT)
output = subprocess.check_output([skslc, worklist.name], stderr=subprocess.STDOUT)
except subprocess.CalledProcessError as err:
if err.returncode != 1:
print("### skslc error:\n")
print("\n".join(err.output.splitlines()))
sys.exit(err.returncode)
pass # Compile errors (exit code 1) are expected and normal in test code
print("### skslc error:\n")
print("\n".join(err.output.splitlines()))
# A special case cleanup pass, just for CPP and H files: if either one of these files starts with
# `### Compilation failed`, its sibling should be replaced by an empty file. This improves clarity

View File

@ -33,13 +33,6 @@ namespace SkOpts {
decltype(hash_fn) hash_fn = skslc_standalone::hash_fn;
}
enum class ResultCode {
kSuccess = 0,
kCompileError = 1,
kInputError = 2,
kOutputError = 3,
};
// Given the path to a file (e.g. src/gpu/effects/GrFooFragmentProcessor.fp) and the expected
// filename prefix and suffix (e.g. "Gr" and ".fp"), returns the "base name" of the
// file (in this case, 'FooFragmentProcessor'). If no match, returns the empty string.
@ -209,7 +202,8 @@ static bool detect_shader_settings(const SkSL::String& text,
* Displays a usage banner; used when the command line arguments don't make sense.
*/
static void show_usage() {
printf("usage: skslc <input> <output> <flags> -- <input2> <output2> <flags> -- ...\n"
printf("usage: skslc <input> <output> <flags>\n"
" skslc <worklist>\n"
"\n"
"Allowed flags:\n"
"--settings: honor embedded /*#pragma settings*/ comments.\n"
@ -219,7 +213,7 @@ static void show_usage() {
/**
* Handle a single input.
*/
ResultCode processCommand(std::vector<SkSL::String>& args) {
int processCommand(std::vector<SkSL::String>& args, bool writeErrorsToOutputFile) {
bool honorSettings = true;
if (args.size() == 4) {
// Handle four-argument case: `skslc in.sksl out.glsl --settings`
@ -231,11 +225,11 @@ ResultCode processCommand(std::vector<SkSL::String>& args) {
} else {
printf("unrecognized flag: %s\n\n", settingsArg.c_str());
show_usage();
return ResultCode::kInputError;
return 1;
}
} else if (args.size() != 3) {
show_usage();
return ResultCode::kInputError;
return 1;
}
SkSL::Program::Kind kind;
@ -253,7 +247,7 @@ ResultCode processCommand(std::vector<SkSL::String>& args) {
} else {
printf("input filename must end in '.vert', '.frag', '.geom', '.fp', '.stage', or "
"'.sksl'\n");
return ResultCode::kInputError;
return 1;
}
std::ifstream in(inputPath);
@ -261,27 +255,30 @@ ResultCode processCommand(std::vector<SkSL::String>& args) {
std::istreambuf_iterator<char>());
if (in.rdstate()) {
printf("error reading '%s'\n", inputPath.c_str());
return ResultCode::kInputError;
return 2;
}
SkSL::Program::Settings settings;
const SkSL::ShaderCapsClass* caps = &SkSL::standaloneCaps;
if (honorSettings) {
if (!detect_shader_settings(text, &settings, &caps)) {
return ResultCode::kInputError;
return 3;
}
}
const SkSL::String& outputPath = args[2];
auto emitCompileError = [&](SkSL::FileOutputStream& out, const char* errorText) {
// Overwrite the compiler output, if any, with an error message.
out.close();
SkSL::FileOutputStream errorStream(outputPath);
errorStream.writeText("### Compilation failed:\n\n");
errorStream.writeText(errorText);
errorStream.close();
// Also emit the error directly to stdout.
puts(errorText);
if (writeErrorsToOutputFile) {
// Overwrite the compiler output, if any, with an error message.
out.close();
SkSL::FileOutputStream errorStream(outputPath);
errorStream.writeText("### Compilation failed:\n\n");
errorStream.writeText(errorText);
errorStream.close();
} else {
// Emit the error directly to stdout.
puts(errorText);
}
};
if (outputPath.endsWith(".spirv")) {
@ -289,89 +286,89 @@ ResultCode processCommand(std::vector<SkSL::String>& args) {
SkSL::Compiler compiler(caps);
if (!out.isValid()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
if (!program || !compiler.toSPIRV(*program, out)) {
emitCompileError(out, compiler.errorText().c_str());
return ResultCode::kCompileError;
return 3;
}
if (!out.close()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
} else if (outputPath.endsWith(".glsl")) {
SkSL::FileOutputStream out(outputPath);
SkSL::Compiler compiler(caps);
if (!out.isValid()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
if (!program || !compiler.toGLSL(*program, out)) {
emitCompileError(out, compiler.errorText().c_str());
return ResultCode::kCompileError;
return 3;
}
if (!out.close()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
} else if (outputPath.endsWith(".metal")) {
SkSL::FileOutputStream out(outputPath);
SkSL::Compiler compiler(caps);
if (!out.isValid()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
if (!program || !compiler.toMetal(*program, out)) {
emitCompileError(out, compiler.errorText().c_str());
return ResultCode::kCompileError;
return 3;
}
if (!out.close()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
} else if (outputPath.endsWith(".h")) {
SkSL::FileOutputStream out(outputPath);
SkSL::Compiler compiler(caps, SkSL::Compiler::kPermitInvalidStaticTests_Flag);
if (!out.isValid()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
settings.fReplaceSettings = false;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
if (!program || !compiler.toH(*program, base_name(inputPath.c_str(), "Gr", ".fp"), out)) {
emitCompileError(out, compiler.errorText().c_str());
return ResultCode::kCompileError;
return 3;
}
if (!out.close()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
} else if (outputPath.endsWith(".cpp")) {
SkSL::FileOutputStream out(outputPath);
SkSL::Compiler compiler(caps, SkSL::Compiler::kPermitInvalidStaticTests_Flag);
if (!out.isValid()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
settings.fReplaceSettings = false;
std::unique_ptr<SkSL::Program> program = compiler.convertProgram(kind, text, settings);
if (!program || !compiler.toCPP(*program, base_name(inputPath.c_str(), "Gr", ".fp"), out)) {
emitCompileError(out, compiler.errorText().c_str());
return ResultCode::kCompileError;
return 3;
}
if (!out.close()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
} else if (outputPath.endsWith(".dehydrated.sksl")) {
SkSL::FileOutputStream out(outputPath);
SkSL::Compiler compiler(caps);
if (!out.isValid()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
auto [symbols, elements] = compiler.loadModule(
kind, SkSL::Compiler::MakeModulePath(inputPath.c_str()), nullptr);
@ -391,29 +388,43 @@ ResultCode processCommand(std::vector<SkSL::String>& args) {
baseName.c_str(), baseName.c_str());
if (!out.close()) {
printf("error writing '%s'\n", outputPath.c_str());
return ResultCode::kOutputError;
return 4;
}
} else {
printf("expected output filename to end with '.spirv', '.glsl', '.cpp', '.h', or '.metal'");
return ResultCode::kInputError; // the "output filename" is still an input argument
return 1;
}
return ResultCode::kSuccess;
return 0;
}
int main(int argc, const char** argv) {
// Search the command line for -- delimiters. When a -- is reached, we process one command.
std::vector<SkSL::String> args = {argv[0]};
auto resultCode = ResultCode::kSuccess;
for (int index = 1; index < argc; ++index) {
SkSL::String arg = argv[index];
if (arg != "--") {
/**
* Processes multiple inputs in a single invocation of skslc.
*/
int processWorklist(const char* worklistPath) {
SkSL::String inputPath(worklistPath);
if (!inputPath.endsWith(".worklist")) {
printf("expected .worklist file, found: %s\n\n", worklistPath);
show_usage();
return 1;
}
// The worklist contains one line per argument to pass to skslc. When a blank line is reached,
// those arguments will be passed to `processCommand`.
std::vector<SkSL::String> args = {"skslc"};
std::ifstream in(worklistPath);
for (SkSL::String line; std::getline(in, line); ) {
if (in.rdstate()) {
printf("error reading '%s'\n", worklistPath);
return 2;
}
if (!line.empty()) {
// We found an argument. Remember it.
args.push_back(std::move(arg));
args.push_back(std::move(line));
} else {
// We found a delimiter. If we have any arguments stored up, process them as a command.
if (args.size() > 1) {
ResultCode outcome = processCommand(args);
resultCode = std::max(resultCode, outcome);
// We found a blank line. If we have any arguments stored up, process them as a command.
if (!args.empty()) {
processCommand(args, /*writeErrorsToOutputFile=*/true);
// Clear every argument except the first ("skslc").
args.resize(1);
@ -421,14 +432,26 @@ int main(int argc, const char** argv) {
}
}
// Execute the final command in the batch.
// If the worklist ended with a list of arguments but no blank line, process those now.
if (args.size() > 1) {
ResultCode outcome = processCommand(args);
resultCode = std::max(resultCode, outcome);
processCommand(args, /*writeErrorsToOutputFile=*/true);
}
// Return the "worst" status we encountered. For our purposes, compilation errors are the least
// serious, because they are expected to occur in unit tests. Other types of errors are not
// expected at all during a build.
return (int) resultCode;
return 0;
}
int main(int argc, const char** argv) {
if (argc == 2) {
// Worklists are the only two-argument case for skslc, and we don't intend to support
// nested worklists, so we can process them here.
return processWorklist(argv[1]);
} else {
// Process non-worklist inputs.
std::vector<SkSL::String> args;
for (int index=0; index<argc; ++index) {
args.push_back(argv[index]);
}
return processCommand(args, /*writeErrorsToOutputFile=*/false);
}
}