Windows: Implementing C++ DEF parser
C++ DEF parser can generating a DEF file from a object file, which can
be used to export symbols during linking DLL on Windows.
This parser is based on an implementation in CMake
See https://github.com/Kitware/CMake/blob/master/Source/bindexplib.cxx
A few changes has been made to make it work better.
Usage: output_deffile dllname [objfile ...] [input_deffile ...] [@paramfile ...]
output_deffile: the output DEF file
dllname: the DLL name this DEF file is used for, if dllname is not empty
string (eg. ""), def_parser writes an 'LIBRARY <dllname>' entry
into DEF file.
objfile: a object file, def_parser parses this file to find symbols,
then merges them into final result.
Can apppear multiple times.
input_deffile: an existing def file, def_parser merges all symbols in this file.
Can appear multiple times.
@paramfile: a parameter file that can contain objfile and input_deffile
Can appear multiple time.
Change-Id: I0ee65fa3119ecae2ea195b707af5690e4bc6a6c2
diff --git a/third_party/def_parser/def_parser_test.py b/third_party/def_parser/def_parser_test.py
new file mode 100644
index 0000000..a413c3a
--- /dev/null
+++ b/third_party/def_parser/def_parser_test.py
@@ -0,0 +1,108 @@
+# Copyright 2017 The Bazel Authors. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import unittest
+from src.test.py.bazel import test_base
+
+class DEFParserTest(test_base.TestBase):
+
+ def createAndBuildProjectFiles(self):
+ self.ScratchFile('WORKSPACE')
+ self.ScratchFile('BUILD', ['cc_library(name="hello", srcs=["x.cc"])'])
+ self.ScratchFile('x.cc', [
+ '#include <stdio.h>',
+ 'int hello_data;',
+ 'void hello_world() {',
+ ' printf("hello world\\n");',
+ '}',
+ ])
+ exit_code, _, stderr = self.RunBazel(['build', '//:hello'])
+ self.AssertExitCode(exit_code, 0, stderr)
+
+ def testParseDefFileFromObjectFile(self):
+ # Skip this test on non-Windows platforms
+ if not self.IsWindows():
+ return
+ self.createAndBuildProjectFiles()
+
+ exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin'])
+ self.AssertExitCode(exit_code, 0, stderr)
+ bazel_bin = stdout[0]
+
+ objfile = os.path.join(bazel_bin, '_objs', 'hello', 'x.o')
+ self.assertTrue(os.path.isfile(objfile))
+ output_def = self.Path('x.def');
+ self.RunProgram([self.Rlocation('io_bazel/third_party/def_parser/def_parser.exe'), output_def, 'my_x.dll', objfile])
+ self.assertTrue(os.path.isfile(output_def))
+
+ with open(output_def, 'r') as def_file:
+ def_content = def_file.read()
+ self.assertIn('LIBRARY my_x.dll', def_content)
+ self.assertIn('hello_data', def_content)
+ self.assertIn('hello_world', def_content)
+
+ def testParseDefFileFromObjectFileWithParamFile(self):
+ # Skip this test on non-Windows platforms
+ if not self.IsWindows():
+ return
+ self.createAndBuildProjectFiles()
+
+ exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin'])
+ self.AssertExitCode(exit_code, 0, stderr)
+ bazel_bin = stdout[0]
+
+ objfile = os.path.join(bazel_bin, '_objs', 'hello', 'x.o')
+ self.assertTrue(os.path.isfile(objfile))
+ objfilelist = self.ScratchFile('objfilelist', [objfile])
+
+ output_def = self.Path('x.def');
+ self.RunProgram([self.Rlocation('io_bazel/third_party/def_parser/def_parser.exe'), output_def, 'my_x.dll', '@' + objfilelist])
+ self.assertTrue(os.path.isfile(output_def))
+
+ with open(output_def, 'r') as def_file:
+ def_content = def_file.read()
+ self.assertIn('LIBRARY my_x.dll', def_content)
+ self.assertIn('hello_data', def_content)
+ self.assertIn('hello_world', def_content)
+
+ def testParseDefFileFromAnotherDefFile(self):
+ # Skip this test on non-Windows platforms
+ if not self.IsWindows():
+ return
+
+ self.createAndBuildProjectFiles()
+
+ exit_code, stdout, stderr = self.RunBazel(['info', 'bazel-bin'])
+ self.AssertExitCode(exit_code, 0, stderr)
+ bazel_bin = stdout[0]
+
+ objfile = os.path.join(bazel_bin, '_objs', 'hello', 'x.o')
+ self.assertTrue(os.path.isfile(objfile))
+ output_def = self.Path('x.def');
+ self.RunProgram([self.Rlocation('io_bazel/third_party/def_parser/def_parser.exe'), output_def, 'my_x.dll', objfile])
+ self.assertTrue(os.path.isfile(output_def))
+
+ new_output_def = self.Path('new_x.def');
+ self.RunProgram([self.Rlocation('io_bazel/third_party/def_parser/def_parser.exe'), new_output_def, 'my_x.dll', output_def])
+ self.assertTrue(os.path.isfile(new_output_def))
+
+ with open(new_output_def, 'r') as def_file:
+ def_content = def_file.read()
+ self.assertIn('LIBRARY my_x.dll', def_content)
+ self.assertIn('hello_data', def_content)
+ self.assertIn('hello_world', def_content)
+
+if __name__ == '__main__':
+ unittest.main()