| # 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 shutil | |
| import stat | |
| import unittest | |
| from src.test.py.bazel import test_base | |
| class CcImportTest(test_base.TestBase): | |
| def createProjectFiles(self, | |
| alwayslink=0, | |
| system_provided=0, | |
| linkstatic=1, | |
| provide_header=True): | |
| self.CreateWorkspaceWithDefaultRepos('WORKSPACE') | |
| # We use the outputs of cc_binary and cc_library as precompiled | |
| # libraries for cc_import | |
| self.ScratchFile( | |
| 'lib/BUILD', | |
| [ | |
| 'package(default_visibility = ["//visibility:public"])', | |
| '', | |
| 'cc_binary(', | |
| ' name = "libA.so",', | |
| ' srcs = ["a.cc"],', | |
| ' linkshared = 1,', | |
| ')', | |
| '', | |
| 'filegroup(', | |
| ' name = "libA_ifso",', | |
| ' srcs = [":libA.so"],', | |
| ' output_group = "interface_library",', | |
| ')', | |
| '', | |
| 'cc_library(', | |
| ' name = "libA",', | |
| ' srcs = ["a.cc", "a_al.cc"],', | |
| ')', | |
| '', | |
| 'filegroup(', | |
| ' name = "libA_archive",', | |
| ' srcs = [":libA"],', | |
| ' output_group = "archive",', | |
| ')', | |
| '', | |
| 'cc_import(', | |
| ' name = "A",', | |
| ' static_library = "//lib:libA_archive",', | |
| ' shared_library = "//lib:libA.so",' | |
| if not system_provided else '', | |
| # On Windows, we always need the interface library | |
| ' interface_library = "//lib:libA_ifso",' | |
| if self.IsWindows() else ( | |
| # On Unix, we use .so file as interface library | |
| # if system_provided is true | |
| ' interface_library = "//lib:libA.so",' | |
| if system_provided else ''), | |
| ' hdrs = ["a.h"],' if provide_header else '', | |
| ' alwayslink = %s,' % str(alwayslink), | |
| ' system_provided = %s,' % str(system_provided), | |
| ')', | |
| ]) | |
| self.ScratchFile('lib/a.cc', [ | |
| '#include <stdio.h>', | |
| '', | |
| '#ifdef _WIN32', | |
| ' #define DLLEXPORT __declspec(dllexport)', | |
| '#else', | |
| ' #define DLLEXPORT', | |
| '#endif', | |
| '', | |
| 'DLLEXPORT void HelloWorld() {', | |
| ' printf("HelloWorld\\n");', | |
| '}', | |
| ]) | |
| # For testing alwayslink=1 | |
| self.ScratchFile('lib/a_al.cc', [ | |
| 'extern int global_variable;', | |
| 'int init() {', | |
| ' ++global_variable;', | |
| ' return global_variable;', | |
| '}', | |
| 'int x = init();', | |
| 'int y = init();', | |
| ]) | |
| self.ScratchFile('lib/a.h', [ | |
| 'void HelloWorld();', | |
| ]) | |
| self.ScratchFile('main/BUILD', [ | |
| 'cc_binary(', | |
| ' name = "B",', | |
| ' srcs = ["b.cc"],', | |
| ' deps = ["//lib:A",],', | |
| ' linkstatic = %s,' % str(linkstatic), | |
| ')', | |
| ]) | |
| self.ScratchFile('main/b.cc', [ | |
| '#include <stdio.h>', | |
| '#include "lib/a.h"', | |
| 'int global_variable = 0;', | |
| 'int main() {', | |
| ' HelloWorld();', | |
| ' printf("global : %d\\n", global_variable);', | |
| ' return 0;', | |
| '}', | |
| ]) | |
| def getBazelInfo(self, info_key): | |
| exit_code, stdout, stderr = self.RunBazel(['info', info_key]) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| return stdout[0] | |
| def testLinkStaticLibrary(self): | |
| self.createProjectFiles(alwayslink=0, linkstatic=1) | |
| bazel_bin = self.getBazelInfo('bazel-bin') | |
| suffix = '.exe' if self.IsWindows() else '' | |
| exit_code, _, stderr = self.RunBazel(['build', '//main:B']) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| b_bin = os.path.join(bazel_bin, 'main/B' + suffix) | |
| self.assertTrue(os.path.exists(b_bin)) | |
| exit_code, stdout, stderr = self.RunProgram([b_bin]) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| self.assertEqual(stdout[0], 'HelloWorld') | |
| self.assertEqual(stdout[1], 'global : 0') | |
| def testAlwayslinkStaticLibrary(self): | |
| self.createProjectFiles(alwayslink=1, linkstatic=1) | |
| bazel_bin = self.getBazelInfo('bazel-bin') | |
| suffix = '.exe' if self.IsWindows() else '' | |
| exit_code, _, stderr = self.RunBazel(['build', '//main:B']) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| b_bin = os.path.join(bazel_bin, 'main/B' + suffix) | |
| self.assertTrue(os.path.exists(b_bin)) | |
| exit_code, stdout, stderr = self.RunProgram([b_bin]) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| self.assertEqual(stdout[0], 'HelloWorld') | |
| self.assertEqual(stdout[1], 'global : 2') | |
| def testLinkSharedLibrary(self): | |
| self.createProjectFiles(linkstatic=0) | |
| bazel_bin = self.getBazelInfo('bazel-bin') | |
| suffix = '.exe' if self.IsWindows() else '' | |
| exit_code, _, stderr = self.RunBazel(['build', '//main:B']) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| b_bin = os.path.join(bazel_bin, 'main/B' + suffix) | |
| self.assertTrue(os.path.exists(b_bin)) | |
| if self.IsWindows(): | |
| self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'main/libA.so'))) | |
| exit_code, stdout, stderr = self.RunProgram([b_bin]) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| self.assertEqual(stdout[0], 'HelloWorld') | |
| def testSystemProvidedSharedLibraryOnWinodws(self): | |
| if not self.IsWindows(): | |
| return | |
| self.createProjectFiles(system_provided=1, linkstatic=0) | |
| bazel_bin = self.getBazelInfo('bazel-bin') | |
| exit_code, _, stderr = self.RunBazel(['build', '//main:B']) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| b_bin = os.path.join(bazel_bin, 'main/B.exe') | |
| exit_code, stdout, stderr = self.RunProgram([b_bin]) | |
| # Should fail because missing libA.so | |
| self.assertFalse(exit_code == 0) | |
| # Let's build libA.so and add it into PATH | |
| exit_code, stdout, stderr = self.RunBazel(['build', '//lib:libA.so']) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| exit_code, stdout, stderr = self.RunProgram( | |
| [b_bin], env_add={ | |
| 'PATH': str(os.path.join(bazel_bin, 'lib')) | |
| }) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| self.assertEqual(stdout[0], 'HelloWorld') | |
| def testSystemProvidedSharedLibraryOnUnix(self): | |
| if not self.IsUnix(): | |
| return | |
| self.createProjectFiles(system_provided=1, linkstatic=0) | |
| bazel_bin = self.getBazelInfo('bazel-bin') | |
| exit_code, _, stderr = self.RunBazel(['build', '//main:B']) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| b_bin = os.path.join(bazel_bin, 'main/B') | |
| tmp_dir = self.ScratchDir('temp_dir_for_run_b_bin') | |
| b_bin_tmp = os.path.join(tmp_dir, 'B') | |
| # Copy the binary to a temp directory to make sure it cannot find | |
| # libA.so | |
| shutil.copyfile(b_bin, b_bin_tmp) | |
| os.chmod(b_bin_tmp, stat.S_IRWXU) | |
| exit_code, stdout, stderr = self.RunProgram([b_bin_tmp]) | |
| # Should fail because missing libA.so | |
| self.assertFalse(exit_code == 0) | |
| # Let's build libA.so and add it into PATH | |
| exit_code, stdout, stderr = self.RunBazel(['build', '//lib:libA.so']) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| exit_code, stdout, stderr = self.RunProgram( | |
| [b_bin_tmp], env_add={ | |
| # For Linux | |
| 'LD_LIBRARY_PATH': str(os.path.join(bazel_bin, 'lib')), | |
| # For Mac | |
| 'DYLD_LIBRARY_PATH': str(os.path.join(bazel_bin, 'lib')), | |
| }) | |
| self.AssertExitCode(exit_code, 0, stderr) | |
| self.assertEqual(stdout[0], 'HelloWorld') | |
| def testCcImportHeaderCheck(self): | |
| self.createProjectFiles(provide_header=False) | |
| # Build should fail, because lib/a.h is not declared in BUILD file, disable | |
| # sandbox so that bazel produces same error across different platforms. | |
| exit_code, _, stderr = self.RunBazel( | |
| ['build', '//main:B', '--spawn_strategy=standalone']) | |
| self.AssertExitCode(exit_code, 1, stderr) | |
| self.assertIn('this rule is missing dependency declarations for the' | |
| ' following files included by \'main/b.cc\':', | |
| ''.join(stderr)) | |
| if __name__ == '__main__': | |
| unittest.main() |