From afcd9fe66943259ea9521ff77b32673e0cdf70b2 Mon Sep 17 00:00:00 2001
From: Brad King <brad.king@kitware.com>
Date: Thu, 30 Jan 2020 09:10:58 -0500
Subject: AIX: Add an option to disable automatic exports from shared libraries

Since commit 0f150b69d3 (AIX: Explicitly compute shared object exports
for both XL and GNU, 2019-07-11, v3.16.0-rc1~418^2~2) we always export
all symbols from shared libraries by default.  Add a new target property
called `AIX_EXPORT_ALL_SYMBOLS` that can be explicitly set to OFF to
suppress this behavior and export no symbols by default.

Fixes: #20290
---
 Help/manual/cmake-properties.7.rst                        |  1 +
 Help/manual/cmake-variables.7.rst                         |  1 +
 Help/prop_tgt/AIX_EXPORT_ALL_SYMBOLS.rst                  | 12 ++++++++++++
 Help/release/dev/aix-no-export-all.rst                    |  7 +++++++
 Help/variable/CMAKE_AIX_EXPORT_ALL_SYMBOLS.rst            |  6 ++++++
 Modules/Platform/AIX-GNU.cmake                            |  4 ++--
 Modules/Platform/AIX-XL.cmake                             |  4 ++--
 Source/cmCommonTargetGenerator.cxx                        | 15 +++++++++++++++
 Source/cmCommonTargetGenerator.h                          |  1 +
 Source/cmMakefileExecutableTargetGenerator.cxx            |  3 +++
 Source/cmMakefileLibraryTargetGenerator.cxx               |  3 +++
 Source/cmNinjaNormalTargetGenerator.cxx                   |  2 ++
 Source/cmRulePlaceholderExpander.cxx                      |  5 +++++
 Source/cmRulePlaceholderExpander.h                        |  1 +
 Source/cmTarget.cxx                                       |  1 +
 .../AutoExportDll/AIXExportExplicit-build-result.txt      |  1 +
 .../AutoExportDll/AIXExportExplicit-build-stdout.txt      |  1 +
 Tests/RunCMake/AutoExportDll/AIXExportExplicit.cmake      |  7 +++++++
 Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.c       |  8 ++++++++
 Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.exp     |  1 +
 Tests/RunCMake/AutoExportDll/AIXExportExplicitMain.c      |  7 +++++++
 Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake           | 11 +++++++++++
 Tests/RunCMake/CMakeLists.txt                             |  5 ++++-
 23 files changed, 102 insertions(+), 5 deletions(-)
 create mode 100644 Help/prop_tgt/AIX_EXPORT_ALL_SYMBOLS.rst
 create mode 100644 Help/release/dev/aix-no-export-all.rst
 create mode 100644 Help/variable/CMAKE_AIX_EXPORT_ALL_SYMBOLS.rst
 create mode 100644 Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-result.txt
 create mode 100644 Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-stdout.txt
 create mode 100644 Tests/RunCMake/AutoExportDll/AIXExportExplicit.cmake
 create mode 100644 Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.c
 create mode 100644 Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.exp
 create mode 100644 Tests/RunCMake/AutoExportDll/AIXExportExplicitMain.c

diff --git a/Help/manual/cmake-properties.7.rst b/Help/manual/cmake-properties.7.rst
index bcf21a5..fb84378 100644
--- a/Help/manual/cmake-properties.7.rst
+++ b/Help/manual/cmake-properties.7.rst
@@ -104,6 +104,7 @@ Properties on Targets
    :maxdepth: 1
 
    /prop_tgt/ADDITIONAL_CLEAN_FILES
+   /prop_tgt/AIX_EXPORT_ALL_SYMBOLS
    /prop_tgt/ALIASED_TARGET
    /prop_tgt/ANDROID_ANT_ADDITIONAL_OPTIONS
    /prop_tgt/ANDROID_API
diff --git a/Help/manual/cmake-variables.7.rst b/Help/manual/cmake-variables.7.rst
index 1bcd9bd..5697574 100644
--- a/Help/manual/cmake-variables.7.rst
+++ b/Help/manual/cmake-variables.7.rst
@@ -323,6 +323,7 @@ Variables that Control the Build
 .. toctree::
    :maxdepth: 1
 
+   /variable/CMAKE_AIX_EXPORT_ALL_SYMBOLS
    /variable/CMAKE_ANDROID_ANT_ADDITIONAL_OPTIONS
    /variable/CMAKE_ANDROID_API
    /variable/CMAKE_ANDROID_API_MIN
diff --git a/Help/prop_tgt/AIX_EXPORT_ALL_SYMBOLS.rst b/Help/prop_tgt/AIX_EXPORT_ALL_SYMBOLS.rst
new file mode 100644
index 0000000..15ddc0b
--- /dev/null
+++ b/Help/prop_tgt/AIX_EXPORT_ALL_SYMBOLS.rst
@@ -0,0 +1,12 @@
+AIX_EXPORT_ALL_SYMBOLS
+----------------------
+
+On AIX, CMake automatically exports all symbols from shared libraries, and
+from executables with the :prop_tgt:`ENABLE_EXPORTS` target property set.
+Explicitly disable this boolean property to suppress the behavior and
+export no symbols by default.  In this case it is expected that the project
+will use other means to export some symbols.
+
+This property is initialized by the value of
+the :variable:`CMAKE_AIX_EXPORT_ALL_SYMBOLS` variable if it is set
+when a target is created.
diff --git a/Help/release/dev/aix-no-export-all.rst b/Help/release/dev/aix-no-export-all.rst
new file mode 100644
index 0000000..fa9ed8d
--- /dev/null
+++ b/Help/release/dev/aix-no-export-all.rst
@@ -0,0 +1,7 @@
+aix-no-export-all
+-----------------
+
+* The :prop_tgt:`AIX_EXPORT_ALL_SYMBOLS` target property and associated
+  :variable:`CMAKE_AIX_EXPORT_ALL_SYMBOLS` variable were created to
+  optionally explicitly disbale automatic export of symbols from shared
+  libraries on AIX.
diff --git a/Help/variable/CMAKE_AIX_EXPORT_ALL_SYMBOLS.rst b/Help/variable/CMAKE_AIX_EXPORT_ALL_SYMBOLS.rst
new file mode 100644
index 0000000..c64dd48
--- /dev/null
+++ b/Help/variable/CMAKE_AIX_EXPORT_ALL_SYMBOLS.rst
@@ -0,0 +1,6 @@
+CMAKE_AIX_EXPORT_ALL_SYMBOLS
+----------------------------
+
+Default value for :prop_tgt:`AIX_EXPORT_ALL_SYMBOLS` target property.
+This variable is used to initialize the property on each target as it is
+created.
diff --git a/Modules/Platform/AIX-GNU.cmake b/Modules/Platform/AIX-GNU.cmake
index 42e5e36..5a532c7 100644
--- a/Modules/Platform/AIX-GNU.cmake
+++ b/Modules/Platform/AIX-GNU.cmake
@@ -23,11 +23,11 @@ macro(__aix_compiler_gnu lang)
   # Construct the export list ourselves to pass only the object files so
   # that we export only the symbols actually provided by the sources.
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <OBJECTS>"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS> <OBJECTS>"
     "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
     )
 
   set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <OBJECTS>"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>"
     "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
 endmacro()
diff --git a/Modules/Platform/AIX-XL.cmake b/Modules/Platform/AIX-XL.cmake
index edaedb4..2a8c159 100644
--- a/Modules/Platform/AIX-XL.cmake
+++ b/Modules/Platform/AIX-XL.cmake
@@ -29,12 +29,12 @@ macro(__aix_compiler_xl lang)
   # Construct the export list ourselves to pass only the object files so
   # that we export only the symbols actually provided by the sources.
   set(CMAKE_${lang}_CREATE_SHARED_LIBRARY
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp${_OBJECTS}"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <OBJECT_DIR>/exports.exp <AIX_EXPORTS>${_OBJECTS}"
     "<CMAKE_${lang}_COMPILER> <CMAKE_SHARED_LIBRARY_${lang}_FLAGS> -Wl,-bE:<OBJECT_DIR>/exports.exp <LANGUAGE_COMPILE_FLAGS> <LINK_FLAGS> <CMAKE_SHARED_LIBRARY_CREATE_${lang}_FLAGS> <SONAME_FLAG><TARGET_SONAME> -o <TARGET> <OBJECTS> <LINK_LIBRARIES>"
     )
 
   set(CMAKE_${lang}_LINK_EXECUTABLE_WITH_EXPORTS
-    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <OBJECTS>"
+    "\"${CMAKE_ROOT}/Modules/Platform/AIX/ExportImportList\" -o <TARGET_IMPLIB> -l . <AIX_EXPORTS> <OBJECTS>"
     "<CMAKE_${lang}_COMPILER> <FLAGS> <CMAKE_${lang}_LINK_FLAGS> -Wl,-bE:<TARGET_IMPLIB> <LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
 
   unset(_OBJECTS)
diff --git a/Source/cmCommonTargetGenerator.cxx b/Source/cmCommonTargetGenerator.cxx
index b8d8b96..5ff6f8c 100644
--- a/Source/cmCommonTargetGenerator.cxx
+++ b/Source/cmCommonTargetGenerator.cxx
@@ -17,6 +17,7 @@
 #include "cmSourceFile.h"
 #include "cmStateTypes.h"
 #include "cmStringAlgorithms.h"
+#include "cmTarget.h"
 
 cmCommonTargetGenerator::cmCommonTargetGenerator(cmGeneratorTarget* gt)
   : GeneratorTarget(gt)
@@ -216,6 +217,20 @@ std::string cmCommonTargetGenerator::GetManifests(const std::string& config)
   return cmJoin(manifests, " ");
 }
 
+std::string cmCommonTargetGenerator::GetAIXExports(std::string const&)
+{
+  std::string aixExports;
+  if (this->GeneratorTarget->Target->IsAIX()) {
+    if (const char* exportAll =
+          this->GeneratorTarget->GetProperty("AIX_EXPORT_ALL_SYMBOLS")) {
+      if (cmIsOff(exportAll)) {
+        aixExports = "-n";
+      }
+    }
+  }
+  return aixExports;
+}
+
 void cmCommonTargetGenerator::AppendOSXVerFlag(std::string& flags,
                                                const std::string& lang,
                                                const char* name, bool so)
diff --git a/Source/cmCommonTargetGenerator.h b/Source/cmCommonTargetGenerator.h
index 2796470..b40a2ed 100644
--- a/Source/cmCommonTargetGenerator.h
+++ b/Source/cmCommonTargetGenerator.h
@@ -55,6 +55,7 @@ protected:
   std::string GetDefines(const std::string& l, const std::string& config);
   std::string GetIncludes(std::string const& l, const std::string& config);
   std::string GetManifests(const std::string& config);
+  std::string GetAIXExports(std::string const& config);
 
   std::vector<std::string> GetLinkedTargetDirectories(
     const std::string& config) const;
diff --git a/Source/cmMakefileExecutableTargetGenerator.cxx b/Source/cmMakefileExecutableTargetGenerator.cxx
index 1df5410..0471a45 100644
--- a/Source/cmMakefileExecutableTargetGenerator.cxx
+++ b/Source/cmMakefileExecutableTargetGenerator.cxx
@@ -196,6 +196,8 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
     this->CreateObjectLists(useLinkScript, false, useResponseFileForObjects,
                             buildObjs, depends, useWatcomQuote);
 
+    std::string const& aixExports = this->GetAIXExports(this->GetConfigName());
+
     cmRulePlaceholderExpander::RuleVariables vars;
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
 
@@ -219,6 +221,7 @@ void cmMakefileExecutableTargetGenerator::WriteDeviceExecutableRule(
                                                   cmOutputConverter::SHELL);
 
     vars.Language = linkLanguage.c_str();
+    vars.AIXExports = aixExports.c_str();
     vars.Objects = buildObjs.c_str();
     vars.ObjectDir = objectDir.c_str();
     vars.Target = target.c_str();
diff --git a/Source/cmMakefileLibraryTargetGenerator.cxx b/Source/cmMakefileLibraryTargetGenerator.cxx
index 357e273..d3f3a4f 100644
--- a/Source/cmMakefileLibraryTargetGenerator.cxx
+++ b/Source/cmMakefileLibraryTargetGenerator.cxx
@@ -727,6 +727,8 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
           cmOutputConverter::SHELL);
     }
 
+    std::string const& aixExports = this->GetAIXExports(this->GetConfigName());
+
     // maybe create .def file from list of objects
     this->GenDefFile(real_link_commands);
 
@@ -756,6 +758,7 @@ void cmMakefileLibraryTargetGenerator::WriteLibraryRules(
     vars.CMTargetType =
       cmState::GetTargetTypeName(this->GeneratorTarget->GetType());
     vars.Language = linkLanguage.c_str();
+    vars.AIXExports = aixExports.c_str();
     vars.Objects = buildObjs.c_str();
     std::string objectDir = this->GeneratorTarget->GetSupportDirectory();
 
diff --git a/Source/cmNinjaNormalTargetGenerator.cxx b/Source/cmNinjaNormalTargetGenerator.cxx
index 437548a..ffb269a 100644
--- a/Source/cmNinjaNormalTargetGenerator.cxx
+++ b/Source/cmNinjaNormalTargetGenerator.cxx
@@ -286,6 +286,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkRule(bool useResponseFile,
 
     std::string lang = this->TargetLinkLanguage(config);
     vars.Language = config.c_str();
+    vars.AIXExports = "$AIX_EXPORTS";
 
     if (this->TargetLinkLanguage(config) == "Swift") {
       vars.SwiftLibraryName = "$SWIFT_LIBRARY_NAME";
@@ -955,6 +956,7 @@ void cmNinjaNormalTargetGenerator::WriteLinkStatement(
   vars["LINK_FLAGS"] = globalGen->EncodeLiteral(vars["LINK_FLAGS"]);
 
   vars["MANIFESTS"] = this->GetManifests(config);
+  vars["AIX_EXPORTS"] = this->GetAIXExports(config);
 
   vars["LINK_PATH"] = frameworkPath + linkPath;
   std::string lwyuFlags;
diff --git a/Source/cmRulePlaceholderExpander.cxx b/Source/cmRulePlaceholderExpander.cxx
index 0a1d109..5ab1b3a 100644
--- a/Source/cmRulePlaceholderExpander.cxx
+++ b/Source/cmRulePlaceholderExpander.cxx
@@ -85,6 +85,11 @@ std::string cmRulePlaceholderExpander::ExpandRuleVariable(
       return replaceValues.ObjectsQuoted;
     }
   }
+  if (replaceValues.AIXExports) {
+    if (variable == "AIX_EXPORTS") {
+      return replaceValues.AIXExports;
+    }
+  }
   if (replaceValues.Defines && variable == "DEFINES") {
     return replaceValues.Defines;
   }
diff --git a/Source/cmRulePlaceholderExpander.h b/Source/cmRulePlaceholderExpander.h
index 8f36196..09e8a3b 100644
--- a/Source/cmRulePlaceholderExpander.h
+++ b/Source/cmRulePlaceholderExpander.h
@@ -36,6 +36,7 @@ public:
     const char* TargetVersionMajor;
     const char* TargetVersionMinor;
     const char* Language;
+    const char* AIXExports;
     const char* Objects;
     const char* Target;
     const char* LinkLibraries;
diff --git a/Source/cmTarget.cxx b/Source/cmTarget.cxx
index a0c217b..d0b6f10 100644
--- a/Source/cmTarget.cxx
+++ b/Source/cmTarget.cxx
@@ -491,6 +491,7 @@ cmTarget::cmTarget(std::string const& name, cmStateEnums::TargetType type,
   }
   if (impl->TargetType == cmStateEnums::SHARED_LIBRARY ||
       impl->TargetType == cmStateEnums::EXECUTABLE) {
+    initProp("AIX_EXPORT_ALL_SYMBOLS");
     initProp("WINDOWS_EXPORT_ALL_SYMBOLS");
   }
 
diff --git a/Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-result.txt b/Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-result.txt
new file mode 100644
index 0000000..d197c91
--- /dev/null
+++ b/Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-result.txt
@@ -0,0 +1 @@
+[^0]
diff --git a/Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-stdout.txt b/Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-stdout.txt
new file mode 100644
index 0000000..760ba3c
--- /dev/null
+++ b/Tests/RunCMake/AutoExportDll/AIXExportExplicit-build-stdout.txt
@@ -0,0 +1 @@
+ERROR: Undefined symbol: .AIXNotExported
diff --git a/Tests/RunCMake/AutoExportDll/AIXExportExplicit.cmake b/Tests/RunCMake/AutoExportDll/AIXExportExplicit.cmake
new file mode 100644
index 0000000..d23b172
--- /dev/null
+++ b/Tests/RunCMake/AutoExportDll/AIXExportExplicit.cmake
@@ -0,0 +1,7 @@
+enable_language(C)
+
+set(CMAKE_AIX_EXPORT_ALL_SYMBOLS OFF)
+add_library(AIXExportExplicitLib SHARED AIXExportExplicitLib.c)
+add_executable(AIXExportExplicitMain AIXExportExplicitMain.c)
+target_link_options(AIXExportExplicitLib PRIVATE LINKER:-bE:${CMAKE_CURRENT_SOURCE_DIR}/AIXExportExplicitLib.exp)
+target_link_libraries(AIXExportExplicitMain PRIVATE AIXExportExplicitLib)
diff --git a/Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.c b/Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.c
new file mode 100644
index 0000000..58fd5ac
--- /dev/null
+++ b/Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.c
@@ -0,0 +1,8 @@
+int AIXNotExported(void)
+{
+  return 0;
+}
+int AIXExportedSymbol(void)
+{
+  return 0;
+}
diff --git a/Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.exp b/Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.exp
new file mode 100644
index 0000000..9eb7bf8
--- /dev/null
+++ b/Tests/RunCMake/AutoExportDll/AIXExportExplicitLib.exp
@@ -0,0 +1 @@
+AIXExportedSymbol
diff --git a/Tests/RunCMake/AutoExportDll/AIXExportExplicitMain.c b/Tests/RunCMake/AutoExportDll/AIXExportExplicitMain.c
new file mode 100644
index 0000000..ad9c8ec
--- /dev/null
+++ b/Tests/RunCMake/AutoExportDll/AIXExportExplicitMain.c
@@ -0,0 +1,7 @@
+extern int AIXNotExported(void);
+extern int AIXExportedSymbol(void);
+
+int main(void)
+{
+  return AIXNotExported() + AIXExportedSymbol();
+}
diff --git a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
index 6c9be4b..75130f2 100644
--- a/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
+++ b/Tests/RunCMake/AutoExportDll/RunCMakeTest.cmake
@@ -55,3 +55,14 @@ if(EXPORTS)
     message(SEND_ERROR "\"${EXPORTS_DEF}\" has been updated.")
   endif()
 endif()
+
+function(run_AIXExportExplicit)
+  set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/AIXExpotExplicit-build")
+  run_cmake(AIXExportExplicit)
+  set(RunCMake_TEST_NO_CLEAN 1)
+  set(RunCMake_TEST_OUTPUT_MERGE TRUE)
+  run_cmake_command(AIXExportExplicit-build ${CMAKE_COMMAND} --build . --config Debug)
+endfunction()
+if(CMAKE_SYSTEM_NAME STREQUAL "AIX")
+  run_AIXExportExplicit()
+endif()
diff --git a/Tests/RunCMake/CMakeLists.txt b/Tests/RunCMake/CMakeLists.txt
index ad70a34..e9f8bca 100644
--- a/Tests/RunCMake/CMakeLists.txt
+++ b/Tests/RunCMake/CMakeLists.txt
@@ -614,7 +614,10 @@ endif()
 add_RunCMake_test_group(CPack "${cpack_tests}")
 # add a test to make sure symbols are exported from a shared library
 # for MSVC compilers CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS property is used
-add_RunCMake_test(AutoExportDll -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID})
+add_RunCMake_test(AutoExportDll
+  -DCMAKE_SYSTEM_NAME=${CMAKE_SYSTEM_NAME}
+  -DCMAKE_CXX_COMPILER_ID=${CMAKE_CXX_COMPILER_ID}
+  )
 
 add_RunCMake_test(AndroidMK)
 
-- 
cgit v0.12