diff --git a/source/dub/commandline.d b/source/dub/commandline.d index ad71aab..519f384 100644 --- a/source/dub/commandline.d +++ b/source/dub/commandline.d @@ -750,6 +750,7 @@ bool m_importPaths = false; bool m_stringImportPaths = false; bool m_dataList = false; + bool m_dataNullDelim = false; string[] m_data; } @@ -809,6 +810,11 @@ "Output --data information in list format (line-by-line), instead "~ "of formatting for a compiler command line.", ]); + + args.getopt("data-0", &m_dataNullDelim, [ + "Output --data information using null-delimiters, rather than "~ + "spaces or newlines. Result is usable with, ex., xargs -0.", + ]); } override int execute(Dub dub, string[] free_args, string[] app_args) @@ -838,11 +844,12 @@ auto config = m_buildConfig.length ? m_buildConfig : m_defaultConfig; if (m_importPaths) { - dub.listImportPaths(m_buildPlatform, config, m_buildType); + dub.listImportPaths(m_buildPlatform, config, m_buildType, m_dataNullDelim); } else if (m_stringImportPaths) { - dub.listStringImportPaths(m_buildPlatform, config, m_buildType); + dub.listStringImportPaths(m_buildPlatform, config, m_buildType, m_dataNullDelim); } else if (m_data) { - dub.listProjectData(m_buildPlatform, config, m_buildType, m_data, m_dataList? null : m_compiler); + dub.listProjectData(m_buildPlatform, config, m_buildType, m_data, + m_dataList? null : m_compiler, m_dataNullDelim); } else { auto desc = dub.project.describe(m_buildPlatform, config, m_buildType); writeln(desc.serializeToPrettyJson()); diff --git a/source/dub/dub.d b/source/dub/dub.d index cd2d1a4..1280a26 100644 --- a/source/dub/dub.d +++ b/source/dub/dub.d @@ -421,31 +421,34 @@ writeln(desc.serializeToPrettyJson()); } - void listImportPaths(BuildPlatform platform, string config, string buildType) + void listImportPaths(BuildPlatform platform, string config, string buildType, bool nullDelim) { import std.stdio; - foreach(path; m_project.listImportPaths(platform, config, buildType)) { + foreach(path; m_project.listImportPaths(platform, config, buildType, nullDelim)) { writeln(path); } } - void listStringImportPaths(BuildPlatform platform, string config, string buildType) + void listStringImportPaths(BuildPlatform platform, string config, string buildType, bool nullDelim) { import std.stdio; - foreach(path; m_project.listStringImportPaths(platform, config, buildType)) { + foreach(path; m_project.listStringImportPaths(platform, config, buildType, nullDelim)) { writeln(path); } } - void listProjectData(BuildPlatform platform, string config, string buildType, string[] requestedData, Compiler formattingCompiler) + void listProjectData(BuildPlatform platform, string config, string buildType, + string[] requestedData, Compiler formattingCompiler, bool nullDelim) { import std.stdio; + import std.ascii : newline; - foreach(data; m_project.listBuildSettings(platform, config, buildType, requestedData, formattingCompiler)) { - writeln(data); - } + auto data = m_project.listBuildSettings(platform, config, buildType, requestedData, formattingCompiler, nullDelim); + write( data.joiner(nullDelim? "\0" : newline) ); + if(!nullDelim) + writeln(); } /// Cleans intermediate/cache files of the given package diff --git a/source/dub/project.d b/source/dub/project.d index 4219c27..1080705 100644 --- a/source/dub/project.d +++ b/source/dub/project.d @@ -620,21 +620,25 @@ dst[key] = value; } - private string[] listBuildSetting(string attributeName)(BuildPlatform platform, string config, ProjectDescription projectDescription, Compiler compiler) + private string[] listBuildSetting(string attributeName)(BuildPlatform platform, + string config, ProjectDescription projectDescription, Compiler compiler, bool disableEscaping) { - return listBuildSetting!attributeName(platform, getPackageConfigs(platform, config), projectDescription, compiler); + return listBuildSetting!attributeName(platform, getPackageConfigs(platform, config), + projectDescription, compiler, disableEscaping); } - private string[] listBuildSetting(string attributeName)(BuildPlatform platform, string[string] configs, ProjectDescription projectDescription, Compiler compiler) + private string[] listBuildSetting(string attributeName)(BuildPlatform platform, + string[string] configs, ProjectDescription projectDescription, Compiler compiler, bool disableEscaping) { if (compiler) - return formatBuildSettingCompiler!attributeName(platform, configs, projectDescription, compiler); + return formatBuildSettingCompiler!attributeName(platform, configs, projectDescription, compiler, disableEscaping); else return formatBuildSettingPlain!attributeName(platform, configs, projectDescription); } // Output a build setting formatted for a compiler - private string[] formatBuildSettingCompiler(string attributeName)(BuildPlatform platform, string[string] configs, ProjectDescription projectDescription, Compiler compiler) + private string[] formatBuildSettingCompiler(string attributeName)(BuildPlatform platform, + string[string] configs, ProjectDescription projectDescription, Compiler compiler, bool disableEscaping) { import std.process : escapeShellFileName; import std.path : dirSeparator; @@ -715,21 +719,26 @@ } // Escape filenames and paths - switch (attributeName) + if(!disableEscaping) { - case "mainSourceFile": - case "libFiles": - case "copyFiles": - case "importFiles": - case "stringImportFiles": - case "sourceFiles": - case "importPaths": - case "stringImportPaths": - return values.map!(escapeShellFileName).array(); - - default: - return values; + switch (attributeName) + { + case "mainSourceFile": + case "libFiles": + case "copyFiles": + case "importFiles": + case "stringImportFiles": + case "sourceFiles": + case "importPaths": + case "stringImportPaths": + return values.map!(escapeShellFileName).array(); + + default: + return values; + } } + + return values; } // Output a build setting without formatting for any particular compiler @@ -826,7 +835,7 @@ // The "compiler" arg is for choosing which compiler the output should be formatted for, // or null to imply "list" format. private string[] listBuildSetting(BuildPlatform platform, string[string] configs, - ProjectDescription projectDescription, string requestedData, Compiler compiler) + ProjectDescription projectDescription, string requestedData, Compiler compiler, bool disableEscaping) { // Certain data cannot be formatter for a compiler if (compiler) @@ -854,31 +863,33 @@ } } + import std.typetuple : TypeTuple; + auto args = TypeTuple!(platform, configs, projectDescription, compiler, disableEscaping); switch (requestedData) { - case "target-type": return listBuildSetting!"targetType"(platform, configs, projectDescription, compiler); - case "target-path": return listBuildSetting!"targetPath"(platform, configs, projectDescription, compiler); - case "target-name": return listBuildSetting!"targetName"(platform, configs, projectDescription, compiler); - case "working-directory": return listBuildSetting!"workingDirectory"(platform, configs, projectDescription, compiler); - case "main-source-file": return listBuildSetting!"mainSourceFile"(platform, configs, projectDescription, compiler); - case "dflags": return listBuildSetting!"dflags"(platform, configs, projectDescription, compiler); - case "lflags": return listBuildSetting!"lflags"(platform, configs, projectDescription, compiler); - case "libs": return listBuildSetting!"libs"(platform, configs, projectDescription, compiler); - case "lib-files": return listBuildSetting!"libFiles"(platform, configs, projectDescription, compiler); - case "source-files": return listBuildSetting!"sourceFiles"(platform, configs, projectDescription, compiler); - case "copy-files": return listBuildSetting!"copyFiles"(platform, configs, projectDescription, compiler); - case "versions": return listBuildSetting!"versions"(platform, configs, projectDescription, compiler); - case "debug-versions": return listBuildSetting!"debugVersions"(platform, configs, projectDescription, compiler); - case "import-paths": return listBuildSetting!"importPaths"(platform, configs, projectDescription, compiler); - case "string-import-paths": return listBuildSetting!"stringImportPaths"(platform, configs, projectDescription, compiler); - case "import-files": return listBuildSetting!"importFiles"(platform, configs, projectDescription, compiler); - case "string-import-files": return listBuildSetting!"stringImportFiles"(platform, configs, projectDescription, compiler); - case "pre-generate-commands": return listBuildSetting!"preGenerateCommands"(platform, configs, projectDescription, compiler); - case "post-generate-commands": return listBuildSetting!"postGenerateCommands"(platform, configs, projectDescription, compiler); - case "pre-build-commands": return listBuildSetting!"preBuildCommands"(platform, configs, projectDescription, compiler); - case "post-build-commands": return listBuildSetting!"postBuildCommands"(platform, configs, projectDescription, compiler); - case "requirements": return listBuildSetting!"requirements"(platform, configs, projectDescription, compiler); - case "options": return listBuildSetting!"options"(platform, configs, projectDescription, compiler); + case "target-type": return listBuildSetting!"targetType"(args); + case "target-path": return listBuildSetting!"targetPath"(args); + case "target-name": return listBuildSetting!"targetName"(args); + case "working-directory": return listBuildSetting!"workingDirectory"(args); + case "main-source-file": return listBuildSetting!"mainSourceFile"(args); + case "dflags": return listBuildSetting!"dflags"(args); + case "lflags": return listBuildSetting!"lflags"(args); + case "libs": return listBuildSetting!"libs"(args); + case "lib-files": return listBuildSetting!"libFiles"(args); + case "source-files": return listBuildSetting!"sourceFiles"(args); + case "copy-files": return listBuildSetting!"copyFiles"(args); + case "versions": return listBuildSetting!"versions"(args); + case "debug-versions": return listBuildSetting!"debugVersions"(args); + case "import-paths": return listBuildSetting!"importPaths"(args); + case "string-import-paths": return listBuildSetting!"stringImportPaths"(args); + case "import-files": return listBuildSetting!"importFiles"(args); + case "string-import-files": return listBuildSetting!"stringImportFiles"(args); + case "pre-generate-commands": return listBuildSetting!"preGenerateCommands"(args); + case "post-generate-commands": return listBuildSetting!"postGenerateCommands"(args); + case "pre-build-commands": return listBuildSetting!"preBuildCommands"(args); + case "post-build-commands": return listBuildSetting!"postBuildCommands"(args); + case "requirements": return listBuildSetting!"requirements"(args); + case "options": return listBuildSetting!"options"(args); default: enforce(false, "--data="~requestedData~ @@ -889,7 +900,8 @@ } /// Outputs requested data for the project, optionally including its dependencies. - string[] listBuildSettings(BuildPlatform platform, string config, string buildType, string[] requestedData, Compiler formattingCompiler) + string[] listBuildSettings(BuildPlatform platform, string config, string buildType, + string[] requestedData, Compiler formattingCompiler, bool nullDelim) { auto projectDescription = describe(platform, config, buildType); auto configs = getPackageConfigs(platform, config); @@ -920,32 +932,32 @@ // Format for a compiler return [ requestedData - .map!(dataName => listBuildSetting(platform, configs, projectDescription, dataName, formattingCompiler)) - .join().join(" ") + .map!(dataName => listBuildSetting(platform, configs, projectDescription, dataName, formattingCompiler, nullDelim)) + .join().join(nullDelim? "\0" : " ") ]; } else { // Format list-style return requestedData - .map!(dataName => listBuildSetting(platform, configs, projectDescription, dataName, null)) - .joiner([""]) // Blank line between each type of requestedData + .map!(dataName => listBuildSetting(platform, configs, projectDescription, dataName, null, nullDelim)) + .joiner([""]) // Blank entry between each type of requestedData .array(); } } /// Outputs the import paths for the project, including its dependencies. - string[] listImportPaths(BuildPlatform platform, string config, string buildType) + string[] listImportPaths(BuildPlatform platform, string config, string buildType, bool nullDelim) { auto projectDescription = describe(platform, config, buildType); - return listBuildSetting!"importPaths"(platform, config, projectDescription, null); + return listBuildSetting!"importPaths"(platform, config, projectDescription, null, nullDelim); } /// Outputs the string import paths for the project, including its dependencies. - string[] listStringImportPaths(BuildPlatform platform, string config, string buildType) + string[] listStringImportPaths(BuildPlatform platform, string config, string buildType, bool nullDelim) { auto projectDescription = describe(platform, config, buildType); - return listBuildSetting!"stringImportPaths"(platform, config, projectDescription, null); + return listBuildSetting!"stringImportPaths"(platform, config, projectDescription, null, nullDelim); } void saveSelections() diff --git a/test/4-describe-data-zero-delim.sh b/test/4-describe-data-zero-delim.sh new file mode 100755 index 0000000..abc1446 --- /dev/null +++ b/test/4-describe-data-zero-delim.sh @@ -0,0 +1,121 @@ +#!/bin/bash + +set -e -o pipefail + +cd "$CURR_DIR"/describe-project + +temp_file_normal=`mktemp` +temp_file_zero_delim=`mktemp` + +function cleanup { + rm $temp_file_normal + rm $temp_file_zero_delim +} + +trap cleanup EXIT + +# Test dmd-style --data=versions +if ! $DUB describe --compiler=dmd --data=versions \ + > "$temp_file_normal"; then + die 'Printing dmd-style --data=versions failed!' +fi + +if ! $DUB describe --compiler=dmd --data-0 --data=versions \ + | xargs -0 printf "%s " > "$temp_file_zero_delim"; then + die 'Printing null-delimited dmd-style --data=versions failed!' +fi + +if ! diff -Z "$temp_file_normal" "$temp_file_zero_delim"; then + die 'The null-delimited dmd-style --data=versions did not match the expected output!' +fi + +# Test dmd-style --data=source-files +if ! $DUB describe --compiler=dmd --data=source-files \ + > "$temp_file_normal"; then + die 'Printing dmd-style --data=source-files failed!' +fi + +if ! $DUB describe --compiler=dmd --data-0 --data=source-files \ + | xargs -0 printf "'%s' " > "$temp_file_zero_delim"; then + die 'Printing null-delimited dmd-style --data=source-files failed!' +fi + +if ! diff -Z "$temp_file_normal" "$temp_file_zero_delim"; then + die 'The null-delimited dmd-style --data=source-files did not match the expected output!' +fi + +# Test list-style project data +if ! $DUB describe --compiler=dmd --data-list \ + --data=target-type \ + --data=target-path \ + --data=target-name \ + --data=working-directory \ + --data=main-source-file \ + --data=dflags \ + --data=lflags \ + --data=libs \ + --data=lib-files \ + --data=source-files \ + --data=copy-files \ + --data=versions \ + --data=debug-versions \ + --data=import-paths \ + --data=string-import-paths \ + --data=import-files \ + --data=string-import-files \ + --data=pre-generate-commands \ + --data=post-generate-commands \ + --data=pre-build-commands \ + --data=post-build-commands \ + --data=requirements \ + --data=options \ + > "$temp_file_normal"; then + die 'Printing list-style project data failed!' +fi + +if ! $DUB describe --compiler=dmd --data-0 --data-list \ + --data=target-type \ + --data=target-path \ + --data=target-name \ + --data=working-directory \ + --data=main-source-file \ + --data=dflags \ + --data=lflags \ + --data=libs \ + --data=lib-files \ + --data=source-files \ + --data=copy-files \ + --data=versions \ + --data=debug-versions \ + --data=import-paths \ + --data=string-import-paths \ + --data=import-files \ + --data=string-import-files \ + --data=pre-generate-commands \ + --data=post-generate-commands \ + --data=pre-build-commands \ + --data=post-build-commands \ + --data=requirements \ + --data=options \ + | xargs -0 printf "%s\n" > "$temp_file_zero_delim"; then + die 'Printing null-delimited list-style project data failed!' +fi + +if ! diff -Z "$temp_file_normal" "$temp_file_zero_delim"; then + die 'The null-delimited list-style project data did not match the expected output!' +fi + +# Test --import-paths +if ! $DUB describe --compiler=dmd --import-paths \ + > "$temp_file_normal"; then + die 'Printing --import-paths failed!' +fi + +if ! $DUB describe --compiler=dmd --data-0 --import-paths \ + | xargs -0 printf "%s\n" > "$temp_file_zero_delim"; then + die 'Printing null-delimited --import-paths failed!' +fi + +if ! diff -Z -B "$temp_file_normal" "$temp_file_zero_delim"; then + die 'The null-delimited --import-paths data did not match the expected output!' +fi