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