| # 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.IsLinux(): |
| 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() |