summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicolas Despres <nicolas.despres@gmail.com>2016-05-14 01:18:20 +0200
committerBrad King <brad.king@kitware.com>2016-05-17 09:34:12 -0400
commit8a862a4d4b852c9f61ae4ed7fc46042b00a83123 (patch)
tree591906a0300c9619e57394105455d93126dab939
parent038e7716e58e4cf79bda6ba72b92814a14978a8f (diff)
downloadcmake-8a862a4d4b852c9f61ae4ed7fc46042b00a83123.tar.gz
Ninja: Support embedding of CMake as subninja project
Add a `CMAKE_NINJA_OUTPUT_PATH_PREFIX` variable. When it is set, CMake generates a `build.ninja` file suitable for embedding into another ninja project potentially generated by an alien generator.
-rw-r--r--Help/manual/cmake-variables.7.rst1
-rw-r--r--Help/release/dev/ninja-output-path-prefix.rst6
-rw-r--r--Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst27
-rw-r--r--Source/cmAlgorithms.h15
-rw-r--r--Source/cmGlobalNinjaGenerator.cxx47
-rw-r--r--Source/cmGlobalNinjaGenerator.h5
-rw-r--r--Source/cmNinjaTargetGenerator.cxx10
-rw-r--r--Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake7
-rw-r--r--Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake8
-rw-r--r--Tests/RunCMake/Ninja/CheckOutput.cmake23
-rw-r--r--Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake13
-rw-r--r--Tests/RunCMake/Ninja/Executable.cmake5
-rw-r--r--Tests/RunCMake/Ninja/RunCMakeTest.cmake145
-rw-r--r--Tests/RunCMake/Ninja/SharedLib.cmake8
-rw-r--r--Tests/RunCMake/Ninja/StaticLib.cmake9
-rw-r--r--Tests/RunCMake/Ninja/SubDirPrefix.cmake8
-rw-r--r--Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt2
-rw-r--r--Tests/RunCMake/Ninja/SubDirPrefix/greeting.c9
-rw-r--r--Tests/RunCMake/Ninja/SubDirPrefix/greeting.h4
-rw-r--r--Tests/RunCMake/Ninja/TwoLibs.cmake17
-rw-r--r--Tests/RunCMake/Ninja/greeting.c9
-rw-r--r--Tests/RunCMake/Ninja/greeting.h4
-rw-r--r--Tests/RunCMake/Ninja/greeting2.c6
-rw-r--r--Tests/RunCMake/Ninja/greeting2.h1
-rw-r--r--Tests/RunCMake/Ninja/hello.c7
-rw-r--r--Tests/RunCMake/Ninja/hello_sub_greeting.c7
-rw-r--r--Tests/RunCMake/Ninja/hello_with_greeting.c7
-rw-r--r--Tests/RunCMake/Ninja/hello_with_two_greetings.c9
28 files changed, 412 insertions, 7 deletions
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 181fbbcd1f..ec741ae12c 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -281,6 +281,7 @@ Variables that Control the Build
/variable/CMAKE_MAP_IMPORTED_CONFIG_CONFIG
/variable/CMAKE_MODULE_LINKER_FLAGS_CONFIG
/variable/CMAKE_MODULE_LINKER_FLAGS
+ /variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX
/variable/CMAKE_NO_BUILTIN_CHRPATH
/variable/CMAKE_NO_SYSTEM_FROM_IMPORTED
/variable/CMAKE_OSX_ARCHITECTURES
diff --git a/Help/release/dev/ninja-output-path-prefix.rst b/Help/release/dev/ninja-output-path-prefix.rst
new file mode 100644
index 0000000000..47a96603a1
--- /dev/null
+++ b/Help/release/dev/ninja-output-path-prefix.rst
@@ -0,0 +1,6 @@
+ninja-output-path-prefix
+------------------------
+
+* The :generator:`Ninja` generator learned to read a new
+ :variable:`CMAKE_NINJA_OUTPUT_PATH_PREFIX` variable to configure
+ the generated ``build.ninja`` file for use as a ``subninja``.
diff --git a/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst
new file mode 100644
index 0000000000..64091aaaf5
--- /dev/null
+++ b/Help/variable/CMAKE_NINJA_OUTPUT_PATH_PREFIX.rst
@@ -0,0 +1,27 @@
+CMAKE_NINJA_OUTPUT_PATH_PREFIX
+------------------------------
+
+Set output files path prefix for the :generator:`Ninja` generator.
+
+Every output files listed in the generated ``build.ninja`` will be
+prefixed by the contents of this variable (a trailing slash is
+appended if missing). This is useful when the generated ninja file is
+meant to be embedded as a ``subninja`` file into a *super* ninja
+project. For example, a ninja build file generated with a command
+like::
+
+ cd top-build-dir/sub &&
+ cmake -G Ninja -DCMAKE_NINJA_OUTPUT_PATH_PREFIX=sub/ path/to/source
+
+can be embedded in ``top-build-dir/build.ninja`` with a directive like
+this::
+
+ subninja sub/build.ninja
+
+The ``auto-regeneration`` rule in ``top-build-dir/build.ninja`` must have an
+order-only dependency on ``sub/build.ninja``.
+
+.. note::
+ When ``CMAKE_NINJA_OUTPUT_PATH_PREFIX`` is set, the project generated
+ by CMake cannot be used as a standalone project. No default targets
+ are specified.
diff --git a/Source/cmAlgorithms.h b/Source/cmAlgorithms.h
index 76acaca8f6..ee803c8e82 100644
--- a/Source/cmAlgorithms.h
+++ b/Source/cmAlgorithms.h
@@ -379,4 +379,19 @@ std::reverse_iterator<Iter> cmMakeReverseIterator(Iter it)
return std::reverse_iterator<Iter>(it);
}
+inline bool cmHasSuffix(const std::string& str, const std::string& suffix)
+{
+ if (str.size() < suffix.size()) {
+ return false;
+ }
+ return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+inline void cmStripSuffixIfExists(std::string& str, const std::string& suffix)
+{
+ if (cmHasSuffix(str, suffix)) {
+ str.resize(str.size() - suffix.size());
+ }
+}
+
#endif
diff --git a/Source/cmGlobalNinjaGenerator.cxx b/Source/cmGlobalNinjaGenerator.cxx
index 3c5a8e3bba..d65b463ded 100644
--- a/Source/cmGlobalNinjaGenerator.cxx
+++ b/Source/cmGlobalNinjaGenerator.cxx
@@ -488,6 +488,7 @@ void cmGlobalNinjaGenerator::Generate()
this->OpenBuildFileStream();
this->OpenRulesFileStream();
+ this->InitOutputPathPrefix();
this->TargetAll = this->NinjaOutputPath("all");
this->CMakeCacheFile = this->NinjaOutputPath("CMakeCache.txt");
@@ -717,6 +718,23 @@ void cmGlobalNinjaGenerator::CloseRulesFileStream()
}
}
+static void EnsureTrailingSlash(std::string& path)
+{
+ if (path.empty()) {
+ return;
+ }
+ std::string::value_type last = path[path.size() - 1];
+#ifdef _WIN32
+ if (last != '\\') {
+ path += '\\';
+ }
+#else
+ if (last != '/') {
+ path += '/';
+ }
+#endif
+}
+
std::string cmGlobalNinjaGenerator::ConvertToNinjaPath(const std::string& path)
{
cmLocalNinjaGenerator* ng =
@@ -1136,8 +1154,10 @@ void cmGlobalNinjaGenerator::WriteTargetAll(std::ostream& os)
this->WritePhonyBuild(os, "The main all target.", outputs,
this->AllDependencies);
- cmGlobalNinjaGenerator::WriteDefault(os, outputs,
- "Make the all target the default.");
+ if (!this->HasOutputPathPrefix()) {
+ cmGlobalNinjaGenerator::WriteDefault(os, outputs,
+ "Make the all target the default.");
+ }
}
void cmGlobalNinjaGenerator::WriteTargetRebuildManifest(std::ostream& os)
@@ -1251,7 +1271,28 @@ void cmGlobalNinjaGenerator::WriteTargetHelp(std::ostream& os)
/*variables=*/cmNinjaVars());
}
+void cmGlobalNinjaGenerator::InitOutputPathPrefix()
+{
+ this->OutputPathPrefix =
+ this->LocalGenerators[0]->GetMakefile()->GetSafeDefinition(
+ "CMAKE_NINJA_OUTPUT_PATH_PREFIX");
+ EnsureTrailingSlash(this->OutputPathPrefix);
+}
+
std::string cmGlobalNinjaGenerator::NinjaOutputPath(std::string const& path)
{
- return path;
+ if (!this->HasOutputPathPrefix() || cmSystemTools::FileIsFullPath(path)) {
+ return path;
+ }
+ return this->OutputPathPrefix + path;
+}
+
+void cmGlobalNinjaGenerator::StripNinjaOutputPathPrefixAsSuffix(
+ std::string& path)
+{
+ if (path.empty()) {
+ return;
+ }
+ EnsureTrailingSlash(path);
+ cmStripSuffixIfExists(path, this->OutputPathPrefix);
}
diff --git a/Source/cmGlobalNinjaGenerator.h b/Source/cmGlobalNinjaGenerator.h
index 15c0f34526..6d9bfe8358 100644
--- a/Source/cmGlobalNinjaGenerator.h
+++ b/Source/cmGlobalNinjaGenerator.h
@@ -315,6 +315,8 @@ public:
bool SupportsConsolePool() const;
std::string NinjaOutputPath(std::string const& path);
+ bool HasOutputPathPrefix() const { return !this->OutputPathPrefix.empty(); }
+ void StripNinjaOutputPathPrefixAsSuffix(std::string& path);
protected:
virtual void Generate();
@@ -401,6 +403,9 @@ private:
std::string NinjaVersion;
private:
+ void InitOutputPathPrefix();
+
+ std::string OutputPathPrefix;
std::string TargetAll;
std::string CMakeCacheFile;
};
diff --git a/Source/cmNinjaTargetGenerator.cxx b/Source/cmNinjaTargetGenerator.cxx
index 994daeae2a..4d58242a5f 100644
--- a/Source/cmNinjaTargetGenerator.cxx
+++ b/Source/cmNinjaTargetGenerator.cxx
@@ -669,10 +669,12 @@ void cmNinjaTargetGenerator::EnsureDirectoryExists(
if (cmSystemTools::FileIsFullPath(path.c_str())) {
cmSystemTools::MakeDirectory(path.c_str());
} else {
- const std::string fullPath = std::string(this->GetGlobalGenerator()
- ->GetCMakeInstance()
- ->GetHomeOutputDirectory()) +
- "/" + path;
+ cmGlobalNinjaGenerator* gg = this->GetGlobalGenerator();
+ std::string fullPath =
+ std::string(gg->GetCMakeInstance()->GetHomeOutputDirectory());
+ // Also ensures their is a trailing slash.
+ gg->StripNinjaOutputPathPrefixAsSuffix(fullPath);
+ fullPath += path;
cmSystemTools::MakeDirectory(fullPath.c_str());
}
}
diff --git a/Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake b/Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake
new file mode 100644
index 0000000000..b845f2f4a7
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CheckNoPrefixSubDir.cmake
@@ -0,0 +1,7 @@
+add_custom_target(check_no_prefix_sub_dir ALL
+ COMMAND "${CMAKE_COMMAND}"
+ "-DNINJA_OUTPUT_PATH_PREFIX=${CMAKE_NINJA_OUTPUT_PATH_PREFIX}"
+ "-DCUR_BIN_DIR=${CMAKE_CURRENT_BINARY_DIR}"
+ -P "${CMAKE_CURRENT_SOURCE_DIR}/CheckNoPrefixSubDirScript.cmake"
+ VERBATIM
+ )
diff --git a/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake
new file mode 100644
index 0000000000..5a03fcb7e6
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CheckNoPrefixSubDirScript.cmake
@@ -0,0 +1,8 @@
+# Check that the prefix sub-directory is not repeated.
+
+if(EXISTS "${CUR_BIN_DIR}/${NINJA_OUTPUT_PATH_PREFIX}")
+ message(FATAL_ERROR
+ "no sub directory named after the CMAKE_NINJA_OUTPUT_PATH_PREFIX "
+ "should be in the binary directory."
+ )
+endif()
diff --git a/Tests/RunCMake/Ninja/CheckOutput.cmake b/Tests/RunCMake/Ninja/CheckOutput.cmake
new file mode 100644
index 0000000000..ddb35a75ed
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CheckOutput.cmake
@@ -0,0 +1,23 @@
+# Add rules to check the generated executable works.
+
+set(hello_output "${CMAKE_CURRENT_BINARY_DIR}/hello.output")
+add_custom_command(
+ OUTPUT "${hello_output}"
+ COMMAND "$<TARGET_FILE:hello>" > "${hello_output}"
+ DEPENDS hello
+ VERBATIM
+ )
+
+if(NOT DEFINED HELLO_OUTPUT_STRING)
+ set(HELLO_OUTPUT_STRING "Hello world!\n")
+endif()
+
+set(hello_output_ref "${CMAKE_CURRENT_BINARY_DIR}/hello.output.ref")
+file(WRITE "${hello_output_ref}" "${HELLO_OUTPUT_STRING}")
+
+add_custom_target(check_output ALL
+ COMMAND "${CMAKE_COMMAND}" -E compare_files
+ "${hello_output}" "${hello_output_ref}"
+ DEPENDS "${hello_output}" "${hello_output_ref}"
+ VERBATIM
+ )
diff --git a/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake
new file mode 100644
index 0000000000..8e01c8c657
--- /dev/null
+++ b/Tests/RunCMake/Ninja/CustomCommandWorkingDirectory.cmake
@@ -0,0 +1,13 @@
+cmake_minimum_required(VERSION 3.5)
+project(hello NONE)
+
+add_custom_command(
+ OUTPUT hello.copy.c
+ COMMAND "${CMAKE_COMMAND}" -E copy
+ "${CMAKE_CURRENT_SOURCE_DIR}/hello.c"
+ hello.copy.c
+ WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
+ )
+add_custom_target(copy ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/hello.copy.c")
+
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/Executable.cmake b/Tests/RunCMake/Ninja/Executable.cmake
new file mode 100644
index 0000000000..4e17d68898
--- /dev/null
+++ b/Tests/RunCMake/Ninja/Executable.cmake
@@ -0,0 +1,5 @@
+cmake_minimum_required(VERSION 3.5)
+project(hello C)
+add_executable(hello hello.c)
+include(CheckOutput.cmake)
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/RunCMakeTest.cmake b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
index 4e068882df..8160c2dd80 100644
--- a/Tests/RunCMake/Ninja/RunCMakeTest.cmake
+++ b/Tests/RunCMake/Ninja/RunCMakeTest.cmake
@@ -32,3 +32,148 @@ function(run_SubDir)
run_cmake_command(SubDir-build ${CMAKE_COMMAND} --build . --target ${SubDir_all})
endfunction()
run_SubDir()
+
+function(run_ninja dir)
+ execute_process(
+ COMMAND "${RunCMake_MAKE_PROGRAM}"
+ WORKING_DIRECTORY "${dir}"
+ OUTPUT_VARIABLE ninja_stdout
+ ERROR_VARIABLE ninja_stderr
+ RESULT_VARIABLE ninja_result
+ )
+ if(NOT ninja_result EQUAL 0)
+ message(STATUS "
+============ beginning of ninja's stdout ============
+${ninja_stdout}
+=============== end of ninja's stdout ===============
+")
+ message(STATUS "
+============ beginning of ninja's stderr ============
+${ninja_stderr}
+=============== end of ninja's stderr ===============
+")
+ message(FATAL_ERROR
+ "top ninja build failed exited with status ${ninja_result}")
+ endif()
+endfunction(run_ninja)
+
+function(sleep delay)
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -E sleep ${delay}
+ RESULT_VARIABLE result
+ )
+ if(NOT result EQUAL 0)
+ message(FATAL_ERROR "failed to sleep for ${delay} second.")
+ endif()
+endfunction(sleep)
+
+function(touch path)
+ execute_process(
+ COMMAND ${CMAKE_COMMAND} -E touch ${path}
+ RESULT_VARIABLE result
+ )
+ if(NOT result EQUAL 0)
+ message(FATAL_ERROR "failed to touch main ${path} file.")
+ endif()
+endfunction(touch)
+
+macro(ninja_escape_path path out)
+ string(REPLACE "\$ " "\$\$" "${out}" "${path}")
+ string(REPLACE " " "\$ " "${out}" "${${out}}")
+ string(REPLACE ":" "\$:" "${out}" "${${out}}")
+endmacro(ninja_escape_path)
+
+macro(shell_escape string out)
+ string(REPLACE "\"" "\\\"" "${out}" "${string}")
+endmacro(shell_escape)
+
+function(run_sub_cmake test ninja_output_path_prefix)
+ set(top_build_dir "${RunCMake_BINARY_DIR}/${test}-build/")
+ file(REMOVE_RECURSE "${top_build_dir}")
+ file(MAKE_DIRECTORY "${top_build_dir}")
+
+ ninja_escape_path("${ninja_output_path_prefix}"
+ escaped_ninja_output_path_prefix)
+
+ # Generate top build ninja file.
+ set(top_build_ninja "${top_build_dir}/build.ninja")
+ shell_escape("${top_build_ninja}" escaped_top_build_ninja)
+ set(build_ninja_dep "${top_build_dir}/build_ninja_dep")
+ ninja_escape_path("${build_ninja_dep}" escaped_build_ninja_dep)
+ shell_escape("${CMAKE_COMMAND}" escaped_CMAKE_COMMAND)
+ file(WRITE "${build_ninja_dep}" "fake dependency of top build.ninja file\n")
+ if(WIN32)
+ set(cmd_prefix "cmd.exe /C \"")
+ set(cmd_suffix "\"")
+ else()
+ set(cmd_prefix "")
+ set(cmd_suffix "")
+ endif()
+ file(WRITE "${top_build_ninja}" "\
+subninja ${escaped_ninja_output_path_prefix}/build.ninja
+default ${escaped_ninja_output_path_prefix}/all
+
+# Sleep for 1 second before to regenerate to make sure the timestamp of
+# the top build.ninja will be strictly greater than the timestamp of the
+# sub/build.ninja file. We assume the system as 1 sec timestamp resolution.
+rule RERUN
+ command = ${cmd_prefix}\"${escaped_CMAKE_COMMAND}\" -E sleep 1 && \"${escaped_CMAKE_COMMAND}\" -E touch \"${escaped_top_build_ninja}\"${cmd_suffix}
+ description = Testing regeneration
+ generator = 1
+
+build build.ninja: RERUN ${escaped_build_ninja_dep} || ${escaped_ninja_output_path_prefix}/build.ninja
+ pool = console
+")
+
+ # Run sub cmake project.
+ set(RunCMake_TEST_OPTIONS "-DCMAKE_NINJA_OUTPUT_PATH_PREFIX=${ninja_output_path_prefix}")
+ set(RunCMake_TEST_BINARY_DIR "${top_build_dir}/${ninja_output_path_prefix}")
+ run_cmake(${test})
+
+ # Check there is no 'default' statement in Ninja file generated by CMake.
+ set(sub_build_ninja "${RunCMake_TEST_BINARY_DIR}/build.ninja")
+ file(READ "${sub_build_ninja}" sub_build_ninja_file)
+ if(sub_build_ninja_file MATCHES "\ndefault [^\n][^\n]*all\n")
+ message(FATAL_ERROR
+ "unexpected 'default' statement found in '${sub_build_ninja}'")
+ endif()
+
+ # Run ninja from the top build directory.
+ run_ninja("${top_build_dir}")
+
+ # Test regeneration rules run in order.
+ set(main_cmakelists "${RunCMake_SOURCE_DIR}/CMakeLists.txt")
+ sleep(1) # Assume the system as 1 sec timestamp resolution.
+ touch("${main_cmakelists}")
+ touch("${build_ninja_dep}")
+ run_ninja("${top_build_dir}")
+ file(TIMESTAMP "${main_cmakelists}" mtime_main_cmakelists UTC)
+ file(TIMESTAMP "${sub_build_ninja}" mtime_sub_build_ninja UTC)
+ file(TIMESTAMP "${top_build_ninja}" mtime_top_build_ninja UTC)
+
+ # Check sub build.ninja is regenerated.
+ if(mtime_main_cmakelists STRGREATER mtime_sub_build_ninja)
+ message(FATAL_ERROR
+ "sub build.ninja not regenerated:
+ CMakeLists.txt = ${mtime_main_cmakelists}
+ sub/build.ninja = ${mtime_sub_build_ninja}")
+ endif()
+
+ # Check top build.ninja is regenerated after sub build.ninja.
+ if(NOT mtime_top_build_ninja STRGREATER mtime_sub_build_ninja)
+ message(FATAL_ERROR
+ "top build.ninja not regenerated strictly after sub build.ninja:
+ sub/build.ninja = ${mtime_sub_build_ninja}
+ build.ninja = ${mtime_top_build_ninja}")
+ endif()
+
+endfunction(run_sub_cmake)
+
+foreach(ninja_output_path_prefix "sub space" "sub")
+ run_sub_cmake(Executable "${ninja_output_path_prefix}")
+ run_sub_cmake(StaticLib "${ninja_output_path_prefix}")
+ run_sub_cmake(SharedLib "${ninja_output_path_prefix}")
+ run_sub_cmake(TwoLibs "${ninja_output_path_prefix}")
+ run_sub_cmake(SubDirPrefix "${ninja_output_path_prefix}")
+ run_sub_cmake(CustomCommandWorkingDirectory "${ninja_output_path_prefix}")
+endforeach(ninja_output_path_prefix)
diff --git a/Tests/RunCMake/Ninja/SharedLib.cmake b/Tests/RunCMake/Ninja/SharedLib.cmake
new file mode 100644
index 0000000000..1a78390e32
--- /dev/null
+++ b/Tests/RunCMake/Ninja/SharedLib.cmake
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.5)
+project(hello C)
+add_library(greeting SHARED greeting.c)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+add_executable(hello hello_with_greeting.c)
+target_link_libraries(hello greeting)
+include(CheckOutput.cmake)
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/StaticLib.cmake b/Tests/RunCMake/Ninja/StaticLib.cmake
new file mode 100644
index 0000000000..0f815ae5e5
--- /dev/null
+++ b/Tests/RunCMake/Ninja/StaticLib.cmake
@@ -0,0 +1,9 @@
+cmake_minimum_required(VERSION 3.5)
+project(hello C)
+add_definitions(-DGREETING_STATIC)
+add_library(greeting STATIC greeting.c)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+add_executable(hello hello_with_greeting.c)
+target_link_libraries(hello greeting)
+include(CheckOutput.cmake)
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/SubDirPrefix.cmake b/Tests/RunCMake/Ninja/SubDirPrefix.cmake
new file mode 100644
index 0000000000..30ad1e6073
--- /dev/null
+++ b/Tests/RunCMake/Ninja/SubDirPrefix.cmake
@@ -0,0 +1,8 @@
+cmake_minimum_required(VERSION 3.5)
+project(hello C)
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/bin")
+add_subdirectory(SubDirPrefix)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+add_executable(hello hello_sub_greeting.c)
+target_link_libraries(hello greeting)
+include(CheckOutput.cmake)
diff --git a/Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt b/Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt
new file mode 100644
index 0000000000..575597d488
--- /dev/null
+++ b/Tests/RunCMake/Ninja/SubDirPrefix/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library(greeting SHARED greeting.c)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/Tests/RunCMake/Ninja/SubDirPrefix/greeting.c b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.c
new file mode 100644
index 0000000000..ba3e55beb0
--- /dev/null
+++ b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+#if defined(_WIN32) && !defined(GREETING_STATIC)
+__declspec(dllexport)
+#endif
+ void greeting(void)
+{
+ printf("Hello world!\n");
+}
diff --git a/Tests/RunCMake/Ninja/SubDirPrefix/greeting.h b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.h
new file mode 100644
index 0000000000..ee4053ef7f
--- /dev/null
+++ b/Tests/RunCMake/Ninja/SubDirPrefix/greeting.h
@@ -0,0 +1,4 @@
+#if defined(_WIN32) && !defined(GREETING_STATIC)
+__declspec(dllimport)
+#endif
+ void greeting(void);
diff --git a/Tests/RunCMake/Ninja/TwoLibs.cmake b/Tests/RunCMake/Ninja/TwoLibs.cmake
new file mode 100644
index 0000000000..666452f340
--- /dev/null
+++ b/Tests/RunCMake/Ninja/TwoLibs.cmake
@@ -0,0 +1,17 @@
+cmake_minimum_required(VERSION 3.5)
+project(hello C)
+
+set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
+set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib-static")
+set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib")
+
+add_library(greeting SHARED greeting.c)
+add_library(greeting2 STATIC greeting2.c)
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+add_executable(hello hello_with_two_greetings.c)
+target_link_libraries(hello greeting greeting2)
+
+set(HELLO_OUTPUT_STRING "Hello world!\nHello world 2!\n")
+include(CheckOutput.cmake)
+
+include(CheckNoPrefixSubDir.cmake)
diff --git a/Tests/RunCMake/Ninja/greeting.c b/Tests/RunCMake/Ninja/greeting.c
new file mode 100644
index 0000000000..ba3e55beb0
--- /dev/null
+++ b/Tests/RunCMake/Ninja/greeting.c
@@ -0,0 +1,9 @@
+#include <stdio.h>
+
+#if defined(_WIN32) && !defined(GREETING_STATIC)
+__declspec(dllexport)
+#endif
+ void greeting(void)
+{
+ printf("Hello world!\n");
+}
diff --git a/Tests/RunCMake/Ninja/greeting.h b/Tests/RunCMake/Ninja/greeting.h
new file mode 100644
index 0000000000..ee4053ef7f
--- /dev/null
+++ b/Tests/RunCMake/Ninja/greeting.h
@@ -0,0 +1,4 @@
+#if defined(_WIN32) && !defined(GREETING_STATIC)
+__declspec(dllimport)
+#endif
+ void greeting(void);
diff --git a/Tests/RunCMake/Ninja/greeting2.c b/Tests/RunCMake/Ninja/greeting2.c
new file mode 100644
index 0000000000..c6ed87daa0
--- /dev/null
+++ b/Tests/RunCMake/Ninja/greeting2.c
@@ -0,0 +1,6 @@
+#include <stdio.h>
+
+void greeting2(void)
+{
+ printf("Hello world 2!\n");
+}
diff --git a/Tests/RunCMake/Ninja/greeting2.h b/Tests/RunCMake/Ninja/greeting2.h
new file mode 100644
index 0000000000..c4d8f9b29e
--- /dev/null
+++ b/Tests/RunCMake/Ninja/greeting2.h
@@ -0,0 +1 @@
+void greeting2(void);
diff --git a/Tests/RunCMake/Ninja/hello.c b/Tests/RunCMake/Ninja/hello.c
new file mode 100644
index 0000000000..aac8b4e0f3
--- /dev/null
+++ b/Tests/RunCMake/Ninja/hello.c
@@ -0,0 +1,7 @@
+#include <stdio.h>
+
+int main(void)
+{
+ printf("Hello world!\n");
+ return 0;
+}
diff --git a/Tests/RunCMake/Ninja/hello_sub_greeting.c b/Tests/RunCMake/Ninja/hello_sub_greeting.c
new file mode 100644
index 0000000000..fc4bd60f16
--- /dev/null
+++ b/Tests/RunCMake/Ninja/hello_sub_greeting.c
@@ -0,0 +1,7 @@
+#include <SubDirPrefix/greeting.h>
+
+int main(void)
+{
+ greeting();
+ return 0;
+}
diff --git a/Tests/RunCMake/Ninja/hello_with_greeting.c b/Tests/RunCMake/Ninja/hello_with_greeting.c
new file mode 100644
index 0000000000..dbf90e314f
--- /dev/null
+++ b/Tests/RunCMake/Ninja/hello_with_greeting.c
@@ -0,0 +1,7 @@
+#include <greeting.h>
+
+int main(void)
+{
+ greeting();
+ return 0;
+}
diff --git a/Tests/RunCMake/Ninja/hello_with_two_greetings.c b/Tests/RunCMake/Ninja/hello_with_two_greetings.c
new file mode 100644
index 0000000000..cbbdaadc07
--- /dev/null
+++ b/Tests/RunCMake/Ninja/hello_with_two_greetings.c
@@ -0,0 +1,9 @@
+#include <greeting.h>
+#include <greeting2.h>
+
+int main(void)
+{
+ greeting();
+ greeting2();
+ return 0;
+}