@@ -10,12 +10,42 @@ class EmbeddedPython(ConanFile):
10
10
description = "Embedded distribution of Python"
11
11
url = "https://www.python.org/"
12
12
license = "PSFL"
13
- settings = {"os" : ["Windows" ]}
14
- options = {"version" : "ANY" , "packages" : "ANY" }
15
- default_options = "packages=None"
13
+ settings = "os" , "compiler" , "build_type" , "arch"
14
+ options = {
15
+ "version" : "ANY" ,
16
+ "packages" : "ANY" ,
17
+ "openssl_variant" : ["lowercase" , "uppercase" ] # see explanation in `build_requirements()`
18
+ }
19
+ default_options = {
20
+ "packages" : None ,
21
+ "openssl_variant" : "lowercase"
22
+ }
16
23
exports = "embedded_python_tools.py" , "embedded_python.cmake"
17
24
short_paths = True # some of the pip packages go over the 260 char path limit on Windows
18
25
26
+ def config_options (self ):
27
+ """On Windows, we download a binary so these options have no effect"""
28
+ if self .settings .os == "Windows" :
29
+ del self .settings .compiler
30
+ del self .settings .build_type
31
+
32
+ def build_requirements (self ):
33
+ """On Windows, we download a binary so we don't need anything else"""
34
+ if self .settings .os == "Windows" :
35
+ return
36
+
37
+ self .build_requires ("sqlite3/3.35.5" )
38
+ self .build_requires ("bzip2/1.0.8" )
39
+
40
+ # The pre-conan-center-index version of `openssl` was capitalized as `OpenSSL`.
41
+ # Both versions can't live in the same Conan cache so we need this compatibility
42
+ # option to pick the available version. The cache case-sensitivity issue should
43
+ # be solved in Conan 2.0, but we need this for now.
44
+ if self .options .openssl_variant == "lowercase" :
45
+ self .build_requires ("openssl/1.1.1k" )
46
+ else :
47
+ self .build_requires ("OpenSSL/1.1.1f" )
48
+
19
49
@property
20
50
def pyversion (self ):
21
51
"""Full Python version that we want to package"""
@@ -64,7 +94,10 @@ def _gather_licenses(self, bootstrap):
64
94
65
95
def build (self ):
66
96
prefix = pathlib .Path (self .build_folder ) / "embedded_python"
67
- build_helper = WindowsBuildHelper (self , prefix )
97
+ if self .settings .os == "Windows" :
98
+ build_helper = WindowsBuildHelper (self , prefix )
99
+ else :
100
+ build_helper = UnixLikeBuildHelper (self , prefix )
68
101
build_helper .build_embedded ()
69
102
70
103
if not self .options .packages :
@@ -142,3 +175,72 @@ def build_bootstrap(self):
142
175
143
176
return python_exe
144
177
178
+
179
+ class UnixLikeBuildHelper :
180
+ def __init__ (self , conanfile , prefix_dir ):
181
+ self .conanfile = conanfile
182
+ self .prefix_dir = prefix_dir
183
+
184
+ def _get_source (self ):
185
+ url = f"https://github.com/python/cpython/archive/v{ self .conanfile .pyversion } .tar.gz"
186
+ tools .get (url )
187
+ os .rename (f"cpython-{ self .conanfile .pyversion } " , "src" )
188
+
189
+ @property
190
+ def _openssl_path (self ):
191
+ if self .conanfile .options .openssl_variant == "lowercase" :
192
+ pck = "openssl"
193
+ else :
194
+ pck = "OpenSSL"
195
+ return self .conanfile .deps_cpp_info [pck ].rootpath
196
+
197
+ def _build (self , dest_dir ):
198
+ from conans import AutoToolsBuildEnvironment
199
+
200
+ autotools = AutoToolsBuildEnvironment (self .conanfile )
201
+ env_vars = autotools .vars
202
+
203
+ # On Linux, we need to set RPATH so that `root/bin/python3` can correctly find the `.so`
204
+ # file in `root/lib` no matter where `root` is. We need it to be portable. We explicitly
205
+ # set `--disable-new-dtags` to use RPATH instead of RUNPATH. RUNPATH can be overridden by
206
+ # the LD_LIBRARY_PATH env variable which is not at all what we want for this self-contained
207
+ # package. Unlike RUNPATH, RPATH takes precedence over LD_LIBRARY_PATH.
208
+ if self .conanfile .settings .os == "Linux" :
209
+ env_vars ["LDFLAGS" ] += " -Wl,-rpath,'$$ORIGIN/../lib' -Wl,--disable-new-dtags"
210
+
211
+ config_args = " " .join ([
212
+ "--enable-shared" ,
213
+ f"--prefix={ dest_dir } " ,
214
+ f"--with-openssl={ self ._openssl_path } " ,
215
+ ])
216
+
217
+ tools .mkdir ("./build" )
218
+ with tools .chdir ("./build" ), tools .environment_append (env_vars ):
219
+ self .conanfile .run (f"../src/configure { config_args } " )
220
+ self .conanfile .run ("make -j8" )
221
+ self .conanfile .run ("make install -j8" )
222
+
223
+ ver = "." .join (self .conanfile .pyversion .split ("." )[:2 ])
224
+ exe = str (dest_dir / f"bin/python{ ver } " )
225
+ self .conanfile .run (f"{ exe } -m pip install -U pip==21.1.1" )
226
+
227
+ # Move the license file to match the Windows layout
228
+ lib_dir = dest_dir / "lib"
229
+ os .rename (lib_dir / f"python{ ver } /LICENSE.txt" , dest_dir / "LICENSE.txt" )
230
+
231
+ # Give write permissions, otherwise end-user projects won't be able to re-import
232
+ # the shared libraries (re-import happens on subsequent `conan install` runs).
233
+ for file in lib_dir .glob ("libpython*" ):
234
+ self .conanfile .run (f"chmod 777 { file } " )
235
+
236
+ def build_embedded (self ):
237
+ self ._get_source ()
238
+ self ._build (self .prefix_dir )
239
+
240
+ def enable_site_packages (self ):
241
+ """These are enabled by default when building from source"""
242
+ pass
243
+
244
+ def build_bootstrap (self ):
245
+ """For now, as a shortcut, we'll let the Unix-like builds bootstrap themselves"""
246
+ return self .prefix_dir / "bin/python3"
0 commit comments