# 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.ScratchFile('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 ', '', '#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 ', '#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)) def AssertFileContentContains(self, file_path, entry): with open(file_path, 'r') as f: if entry not in f.read(): self.fail('File "%s" does not contain "%s"' % (file_path, entry)) if __name__ == '__main__': unittest.main()