Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 11 additions & 13 deletions modules/UseCorrade.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -506,19 +506,17 @@ function(corrade_add_resource name configurationFile)
message(FATAL_ERROR "The Corrade::rc target, needed by corrade_add_resource() and corrade_add_static_plugin(), doesn't exist. Add the Utility / rc component to your find_package() or enable WITH_UTILITY / WITH_RC if you have Corrade as a CMake subproject.")
endif()

# Parse dependencies from the file
set(dependencies )
set(filenameRegex "^[ \t]*filename[ \t]*=[ \t]*\"?([^\"]+)\"?[ \t]*$")
get_filename_component(configurationFilePath ${configurationFile} PATH)

file(STRINGS "${configurationFile}" files REGEX ${filenameRegex} ENCODING UTF-8)
foreach(file ${files})
string(REGEX REPLACE ${filenameRegex} "\\1" filename "${file}")
if(NOT IS_ABSOLUTE "${filename}" AND configurationFilePath)
set(filename "${configurationFilePath}/${filename}")
endif()
list(APPEND dependencies "${filename}")
endforeach()
# Get dependency information for proper incremental rebuilds
execute_process(
# TODO yeah can't use a target name here, and of course what would this
# do if the corrade-rc binary isn't even built yet?!
COMMAND Corrade::rc ${name} --dependencies ${configurationFile}
RESULT_VARIABLE dependencies
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
message(STATUS "eyy" ${dependencies})
# In the unlikely case where there would be ; in the filenames themselves
string(REGEX REPLACE ";" "\\\\;" dependencies "${dependencies}")
string(REGEX REPLACE "\n" ";" dependencies "${dependencies}")

# Force IDEs display also the resource files in project view
add_custom_target(${name}-dependencies SOURCES ${dependencies})
Expand Down
1 change: 1 addition & 0 deletions src/Corrade/Utility/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,7 @@ if(NOT CMAKE_CROSSCOMPILING)
Debug.cpp
Configuration.cpp
ConfigurationGroup.cpp
ConfigurationValue.cpp
Format.cpp
Path.cpp
String.cpp
Expand Down
34 changes: 34 additions & 0 deletions src/Corrade/Utility/Implementation/ResourceCompile.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
#include <sstream>
#include <vector>

#include "Corrade/Containers/Array.h"
#include "Corrade/Containers/GrowableArray.h"
#include "Corrade/Containers/Optional.h"
#include "Corrade/Containers/Pair.h"
#include "Corrade/Utility/Configuration.h"
Expand Down Expand Up @@ -248,6 +250,38 @@ std::string resourceCompileFrom(const std::string& name, const std::string& conf
return resourceCompile(name, group, fileData);
}

Containers::Optional<Containers::Array<Containers::String>> resourceDependencies(const Containers::StringView configurationFile) {
if(!Path::exists(configurationFile)) {
Error() << " Error: file" << configurationFile << "does not exist";
return {};
}

const Containers::StringView path = Path::split(configurationFile).first();
const Configuration conf(configurationFile, Configuration::Flag::ReadOnly);

/* A subset of what's done in resourceCompileFrom(), keep in sync */
std::vector<const ConfigurationGroup*> files = conf.groups("file");
Containers::Array<Containers::String> out;
arrayReserve(out, files.size());
for(const auto file: files) {
const Containers::StringView filename = file->value<Containers::StringView>("filename");

/* This would mean printing a whole directory on the output with
unforeseeable consequences for unsuspecting build systems, so fail
in that case */
if(!filename) {
Error() << " Error: filename of file" << out.size() + 1 << "in group" << conf.value("group") << "is empty";
return {};
}

/* Absolute paths to keep it as simple as possible for the consumers */
arrayAppend(out, Path::join(path, filename));
}

/* GCC 4.8 and Clang 3.8 need extra help here */
return Containers::optional(std::move(out));
}

}}}}

#endif
35 changes: 34 additions & 1 deletion src/Corrade/Utility/Test/ResourceCompileTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,32 +116,61 @@ void ResourceCompileTest::compileFrom() {
CORRADE_COMPARE_AS(Implementation::resourceCompileFrom("ResourceTestData", conf),
Path::join(RESOURCE_TEST_DIR, "compiled.cpp"),
TestSuite::Compare::StringToFile);

Containers::Optional<Containers::Array<Containers::String>> dependencies = Implementation::resourceDependencies(conf);
CORRADE_VERIFY(dependencies);
CORRADE_COMPARE_AS(*dependencies, Containers::arrayView<Containers::String>({
Path::join(RESOURCE_TEST_DIR, "../ResourceTestFiles/predisposition.bin"),
Path::join(RESOURCE_TEST_DIR, "consequence.bin")
}), TestSuite::Compare::Container);
}

void ResourceCompileTest::compileFromNothing() {
Containers::String conf = Path::join(RESOURCE_TEST_DIR, "resources-nothing.conf");
CORRADE_COMPARE_AS(Implementation::resourceCompileFrom("ResourceTestNothingData", conf),
Path::join(RESOURCE_TEST_DIR, "compiled-nothing.cpp"),
TestSuite::Compare::StringToFile);

Containers::Optional<Containers::Array<Containers::String>> dependencies = Implementation::resourceDependencies(conf);
CORRADE_VERIFY(dependencies);
CORRADE_COMPARE_AS(*dependencies, Containers::arrayView<Containers::String>({
}), TestSuite::Compare::Container);
}

void ResourceCompileTest::compileFromUtf8Filenames() {
Containers::String conf = Path::join(RESOURCE_TEST_DIR, "hýždě.conf");
CORRADE_COMPARE_AS(Implementation::resourceCompileFrom("ResourceTestUtf8Data", conf),
Path::join(RESOURCE_TEST_DIR, "compiled-unicode.cpp"),
TestSuite::Compare::StringToFile);

Containers::Optional<Containers::Array<Containers::String>> dependencies = Implementation::resourceDependencies(conf);
CORRADE_VERIFY(dependencies);
CORRADE_COMPARE_AS(*dependencies, Containers::arrayView<Containers::String>({
Path::join(RESOURCE_TEST_DIR, "hýždě.bin")
}), TestSuite::Compare::Container);
}

void ResourceCompileTest::compileFromNonexistentResource() {
std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(Implementation::resourceCompileFrom("ResourceTestData", "nonexistent.conf").empty());
CORRADE_COMPARE(out.str(), " Error: file nonexistent.conf does not exist\n");
CORRADE_VERIFY(!Implementation::resourceDependencies("nonexistent.conf"));
CORRADE_COMPARE(out.str(),
" Error: file nonexistent.conf does not exist\n"
" Error: file nonexistent.conf does not exist\n");
}

void ResourceCompileTest::compileFromNonexistentFile() {
Containers::String conf = Path::join(RESOURCE_TEST_DIR, "resources-nonexistent.conf");

/* In this case the file existence is not checked, as the file could an
output of another buildsystem job */
Containers::Optional<Containers::Array<Containers::String>> dependencies = Implementation::resourceDependencies(conf);
CORRADE_VERIFY(dependencies);
CORRADE_COMPARE_AS(*dependencies, Containers::arrayView<Containers::String>({
"/nonexistent.dat"
}), TestSuite::Compare::Container);

std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(Implementation::resourceCompileFrom("ResourceTestData", conf).empty());
Expand All @@ -152,6 +181,8 @@ void ResourceCompileTest::compileFromNonexistentFile() {
}

void ResourceCompileTest::compileFromEmptyGroup() {
/* Group name has no effect on dependency lists, not testing */

std::ostringstream out;
Error redirectError{&out};

Expand All @@ -175,6 +206,8 @@ void ResourceCompileTest::compileFromEmptyFilename() {
}

void ResourceCompileTest::compileFromEmptyAlias() {
/* Alias name has no effect on dependency lists, not testing */

std::ostringstream out;
Error redirectError{&out};
CORRADE_VERIFY(Implementation::resourceCompileFrom("ResourceTestData",
Expand Down
39 changes: 35 additions & 4 deletions src/Corrade/Utility/rc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,16 @@ corrade-rc [-h|--help] [--] name resources.conf output.cpp
- `name` --- exported symbol name
- `resources.conf` --- resource configuration file (see @ref Utility::Resource
for format description)
- `output.cpp` --- output file
- `output.cpp` --- output file; ignored if `--dependencies` is present
- `--dependencies` --- print a list of files the compiled resource is made of
and exit
- `-h`, `--help` --- display this help message and exit

The `--dependencies` option is meant to be used by build systems to track
transitive dependencies. It prints one file path per line with the path to the
`resources.conf` prepended -- if it's passed as an absolute path, the printed
paths are absolute, if not then relative. The configuration file itself is not
a part of the printed list.
*/

}
Expand All @@ -79,11 +87,34 @@ int main(int argc, char** argv) {
Utility::Arguments args;
args.addArgument("name").setHelp("name", "exported symbol name")
.addArgument("conf").setHelp("conf", "resource configuration file", "resources.conf")
.addArgument("output").setHelp("output", "output file", "output.cpp")
.setCommand("corrade-rc")
.setGlobalHelp("Corrade resource compiler.")
.addArgument("output").setHelp("output", "output file; ignored if --dependencies is present", "output.cpp")
.addBooleanOption("dependencies").setHelp("dependencies", "print a list of files the compiled resource is made of and exit")
.setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) {
/* If --info is passed, we don't need the output argument */
if(error == Utility::Arguments::ParseError::MissingArgument &&
key == "output" && args.isSet("dependencies")) return true;

/* Handle all other errors as usual */
return false;
})
.setGlobalHelp(R"(Corrade resource compiler.

The --dependencies option is meant to be used by build systems to track
transitive dependencies. It prints one file path per line with the path to the
resources.conf prepended -- if it's passed as an absolute path, the printed
paths are absolute, if not then relative. The configuration file itself is not
a part of the printed list.)")
.parse(argc, argv);

/* Print dependencies and exit, if requested */
if(args.isSet("dependencies")) {
Containers::Optional<Containers::Array<Containers::String>> dependencies = Utility::Implementation::resourceDependencies(args.value("conf"));
if(!dependencies) return 4;
for(const Containers::String& filename: *dependencies)
Utility::Debug{} << filename;
return 0;
}

/* Remove previous output file. Only if it exists, to not print an error
message when compiling for the first time. If it fails, die as well --
we'd not succeed after either. */
Expand Down