blob: 64b654b67ecf0423ee07fb512ebce02195d9c301 [file] [log] [blame]
# 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 glob
import os
import unittest
from src.test.py.bazel import test_base
class BazelWindowsCppTest(test_base.TestBase):
def createProjectFiles(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'package(',
' default_visibility = ["//visibility:public"],',
' features=["windows_export_all_symbols"]',
')',
'',
'cc_library(',
' name = "A",',
' srcs = ["a.cc"],',
' hdrs = ["a.h"],',
' copts = ["/DCOMPILING_A_DLL"],',
' features = ["no_windows_export_all_symbols"],',
')',
'',
'cc_library(',
' name = "B",',
' srcs = ["b.cc"],',
' hdrs = ["b.h"],',
' deps = [":A"],',
' copts = ["/DNO_DLLEXPORT"],',
')',
'',
'cc_binary(',
' name = "C",',
' srcs = ["c.cc"],',
' deps = [":A", ":B" ],',
' linkstatic = 0,',
')',
])
self.ScratchFile('a.cc', [
'#include <stdio.h>',
'#include "a.h"',
'int a = 0;',
'void hello_A() {',
' a++;',
' printf("Hello A, %d\\n", a);',
'}',
])
self.ScratchFile('b.cc', [
'#include <stdio.h>',
'#include "a.h"',
'#include "b.h"',
'void hello_B() {',
' hello_A();',
' printf("Hello B\\n");',
'}',
])
header_temp = [
'#ifndef %{name}_H',
'#define %{name}_H',
'',
'#if NO_DLLEXPORT',
' #define DLLEXPORT',
'#elif COMPILING_%{name}_DLL',
' #define DLLEXPORT __declspec(dllexport)',
'#else',
' #define DLLEXPORT __declspec(dllimport)',
'#endif',
'',
'DLLEXPORT void hello_%{name}();',
'',
'#endif',
]
self.ScratchFile('a.h',
[line.replace('%{name}', 'A') for line in header_temp])
self.ScratchFile('b.h',
[line.replace('%{name}', 'B') for line in header_temp])
c_cc_content = [
'#include <stdio.h>',
'#include "a.h"',
'#include "b.h"',
'',
'void hello_C() {',
' hello_A();',
' hello_B();',
' printf("Hello C\\n");',
'}',
'',
'int main() {',
' hello_C();',
' return 0;',
'}',
]
self.ScratchFile('c.cc', c_cc_content)
self.ScratchFile('lib/BUILD', [
'cc_library(',
' name = "A",',
' srcs = ["dummy.cc"],',
' features = ["windows_export_all_symbols"],',
' visibility = ["//visibility:public"],',
')',
])
self.ScratchFile('lib/dummy.cc', ['void dummy() {}'])
self.ScratchFile('main/main.cc', c_cc_content)
def getBazelInfo(self, info_key):
exit_code, stdout, stderr = self.RunBazel(['info', info_key])
self.AssertExitCode(exit_code, 0, stderr)
return stdout[0]
def testBuildDynamicLibraryWithUserExportedSymbol(self):
self.createProjectFiles()
bazel_bin = self.getBazelInfo('bazel-bin')
# //:A export symbols by itself using __declspec(dllexport), so it doesn't
# need Bazel to export symbols using DEF file.
exit_code, _, stderr = self.RunBazel(
['build', '//:A', '--output_groups=dynamic_library'])
self.AssertExitCode(exit_code, 0, stderr)
# TODO(pcloudy): change suffixes to .lib and .dll after making DLL
# extensions correct on Windows.
import_library = os.path.join(bazel_bin, 'A.if.lib')
shared_library = os.path.join(bazel_bin, 'A_0.dll')
empty_def_file = os.path.join(bazel_bin, 'A.gen.empty.def')
self.assertTrue(os.path.exists(import_library))
self.assertTrue(os.path.exists(shared_library))
# An empty DEF file should be generated for //:A
self.assertTrue(os.path.exists(empty_def_file))
def testBuildDynamicLibraryWithExportSymbolFeature(self):
self.createProjectFiles()
bazel_bin = self.getBazelInfo('bazel-bin')
# //:B doesn't export symbols by itself, so it need Bazel to export symbols
# using DEF file.
exit_code, _, stderr = self.RunBazel(
['build', '//:B', '--output_groups=dynamic_library'])
self.AssertExitCode(exit_code, 0, stderr)
# TODO(pcloudy): change suffixes to .lib and .dll after making DLL
# extensions correct on Windows.
import_library = os.path.join(bazel_bin, 'B.if.lib')
shared_library = os.path.join(bazel_bin, 'B_0.dll')
def_file = os.path.join(bazel_bin, 'B.gen.def')
self.assertTrue(os.path.exists(import_library))
self.assertTrue(os.path.exists(shared_library))
# DEF file should be generated for //:B
self.assertTrue(os.path.exists(def_file))
# Test build //:B if windows_export_all_symbols feature is disabled by
# no_windows_export_all_symbols.
exit_code, _, stderr = self.RunBazel([
'build', '//:B', '--output_groups=dynamic_library',
'--features=no_windows_export_all_symbols'
])
self.AssertExitCode(exit_code, 0, stderr)
import_library = os.path.join(bazel_bin, 'B.if.lib')
shared_library = os.path.join(bazel_bin, 'B_0.dll')
empty_def_file = os.path.join(bazel_bin, 'B.gen.empty.def')
self.assertTrue(os.path.exists(import_library))
self.assertTrue(os.path.exists(shared_library))
# An empty DEF file should be generated for //:B
self.assertTrue(os.path.exists(empty_def_file))
self.AssertFileContentNotContains(empty_def_file, 'hello_B')
def testBuildCcBinaryWithDependenciesDynamicallyLinked(self):
self.createProjectFiles()
bazel_bin = self.getBazelInfo('bazel-bin')
# Since linkstatic=0 is specified for //:C, it's dependencies should be
# dynamically linked.
exit_code, _, stderr = self.RunBazel(['build', '//:C'])
self.AssertExitCode(exit_code, 0, stderr)
# TODO(pcloudy): change suffixes to .lib and .dll after making DLL
# extensions correct on
# Windows.
# a_import_library
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'A.if.lib')))
# a_shared_library
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'A_0.dll')))
# a_def_file
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'A.gen.empty.def')))
# b_import_library
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'B.if.lib')))
# b_shared_library
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'B_0.dll')))
# b_def_file
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'B.gen.def')))
# c_exe
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'C.exe')))
def testBuildCcBinaryFromDifferentPackage(self):
self.createProjectFiles()
self.ScratchFile('main/BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
' deps = ["//:B"],',
' linkstatic = 0,'
')',
])
bazel_bin = self.getBazelInfo('bazel-bin')
exit_code, _, stderr = self.RunBazel(['build', '//main:main'])
self.AssertExitCode(exit_code, 0, stderr)
# Test if A.dll and B.dll are copied to the directory of main.exe
main_bin = os.path.join(bazel_bin, 'main/main.exe')
self.assertTrue(os.path.exists(main_bin))
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'main/A_0.dll')))
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'main/B_0.dll')))
# Run the binary to see if it runs successfully
exit_code, stdout, stderr = self.RunProgram([main_bin])
self.AssertExitCode(exit_code, 0, stderr)
self.assertEqual(['Hello A, 1', 'Hello A, 2', 'Hello B', 'Hello C'], stdout)
def testBuildCcBinaryDependsOnConflictDLLs(self):
self.createProjectFiles()
self.ScratchFile(
'main/BUILD',
[
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
' deps = ["//:B", "//lib:A"],', # Transitively depends on //:A
' linkstatic = 0,'
')',
])
bazel_bin = self.getBazelInfo('bazel-bin')
# //main:main depends on both //lib:A and //:A
exit_code, _, stderr = self.RunBazel(['build', '//main:main'])
self.AssertExitCode(exit_code, 0, stderr)
# Run the binary to see if it runs successfully
main_bin = os.path.join(bazel_bin, 'main/main.exe')
exit_code, stdout, stderr = self.RunProgram([main_bin])
self.AssertExitCode(exit_code, 0, stderr)
self.assertEqual(['Hello A, 1', 'Hello A, 2', 'Hello B', 'Hello C'], stdout)
# There are 2 A_{hash}.dll since //main:main depends on both //lib:A and
# //:A
self.assertEqual(
len(glob.glob(os.path.join(bazel_bin, 'main', 'A_*.dll'))), 2)
# There is only 1 B_{hash}.dll
self.assertEqual(
len(glob.glob(os.path.join(bazel_bin, 'main', 'B_*.dll'))), 1)
def testBuildDifferentCcBinariesDependOnConflictDLLs(self):
self.createProjectFiles()
self.ScratchFile(
'main/BUILD',
[
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
' deps = ["//:B"],', # Transitively depends on //:A
' linkstatic = 0,'
')',
'',
'cc_binary(',
' name = "other_main",',
' srcs = ["other_main.cc"],',
' deps = ["//lib:A"],',
' linkstatic = 0,'
')',
])
bazel_bin = self.getBazelInfo('bazel-bin')
self.ScratchFile('main/other_main.cc', ['int main() {return 0;}'])
# Building //main:main should succeed
exit_code, _, stderr = self.RunBazel(
['build', '//main:main', '--incompatible_avoid_conflict_dlls'])
self.AssertExitCode(exit_code, 0, stderr)
main_bin = os.path.join(bazel_bin, 'main/main.exe')
# Run the main_bin binary to see if it runs successfully
exit_code, stdout, stderr = self.RunProgram([main_bin])
self.AssertExitCode(exit_code, 0, stderr)
self.assertEqual(['Hello A, 1', 'Hello A, 2', 'Hello B', 'Hello C'], stdout)
# There is only 1 A_{hash}.dll since //main:main depends transitively on
# //:A
self.assertEqual(
len(glob.glob(os.path.join(bazel_bin, 'main', 'A_*.dll'))), 1)
# There is only 1 B_{hash}.dll
self.assertEqual(
len(glob.glob(os.path.join(bazel_bin, 'main', 'B_*.dll'))), 1)
# Building //main:other_main should succeed
exit_code, _, stderr = self.RunBazel([
'build', '//main:main', '//main:other_main',
'--incompatible_avoid_conflict_dlls'
])
self.AssertExitCode(exit_code, 0, stderr)
other_main_bin = os.path.join(bazel_bin, 'main/other_main.exe')
# Run the other_main_bin binary to see if it runs successfully
exit_code, stdout, stderr = self.RunProgram([other_main_bin])
self.AssertExitCode(exit_code, 0, stderr)
# There are 2 A_{hash}.dll since //main:main depends on //:A
# and //main:other_main depends on //lib:A
self.assertEqual(
len(glob.glob(os.path.join(bazel_bin, 'main', 'A_*.dll'))), 2)
def testDLLIsCopiedFromExternalRepo(self):
self.ScratchFile('ext_repo/WORKSPACE')
self.ScratchFile('ext_repo/BUILD', [
'cc_library(',
' name = "A",',
' srcs = ["a.cc"],',
' features = ["windows_export_all_symbols"],',
' visibility = ["//visibility:public"],',
')',
])
self.ScratchFile('ext_repo/a.cc', [
'#include <stdio.h>',
'void hello_A() {',
' printf("Hello A\\n");',
'}',
])
self.ScratchFile('WORKSPACE', [
'local_repository(',
' name = "ext_repo",',
' path = "ext_repo",',
')',
])
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
' deps = ["@ext_repo//:A"],',
' linkstatic = 0,',
')',
])
self.ScratchFile('main.cc', [
'extern void hello_A();',
'',
'int main() {',
' hello_A();',
' return 0;',
'}',
])
bazel_bin = self.getBazelInfo('bazel-bin')
exit_code, _, stderr = self.RunBazel(['build', '//:main', '-s'])
self.AssertExitCode(exit_code, 0, stderr)
# Test if A.dll is copied to the directory of main.exe
main_bin = os.path.join(bazel_bin, 'main.exe')
self.assertTrue(os.path.exists(main_bin))
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'A_9324b6d0.dll')))
# Run the binary to see if it runs successfully
exit_code, stdout, stderr = self.RunProgram([main_bin])
self.AssertExitCode(exit_code, 0, stderr)
self.assertEqual(['Hello A'], stdout)
def testDynamicLinkingMSVCRT(self):
self.createProjectFiles()
bazel_output = self.getBazelInfo('output_path')
# By default, it should link to msvcrt dynamically.
exit_code, _, stderr = self.RunBazel(
['build', '//:A', '--output_groups=dynamic_library', '-s'])
paramfile = os.path.join(bazel_output,
'x64_windows-fastbuild/bin/A_0.dll-2.params')
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('/MD', ''.join(stderr))
self.AssertFileContentContains(paramfile, '/DEFAULTLIB:msvcrt.lib')
self.assertNotIn('/MT', ''.join(stderr))
self.AssertFileContentNotContains(paramfile, '/DEFAULTLIB:libcmt.lib')
# Test build in debug mode.
exit_code, _, stderr = self.RunBazel(
['build', '-c', 'dbg', '//:A', '--output_groups=dynamic_library', '-s'])
paramfile = os.path.join(bazel_output,
'x64_windows-dbg/bin/A_0.dll-2.params')
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('/MDd', ''.join(stderr))
self.AssertFileContentContains(paramfile, '/DEFAULTLIB:msvcrtd.lib')
self.assertNotIn('/MTd', ''.join(stderr))
self.AssertFileContentNotContains(paramfile, '/DEFAULTLIB:libcmtd.lib')
def testStaticLinkingMSVCRT(self):
self.createProjectFiles()
bazel_output = self.getBazelInfo('output_path')
# With static_link_msvcrt feature, it should link to msvcrt statically.
exit_code, _, stderr = self.RunBazel([
'build', '//:A', '--output_groups=dynamic_library',
'--features=static_link_msvcrt', '-s'
])
paramfile = os.path.join(bazel_output,
'x64_windows-fastbuild/bin/A_0.dll-2.params')
self.AssertExitCode(exit_code, 0, stderr)
self.assertNotIn('/MD', ''.join(stderr))
self.AssertFileContentNotContains(paramfile, '/DEFAULTLIB:msvcrt.lib')
self.assertIn('/MT', ''.join(stderr))
self.AssertFileContentContains(paramfile, '/DEFAULTLIB:libcmt.lib')
# Test build in debug mode.
exit_code, _, stderr = self.RunBazel([
'build', '-c', 'dbg', '//:A', '--output_groups=dynamic_library',
'--features=static_link_msvcrt', '-s'
])
paramfile = os.path.join(bazel_output,
'x64_windows-dbg/bin/A_0.dll-2.params')
self.AssertExitCode(exit_code, 0, stderr)
self.assertNotIn('/MDd', ''.join(stderr))
self.AssertFileContentNotContains(paramfile, '/DEFAULTLIB:msvcrtd.lib')
self.assertIn('/MTd', ''.join(stderr))
self.AssertFileContentContains(paramfile, '/DEFAULTLIB:libcmtd.lib')
def testBuildSharedLibraryFromCcBinaryWithStaticLink(self):
self.createProjectFiles()
self.ScratchFile(
'main/BUILD',
[
'cc_binary(',
' name = "main.dll",',
' srcs = ["main.cc"],',
' deps = ["//:B"],', # Transitively depends on //:A
' linkstatic = 1,'
' linkshared = 1,'
' features=["windows_export_all_symbols"]',
')',
])
bazel_bin = self.getBazelInfo('bazel-bin')
exit_code, _, stderr = self.RunBazel([
'build', '//main:main.dll',
'--output_groups=default,runtime_dynamic_libraries,interface_library'
])
self.AssertExitCode(exit_code, 0, stderr)
main_library = os.path.join(bazel_bin, 'main/main.dll')
main_interface = os.path.join(bazel_bin, 'main/main.dll.if.lib')
def_file = os.path.join(bazel_bin, 'main/main.dll.gen.def')
self.assertTrue(os.path.exists(main_library))
self.assertTrue(os.path.exists(main_interface))
self.assertTrue(os.path.exists(def_file))
# A.dll and B.dll should not be copied.
self.assertFalse(os.path.exists(os.path.join(bazel_bin, 'main/A.dll')))
self.assertFalse(os.path.exists(os.path.join(bazel_bin, 'main/B.dll')))
self.AssertFileContentContains(def_file, 'hello_A')
self.AssertFileContentContains(def_file, 'hello_B')
self.AssertFileContentContains(def_file, 'hello_C')
def testBuildSharedLibraryFromCcBinaryWithDynamicLink(self):
self.createProjectFiles()
self.ScratchFile(
'main/BUILD',
[
'cc_binary(',
' name = "main.dll",',
' srcs = ["main.cc"],',
' deps = ["//:B"],', # Transitively depends on //:A
' linkstatic = 0,'
' linkshared = 1,'
' features=["windows_export_all_symbols"]',
')',
'',
'genrule(',
' name = "renamed_main",',
' srcs = [":main.dll"],',
' outs = ["main_renamed.dll"],',
' cmd = "cp $< $@",',
')',
])
bazel_bin = self.getBazelInfo('bazel-bin')
exit_code, _, stderr = self.RunBazel([
'build', '//main:main.dll',
'--output_groups=default,runtime_dynamic_libraries,interface_library'
])
self.AssertExitCode(exit_code, 0, stderr)
main_library = os.path.join(bazel_bin, 'main/main.dll')
main_interface = os.path.join(bazel_bin, 'main/main.dll.if.lib')
def_file = os.path.join(bazel_bin, 'main/main.dll.gen.def')
self.assertTrue(os.path.exists(main_library))
self.assertTrue(os.path.exists(main_interface))
self.assertTrue(os.path.exists(def_file))
# A.dll and B.dll should be built and copied because they belong to
# runtime_dynamic_libraries output group.
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'main/A_0.dll')))
self.assertTrue(os.path.exists(os.path.join(bazel_bin, 'main/B_0.dll')))
# hello_A and hello_B should not be exported.
self.AssertFileContentNotContains(def_file, 'hello_A')
self.AssertFileContentNotContains(def_file, 'hello_B')
self.AssertFileContentContains(def_file, 'hello_C')
# The copy should succeed since //main:main.dll is only supposed to refer to
# main.dll, A.dll and B.dll should be in a separate output group.
exit_code, _, stderr = self.RunBazel(['build', '//main:renamed_main'])
self.AssertExitCode(exit_code, 0, stderr)
def testGetDefFileOfSharedLibraryFromCcBinary(self):
self.createProjectFiles()
self.ScratchFile(
'main/BUILD',
[
'cc_binary(',
' name = "main.dll",',
' srcs = ["main.cc"],',
' deps = ["//:B"],', # Transitively depends on //:A
' linkstatic = 1,'
' linkshared = 1,'
')',
])
bazel_bin = self.getBazelInfo('bazel-bin')
exit_code, _, stderr = self.RunBazel(
['build', '//main:main.dll', '--output_groups=def_file'])
self.AssertExitCode(exit_code, 0, stderr)
# Although windows_export_all_symbols is not specified for this target,
# we should still be able to get the DEF file by def_file output group.
def_file = os.path.join(bazel_bin, 'main/main.dll.gen.def')
self.assertTrue(os.path.exists(def_file))
self.AssertFileContentContains(def_file, 'hello_A')
self.AssertFileContentContains(def_file, 'hello_B')
self.AssertFileContentContains(def_file, 'hello_C')
def testBuildSharedLibraryWithoutAnySymbolExported(self):
self.createProjectFiles()
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "A.dll",',
' srcs = ["a.cc", "a.h"],',
' copts = ["/DNO_DLLEXPORT"],',
' linkshared = 1,'
')',
])
bazel_bin = self.getBazelInfo('bazel-bin')
exit_code, _, stderr = self.RunBazel(['build', '//:A.dll'])
self.AssertExitCode(exit_code, 0, stderr)
# Although windows_export_all_symbols is not specified for this target,
# we should still be able to build a DLL without any symbol exported.
empty_def_file = os.path.join(bazel_bin, 'A.dll.gen.empty.def')
self.assertTrue(os.path.exists(empty_def_file))
self.AssertFileContentNotContains(empty_def_file, 'hello_A')
def testUsingDefFileGeneratedFromCcLibrary(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('lib_A.cc', ['void hello_A() {}'])
self.ScratchFile('lib_B.cc', ['void hello_B() {}'])
self.ScratchFile('BUILD', [
'cc_library(',
' name = "lib_A",',
' srcs = ["lib_A.cc"],',
')',
'',
'cc_library(',
' name = "lib_B",',
' srcs = ["lib_B.cc"],',
' deps = [":lib_A"]',
')',
'',
'filegroup(',
' name = "lib_B_symbols",',
' srcs = [":lib_B"],',
' output_group = "def_file",',
')',
'',
'cc_binary(',
' name = "lib.dll",',
' deps = [":lib_B"],',
' win_def_file = ":lib_B_symbols",',
' linkshared = 1,',
')',
])
# Test specifying DEF file in cc_binary
bazel_bin = self.getBazelInfo('bazel-bin')
exit_code, _, stderr = self.RunBazel(['build', '//:lib.dll', '-s'])
self.AssertExitCode(exit_code, 0, stderr)
def_file = bazel_bin + '/lib_B.gen.def'
self.assertTrue(os.path.exists(def_file))
# hello_A should not be exported
self.AssertFileContentNotContains(def_file, 'hello_A')
# hello_B should be exported
self.AssertFileContentContains(def_file, 'hello_B')
def testWinDefFileAttribute(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('lib.cc', ['void hello() {}'])
self.ScratchFile('my_lib.def', [
'EXPORTS',
' ?hello@@YAXXZ',
])
self.ScratchFile('BUILD', [
'cc_library(',
' name = "lib",',
' srcs = ["lib.cc"],',
' win_def_file = "my_lib.def",',
')',
'',
'cc_binary(',
' name = "lib_dy.dll",',
' srcs = ["lib.cc"],',
' win_def_file = "my_lib.def",',
' linkshared = 1,',
')',
])
# Test exporting symbols using custom DEF file in cc_library.
# Auto-generating DEF file should be disabled when custom DEF file specified
# Rename DLL shoud be disabled when when custom DEF file specified
exit_code, _, stderr = self.RunBazel([
'build', '//:lib', '-s', '--output_groups=dynamic_library',
'--features=windows_export_all_symbols'
])
self.AssertExitCode(exit_code, 0, stderr)
bazel_bin = self.getBazelInfo('bazel-bin')
lib_if = os.path.join(bazel_bin, 'lib.if.lib')
lib_def = os.path.join(bazel_bin, 'lib.gen.def')
lib_dll = os.path.join(bazel_bin, 'lib.dll')
self.assertTrue(os.path.exists(lib_if))
self.assertFalse(os.path.exists(lib_def))
self.assertTrue(os.path.exists(lib_dll))
# Test specifying DEF file in cc_binary
exit_code, _, stderr = self.RunBazel(['build', '//:lib_dy.dll', '-s'])
self.AssertExitCode(exit_code, 0, stderr)
filepath = bazel_bin + '/lib_dy.dll-2.params'
with open(filepath, 'r', encoding='latin-1') as param_file:
self.assertIn('/DEF:my_lib.def', param_file.read())
def testCcImportRule(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('A.lib', [])
self.ScratchFile('A.dll', [])
self.ScratchFile('A.if.lib', [])
self.ScratchFile('BUILD', [
'cc_import(',
' name = "a_import",',
' static_library = "A.lib",',
' shared_library = "A.dll",',
' interface_library = "A.if.lib",',
' hdrs = ["a.h"],',
' alwayslink = 1,',
')',
])
exit_code, _, stderr = self.RunBazel([
'build', '//:a_import',
])
self.AssertExitCode(exit_code, 0, stderr)
def testCopyDLLAsSource(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_import(',
' name = "a_import",',
' shared_library = "A.dll",',
' visibility = ["//:__subpackages__"],',
')',
''
'filegroup(',
' name = "bin_src",',
' srcs = ["bin.cc"],',
' visibility = ["//:__subpackages__"],',
')',
'',
'cc_binary(',
' name = "bin",',
' srcs = ["//:bin_src"],',
' deps = ["//:a_import"],',
')',
])
self.ScratchFile('package/BUILD', [
'cc_binary(',
' name = "dir1/dir2/bin",',
' srcs = ["//:bin_src"],',
' deps = ["//:a_import"],',
')',
])
self.ScratchFile('A.dll')
self.ScratchFile('bin.cc', [
'int main() {',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel([
'build',
'//:bin',
'//package:dir1/dir2/bin',
])
self.AssertExitCode(exit_code, 0, stderr)
bazel_bin = self.getBazelInfo('bazel-bin')
# Even though A.dll is in the same package as bin.exe, it still should
# be copied to the output directory of bin.exe.
a_dll = os.path.join(bazel_bin, 'A.dll')
self.assertTrue(os.path.exists(a_dll))
nested_a_dll = os.path.join(bazel_bin, 'package/dir1/dir2/A.dll')
self.assertTrue(os.path.exists(nested_a_dll))
def testCppErrorShouldBeVisible(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "bad",',
' srcs = ["bad.cc"],',
')',
])
self.ScratchFile('bad.cc', [
'int main(int argc, char** argv) {',
' this_is_an_error();',
'}',
])
exit_code, stdout, stderr = self.RunBazel(['build', '//:bad'])
self.AssertExitCode(exit_code, 1, stderr)
self.assertIn('this_is_an_error', ''.join(stdout))
def testBuildWithClangClByCompilerFlag(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--compiler=clang-cl',
'--incompatible_enable_cc_toolchain_resolution=false', '//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('clang-cl.exe', ''.join(stderr))
def testBuildWithClangClByToolchainResolution(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE', [
'register_execution_platforms(',
' ":windows_clang"',
')',
'',
'register_toolchains(',
' "@local_config_cc//:cc-toolchain-x64_windows-clang-cl",',
')',
])
self.ScratchFile('BUILD', [
'platform(',
' name = "windows_clang",',
' constraint_values = [',
' "@platforms//cpu:x86_64",',
' "@platforms//os:windows",',
' "@bazel_tools//tools/cpp:clang-cl",',
' ]',
')',
'',
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--incompatible_enable_cc_toolchain_resolution=true',
'//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('clang-cl.exe', ''.join(stderr))
def createSimpleCppWorkspace(self, name):
work_dir = self.ScratchDir(name)
self.ScratchFile(name + '/WORKSPACE', ['workspace(name = "%s")' % name])
self.ScratchFile(
name + '/BUILD',
['cc_library(name = "lib", srcs = ["lib.cc"], hdrs = ["lib.h"])'])
self.ScratchFile(name + '/lib.h', ['void hello();'])
self.ScratchFile(name + '/lib.cc', ['#include "lib.h"', 'void hello() {}'])
return work_dir
# Regression test for https://github.com/bazelbuild/bazel/issues/9172
def testCacheBetweenWorkspaceWithDifferentNames(self):
cache_dir = self.ScratchDir('cache')
dir_a = self.createSimpleCppWorkspace('A')
dir_b = self.createSimpleCppWorkspace('B')
exit_code, _, stderr = self.RunBazel(
['build', '--disk_cache=' + cache_dir, ':lib'], cwd=dir_a)
self.AssertExitCode(exit_code, 0, stderr)
exit_code, _, stderr = self.RunBazel(
['build', '--disk_cache=' + cache_dir, ':lib'], cwd=dir_b)
self.AssertExitCode(exit_code, 0, stderr)
# Regression test for https://github.com/bazelbuild/bazel/issues/9321
def testCcCompileWithTreeArtifactAsSource(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'load(":genccs.bzl", "genccs")',
'',
'genccs(',
' name = "gen_tree",',
')',
'',
'cc_library(',
' name = "main",',
' srcs = [ "gen_tree" ]',
')',
'',
'cc_binary(',
' name = "genccs",',
' srcs = [ "genccs.cpp" ],',
')',
])
self.ScratchFile('genccs.bzl', [
'def _impl(ctx):',
' tree = ctx.actions.declare_directory(ctx.attr.name + ".cc")',
' ctx.actions.run(',
' inputs = [],',
' outputs = [ tree ],',
' arguments = [ tree.path ],',
' progress_message = "Generating cc files into \'%s\'" % tree.path,',
' executable = ctx.executable._tool,',
' )',
'',
' return [ DefaultInfo(files = depset([ tree ])) ]',
'',
'genccs = rule(',
' implementation = _impl,',
' attrs = {',
' "_tool": attr.label(',
' executable = True,',
' cfg = "exec",',
' allow_files = True,',
' default = Label("//:genccs"),',
' )',
' }',
')',
])
self.ScratchFile('genccs.cpp', [
'#include <fstream>',
'#include <Windows.h>',
'using namespace std;',
'',
'int main (int argc, char *argv[]) {',
' CreateDirectory(argv[1], NULL);',
' ofstream myfile;',
' myfile.open(string(argv[1]) + string("/foo.cpp"));',
' myfile << "int main() { return 42; }";',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel(['build', '//:main'])
self.AssertExitCode(exit_code, 0, stderr)
def testBuild32BitCppBinaryWithMsvcCL(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--cpu=x64_x86_windows',
'--noincompatible_enable_cc_toolchain_resolution', '//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('x86\\cl.exe', '\n'.join(stderr))
def testBuildArmCppBinaryWithMsvcCL(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--cpu=x64_arm_windows',
'--noincompatible_enable_cc_toolchain_resolution', '//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('arm\\cl.exe', '\n'.join(stderr))
def testBuildArm64CppBinaryWithMsvcCLAndCpuX64Arm64Windows(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--cpu=x64_arm64_windows',
'--noincompatible_enable_cc_toolchain_resolution', '//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('arm64\\cl.exe', '\n'.join(stderr))
def testBuildCppBinaryWithMingwGCC(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
# Test build without debug and optimize modes.
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--compiler=mingw-gcc',
'--noincompatible_enable_cc_toolchain_resolution', '//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('mingw64\\bin\\gcc', '\n'.join(stderr))
self.assertNotIn('-g -Og', ''.join(stderr))
self.assertNotIn('-g0 -O3 -DNDEBUG -ffunction-sections -fdata-sections',
''.join(stderr))
self.assertNotIn('-Wl,--gc-sections', ''.join(stderr))
# Test build in debug mode.
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--compiler=mingw-gcc',
'--noincompatible_enable_cc_toolchain_resolution', '-c', 'dbg',
'//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('mingw64\\bin\\gcc', '\n'.join(stderr))
self.assertIn('-g -Og', ''.join(stderr))
self.assertNotIn('-g0 -O3 -DNDEBUG -ffunction-sections -fdata-sections',
''.join(stderr))
self.assertNotIn('-Wl,--gc-sections', ''.join(stderr))
# Test build in optimize mode.
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--compiler=mingw-gcc',
'--noincompatible_enable_cc_toolchain_resolution', '-c', 'opt',
'//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('mingw64\\bin\\gcc', '\n'.join(stderr))
self.assertNotIn('-g -Og', ''.join(stderr))
self.assertIn('-g0 -O3 -DNDEBUG -ffunction-sections -fdata-sections',
''.join(stderr))
self.assertIn('-Wl,--gc-sections', ''.join(stderr))
def testBuildCppBinaryWithMsysGCC(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
bazel_output = self.getBazelInfo('output_path')
paramfile = 'x64_windows-%s/bin/main.exe-2.params'
# Test build without debug and optimize modes.
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--compiler=msys-gcc',
'--noincompatible_enable_cc_toolchain_resolution', '//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('usr\\bin\\gcc', '\n'.join(stderr))
self.assertNotIn('-g -Og', ''.join(stderr))
self.assertNotIn('-g0 -O3 -DNDEBUG -ffunction-sections -fdata-sections',
''.join(stderr))
self.AssertFileContentNotContains(
os.path.join(bazel_output, paramfile % 'fastbuild'),
'-Wl,--gc-sections')
# Test build in debug mode.
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--compiler=msys-gcc',
'--noincompatible_enable_cc_toolchain_resolution', '-c', 'dbg',
'//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('usr\\bin\\gcc', '\n'.join(stderr))
self.assertIn('-g -Og', ''.join(stderr))
self.assertNotIn('-g0 -O3 -DNDEBUG -ffunction-sections -fdata-sections',
''.join(stderr))
self.AssertFileContentNotContains(
os.path.join(bazel_output, paramfile % 'dbg'), '-Wl,--gc-sections')
# Test build in optimize mode.
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--compiler=msys-gcc',
'--noincompatible_enable_cc_toolchain_resolution', '-c', 'opt',
'//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('usr\\bin\\gcc', '\n'.join(stderr))
self.assertNotIn('-g -Og', ''.join(stderr))
self.assertIn('-g0 -O3 -DNDEBUG -ffunction-sections -fdata-sections',
''.join(stderr))
self.AssertFileContentContains(
os.path.join(bazel_output, paramfile % 'opt'), '-Wl,--gc-sections')
def testBuildArm64CppBinaryWithMsvcCLAndCpuArm64Windows(self):
self.CreateWorkspaceWithDefaultRepos('WORKSPACE')
self.ScratchFile('BUILD', [
'cc_binary(',
' name = "main",',
' srcs = ["main.cc"],',
')',
])
self.ScratchFile('main.cc', [
'int main() {',
' return 0;',
'}',
])
exit_code, _, stderr = self.RunBazel([
'build', '-s', '--cpu=arm64_windows',
'--noincompatible_enable_cc_toolchain_resolution', '//:main'
])
self.AssertExitCode(exit_code, 0, stderr)
self.assertIn('arm64\\cl.exe', ''.join(stderr))
if __name__ == '__main__':
unittest.main()