瀏覽代碼

Merge branch 'policy-CMP0022-fixes' into policy-CMP0022-fixes-for-master

Resolve conflict in Source/cmTarget.cxx by integrating the changes to
the internal copy constructor from both sides.  Also resolve a logical
conflict by dropping the special case for INTERFACE_LIBRARY targets.
Since cmTarget::SetMakefile already forces CMP0022 to NEW for such
targets we need no special handling.

Resolve conflict in Source/cmTargetLinkLibrariesCommand.h by dropping
the documentation change.  We will make the same change in the new
location of the same documentation in a separate commit.

Resolve conflicts in

 Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt
 Tests/RunCMake/CMP0022/RunCMakeTest.cmake

by taking the side from the 'policy-CMP0022-fixes' branch.
Brad King 12 年之前
父節點
當前提交
25b7f87eca

+ 5 - 0
Source/cmExportFileGenerator.cxx

@@ -191,6 +191,10 @@ bool cmExportFileGenerator::PopulateInterfaceLinkLibrariesProperty(
                       ImportPropertyMap &properties,
                       std::vector<std::string> &missingTargets)
 {
+  if(!target->IsLinkable())
+    {
+    return false;
+    }
   const char *input = target->GetProperty("INTERFACE_LINK_LIBRARIES");
   if (input)
     {
@@ -654,6 +658,7 @@ cmExportFileGenerator
 
   if (iface->ImplementationIsInterface)
     {
+    // Policy CMP0022 must not be NEW.
     this->SetImportLinkProperty(suffix, target,
                                 "IMPORTED_LINK_INTERFACE_LIBRARIES",
                                 iface->Libraries, properties, missingTargets);

+ 112 - 140
Source/cmTarget.cxx

@@ -81,10 +81,12 @@ class cmTargetInternals
 public:
   cmTargetInternals()
     {
+    this->PolicyWarnedCMP0022 = false;
     this->SourceFileFlagsConstructed = false;
     }
   cmTargetInternals(cmTargetInternals const&)
     {
+    this->PolicyWarnedCMP0022 = false;
     this->SourceFileFlagsConstructed = false;
     }
   ~cmTargetInternals();
@@ -104,6 +106,7 @@ public:
   typedef std::map<TargetConfigPair, OptionalLinkInterface>
                                                           LinkInterfaceMapType;
   LinkInterfaceMapType LinkInterfaceMap;
+  bool PolicyWarnedCMP0022;
 
   typedef std::map<cmStdString, cmTarget::OutputInfo> OutputInfoMapType;
   OutputInfoMapType OutputInfoMap;
@@ -685,21 +688,10 @@ void cmTarget::MergeLinkLibraries( cmMakefile& mf,
   i += this->PrevLinkedLibraries.size();
   for( ; i != libs.end(); ++i )
     {
-    const char *lib = i->first.c_str();
-    // We call this so that the dependencies get written to the cache
-    this->AddLinkLibrary( mf, selfname, lib, i->second );
-
-    if (this->GetType() == cmTarget::STATIC_LIBRARY)
-      {
-      std::string configLib = this->GetDebugGeneratorExpressions(lib,
-                                                                 i->second);
-      if (cmGeneratorExpression::IsValidTargetName(lib)
-          || cmGeneratorExpression::Find(lib) != std::string::npos)
-        {
-        configLib = "$<LINK_ONLY:" + configLib + ">";
-        }
-        this->AppendProperty("INTERFACE_LINK_LIBRARIES", configLib.c_str());
-      }
+    // This is equivalent to the target_link_libraries plain signature.
+    this->AddLinkLibrary( mf, selfname, i->first.c_str(), i->second );
+    this->AppendProperty("INTERFACE_LINK_LIBRARIES",
+      this->GetDebugGeneratorExpressions(i->first.c_str(), i->second).c_str());
     }
   this->PrevLinkedLibraries = libs;
 }
@@ -5030,12 +5022,20 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface,
   // An explicit list of interface libraries may be set for shared
   // libraries and executables that export symbols.
   const char* explicitLibraries = 0;
-  const char* newExplicitLibraries =
-                                this->GetProperty("INTERFACE_LINK_LIBRARIES");
   std::string linkIfaceProp;
-  if(this->GetType() == cmTarget::SHARED_LIBRARY ||
-     this->IsExecutableWithExports())
+  if(this->PolicyStatusCMP0022 != cmPolicies::OLD &&
+     this->PolicyStatusCMP0022 != cmPolicies::WARN)
     {
+    // CMP0022 NEW behavior is to use INTERFACE_LINK_LIBRARIES.
+    linkIfaceProp = "INTERFACE_LINK_LIBRARIES";
+    explicitLibraries = this->GetProperty(linkIfaceProp.c_str());
+    }
+  else if(this->GetType() == cmTarget::SHARED_LIBRARY ||
+          this->IsExecutableWithExports())
+    {
+    // CMP0022 OLD behavior is to use LINK_INTERFACE_LIBRARIES if set on a
+    // shared lib or executable.
+
     // Lookup the per-configuration property.
     linkIfaceProp = "LINK_INTERFACE_LIBRARIES";
     linkIfaceProp += suffix;
@@ -5047,127 +5047,34 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface,
       linkIfaceProp = "LINK_INTERFACE_LIBRARIES";
       explicitLibraries = this->GetProperty(linkIfaceProp.c_str());
       }
-    if (newExplicitLibraries
-        && (!explicitLibraries ||
-            (explicitLibraries
-              && strcmp(newExplicitLibraries, explicitLibraries) != 0)))
-      {
-      switch(this->GetPolicyStatusCMP0022())
-        {
-        case cmPolicies::WARN:
-          {
-          cmOStringStream w;
-          w << (this->Makefile->GetPolicies()
-                ->GetPolicyWarning(cmPolicies::CMP0022)) << "\n"
-            << "Target \"" << this->GetName() << "\" has a "
-              "INTERFACE_LINK_LIBRARIES property which differs from its "
-            << linkIfaceProp << " properties."
-              "\n"
-              "INTERFACE_LINK_LIBRARIES:\n  "
-            << newExplicitLibraries
-            << "\n"
-            << linkIfaceProp << ":\n  "
-            << (explicitLibraries ? explicitLibraries : "(empty)") << "\n";
-          this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
-          }
-          // Fall through
-        case cmPolicies::OLD:
-          break;
-        case cmPolicies::REQUIRED_IF_USED:
-        case cmPolicies::REQUIRED_ALWAYS:
-        case cmPolicies::NEW:
-          explicitLibraries = newExplicitLibraries;
-          linkIfaceProp = "INTERFACE_LINK_LIBRARIES";
-          break;
-        }
-      }
     }
-  else if(this->GetType() == cmTarget::STATIC_LIBRARY)
+
+  if(explicitLibraries && this->PolicyStatusCMP0022 == cmPolicies::WARN &&
+     !this->Internal->PolicyWarnedCMP0022)
     {
-    if (newExplicitLibraries)
+    // Compare the explicitly set old link interface properties to the
+    // preferred new link interface property one and warn if different.
+    const char* newExplicitLibraries =
+      this->GetProperty("INTERFACE_LINK_LIBRARIES");
+    if (newExplicitLibraries
+        && strcmp(newExplicitLibraries, explicitLibraries) != 0)
       {
-      cmListFileBacktrace lfbt;
-      cmGeneratorExpression ge(lfbt);
-      cmGeneratorExpressionDAGChecker dagChecker(lfbt, this->GetName(),
-                                            "INTERFACE_LINK_LIBRARIES", 0, 0);
-      std::vector<std::string> ifaceLibs;
-      cmSystemTools::ExpandListArgument(
-          ge.Parse(newExplicitLibraries)->Evaluate(
-                                          this->Makefile,
-                                          config,
-                                          false,
-                                          headTarget,
-                                          this, &dagChecker), ifaceLibs);
-      LinkImplementation const* impl = this->GetLinkImplementation(config,
-                                                                headTarget);
-      if (ifaceLibs != impl->Libraries)
-        {
-        switch(this->GetPolicyStatusCMP0022())
-          {
-          case cmPolicies::WARN:
-            {
-            std::string oldLibraries;
-            std::string newLibraries;
-            const char *sep = "";
-            for(std::vector<std::string>::const_iterator it
-                = impl->Libraries.begin(); it != impl->Libraries.end(); ++it)
-              {
-              oldLibraries += sep;
-              oldLibraries += *it;
-              sep = ";";
-              }
-            sep = "";
-            for(std::vector<std::string>::const_iterator it
-                = ifaceLibs.begin(); it != ifaceLibs.end(); ++it)
-              {
-              newLibraries += sep;
-              newLibraries += *it;
-              sep = ";";
-              }
-
-            cmOStringStream w;
-            w << (this->Makefile->GetPolicies()
-                  ->GetPolicyWarning(cmPolicies::CMP0022)) << "\n"
-              << "Static library target \"" << this->GetName() << "\" has a "
-                "INTERFACE_LINK_LIBRARIES property.  This should be preferred "
-                "as the source of the link interface for this library.  "
-                "Ignoring the property and using the link implementation "
-                "as the link interface instead."
-                "\n"
-                "INTERFACE_LINK_LIBRARIES:\n  "
-              << newLibraries
-              << "\n"
-              << "Link implementation:\n  "
-              << oldLibraries << "\n";
-            this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
-            }
-            // Fall through
-          case cmPolicies::OLD:
-            break;
-          case cmPolicies::REQUIRED_IF_USED:
-          case cmPolicies::REQUIRED_ALWAYS:
-          case cmPolicies::NEW:
-            explicitLibraries = newExplicitLibraries;
-            linkIfaceProp = "INTERFACE_LINK_LIBRARIES";
-            break;
-          }
-        }
-      else
-        {
-        iface.Libraries = impl->Libraries;
-        if(this->LinkLanguagePropagatesToDependents())
-          {
-          // Targets using this archive need its language runtime libraries.
-          iface.Languages = impl->Languages;
-          }
-        }
+      cmOStringStream w;
+      w <<
+        (this->Makefile->GetPolicies()
+         ->GetPolicyWarning(cmPolicies::CMP0022)) << "\n"
+        "Target \"" << this->GetName() << "\" has an "
+        "INTERFACE_LINK_LIBRARIES property which differs from its " <<
+        linkIfaceProp << " properties."
+        "\n"
+        "INTERFACE_LINK_LIBRARIES:\n"
+        "  " << newExplicitLibraries << "\n" <<
+        linkIfaceProp << ":\n"
+        "  " << (explicitLibraries ? explicitLibraries : "(empty)") << "\n";
+      this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
+      this->Internal->PolicyWarnedCMP0022 = true;
       }
     }
-  else if (this->GetType() == cmTarget::INTERFACE_LIBRARY)
-    {
-    explicitLibraries = newExplicitLibraries;
-    linkIfaceProp = "INTERFACE_LINK_LIBRARIES";
-    }
 
   // There is no implicit link interface for executables or modules
   // so if none was explicitly set then there is no link interface.
@@ -5235,11 +5142,12 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface,
         }
       }
     }
-  else if (this->GetPolicyStatusCMP0022() == cmPolicies::WARN
-        || this->GetPolicyStatusCMP0022() == cmPolicies::OLD)
-    // The implementation shouldn't be the interface if CMP0022 is NEW. That
-    // way, the LINK_LIBRARIES property can be set directly without having to
-    // empty the INTERFACE_LINK_LIBRARIES
+  else if (this->PolicyStatusCMP0022 == cmPolicies::WARN
+        || this->PolicyStatusCMP0022 == cmPolicies::OLD)
+    // If CMP0022 is NEW then the plain tll signature sets the
+    // INTERFACE_LINK_LIBRARIES, so if we get here then the project
+    // cleared the property explicitly and we should not fall back
+    // to the link implementation.
     {
     // The link implementation is the default link interface.
     LinkImplementation const* impl = this->GetLinkImplementation(config,
@@ -5252,6 +5160,70 @@ bool cmTarget::ComputeLinkInterface(const char* config, LinkInterface& iface,
       // Targets using this archive need its language runtime libraries.
       iface.Languages = impl->Languages;
       }
+
+    if(this->PolicyStatusCMP0022 == cmPolicies::WARN &&
+       !this->Internal->PolicyWarnedCMP0022)
+      {
+      // Compare the link implementation fallback link interface to the
+      // preferred new link interface property and warn if different.
+      cmListFileBacktrace lfbt;
+      cmGeneratorExpression ge(lfbt);
+      cmGeneratorExpressionDAGChecker dagChecker(lfbt, this->GetName(),
+                                      "INTERFACE_LINK_LIBRARIES", 0, 0);
+      std::vector<std::string> ifaceLibs;
+      const char* newExplicitLibraries =
+        this->GetProperty("INTERFACE_LINK_LIBRARIES");
+      cmSystemTools::ExpandListArgument(
+        ge.Parse(newExplicitLibraries)->Evaluate(this->Makefile,
+                                                 config,
+                                                 false,
+                                                 headTarget,
+                                                 this, &dagChecker),
+        ifaceLibs);
+      if (ifaceLibs != impl->Libraries)
+        {
+        std::string oldLibraries;
+        std::string newLibraries;
+        const char *sep = "";
+        for(std::vector<std::string>::const_iterator it
+              = impl->Libraries.begin(); it != impl->Libraries.end(); ++it)
+          {
+          oldLibraries += sep;
+          oldLibraries += *it;
+          sep = ";";
+          }
+        sep = "";
+        for(std::vector<std::string>::const_iterator it
+              = ifaceLibs.begin(); it != ifaceLibs.end(); ++it)
+          {
+          newLibraries += sep;
+          newLibraries += *it;
+          sep = ";";
+          }
+        if(oldLibraries.empty())
+          { oldLibraries = "(empty)"; }
+        if(newLibraries.empty())
+          { newLibraries = "(empty)"; }
+
+        cmOStringStream w;
+        w <<
+          (this->Makefile->GetPolicies()
+           ->GetPolicyWarning(cmPolicies::CMP0022)) << "\n"
+          "Target \"" << this->GetName() << "\" has an "
+          "INTERFACE_LINK_LIBRARIES property.  "
+          "This should be preferred as the source of the link interface "
+          "for this library but because CMP0022 is not set CMake is "
+          "ignoring the property and using the link implementation "
+          "as the link interface instead."
+          "\n"
+          "INTERFACE_LINK_LIBRARIES:\n"
+          "  " << newLibraries << "\n"
+          "Link implementation:\n"
+          "  " << oldLibraries << "\n";
+        this->Makefile->IssueMessage(cmake::AUTHOR_WARNING, w.str());
+        this->Internal->PolicyWarnedCMP0022 = true;
+        }
+      }
     }
 
   if(this->GetType() == cmTarget::STATIC_LIBRARY)

+ 8 - 2
Source/cmTargetLinkLibrariesCommand.cxx

@@ -390,8 +390,14 @@ cmTargetLinkLibrariesCommand::HandleLibrary(const char* lib,
     {
     this->Makefile
       ->AddLinkLibraryForTarget(this->Target->GetName(), lib, llt);
-    if (this->CurrentProcessingState != ProcessingKeywordPublicInterface
-        && this->CurrentProcessingState != ProcessingPlainPublicInterface)
+    if(this->CurrentProcessingState == ProcessingLinkLibraries)
+      {
+      this->Target->AppendProperty("INTERFACE_LINK_LIBRARIES",
+        this->Target->GetDebugGeneratorExpressions(lib, llt).c_str());
+      return true;
+      }
+    else if(this->CurrentProcessingState != ProcessingKeywordPublicInterface
+            && this->CurrentProcessingState != ProcessingPlainPublicInterface)
       {
       if (this->Target->GetType() == cmTarget::STATIC_LIBRARY)
         {

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/CMakeLists.txt

@@ -28,3 +28,10 @@ endif()
 
 add_executable(staticlib_exe staticlib_exe.cpp)
 target_link_libraries(staticlib_exe staticlib1)
+
+add_library(onlyplainlib1 SHARED onlyplainlib1.cpp)
+add_library(onlyplainlib2 SHARED onlyplainlib2.cpp)
+target_link_libraries(onlyplainlib2 onlyplainlib1)
+
+add_executable(onlyplainlib_user onlyplainlib_user.cpp)
+target_link_libraries(onlyplainlib_user onlyplainlib2)

+ 13 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/onlyplainlib1.cpp

@@ -0,0 +1,13 @@
+
+#include "onlyplainlib1.h"
+
+OnlyPlainLib1::OnlyPlainLib1()
+  : result(0)
+{
+
+}
+
+int OnlyPlainLib1::GetResult()
+{
+  return result;
+}

+ 14 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/onlyplainlib1.h

@@ -0,0 +1,14 @@
+
+struct
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+OnlyPlainLib1
+{
+  OnlyPlainLib1();
+
+  int GetResult();
+
+private:
+  int result;
+};

+ 8 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/onlyplainlib2.cpp

@@ -0,0 +1,8 @@
+
+#include "onlyplainlib2.h"
+
+OnlyPlainLib1 onlyPlainLib2()
+{
+  OnlyPlainLib1 opl1;
+  return opl1;
+}

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/onlyplainlib2.h

@@ -0,0 +1,7 @@
+
+#include "onlyplainlib1.h"
+
+#ifdef _WIN32
+__declspec(dllexport)
+#endif
+OnlyPlainLib1 onlyPlainLib2();

+ 7 - 0
Tests/CMakeCommands/target_link_libraries/cmp0022/onlyplainlib_user.cpp

@@ -0,0 +1,7 @@
+
+#include "onlyplainlib2.h"
+
+int main(int argc, char **argv)
+{
+  return onlyPlainLib2().GetResult();
+}

+ 1 - 0
Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe-stderr.txt

@@ -0,0 +1 @@
+^$

+ 7 - 0
Tests/RunCMake/CMP0022/CMP0022-NOWARN-exe.cmake

@@ -0,0 +1,7 @@
+enable_language(CXX)
+
+add_library(testLib empty_vs6_1.cpp)
+add_executable(testExe empty_vs6_2.cpp)
+target_link_libraries(testExe testLib)
+
+export(TARGETS testExe FILE "${CMAKE_CURRENT_BINARY_DIR}/cmp0022NOWARN-exe.cmake")

+ 1 - 0
Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared-stderr.txt

@@ -0,0 +1 @@
+^$

+ 8 - 0
Tests/RunCMake/CMP0022/CMP0022-NOWARN-shared.cmake

@@ -0,0 +1,8 @@
+enable_language(CXX)
+
+add_library(foo SHARED empty_vs6_1.cpp)
+add_library(bar SHARED empty_vs6_2.cpp)
+target_link_libraries(bar foo)
+
+add_executable(zot empty.cpp)
+target_link_libraries(zot bar)

+ 5 - 3
Tests/RunCMake/CMP0022/CMP0022-WARN-empty-old-stderr.txt

@@ -3,14 +3,16 @@ CMake Warning \(dev\) in CMakeLists.txt:
   interface.  Run "cmake --help-policy CMP0022" for policy details.  Use the
   cmake_policy command to set the policy and suppress this warning.
 
-  Target "bar" has a INTERFACE_LINK_LIBRARIES property which differs from its
-  LINK_INTERFACE_LIBRARIES properties.
+  Target "bar" has an INTERFACE_LINK_LIBRARIES property.  This should be
+  preferred as the source of the link interface for this library but because
+  CMP0022 is not set CMake is ignoring the property and using the link
+  implementation as the link interface instead.
 
   INTERFACE_LINK_LIBRARIES:
 
     foo
 
-  LINK_INTERFACE_LIBRARIES:
+  Link implementation:
 
     \(empty\)
 

+ 4 - 4
Tests/RunCMake/CMP0022/CMP0022-WARN-static-stderr.txt

@@ -3,10 +3,10 @@ CMake Warning \(dev\) in CMakeLists.txt:
   interface.  Run "cmake --help-policy CMP0022" for policy details.  Use the
   cmake_policy command to set the policy and suppress this warning.
 
-  Static library target "bar" has a INTERFACE_LINK_LIBRARIES property.  This
-  should be preferred as the source of the link interface for this library.
-  Ignoring the property and using the link implementation as the link
-  interface instead.
+  Target "bar" has an INTERFACE_LINK_LIBRARIES property.  This should be
+  preferred as the source of the link interface for this library but because
+  CMP0022 is not set CMake is ignoring the property and using the link
+  implementation as the link interface instead.
 
   INTERFACE_LINK_LIBRARIES:
 

+ 3 - 3
Tests/RunCMake/CMP0022/CMP0022-WARN-stderr.txt

@@ -1,10 +1,10 @@
-CMake Warning \(dev\) in CMakeLists.txt:
+^CMake Warning \(dev\) in CMakeLists.txt:
   Policy CMP0022 is not set: INTERFACE_LINK_LIBRARIES defines the link
   interface.  Run "cmake --help-policy CMP0022" for policy details.  Use the
   cmake_policy command to set the policy and suppress this warning.
 
-  Target "bar" has a INTERFACE_LINK_LIBRARIES property which differs from its
-  LINK_INTERFACE_LIBRARIES properties.
+  Target "bar" has an INTERFACE_LINK_LIBRARIES property which differs from
+  its LINK_INTERFACE_LIBRARIES properties.
 
   INTERFACE_LINK_LIBRARIES:
 

+ 2 - 2
Tests/RunCMake/CMP0022/CMP0022-WARN-tll-stderr.txt

@@ -3,8 +3,8 @@ CMake Warning \(dev\) in CMakeLists.txt:
   interface.  Run "cmake --help-policy CMP0022" for policy details.  Use the
   cmake_policy command to set the policy and suppress this warning.
 
-  Target "bar" has a INTERFACE_LINK_LIBRARIES property which differs from its
-  LINK_INTERFACE_LIBRARIES properties.
+  Target "bar" has an INTERFACE_LINK_LIBRARIES property which differs from
+  its LINK_INTERFACE_LIBRARIES properties.
 
   INTERFACE_LINK_LIBRARIES:
 

+ 5 - 0
Tests/RunCMake/CMP0022/CMP0022-WARN.cmake

@@ -9,3 +9,8 @@ set_property(TARGET bar PROPERTY LINK_INTERFACE_LIBRARIES bat)
 
 add_library(user empty.cpp)
 target_link_libraries(user bar)
+
+# Use "bar" again with a different "head" target to check
+# that the warning does not appear again.
+add_library(user2 empty_vs6_3.cpp)
+target_link_libraries(user2 bar)

+ 1 - 0
Tests/RunCMake/CMP0022/CMP0022-export-exe-stderr.txt

@@ -0,0 +1 @@
+^$

+ 9 - 0
Tests/RunCMake/CMP0022/CMP0022-export-exe.cmake

@@ -0,0 +1,9 @@
+enable_language(CXX)
+
+cmake_policy(SET CMP0022 NEW)
+
+add_library(testLib empty_vs6_1.cpp)
+add_executable(testExe empty_vs6_2.cpp)
+target_link_libraries(testExe testLib)
+
+export(TARGETS testExe FILE "${CMAKE_CURRENT_BINARY_DIR}/cmp0022NEW-exe.cmake")

+ 3 - 0
Tests/RunCMake/CMP0022/RunCMakeTest.cmake

@@ -4,7 +4,10 @@ run_cmake(CMP0022-WARN)
 run_cmake(CMP0022-WARN-tll)
 run_cmake(CMP0022-WARN-static)
 run_cmake(CMP0022-WARN-empty-old)
+run_cmake(CMP0022-NOWARN-exe)
+run_cmake(CMP0022-NOWARN-shared)
 run_cmake(CMP0022-NOWARN-static)
 run_cmake(CMP0022-NOWARN-static-link_libraries)
 run_cmake(CMP0022-export)
+run_cmake(CMP0022-export-exe)
 run_cmake(CMP0022-install-export)

+ 1 - 0
Tests/RunCMake/CMP0022/empty_vs6_4.cpp

@@ -0,0 +1 @@
+#include "empty.cpp"