1#!/usr/bin/env python
2#
3# Copyright (c) 2017-2020 Intel Corporation
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17# Provides unified tool for preparing TBB for packaging
18
19from __future__ import print_function
20import os
21import re
22import sys
23import shutil
24import platform
25import argparse
26from glob import glob
27from collections import OrderedDict
28
29jp = os.path.join
30is_win = (platform.system() == 'Windows')
31is_lin = (platform.system() == 'Linux')
32is_mac = (platform.system() == 'Darwin')
33
34default_prefix = os.getenv('PREFIX', 'install_prefix')
35if is_win:
36    default_prefix = jp(default_prefix, 'Library') # conda-specific by default on Windows
37
38parser = argparse.ArgumentParser()
39parser.add_argument('--tbbroot',        default='.', help='Take Intel TBB from here')
40parser.add_argument('--prefix',         default=default_prefix, help='Prefix')
41parser.add_argument('--prebuilt',       default=[],    action='append', help='Directories to find prebuilt files')
42parser.add_argument('--no-rebuild',     default=False, action='store_true', help='do not rebuild')
43parser.add_argument('--install',        default=False, action='store_true', help='install all')
44parser.add_argument('--install-libs',   default=False, action='store_true', help='install libs')
45parser.add_argument('--install-devel',  default=False, action='store_true', help='install devel')
46parser.add_argument('--install-docs',   default=False, action='store_true', help='install docs')
47parser.add_argument('--install-python', default=False, action='store_true', help='install python module')
48parser.add_argument('--make-tool',      default='make', help='Use different make command instead')
49parser.add_argument('--copy-tool',      default=None, help='Use this command for copying ($ tool file dest-dir)')
50parser.add_argument('--build-args',     default="", help='specify extra build args')
51parser.add_argument('--build-prefix',   default='local', help='build dir prefix')
52parser.add_argument('--cmake-dir',      help='directory to install CMake configuration files. Default: <prefix>/lib/cmake/tbb')
53if is_win:
54    parser.add_argument('--msbuild',   default=False, action='store_true', help='Use msbuild')
55    parser.add_argument('--vs',          default="2012", help='select VS version for build')
56    parser.add_argument('--vs-platform', default="x64",  help='select VS platform for build')
57parser.add_argument('ignore', nargs='?', help="workaround conda-build issue #2512")
58
59args = parser.parse_args()
60
61if args.install:
62    args.install_libs  = True
63    args.install_devel = True
64    args.install_docs  = True
65    args.install_python= True
66
67def custom_cp(src, dst):
68     assert os.system(' '.join([args.copy_tool, src, dst])) == 0
69
70if args.copy_tool:
71    install_cp = custom_cp # e.g. to use install -p -D -m 755 on Linux
72else:
73    install_cp = shutil.copy
74
75bin_dir   = jp(args.prefix, "bin")
76lib_dir   = jp(args.prefix, "lib")
77inc_dir   = jp(args.prefix, 'include')
78doc_dir   = jp(args.prefix, 'share', 'doc', 'tbb')
79cmake_dir = jp(args.prefix, "lib", "cmake", "tbb") if args.cmake_dir is None else args.cmake_dir
80
81if is_win:
82    os.environ["OS"] = "Windows_NT" # make sure TBB will interpret it correctly
83    libext = '.dll'
84    libpref = ''
85    dll_dir = bin_dir
86else:
87    libext = '.dylib' if is_mac else '.so.2'
88    libpref = 'lib'
89    dll_dir = lib_dir
90
91tbb_names = ["tbb", "tbbmalloc", "tbbmalloc_proxy"]
92
93##############################################################
94
95def system(arg):
96    print('$ ', arg)
97    return os.system(arg)
98
99def run_make(arg):
100    if system('%s -j %s'% (args.make_tool, arg)) != 0:
101       print("\nBummer. Running serial build in order to recover the log and have a chance to fix the build")
102       assert system('%s %s'% (args.make_tool, arg)) == 0
103
104os.chdir(args.tbbroot)
105if args.prebuilt:
106    release_dirs = sum([glob(d) for d in args.prebuilt], [])
107    print("Using pre-built files from ", release_dirs)
108else:
109    if is_win and args.msbuild:
110        preview_release_dir = release_dir = jp(args.tbbroot, 'build', 'vs'+args.vs, args.vs_platform, 'Release')
111        if not args.no_rebuild or not os.path.isdir(release_dir):
112            assert os.system('msbuild /m /p:Platform=%s /p:Configuration=Release %s build/vs%s/makefile.sln'% \
113                            (args.vs_platform, args.build_args, args.vs)) == 0
114        preview_debug_dir = debug_dir = jp(args.tbbroot, 'build', 'vs'+args.vs, args.vs_platform, 'Debug')
115        if not args.no_rebuild or not os.path.isdir(debug_dir):
116            assert os.system('msbuild /m /p:Platform=%s /p:Configuration=Debug %s build/vs%s/makefile.sln'% \
117                            (args.vs_platform, args.build_args, args.vs)) == 0
118    else:
119        release_dir = jp(args.tbbroot, 'build', args.build_prefix+'_release')
120        debug_dir = jp(args.tbbroot, 'build', args.build_prefix+'_debug')
121        if not args.no_rebuild or not (os.path.isdir(release_dir) and os.path.isdir(debug_dir)):
122            run_make('tbb_build_prefix=%s %s'% (args.build_prefix, args.build_args))
123        preview_release_dir = jp(args.tbbroot, 'build', args.build_prefix+'_preview_release')
124        preview_debug_dir = jp(args.tbbroot, 'build', args.build_prefix+'_preview_debug')
125        if not args.no_rebuild or not (os.path.isdir(preview_release_dir) and os.path.isdir(preview_debug_dir)):
126            run_make('tbb_build_prefix=%s_preview %s tbb_cpf=1 tbb'% (args.build_prefix, args.build_args))
127    release_dirs = [release_dir, debug_dir, preview_release_dir, preview_debug_dir]
128
129filemap = OrderedDict()
130def append_files(names, dst, paths=release_dirs):
131    global filemap
132    files = sum([glob(jp(d, f)) for d in paths for f in names], [])
133    filemap.update(dict(zip(files, [dst]*len(files))))
134
135
136if args.install_libs:
137    append_files([libpref+f+libext for f in tbb_names], dll_dir)
138
139if args.install_devel:
140    dll_files = [libpref+f+'_debug'+libext for f in tbb_names]          # adding debug libraries
141    if not is_win or not args.msbuild:
142        dll_files += [libpref+"tbb_preview"+libext, libpref+"tbb_preview_debug"+libext]
143    if is_win:
144        dll_files += ['tbb*.pdb']                                       # copying debug info
145    if is_lin:
146        dll_files += ['libtbb*.so']                                     # copying linker scripts
147        # symlinks .so -> .so.2 should not be created instead
148        # since linking with -ltbb when using links can result in
149        # incorrect dependence upon unversioned .so files
150    append_files(dll_files, dll_dir)
151    if is_win:
152        append_files(['*.lib', '*.def'], lib_dir)                       # copying linker libs and defs
153    for rootdir, dirnames, filenames in os.walk(jp(args.tbbroot,'include')):
154        files = [f for f in filenames if not '.html' in f]
155        append_files(files, jp(inc_dir, rootdir.split('include')[1][1:]), paths=(rootdir,))
156
157    # Preparing CMake configuration files
158    cmake_build_dir = jp(args.tbbroot, 'build', args.build_prefix+'_release', 'cmake_configs')
159    assert system('cmake -DINSTALL_DIR=%s -DSYSTEM_NAME=%s -DTBB_VERSION_FILE=%s -DINC_REL_PATH=%s -DLIB_REL_PATH=%s -DBIN_REL_PATH=%s -P %s' % \
160                  (cmake_build_dir,
161                   platform.system(),
162                   jp(args.tbbroot, 'include', 'tbb', 'tbb_stddef.h'),
163                   os.path.relpath(inc_dir, cmake_dir),
164                   os.path.relpath(lib_dir, cmake_dir),
165                   os.path.relpath(bin_dir, cmake_dir),
166                   jp(args.tbbroot, 'cmake', 'tbb_config_installer.cmake'))) == 0
167    append_files(['TBBConfig.cmake', 'TBBConfigVersion.cmake'], cmake_dir, paths=[cmake_build_dir])
168
169if args.install_python: # RML part
170    irml_dir = jp(args.tbbroot, 'build', args.build_prefix+'_release')
171    run_make('-C src tbb_build_prefix=%s %s python_rml'% (args.build_prefix, args.build_args))
172    if is_lin:
173        append_files(['libirml.so.1'], dll_dir, paths=[irml_dir])
174
175if args.install_docs:
176    files = [
177            'CHANGES',
178            'LICENSE',
179            'README',
180            'README.md',
181            'Release_Notes.txt',
182            ]
183    append_files(files, doc_dir, paths=release_dirs+[jp(args.tbbroot, d) for d in ('.', 'doc')])
184
185for f in filemap.keys():
186    assert os.path.exists(f)
187    assert os.path.isfile(f)
188
189if filemap:
190    print("Copying to prefix =", args.prefix)
191for f, dest in filemap.items():
192    if not os.path.isdir(dest):
193        os.makedirs(dest)
194    print("+ %s to $prefix%s"%(f,dest.replace(args.prefix, '')))
195    install_cp(f, dest)
196
197if args.install_python: # Python part
198    paths = [os.path.abspath(d) for d in [args.prefix, inc_dir, irml_dir, lib_dir]+release_dirs]
199    os.environ["TBBROOT"] = paths[0]
200    # all the paths must be relative to python/ directory or be absolute
201    assert system('python python/setup.py build -b%s build_ext -I%s -L%s install -f'% \
202        (paths[2], paths[1], ':'.join(paths[2:]))) == 0
203
204print("done")
205