-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathconanfile.py
181 lines (158 loc) · 6.92 KB
/
conanfile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
import os
import shutil
from conans import ConanFile, Variants, tools
from conans.errors import ConanException
from conans.model import Middleware
from conans.model.conan_file import get_env_context_manager
from conans.client.tools.apple import is_apple_os
from conans.client.build.cmake_flags import get_generator
required_conan_version = ">=1.40.0"
# These are for optimization only, to avoid unnecessarily reading files.
_binary_exts = ['.a', '.dylib']
_regular_exts = [
'.h', '.hpp', '.hxx', '.c', '.cc', '.cxx', '.cpp', '.m', '.mm', '.txt', '.md', '.html', '.jpg', '.png'
]
def is_macho_binary(filename):
ext = os.path.splitext(filename)[1]
if ext in _binary_exts:
return True
if ext in _regular_exts:
return False
with open(filename, "rb") as f:
header = f.read(4)
if header == b'\xcf\xfa\xed\xfe':
# cffaedfe is Mach-O binary
return True
elif header == b'\xca\xfe\xba\xbe':
# cafebabe is Mach-O fat binary
return True
elif header == b'!<arch>\n':
# ar archive
return True
return False
def copy_variant_file(conanfile, src, dst, top=None, variants=[]):
if os.path.isfile(src):
if top and variants and is_macho_binary(src):
# Try to lipo all available variants on the first path.
src_components = src.split(os.path.sep)
top_components = top.split(os.path.sep)
if src_components[:len(top_components)] == top_components:
variant_dir = src_components[len(top_components)]
subpath = src_components[len(top_components) + 2:]
variant_paths = [os.path.join(*([top, variant_dir, variant] + subpath)) for variant in variants]
variant_paths = [p for p in variant_paths if os.path.isfile(p)]
if len(variant_paths) > 1:
conanfile.run(['lipo', '-create', '-output', dst] + variant_paths)
return
if os.path.exists(dst):
pass # don't overwrite existing files
else:
shutil.copy2(src, dst)
# Modified copytree to copy new files to an existing tree.
def graft_tree(src, dst, symlinks=False, copy_function=shutil.copy2, dirs_exist_ok=False):
names = os.listdir(src)
os.makedirs(dst, exist_ok=dirs_exist_ok)
errors = []
for name in names:
srcname = os.path.join(src, name)
dstname = os.path.join(dst, name)
if os.path.exists(dstname):
continue
try:
if symlinks and os.path.islink(srcname):
linkto = os.readlink(srcname)
os.symlink(linkto, dstname)
elif os.path.isdir(srcname):
graft_tree(srcname, dstname, symlinks, copy_function, dirs_exist_ok)
else:
copy_function(srcname, dstname)
# XXX What about devices, sockets etc.?
except OSError as why:
errors.append((srcname, dstname, str(why)))
# catch the Error from the recursive graft_tree so that we can
# continue with other files
except shutil.Error as err:
errors.extend(err.args[0])
try:
shutil.copystat(src, dst)
except OSError as why:
# can't copy file access times on Windows
if why.winerror is None:
errors.extend((src, dst, str(why)))
if errors:
raise shutil.Error(errors)
class patch_arguments:
def __init__(self, target, name, func):
self._target = target
self._name = name
self._func = func
def __call__(self, *args, **kw):
return self._func(self._original, *args, **kw)
def __enter__(self):
self._original = getattr(self._target, self._name)
setattr(self._target, self._name, self)
def __exit__(self, exc_type, exc_val, exc_tb):
setattr(self._target, self._name, self._original)
class lipo(Middleware):
settings = "os", "multiarch"
def should_apply(self, base):
if not Middleware.is_binary(base):
return False
if not is_apple_os(self.settings.os):
return True
if "multiarch" in (base.settings or ()):
# Recipe handles this itself
return False
return self.settings.multiarch and len(str(self.settings.multiarch).split()) > 1
def __call__(self, base):
class LipoConan(Variants, base):
settings = Middleware.extend(base.settings, lipo.settings)
""" If the user is using Xcode, we assume they are using a custom toolchain for multiarch. """
def is_xcode(self):
if "cmake" in self.generators:
with get_env_context_manager(self):
if get_generator(self) == "Xcode":
return True
return False
def is_universal(self):
# Recipe handles this itself
return self.is_xcode() or "multiarch" in (base.settings or ())
def is_binary(self):
return Middleware.is_binary(self) and is_apple_os(self.settings.os)
def variants(self):
if not self.is_binary():
return None
if self.is_universal():
return None
return super().variants()
@staticmethod
def xcode_copy(copy, *args, excludes=(), **kw):
# Some packages (libpng) use cmake but package with self.copy("*")
# so make sure that we don't find the single arch files.
copy(*args, excludes=list(excludes) + ["*Objects-normal"], **kw)
def configure(self):
super().configure()
self.set_variants(self.settings.multiarch)
def package(self):
with patch_arguments(self, "copy", self.xcode_copy):
if self.is_binary() and self.is_universal():
return super().package()
self.package_variants()
variants = self.variants()
if not variants:
return
package_folder = self.package_folder
variant_folders = [self.get_variant_basename(v) for v in variants]
for v in variants:
graft_tree(self.get_variant_folder(package_folder, v),
package_folder,
symlinks=True,
copy_function=lambda s, d: copy_variant_file(self, s, d,
top=package_folder,
variants=variant_folders),
dirs_exist_ok=True)
shutil.rmtree(os.path.join(package_folder, self.variants_folder))
return LipoConan
class LipoMiddleware(ConanFile):
name = "lipo-middleware"
version = "0.1"