1use {
6 super::{
7 binary::{
8 LibpythonLinkMode, PackedResourcesLoadMode, PythonBinaryBuilder,
9 ResourceAddCollectionContextCallback, WindowsRuntimeDllsMode,
10 },
11 config::{PyembedPackedResourcesSource, PyembedPythonInterpreterConfig},
12 distribution::{AppleSdkInfo, BinaryLibpythonLinkMode, PythonDistribution},
13 embedding::{
14 EmbeddedPythonContext, LibpythonLinkSettings, LinkSharedLibraryPath,
15 LinkStaticLibraryData, LinkingAnnotation,
16 },
17 filtering::{filter_btreemap, resolve_resource_names_from_files},
18 libpython::link_libpython,
19 packaging_tool::{
20 find_resources, pip_download, pip_install, read_virtualenv, setup_py_install,
21 },
22 standalone_distribution::StandaloneDistribution,
23 },
24 crate::environment::Environment,
25 anyhow::{anyhow, Context, Result},
26 log::warn,
27 once_cell::sync::Lazy,
28 pyo3_build_config::{BuildFlag, BuildFlags, PythonImplementation, PythonVersion},
29 python_packaging::{
30 bytecode::BytecodeCompiler,
31 interpreter::MemoryAllocatorBackend,
32 libpython::LibPythonBuildContext,
33 licensing::{
34 derive_package_license_infos, ComponentFlavor, LicensedComponent, LicensedComponents,
35 },
36 location::AbstractResourceLocation,
37 policy::PythonPackagingPolicy,
38 resource::{
39 PythonExtensionModule, PythonModuleSource, PythonPackageDistributionResource,
40 PythonPackageResource, PythonResource,
41 },
42 resource_collection::{
43 AddResourceAction, PrePackagedResource, PythonResourceAddCollectionContext,
44 PythonResourceCollector,
45 },
46 },
47 simple_file_manifest::{File, FileData, FileEntry, FileManifest},
48 std::{
49 collections::{BTreeMap, BTreeSet, HashMap},
50 path::{Path, PathBuf},
51 str::FromStr,
52 sync::Arc,
53 },
54 tugger_windows::{find_visual_cpp_redistributable, VcRedistributablePlatform},
55};
56
57static LINUX_IGNORE_LIBRARIES: Lazy<Vec<&'static str>> = Lazy::new(|| vec!["dl", "m"]);
59
60static MACOS_IGNORE_LIBRARIES: Lazy<Vec<&'static str>> = Lazy::new(|| vec!["dl", "m"]);
62
63fn ignored_libraries_for_target(target_triple: &str) -> Vec<&'static str> {
65 if crate::environment::LINUX_TARGET_TRIPLES.contains(&target_triple) {
66 LINUX_IGNORE_LIBRARIES.clone()
67 } else if crate::environment::MACOS_TARGET_TRIPLES.contains(&target_triple) {
68 MACOS_IGNORE_LIBRARIES.clone()
69 } else {
70 vec![]
71 }
72}
73
74#[derive(Clone)]
76pub struct StandalonePythonExecutableBuilder {
77 host_triple: String,
79
80 target_triple: String,
82
83 exe_name: String,
85
86 host_distribution: Arc<dyn PythonDistribution>,
88
89 target_distribution: Arc<StandaloneDistribution>,
91
92 link_mode: LibpythonLinkMode,
94
95 #[allow(dead_code)]
98 supports_in_memory_dynamically_linked_extension_loading: bool,
99
100 packaging_policy: PythonPackagingPolicy,
102
103 resources_collector: PythonResourceCollector,
105
106 resources_load_mode: PackedResourcesLoadMode,
108
109 core_build_context: LibPythonBuildContext,
111
112 extension_build_contexts: BTreeMap<String, LibPythonBuildContext>,
117
118 config: PyembedPythonInterpreterConfig,
120
121 host_python_exe: PathBuf,
123
124 licenses_filename: Option<String>,
126
127 windows_subsystem: String,
129
130 tcl_files_path: Option<String>,
132
133 windows_runtime_dlls_mode: WindowsRuntimeDllsMode,
135}
136
137impl StandalonePythonExecutableBuilder {
138 #[allow(clippy::too_many_arguments)]
139 pub fn from_distribution(
140 host_distribution: Arc<dyn PythonDistribution>,
141 target_distribution: Arc<StandaloneDistribution>,
142 host_triple: String,
143 target_triple: String,
144 exe_name: String,
145 link_mode: BinaryLibpythonLinkMode,
146 packaging_policy: PythonPackagingPolicy,
147 config: PyembedPythonInterpreterConfig,
148 ) -> Result<Box<Self>> {
149 let host_python_exe = host_distribution.python_exe_path().to_path_buf();
150
151 let (supports_static_libpython, supports_dynamic_libpython) =
152 target_distribution.libpython_link_support();
153
154 let link_mode = match link_mode {
155 BinaryLibpythonLinkMode::Default => {
156 if supports_static_libpython {
157 LibpythonLinkMode::Static
158 } else if supports_dynamic_libpython {
159 LibpythonLinkMode::Dynamic
160 } else {
161 return Err(anyhow!("no link modes supported; please report this bug"));
162 }
163 }
164 BinaryLibpythonLinkMode::Static => {
165 if !supports_static_libpython {
166 return Err(anyhow!(
167 "Python distribution does not support statically linking libpython"
168 ));
169 }
170
171 LibpythonLinkMode::Static
172 }
173 BinaryLibpythonLinkMode::Dynamic => {
174 if !supports_dynamic_libpython {
175 return Err(anyhow!(
176 "Python distribution does not support dynamically linking libpython"
177 ));
178 }
179
180 LibpythonLinkMode::Dynamic
181 }
182 };
183
184 let supports_in_memory_dynamically_linked_extension_loading =
185 target_distribution.supports_in_memory_shared_library_loading();
186
187 let mut allowed_locations = vec![AbstractResourceLocation::from(
188 packaging_policy.resources_location(),
189 )];
190 if let Some(fallback) = packaging_policy.resources_location_fallback() {
191 allowed_locations.push(AbstractResourceLocation::from(fallback));
192 }
193
194 let mut allowed_extension_module_locations = vec![];
195
196 if supports_in_memory_dynamically_linked_extension_loading
197 && packaging_policy.allow_in_memory_shared_library_loading()
198 {
199 allowed_extension_module_locations.push(AbstractResourceLocation::InMemory);
200 }
201
202 if target_distribution.is_extension_module_file_loadable() {
203 allowed_extension_module_locations.push(AbstractResourceLocation::RelativePath);
204 }
205
206 let allow_new_builtin_extension_modules = link_mode == LibpythonLinkMode::Static;
207
208 let mut builder = Box::new(Self {
209 host_triple,
210 target_triple,
211 exe_name,
212 host_distribution,
213 target_distribution,
214 link_mode,
215 supports_in_memory_dynamically_linked_extension_loading,
216 packaging_policy: packaging_policy.clone(),
217 resources_collector: PythonResourceCollector::new(
218 allowed_locations,
219 allowed_extension_module_locations,
220 allow_new_builtin_extension_modules,
221 packaging_policy.allow_files(),
222 ),
223 resources_load_mode: PackedResourcesLoadMode::EmbeddedInBinary(
224 "packed-resources".to_string(),
225 ),
226 core_build_context: LibPythonBuildContext::default(),
227 extension_build_contexts: BTreeMap::new(),
228 config,
229 host_python_exe,
230 licenses_filename: Some("COPYING.txt".into()),
231 windows_subsystem: "console".to_string(),
232 tcl_files_path: None,
233 windows_runtime_dlls_mode: WindowsRuntimeDllsMode::WhenPresent,
234 });
235
236 builder.add_distribution_core_state()?;
237
238 Ok(builder)
239 }
240
241 fn add_distribution_core_state(&mut self) -> Result<()> {
242 self.core_build_context.inittab_cflags =
243 Some(self.target_distribution.inittab_cflags.clone());
244
245 for (name, path) in &self.target_distribution.includes {
246 self.core_build_context
247 .includes
248 .insert(PathBuf::from(name), FileData::Path(path.clone()));
249 }
250
251 for fs_path in self.target_distribution.objs_core.values() {
253 if fs_path == &self.target_distribution.inittab_object {
256 continue;
257 }
258
259 self.core_build_context
260 .object_files
261 .push(FileData::Path(fs_path.clone()));
262 }
263
264 for entry in &self.target_distribution.links_core {
265 if entry.framework {
266 self.core_build_context
267 .frameworks
268 .insert(entry.name.clone());
269 } else if entry.system {
270 self.core_build_context
271 .system_libraries
272 .insert(entry.name.clone());
273 }
274 }
276
277 for path in self.target_distribution.libraries.values() {
278 self.core_build_context.library_search_paths.insert(
279 path.parent()
280 .ok_or_else(|| anyhow!("unable to resolve parent directory"))?
281 .to_path_buf(),
282 );
283 }
284
285 if crate::environment::WINDOWS_TARGET_TRIPLES.contains(&self.target_triple.as_str()) {
287 self.core_build_context
288 .system_libraries
289 .insert("msvcrt".to_string());
290 }
291
292 if let Some(component) = &self.target_distribution.core_license {
293 self.core_build_context
294 .licensed_components
295 .add_component(component.clone());
296 }
297
298 Ok(())
299 }
300
301 fn resolve_python_link_settings(
308 &self,
309 env: &Environment,
310 opt_level: &str,
311 ) -> Result<LibpythonLinkSettings> {
312 match self.link_mode {
313 LibpythonLinkMode::Static => {
314 warn!("generating custom link library containing Python...");
315
316 let mut link_contexts = vec![&self.core_build_context];
317 for c in self.extension_build_contexts.values() {
318 link_contexts.push(c);
319 }
320
321 let library_info = link_libpython(
322 env,
323 &LibPythonBuildContext::merge(&link_contexts),
324 &self.host_triple,
325 &self.target_triple,
326 opt_level,
327 self.apple_sdk_info(),
328 )?;
329
330 Ok(LinkStaticLibraryData {
331 library_data: library_info.libpython_data,
332 linking_annotations: library_info.linking_annotations,
333 }
334 .into())
335 }
336
337 LibpythonLinkMode::Dynamic => {
338 let library_path = self
339 .target_distribution
340 .libpython_shared_library
341 .clone()
342 .ok_or_else(|| {
343 anyhow!("target Python distribution does not have a shared libpython")
344 })?;
345
346 let filename = library_path
347 .file_name()
348 .ok_or_else(|| anyhow!("unable to resolve shared library filename"))?
349 .to_string_lossy();
350
351 let library_search_path = library_path
352 .parent()
353 .ok_or_else(|| anyhow!("unable to obtain shared library directory"))?;
354
355 let library_search_path = if filename.ends_with(".dll") {
357 library_search_path.join("libs")
358 } else {
359 library_search_path.to_path_buf()
360 };
361
362 let linking_annotations =
363 vec![LinkingAnnotation::SearchNative(library_search_path)];
364
365 Ok(LinkSharedLibraryPath {
366 library_path,
367
368 linking_annotations,
369 }
370 .into())
371 }
372 }
373 }
374
375 fn resolve_windows_runtime_dll_files(&self) -> Result<FileManifest> {
377 let mut manifest = FileManifest::default();
378
379 if let Some((version, platform)) = self.vc_runtime_requirements() {
381 if matches!(
382 self.windows_runtime_dlls_mode(),
383 WindowsRuntimeDllsMode::WhenPresent | WindowsRuntimeDllsMode::Always
384 ) {
385 match find_visual_cpp_redistributable(&version, platform) {
386 Ok(paths) => {
387 for path in paths {
388 let file_name = PathBuf::from(
389 path.file_name()
390 .ok_or_else(|| anyhow!("could not determine file name"))?,
391 );
392 manifest
393 .add_file_entry(file_name, FileEntry::new_from_path(path, true))?;
394 }
395 }
396 Err(err) => {
397 if matches!(
399 self.windows_runtime_dlls_mode(),
400 WindowsRuntimeDllsMode::Always
401 ) {
402 return Err(anyhow!(
403 "Windows Runtime DLLs mode of 'always' failed to locate files: {}",
404 err
405 ));
406 }
407 }
408 }
409 }
410 }
411
412 Ok(manifest)
413 }
414}
415
416impl PythonBinaryBuilder for StandalonePythonExecutableBuilder {
417 fn clone_trait(&self) -> Arc<dyn PythonBinaryBuilder> {
418 Arc::new(self.clone())
419 }
420
421 fn name(&self) -> String {
422 self.exe_name.clone()
423 }
424
425 fn libpython_link_mode(&self) -> LibpythonLinkMode {
426 self.link_mode
427 }
428
429 fn target_triple(&self) -> &str {
430 &self.target_triple
431 }
432
433 fn vc_runtime_requirements(&self) -> Option<(String, VcRedistributablePlatform)> {
434 let platform = if self.target_triple.starts_with("i686-") {
435 VcRedistributablePlatform::X86
436 } else if self.target_triple.starts_with("x86_64-") {
437 VcRedistributablePlatform::X64
438 } else if self.target_triple.starts_with("aarch64-") {
439 VcRedistributablePlatform::Arm64
440 } else {
441 return None;
442 };
443
444 self.target_distribution
445 .crt_features
446 .iter()
447 .find(|s| s.starts_with("vcruntime:"))
448 .map(|s| (s.split(':').nth(1).unwrap()[0..2].to_string(), platform))
449 }
450
451 fn cache_tag(&self) -> &str {
452 self.target_distribution.cache_tag()
453 }
454
455 fn python_packaging_policy(&self) -> &PythonPackagingPolicy {
456 &self.packaging_policy
457 }
458
459 fn host_python_exe_path(&self) -> &Path {
460 &self.host_python_exe
461 }
462
463 fn target_python_exe_path(&self) -> &Path {
464 self.target_distribution.python_exe_path()
465 }
466
467 fn apple_sdk_info(&self) -> Option<&AppleSdkInfo> {
468 self.target_distribution.apple_sdk_info()
469 }
470
471 fn windows_runtime_dlls_mode(&self) -> &WindowsRuntimeDllsMode {
472 &self.windows_runtime_dlls_mode
473 }
474
475 fn set_windows_runtime_dlls_mode(&mut self, value: WindowsRuntimeDllsMode) {
476 self.windows_runtime_dlls_mode = value;
477 }
478
479 fn tcl_files_path(&self) -> &Option<String> {
480 &self.tcl_files_path
481 }
482
483 fn set_tcl_files_path(&mut self, value: Option<String>) {
484 self.tcl_files_path = value;
485
486 self.config.tcl_library = if let Some(path) = &self.tcl_files_path {
487 Some(
488 PathBuf::from("$ORIGIN").join(path).join(
489 self.target_distribution
490 .tcl_library_path_directory()
491 .expect("should have a tcl library path directory"),
492 ),
493 )
494 } else {
495 None
496 };
497 }
498
499 fn windows_subsystem(&self) -> &str {
500 &self.windows_subsystem
501 }
502
503 fn set_windows_subsystem(&mut self, value: &str) -> Result<()> {
504 self.windows_subsystem = value.to_string();
505
506 Ok(())
507 }
508
509 fn licenses_filename(&self) -> Option<&str> {
510 self.licenses_filename.as_deref()
511 }
512
513 fn set_licenses_filename(&mut self, value: Option<String>) {
514 self.licenses_filename = value;
515 }
516
517 fn packed_resources_load_mode(&self) -> &PackedResourcesLoadMode {
518 &self.resources_load_mode
519 }
520
521 fn set_packed_resources_load_mode(&mut self, load_mode: PackedResourcesLoadMode) {
522 self.resources_load_mode = load_mode;
523 }
524
525 fn iter_resources<'a>(
526 &'a self,
527 ) -> Box<dyn Iterator<Item = (&'a String, &'a PrePackagedResource)> + 'a> {
528 Box::new(self.resources_collector.iter_resources())
529 }
530
531 fn index_package_license_info_from_resources<'a>(
532 &mut self,
533 resources: &[PythonResource<'a>],
534 ) -> Result<()> {
535 for info in derive_package_license_infos(resources.iter())? {
536 self.resources_collector
537 .add_licensed_component(info.try_into()?)?;
538 }
539
540 Ok(())
541 }
542
543 fn pip_download(
544 &mut self,
545 env: &Environment,
546 verbose: bool,
547 args: &[String],
548 ) -> Result<Vec<PythonResource>> {
549 let resources = pip_download(
550 env,
551 &*self.host_distribution,
552 &*self.target_distribution,
553 self.python_packaging_policy(),
554 verbose,
555 args,
556 )
557 .context("calling pip download")?;
558
559 self.index_package_license_info_from_resources(&resources)
560 .context("indexing package license metadata")?;
561
562 Ok(resources)
563 }
564
565 fn pip_install(
566 &mut self,
567 env: &Environment,
568 verbose: bool,
569 install_args: &[String],
570 extra_envs: &HashMap<String, String>,
571 ) -> Result<Vec<PythonResource>> {
572 let resources = pip_install(
573 env,
574 &*self.target_distribution,
575 self.python_packaging_policy(),
576 self.link_mode,
577 verbose,
578 install_args,
579 extra_envs,
580 )
581 .context("calling pip install")?;
582
583 self.index_package_license_info_from_resources(&resources)
584 .context("indexing package license metadata")?;
585
586 Ok(resources)
587 }
588
589 fn read_package_root(
590 &mut self,
591 path: &Path,
592 packages: &[String],
593 ) -> Result<Vec<PythonResource>> {
594 let resources = find_resources(
595 &*self.target_distribution,
596 self.python_packaging_policy(),
597 path,
598 None,
599 )
600 .context("finding resources")?
601 .iter()
602 .filter_map(|x| {
603 if x.is_in_packages(packages) {
604 Some(x.clone())
605 } else {
606 None
607 }
608 })
609 .collect::<Vec<_>>();
610
611 self.index_package_license_info_from_resources(&resources)
612 .context("indexing package license metadata")?;
613
614 Ok(resources)
615 }
616
617 fn read_virtualenv(&mut self, path: &Path) -> Result<Vec<PythonResource>> {
618 let resources = read_virtualenv(
619 &*self.target_distribution,
620 self.python_packaging_policy(),
621 path,
622 )
623 .context("reading virtualenv")?;
624
625 self.index_package_license_info_from_resources(&resources)
626 .context("indexing package license metadata")?;
627
628 Ok(resources)
629 }
630
631 fn setup_py_install(
632 &mut self,
633 env: &Environment,
634 package_path: &Path,
635 verbose: bool,
636 extra_envs: &HashMap<String, String>,
637 extra_global_arguments: &[String],
638 ) -> Result<Vec<PythonResource>> {
639 let resources = setup_py_install(
640 env,
641 &*self.target_distribution,
642 self.python_packaging_policy(),
643 self.link_mode,
644 package_path,
645 verbose,
646 extra_envs,
647 extra_global_arguments,
648 )
649 .context("running setup.py install")?;
650
651 self.index_package_license_info_from_resources(&resources)
652 .context("indexing package license metadata")?;
653
654 Ok(resources)
655 }
656
657 fn add_distribution_resources(
658 &mut self,
659 callback: Option<ResourceAddCollectionContextCallback>,
660 ) -> Result<Vec<AddResourceAction>> {
661 let mut actions = vec![];
662
663 let core_license =
664 if let Some(core_license) = self.target_distribution.core_license.as_ref() {
665 self.resources_collector
666 .add_licensed_component(core_license.clone())?;
667 core_license.clone()
668 } else {
669 return Err(anyhow!("could not resolve Python standard library license"));
670 };
671
672 for ext in self.packaging_policy.resolve_python_extension_modules(
674 self.target_distribution.extension_modules.values(),
675 &self.target_triple,
676 )? {
677 let resource = (&ext).into();
678 let mut add_context = self
679 .packaging_policy
680 .derive_add_collection_context(&resource);
681
682 if let Some(callback) = &callback {
683 callback(&self.packaging_policy, &resource, &mut add_context)?;
684 }
685
686 if let Some(component) = &ext.license {
687 self.resources_collector
688 .add_licensed_component(component.clone())?;
689 }
690
691 actions.extend(self.add_python_extension_module(&ext, Some(add_context))?);
692 }
693
694 for resource in self
695 .target_distribution
696 .python_resources()
697 .iter()
698 .filter(|r| match r {
699 PythonResource::ModuleSource(_) => true,
700 PythonResource::PackageResource(_) => true,
701 PythonResource::ModuleBytecode(_) => false,
702 PythonResource::ModuleBytecodeRequest(_) => false,
703 PythonResource::ExtensionModule(_) => false,
704 PythonResource::PackageDistributionResource(_) => false,
705 PythonResource::EggFile(_) => false,
706 PythonResource::PathExtension(_) => false,
707 PythonResource::File(_) => false,
708 })
709 {
710 let mut add_context = self
711 .packaging_policy
712 .derive_add_collection_context(resource);
713
714 if let Some(callback) = &callback {
715 callback(&self.packaging_policy, resource, &mut add_context)?;
716 }
717
718 match resource {
719 PythonResource::ModuleSource(source) => {
720 self.resources_collector.add_licensed_component(
721 LicensedComponent::new_spdx(
722 ComponentFlavor::PythonStandardLibraryModule(source.name.to_string()),
723 core_license
724 .spdx_expression()
725 .ok_or_else(|| anyhow!("should have resolved SPDX expression"))?
726 .as_ref(),
727 )?,
728 )?;
729
730 actions.extend(self.add_python_module_source(source, Some(add_context))?);
731 }
732 PythonResource::PackageResource(r) => {
733 actions.extend(self.add_python_package_resource(r, Some(add_context))?);
734 }
735 _ => panic!("should not get here since resources should be filtered above"),
736 }
737 }
738
739 Ok(actions)
740 }
741
742 fn add_python_module_source(
743 &mut self,
744 module: &PythonModuleSource,
745 add_context: Option<PythonResourceAddCollectionContext>,
746 ) -> Result<Vec<AddResourceAction>> {
747 let add_context = add_context.unwrap_or_else(|| {
748 self.packaging_policy
749 .derive_add_collection_context(&module.into())
750 });
751
752 self.resources_collector
753 .add_python_module_source_with_context(module, &add_context)
754 }
755
756 fn add_python_package_resource(
757 &mut self,
758 resource: &PythonPackageResource,
759 add_context: Option<PythonResourceAddCollectionContext>,
760 ) -> Result<Vec<AddResourceAction>> {
761 let add_context = add_context.unwrap_or_else(|| {
762 self.packaging_policy
763 .derive_add_collection_context(&resource.into())
764 });
765
766 self.resources_collector
767 .add_python_package_resource_with_context(resource, &add_context)
768 }
769
770 fn add_python_package_distribution_resource(
771 &mut self,
772 resource: &PythonPackageDistributionResource,
773 add_context: Option<PythonResourceAddCollectionContext>,
774 ) -> Result<Vec<AddResourceAction>> {
775 let add_context = add_context.unwrap_or_else(|| {
776 self.packaging_policy
777 .derive_add_collection_context(&resource.into())
778 });
779
780 self.resources_collector
781 .add_python_package_distribution_resource_with_context(resource, &add_context)
782 }
783
784 fn add_python_extension_module(
785 &mut self,
786 extension_module: &PythonExtensionModule,
787 add_context: Option<PythonResourceAddCollectionContext>,
788 ) -> Result<Vec<AddResourceAction>> {
789 let add_context = add_context.unwrap_or_else(|| {
790 self.packaging_policy
791 .derive_add_collection_context(&extension_module.into())
792 });
793
794 let (actions, build_context) = self
795 .resources_collector
796 .add_python_extension_module_with_context(extension_module, &add_context)?;
797
798 if let Some(mut build_context) = build_context {
799 build_context.static_libraries = build_context
802 .static_libraries
803 .iter()
804 .filter(|x| {
805 !ignored_libraries_for_target(&self.target_triple).contains(&x.as_str())
806 })
807 .cloned()
808 .collect::<BTreeSet<_>>();
809 build_context.dynamic_libraries = build_context
810 .dynamic_libraries
811 .iter()
812 .filter(|x| {
813 !ignored_libraries_for_target(&self.target_triple).contains(&x.as_str())
814 })
815 .cloned()
816 .collect::<BTreeSet<_>>();
817
818 self.extension_build_contexts
819 .insert(extension_module.name.clone(), build_context);
820 }
821
822 Ok(actions)
823 }
824
825 fn add_file_data(
826 &mut self,
827 file: &File,
828 add_context: Option<PythonResourceAddCollectionContext>,
829 ) -> Result<Vec<AddResourceAction>> {
830 let add_context = add_context.unwrap_or_else(|| {
831 self.packaging_policy
832 .derive_add_collection_context(&file.into())
833 });
834
835 self.resources_collector
836 .add_file_data_with_context(file, &add_context)
837 }
838
839 fn filter_resources_from_files(
840 &mut self,
841 files: &[&Path],
842 glob_patterns: &[&str],
843 ) -> Result<()> {
844 let resource_names = resolve_resource_names_from_files(files, glob_patterns)?;
845
846 warn!("filtering module entries");
847
848 self.resources_collector.filter_resources_mut(|resource| {
849 if !resource_names.contains(&resource.name) {
850 warn!("removing {}", resource.name);
851 false
852 } else {
853 true
854 }
855 })?;
856
857 warn!("filtering embedded extension modules");
858 filter_btreemap(&mut self.extension_build_contexts, &resource_names);
859
860 Ok(())
861 }
862
863 fn requires_jemalloc(&self) -> bool {
864 self.config.allocator_backend == MemoryAllocatorBackend::Jemalloc
865 }
866
867 fn requires_mimalloc(&self) -> bool {
868 self.config.allocator_backend == MemoryAllocatorBackend::Mimalloc
869 }
870
871 fn requires_snmalloc(&self) -> bool {
872 self.config.allocator_backend == MemoryAllocatorBackend::Snmalloc
873 }
874
875 fn licensed_components(&self) -> Result<LicensedComponents> {
876 Ok(self.resources_collector.normalized_licensed_components())
877 }
878
879 fn add_licensed_component(&mut self, component: LicensedComponent) -> Result<()> {
880 self.resources_collector.add_licensed_component(component)
881 }
882
883 fn to_embedded_python_context(
884 &self,
885 env: &Environment,
886 opt_level: &str,
887 ) -> Result<EmbeddedPythonContext> {
888 let mut file_seen = false;
889 for module in self.resources_collector.find_dunder_file()? {
890 file_seen = true;
891 warn!("warning: {} contains __file__", module);
892 }
893
894 if file_seen {
895 warn!("__file__ was encountered in some embedded modules");
896 warn!("PyOxidizer does not set __file__ and this may create problems at run-time");
897 warn!("See https://github.com/indygreg/PyOxidizer/issues/69 for more");
898 }
899
900 let compiled_resources = {
901 let temp_dir = env.temporary_directory("pyoxidizer-bytecode-compile")?;
902 let mut compiler = BytecodeCompiler::new(self.host_python_exe_path(), temp_dir.path())?;
903 let resources = self.resources_collector.compile_resources(&mut compiler)?;
904
905 temp_dir.close().context("closing temporary directory")?;
906
907 resources
908 };
909
910 let mut pending_resources = vec![];
911
912 let mut extra_files = compiled_resources.extra_files_manifest()?;
913
914 let mut config = self.config.clone();
915
916 match &self.resources_load_mode {
917 PackedResourcesLoadMode::None => {}
918 PackedResourcesLoadMode::EmbeddedInBinary(filename) => {
919 pending_resources.push((compiled_resources, PathBuf::from(filename)));
920 config
921 .packed_resources
922 .push(PyembedPackedResourcesSource::MemoryIncludeBytes(
923 PathBuf::from(filename),
924 ));
925 }
926 PackedResourcesLoadMode::BinaryRelativePathMemoryMapped(path) => {
927 let mut buffer = vec![];
929 compiled_resources
930 .write_packed_resources(&mut buffer)
931 .context("serializing packed resources")?;
932 extra_files.add_file_entry(Path::new(path), buffer)?;
933
934 config
935 .packed_resources
936 .push(PyembedPackedResourcesSource::MemoryMappedPath(
937 PathBuf::from("$ORIGIN").join(path),
938 ));
939 }
940 }
941
942 let link_settings = self.resolve_python_link_settings(env, opt_level)?;
943
944 if self.link_mode == LibpythonLinkMode::Dynamic {
945 if let Some(p) = &self.target_distribution.libpython_shared_library {
946 let manifest_path = Path::new(p.file_name().unwrap());
947 let content = std::fs::read(p)?;
948
949 extra_files.add_file_entry(manifest_path, content)?;
950
951 let python3_dll_path = p.with_file_name("python3.dll");
955 let manifest_path = Path::new(python3_dll_path.file_name().unwrap());
956 if python3_dll_path.exists() {
957 let content = std::fs::read(&python3_dll_path)?;
958
959 extra_files.add_file_entry(manifest_path, content)?;
960 }
961 }
962 }
963
964 if let Some(tcl_files_path) = self.tcl_files_path() {
965 for (path, location) in self.target_distribution.tcl_files()? {
966 let install_path = PathBuf::from(tcl_files_path).join(path);
967
968 extra_files.add_file_entry(&install_path, location)?;
969 }
970 }
971
972 extra_files.add_manifest(&self.resolve_windows_runtime_dll_files()?)?;
974
975 let python_implementation = if self
976 .target_distribution
977 .python_implementation
978 .starts_with("cpython")
979 {
980 PythonImplementation::CPython
981 } else if self
982 .target_distribution
983 .python_implementation
984 .starts_with("pypy")
985 {
986 PythonImplementation::PyPy
987 } else {
988 return Err(anyhow!(
989 "unknown Python implementation: {}",
990 self.target_distribution.python_implementation
991 ));
992 };
993
994 let python_version =
995 PythonVersion::from_str(&self.target_distribution.python_major_minor_version())
996 .map_err(|e| anyhow!("unable to determine Python version: {}", e))?;
997
998 let mut python_build_flags = BuildFlags::new();
1000
1001 if self
1002 .target_distribution
1003 .python_config_vars()
1004 .get("Py_DEBUG")
1005 == Some(&"1".to_string())
1006 {
1007 python_build_flags.0.insert(BuildFlag::Py_DEBUG);
1008 }
1009 if self
1010 .target_distribution
1011 .python_config_vars()
1012 .get("Py_REF_DEBUG")
1013 == Some(&"1".to_string())
1014 {
1015 python_build_flags.0.insert(BuildFlag::Py_REF_DEBUG);
1016 }
1017 if self
1018 .target_distribution
1019 .python_config_vars()
1020 .get("Py_TRACE_REFS")
1021 == Some(&"1".to_string())
1022 {
1023 python_build_flags.0.insert(BuildFlag::Py_TRACE_REFS);
1024 }
1025 if self
1026 .target_distribution
1027 .python_config_vars()
1028 .get("COUNT_ALLOCS")
1029 == Some(&"1".to_string())
1030 {
1031 python_build_flags.0.insert(BuildFlag::COUNT_ALLOCS);
1032 }
1033
1034 let mut context = EmbeddedPythonContext {
1035 config,
1036 link_settings,
1037 pending_resources,
1038 extra_files,
1039 host_triple: self.host_triple.clone(),
1040 target_triple: self.target_triple.clone(),
1041 python_implementation,
1042 python_version,
1043 python_exe_host: self.host_python_exe.clone(),
1044 python_build_flags,
1045 licensing_filename: self.licenses_filename.clone(),
1046 licensing: self.licensed_components()?,
1047 };
1048
1049 context.synchronize_licensing()?;
1050
1051 Ok(context)
1052 }
1053}
1054
1055#[cfg(test)]
1056pub mod tests {
1057 use {
1058 super::*,
1059 crate::{
1060 environment::{default_target_triple, MACOS_TARGET_TRIPLES},
1061 py_packaging::distribution::{BinaryLibpythonLinkMode, DistributionFlavor},
1062 python_distributions::PYTHON_DISTRIBUTIONS,
1063 testutil::*,
1064 },
1065 once_cell::sync::Lazy,
1066 python_packaging::{
1067 licensing::LicensedComponents, location::ConcreteResourceLocation,
1068 policy::ExtensionModuleFilter,
1069 },
1070 std::ops::DerefMut,
1071 };
1072
1073 #[cfg(target_os = "linux")]
1074 use python_packaging::resource::LibraryDependency;
1075
1076 pub static WINDOWS_TARGET_TRIPLES: Lazy<Vec<&'static str>> =
1077 Lazy::new(|| vec!["i686-pc-windows-msvc", "x86_64-pc-windows-msvc"]);
1078
1079 pub static EXTENSION_MODULE_SHARED_LIBRARY_ONLY: Lazy<PythonExtensionModule> =
1081 Lazy::new(|| PythonExtensionModule {
1082 name: "shared_only".to_string(),
1083 init_fn: Some("PyInit_shared_only".to_string()),
1084 extension_file_suffix: ".so".to_string(),
1085 shared_library: Some(FileData::Memory(vec![42])),
1086 object_file_data: vec![],
1087 is_package: false,
1088 link_libraries: vec![],
1089 is_stdlib: false,
1090 builtin_default: false,
1091 required: false,
1092 variant: None,
1093 license: None,
1094 });
1095
1096 pub static EXTENSION_MODULE_OBJECT_FILES_ONLY: Lazy<PythonExtensionModule> =
1098 Lazy::new(|| PythonExtensionModule {
1099 name: "object_files_only".to_string(),
1100 init_fn: Some("PyInit_object_files_only".to_string()),
1101 extension_file_suffix: ".so".to_string(),
1102 shared_library: None,
1103 object_file_data: vec![FileData::Memory(vec![0]), FileData::Memory(vec![1])],
1104 is_package: false,
1105 link_libraries: vec![],
1106 is_stdlib: false,
1107 builtin_default: false,
1108 required: false,
1109 variant: None,
1110 license: None,
1111 });
1112
1113 pub static EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES: Lazy<PythonExtensionModule> =
1115 Lazy::new(|| PythonExtensionModule {
1116 name: "shared_and_object_files".to_string(),
1117 init_fn: Some("PyInit_shared_and_object_files".to_string()),
1118 extension_file_suffix: ".so".to_string(),
1119 shared_library: Some(FileData::Memory(b"shared".to_vec())),
1120 object_file_data: vec![FileData::Memory(vec![0]), FileData::Memory(vec![1])],
1121 is_package: false,
1122 link_libraries: vec![],
1123 is_stdlib: false,
1124 builtin_default: false,
1125 required: false,
1126 variant: None,
1127 license: None,
1128 });
1129
1130 pub struct StandalonePythonExecutableBuilderOptions {
1135 pub host_triple: String,
1136 pub target_triple: String,
1137 pub distribution_version: Option<String>,
1138 pub distribution_flavor: DistributionFlavor,
1139 pub app_name: String,
1140 pub libpython_link_mode: BinaryLibpythonLinkMode,
1141 pub extension_module_filter: Option<ExtensionModuleFilter>,
1142 pub resources_location: Option<ConcreteResourceLocation>,
1143 pub resources_location_fallback: Option<Option<ConcreteResourceLocation>>,
1144 pub allow_in_memory_shared_library_loading: Option<bool>,
1145 pub config: PyembedPythonInterpreterConfig,
1146 }
1147
1148 impl Default for StandalonePythonExecutableBuilderOptions {
1149 fn default() -> Self {
1150 Self {
1151 host_triple: default_target_triple().to_string(),
1152 target_triple: default_target_triple().to_string(),
1153 distribution_version: None,
1154 distribution_flavor: DistributionFlavor::Standalone,
1155 app_name: "testapp".to_string(),
1156 libpython_link_mode: BinaryLibpythonLinkMode::Default,
1157 extension_module_filter: None,
1158 resources_location: None,
1159 resources_location_fallback: None,
1160 allow_in_memory_shared_library_loading: None,
1161 config: PyembedPythonInterpreterConfig::default(),
1162 }
1163 }
1164 }
1165
1166 impl StandalonePythonExecutableBuilderOptions {
1167 pub fn new_builder(&self) -> Result<Box<StandalonePythonExecutableBuilder>> {
1168 let target_record = PYTHON_DISTRIBUTIONS
1169 .find_distribution(
1170 &self.target_triple,
1171 &self.distribution_flavor,
1172 self.distribution_version.as_deref(),
1173 )
1174 .ok_or_else(|| anyhow!("could not find target Python distribution"))?;
1175
1176 let target_distribution = get_distribution(&target_record.location)?;
1177
1178 let host_distribution = if target_distribution
1179 .compatible_host_triples()
1180 .contains(&self.host_triple)
1181 {
1182 target_distribution.clone_trait()
1183 } else {
1184 let host_record = PYTHON_DISTRIBUTIONS
1185 .find_distribution(&self.host_triple, &DistributionFlavor::Standalone, None)
1186 .ok_or_else(|| anyhow!("could not find host Python distribution"))?;
1187
1188 get_distribution(&host_record.location)?.clone_trait()
1189 };
1190
1191 let mut policy = target_distribution.create_packaging_policy()?;
1192 if let Some(filter) = &self.extension_module_filter {
1193 policy.set_extension_module_filter(filter.clone());
1194 }
1195 if let Some(location) = &self.resources_location {
1196 policy.set_resources_location(location.clone());
1197 }
1198 if let Some(location) = &self.resources_location_fallback {
1199 policy.set_resources_location_fallback(location.clone());
1200 }
1201 if let Some(value) = &self.allow_in_memory_shared_library_loading {
1202 policy.set_allow_in_memory_shared_library_loading(*value);
1203 }
1204
1205 let mut builder = StandalonePythonExecutableBuilder::from_distribution(
1206 host_distribution,
1207 target_distribution,
1208 self.host_triple.clone(),
1209 self.target_triple.clone(),
1210 self.app_name.clone(),
1211 self.libpython_link_mode.clone(),
1212 policy,
1213 self.config.clone(),
1214 )?;
1215
1216 builder.add_distribution_resources(None)?;
1217
1218 Ok(builder)
1219 }
1220 }
1221
1222 fn assert_extension_builtin(
1223 builder: &StandalonePythonExecutableBuilder,
1224 extension: &PythonExtensionModule,
1225 ) {
1226 assert_eq!(
1227 builder.iter_resources().find_map(|(name, r)| {
1228 if *name == extension.name {
1229 Some(r)
1230 } else {
1231 None
1232 }
1233 }),
1234 Some(&PrePackagedResource {
1235 is_builtin_extension_module: true,
1236 name: extension.name.clone(),
1237 ..PrePackagedResource::default()
1238 }),
1239 "extension module {} is built-in",
1240 extension.name,
1241 );
1242
1243 assert_eq!(
1244 builder.extension_build_contexts.get(&extension.name),
1245 Some(&LibPythonBuildContext {
1246 object_files: extension.object_file_data.clone(),
1247 init_functions: [(
1248 extension.name.to_string(),
1249 extension.init_fn.as_ref().unwrap().to_string()
1250 )]
1251 .iter()
1252 .cloned()
1253 .collect(),
1254 ..LibPythonBuildContext::default()
1255 }),
1256 "build context for extension module {} is present",
1257 extension.name
1258 );
1259 }
1260
1261 fn assert_extension_shared_library(
1262 builder: &StandalonePythonExecutableBuilder,
1263 extension: &PythonExtensionModule,
1264 location: ConcreteResourceLocation,
1265 ) {
1266 let mut entry = PrePackagedResource {
1267 is_extension_module: true,
1268 name: extension.name.clone(),
1269 shared_library_dependency_names: Some(vec![]),
1270 ..PrePackagedResource::default()
1271 };
1272
1273 match location {
1274 ConcreteResourceLocation::InMemory => {
1275 assert!(extension.shared_library.is_some());
1276 entry.in_memory_extension_module_shared_library =
1277 Some(extension.shared_library.as_ref().unwrap().clone());
1278 }
1279 ConcreteResourceLocation::RelativePath(prefix) => {
1280 assert!(extension.shared_library.is_some());
1281 entry.relative_path_extension_module_shared_library = Some((
1282 PathBuf::from(prefix).join(format!(
1283 "{}{}",
1284 extension.name, extension.extension_file_suffix
1285 )),
1286 extension.shared_library.as_ref().unwrap().clone(),
1287 ));
1288 }
1289 }
1290
1291 assert_eq!(
1292 builder.iter_resources().find_map(|(name, r)| {
1293 if *name == extension.name {
1294 Some(r)
1295 } else {
1296 None
1297 }
1298 }),
1299 Some(&entry)
1300 );
1301
1302 assert_eq!(builder.extension_build_contexts.get(&extension.name), None);
1306 }
1307
1308 fn licensed_components_from_extension(ext: &PythonExtensionModule) -> LicensedComponents {
1309 let mut r = LicensedComponents::default();
1310
1311 if let Some(component) = &ext.license {
1312 r.add_component(component.clone());
1313 }
1314
1315 r
1316 }
1317
1318 #[test]
1319 fn test_write_embedded_files() -> Result<()> {
1320 let temp_dir = get_env()?.temporary_directory("pyoxidizer-test")?;
1321
1322 let options = StandalonePythonExecutableBuilderOptions::default();
1323 let exe = options.new_builder()?;
1324 let embedded = exe.to_embedded_python_context(&get_env()?, "0")?;
1325
1326 embedded.write_files(temp_dir.path())?;
1327
1328 let resources_path = temp_dir.path().join("packed-resources");
1329 assert!(resources_path.exists(), "packed-resources file exists");
1330
1331 temp_dir.close()?;
1332
1333 Ok(())
1334 }
1335
1336 #[test]
1337 fn test_memory_mapped_file_resources() -> Result<()> {
1338 let options = StandalonePythonExecutableBuilderOptions::default();
1339 let mut exe = options.new_builder()?;
1340 exe.resources_load_mode =
1341 PackedResourcesLoadMode::BinaryRelativePathMemoryMapped("resources".into());
1342
1343 let embedded = exe.to_embedded_python_context(&get_env()?, "0")?;
1344
1345 assert_eq!(
1346 &embedded.config.packed_resources,
1347 &vec![PyembedPackedResourcesSource::MemoryMappedPath(
1348 "$ORIGIN/resources".into()
1349 )],
1350 "load mode should have mapped to MemoryMappedPath"
1351 );
1352
1353 assert!(
1354 embedded.extra_files.has_path(Path::new("resources")),
1355 "resources file should be present in extra files manifest"
1356 );
1357
1358 Ok(())
1359 }
1360
1361 #[test]
1362 fn test_minimal_extensions_present() -> Result<()> {
1363 let options = StandalonePythonExecutableBuilderOptions::default();
1364 let builder = options.new_builder()?;
1365
1366 let expected = builder
1367 .target_distribution
1368 .extension_modules
1369 .iter()
1370 .filter_map(|(_, extensions)| {
1371 if extensions.default_variant().is_minimally_required() {
1372 Some(extensions.default_variant().name.clone())
1373 } else {
1374 None
1375 }
1376 })
1377 .collect::<Vec<_>>();
1378
1379 assert!(expected.contains(&"_io".to_string()));
1381
1382 for name in &expected {
1383 assert!(builder.extension_build_contexts.keys().any(|x| x == name));
1386 assert!(builder.iter_resources().any(|(x, _)| x == name));
1387 }
1388
1389 Ok(())
1390 }
1391
1392 #[test]
1393 fn test_linux_distribution_extensions() -> Result<()> {
1394 for libpython_link_mode in vec![
1395 BinaryLibpythonLinkMode::Static,
1396 BinaryLibpythonLinkMode::Dynamic,
1397 ] {
1398 let options = StandalonePythonExecutableBuilderOptions {
1399 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1400 extension_module_filter: Some(ExtensionModuleFilter::All),
1401 libpython_link_mode,
1402 ..StandalonePythonExecutableBuilderOptions::default()
1403 };
1404
1405 let builder = options.new_builder()?;
1406
1407 let builtin_names = builder.extension_build_contexts.keys().collect::<Vec<_>>();
1408
1409 for (name, _) in builder.target_distribution.extension_modules.iter() {
1411 if builder
1412 .python_packaging_policy()
1413 .broken_extensions_for_triple(&builder.target_triple)
1414 .unwrap_or(&vec![])
1415 .contains(name)
1416 {
1417 assert!(!builtin_names.contains(&name))
1418 } else {
1419 assert!(builtin_names.contains(&name));
1420 }
1421 }
1422 }
1423
1424 Ok(())
1425 }
1426
1427 #[test]
1428 fn test_linux_distribution_extension_static() -> Result<()> {
1429 for libpython_link_mode in vec![
1430 BinaryLibpythonLinkMode::Static,
1431 BinaryLibpythonLinkMode::Dynamic,
1432 ] {
1433 let options = StandalonePythonExecutableBuilderOptions {
1434 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1435 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1436 libpython_link_mode,
1437 ..StandalonePythonExecutableBuilderOptions::default()
1438 };
1439
1440 let mut builder = options.new_builder()?;
1441
1442 let sqlite = builder
1446 .target_distribution
1447 .extension_modules
1448 .get("_sqlite3")
1449 .unwrap()
1450 .default_variant()
1451 .clone();
1452
1453 builder.add_python_extension_module(&sqlite, None)?;
1454
1455 assert_eq!(
1456 builder.extension_build_contexts.get("_sqlite3"),
1457 Some(&LibPythonBuildContext {
1458 object_files: sqlite.object_file_data.clone(),
1459 static_libraries: ["sqlite3".to_string()].iter().cloned().collect(),
1460 init_functions: [("_sqlite3".to_string(), "PyInit__sqlite3".to_string())]
1461 .iter()
1462 .cloned()
1463 .collect(),
1464 licensed_components: licensed_components_from_extension(&sqlite),
1465 ..LibPythonBuildContext::default()
1466 })
1467 );
1468
1469 assert_eq!(
1470 builder
1471 .iter_resources()
1472 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
1473 Some(&PrePackagedResource {
1474 is_builtin_extension_module: true,
1475 name: "_sqlite3".to_string(),
1476 ..PrePackagedResource::default()
1477 })
1478 );
1479 }
1480
1481 Ok(())
1482 }
1483
1484 #[test]
1485 fn test_linux_extension_in_memory_only() -> Result<()> {
1486 for libpython_link_mode in vec![
1487 BinaryLibpythonLinkMode::Static,
1488 BinaryLibpythonLinkMode::Dynamic,
1489 ] {
1490 let options = StandalonePythonExecutableBuilderOptions {
1491 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1492 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1493 libpython_link_mode: libpython_link_mode.clone(),
1494 resources_location: Some(ConcreteResourceLocation::InMemory),
1495 resources_location_fallback: Some(None),
1496 ..StandalonePythonExecutableBuilderOptions::default()
1497 };
1498
1499 let mut builder = options.new_builder()?;
1500
1501 let res =
1502 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None);
1503 assert!(res.is_err());
1504 assert_eq!(
1505 res.err().unwrap().to_string(),
1506 "extension module shared_only cannot be loaded from memory but memory loading required"
1507 );
1508
1509 let res =
1510 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
1511 match libpython_link_mode {
1512 BinaryLibpythonLinkMode::Static => {
1513 assert!(res.is_ok());
1514 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
1515 }
1516 BinaryLibpythonLinkMode::Dynamic => {
1517 assert!(res.is_err());
1518 assert_eq!(res.err().unwrap().to_string(), "extension module object_files_only cannot be loaded from memory but memory loading required");
1519 }
1520 BinaryLibpythonLinkMode::Default => {
1521 panic!("should not get here");
1522 }
1523 }
1524
1525 let res = builder.add_python_extension_module(
1526 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
1527 None,
1528 );
1529 match libpython_link_mode {
1530 BinaryLibpythonLinkMode::Static => {
1531 assert!(res.is_ok());
1532 assert_extension_builtin(
1533 &builder,
1534 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
1535 );
1536 }
1537 BinaryLibpythonLinkMode::Dynamic => {
1538 assert!(res.is_err());
1539 assert_eq!(res.err().unwrap().to_string(), "extension module shared_and_object_files cannot be loaded from memory but memory loading required")
1540 }
1541 BinaryLibpythonLinkMode::Default => {
1542 panic!("should not get here");
1543 }
1544 }
1545 }
1546
1547 Ok(())
1548 }
1549
1550 #[test]
1551 fn test_linux_extension_prefer_in_memory() -> Result<()> {
1552 for libpython_link_mode in vec![
1553 BinaryLibpythonLinkMode::Static,
1554 BinaryLibpythonLinkMode::Dynamic,
1555 ] {
1556 let options = StandalonePythonExecutableBuilderOptions {
1557 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1558 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1559 libpython_link_mode: libpython_link_mode.clone(),
1560 resources_location: Some(ConcreteResourceLocation::InMemory),
1561 resources_location_fallback: Some(Some(ConcreteResourceLocation::RelativePath(
1562 "prefix_policy".to_string(),
1563 ))),
1564 ..StandalonePythonExecutableBuilderOptions::default()
1565 };
1566
1567 let mut builder = options.new_builder()?;
1568
1569 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None)?;
1570 assert_extension_shared_library(
1571 &builder,
1572 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
1573 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
1574 );
1575
1576 let res =
1577 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
1578 match libpython_link_mode {
1579 BinaryLibpythonLinkMode::Static => {
1580 assert!(res.is_ok());
1581 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
1582 }
1583 BinaryLibpythonLinkMode::Dynamic => {
1584 assert!(res.is_err());
1585 assert_eq!(
1586 res.err().unwrap().to_string(),
1587 "no shared library data present"
1588 );
1589 }
1590 BinaryLibpythonLinkMode::Default => {
1591 panic!("should not get here");
1592 }
1593 }
1594
1595 builder.add_python_extension_module(
1596 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
1597 None,
1598 )?;
1599 match libpython_link_mode {
1600 BinaryLibpythonLinkMode::Static => {
1601 assert_extension_builtin(
1602 &builder,
1603 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
1604 );
1605 }
1606 BinaryLibpythonLinkMode::Dynamic => {
1607 assert_extension_shared_library(
1608 &builder,
1609 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
1610 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
1611 );
1612 }
1613 BinaryLibpythonLinkMode::Default => {
1614 panic!("should not get here");
1615 }
1616 }
1617 }
1618 Ok(())
1619 }
1620
1621 #[test]
1622 fn test_linux_distribution_extension_filesystem_relative_only() -> Result<()> {
1623 for libpython_link_mode in vec![
1624 BinaryLibpythonLinkMode::Static,
1625 BinaryLibpythonLinkMode::Dynamic,
1626 ] {
1627 let options = StandalonePythonExecutableBuilderOptions {
1628 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1629 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1630 libpython_link_mode,
1631 resources_location: Some(ConcreteResourceLocation::RelativePath(
1632 "prefix_policy".to_string(),
1633 )),
1634 resources_location_fallback: Some(None),
1635 ..StandalonePythonExecutableBuilderOptions::default()
1636 };
1637
1638 let mut builder = options.new_builder()?;
1639
1640 let ext = builder
1641 .target_distribution
1642 .extension_modules
1643 .get("_sqlite3")
1644 .unwrap()
1645 .default_variant()
1646 .clone();
1647
1648 builder.add_python_extension_module(&ext, None)?;
1651
1652 assert_eq!(
1653 builder.extension_build_contexts.get("_sqlite3"),
1654 Some(&LibPythonBuildContext {
1655 object_files: ext.object_file_data.clone(),
1656 static_libraries: ["sqlite3".to_string()].iter().cloned().collect(),
1657 init_functions: [("_sqlite3".to_string(), "PyInit__sqlite3".to_string())]
1658 .iter()
1659 .cloned()
1660 .collect(),
1661 licensed_components: licensed_components_from_extension(&ext),
1662 ..LibPythonBuildContext::default()
1663 })
1664 );
1665
1666 assert_eq!(
1667 builder
1668 .iter_resources()
1669 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
1670 Some(&PrePackagedResource {
1671 is_builtin_extension_module: true,
1672 name: "_sqlite3".to_string(),
1673 ..PrePackagedResource::default()
1674 })
1675 );
1676 }
1677
1678 Ok(())
1679 }
1680
1681 #[test]
1682 fn test_linux_extension_filesystem_relative_only() -> Result<()> {
1683 for libpython_link_mode in vec![
1684 BinaryLibpythonLinkMode::Static,
1685 BinaryLibpythonLinkMode::Dynamic,
1686 ] {
1687 let options = StandalonePythonExecutableBuilderOptions {
1688 target_triple: "x86_64-unknown-linux-gnu".to_string(),
1689 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1690 libpython_link_mode: libpython_link_mode.clone(),
1691 resources_location: Some(ConcreteResourceLocation::RelativePath(
1692 "prefix_policy".to_string(),
1693 )),
1694 resources_location_fallback: Some(None),
1695 ..StandalonePythonExecutableBuilderOptions::default()
1696 };
1697
1698 let mut builder = options.new_builder()?;
1699
1700 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None)?;
1701 assert_extension_shared_library(
1702 &builder,
1703 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
1704 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
1705 );
1706
1707 let res =
1708 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
1709 match libpython_link_mode {
1710 BinaryLibpythonLinkMode::Static => {
1711 assert!(res.is_ok());
1712 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
1713 }
1714 BinaryLibpythonLinkMode::Dynamic => {
1715 assert!(res.is_err());
1716 assert_eq!(res.err().unwrap().to_string(), "extension module object_files_only cannot be materialized as a shared library extension but filesystem loading required");
1717 }
1718 BinaryLibpythonLinkMode::Default => {
1719 panic!("should not get here");
1720 }
1721 }
1722
1723 builder.add_python_extension_module(
1724 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
1725 None,
1726 )?;
1727 assert_extension_shared_library(
1728 &builder,
1729 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
1730 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
1731 );
1732 }
1733
1734 Ok(())
1735 }
1736
1737 #[test]
1738 fn test_linux_musl_distribution_dynamic() {
1739 let options = StandalonePythonExecutableBuilderOptions {
1740 target_triple: "x86_64-unknown-linux-musl".to_string(),
1741 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1742 libpython_link_mode: BinaryLibpythonLinkMode::Dynamic,
1743 ..StandalonePythonExecutableBuilderOptions::default()
1744 };
1745
1746 let err = options.new_builder().err();
1748 assert!(err.is_some());
1749 assert_eq!(
1750 err.unwrap().to_string(),
1751 "Python distribution does not support dynamically linking libpython"
1752 );
1753 }
1754
1755 #[test]
1756 fn test_linux_musl_distribution_extensions() -> Result<()> {
1757 let options = StandalonePythonExecutableBuilderOptions {
1758 target_triple: "x86_64-unknown-linux-musl".to_string(),
1759 extension_module_filter: Some(ExtensionModuleFilter::All),
1760 ..StandalonePythonExecutableBuilderOptions::default()
1761 };
1762
1763 let builder = options.new_builder()?;
1764
1765 for name in builder.target_distribution.extension_modules.keys() {
1768 if builder
1769 .python_packaging_policy()
1770 .broken_extensions_for_triple(&builder.target_triple)
1771 .unwrap_or(&vec![])
1772 .contains(name)
1773 {
1774 assert!(!builder.extension_build_contexts.keys().any(|e| name == e));
1775 } else {
1776 assert!(builder.extension_build_contexts.keys().any(|e| name == e));
1777 }
1778 }
1779
1780 Ok(())
1781 }
1782
1783 #[test]
1784 fn test_linux_musl_distribution_extension_static() -> Result<()> {
1785 let options = StandalonePythonExecutableBuilderOptions {
1786 target_triple: "x86_64-unknown-linux-musl".to_string(),
1787 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1788 libpython_link_mode: BinaryLibpythonLinkMode::Static,
1789 ..StandalonePythonExecutableBuilderOptions::default()
1790 };
1791
1792 let mut builder = options.new_builder()?;
1793
1794 let sqlite = builder
1798 .target_distribution
1799 .extension_modules
1800 .get("_sqlite3")
1801 .unwrap()
1802 .default_variant()
1803 .clone();
1804
1805 builder.add_python_extension_module(&sqlite, None)?;
1806
1807 assert_eq!(
1808 builder.extension_build_contexts.get("_sqlite3"),
1809 Some(&LibPythonBuildContext {
1810 object_files: sqlite.object_file_data.clone(),
1811 static_libraries: ["sqlite3".to_string()].iter().cloned().collect(),
1812 init_functions: [("_sqlite3".to_string(), "PyInit__sqlite3".to_string())]
1813 .iter()
1814 .cloned()
1815 .collect(),
1816 licensed_components: licensed_components_from_extension(&sqlite),
1817 ..LibPythonBuildContext::default()
1818 })
1819 );
1820
1821 assert_eq!(
1822 builder
1823 .iter_resources()
1824 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
1825 Some(&PrePackagedResource {
1826 is_builtin_extension_module: true,
1827 name: "_sqlite3".to_string(),
1828 ..PrePackagedResource::default()
1829 })
1830 );
1831
1832 Ok(())
1833 }
1834
1835 #[test]
1836 fn test_linux_musl_distribution_extension_filesystem_relative_only() -> Result<()> {
1837 let options = StandalonePythonExecutableBuilderOptions {
1838 target_triple: "x86_64-unknown-linux-musl".to_string(),
1839 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1840 libpython_link_mode: BinaryLibpythonLinkMode::Static,
1841 resources_location: Some(ConcreteResourceLocation::RelativePath(
1842 "prefix_policy".to_string(),
1843 )),
1844 resources_location_fallback: Some(None),
1845 ..StandalonePythonExecutableBuilderOptions::default()
1846 };
1847
1848 let mut builder = options.new_builder()?;
1849
1850 let ext = builder
1851 .target_distribution
1852 .extension_modules
1853 .get("_sqlite3")
1854 .unwrap()
1855 .default_variant()
1856 .clone();
1857
1858 builder.add_python_extension_module(&ext, None)?;
1861
1862 assert_eq!(
1863 builder.extension_build_contexts.get("_sqlite3"),
1864 Some(&LibPythonBuildContext {
1865 object_files: ext.object_file_data.clone(),
1866 static_libraries: ["sqlite3".to_string()].iter().cloned().collect(),
1867 init_functions: [("_sqlite3".to_string(), "PyInit__sqlite3".to_string())]
1868 .iter()
1869 .cloned()
1870 .collect(),
1871 licensed_components: licensed_components_from_extension(&ext),
1872 ..LibPythonBuildContext::default()
1873 })
1874 );
1875
1876 assert_eq!(
1877 builder
1878 .iter_resources()
1879 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
1880 Some(&PrePackagedResource {
1881 is_builtin_extension_module: true,
1882 name: "_sqlite3".to_string(),
1883 ..PrePackagedResource::default()
1884 })
1885 );
1886
1887 Ok(())
1888 }
1889
1890 #[test]
1891 fn test_linux_musl_extension_in_memory_only() -> Result<()> {
1892 let options = StandalonePythonExecutableBuilderOptions {
1893 target_triple: "x86_64-unknown-linux-musl".to_string(),
1894 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1895 libpython_link_mode: BinaryLibpythonLinkMode::Static,
1896 resources_location: Some(ConcreteResourceLocation::InMemory),
1897 resources_location_fallback: Some(None),
1898 ..StandalonePythonExecutableBuilderOptions::default()
1899 };
1900
1901 let mut builder = options.new_builder()?;
1902
1903 let res = builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None);
1904 assert!(res.is_err());
1905 assert_eq!(
1906 res.err().unwrap().to_string(),
1907 "extension module shared_only cannot be loaded from memory but memory loading required"
1908 );
1909
1910 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None)?;
1911 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
1912
1913 builder
1914 .add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES, None)?;
1915 assert_extension_builtin(&builder, &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES);
1916
1917 Ok(())
1918 }
1919
1920 #[test]
1921 fn test_linux_musl_extension_prefer_in_memory() -> Result<()> {
1922 let options = StandalonePythonExecutableBuilderOptions {
1923 target_triple: "x86_64-unknown-linux-musl".to_string(),
1924 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1925 libpython_link_mode: BinaryLibpythonLinkMode::Static,
1926 resources_location: Some(ConcreteResourceLocation::InMemory),
1927 resources_location_fallback: Some(Some(ConcreteResourceLocation::RelativePath(
1928 "prefix_policy".to_string(),
1929 ))),
1930 ..StandalonePythonExecutableBuilderOptions::default()
1931 };
1932
1933 let mut builder = options.new_builder()?;
1934
1935 let res = builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None);
1936 assert!(res.is_err());
1937 assert_eq!(
1938 res.err().unwrap().to_string(),
1939 "extension module shared_only cannot be materialized as a shared library because distribution does not support loading extension module shared libraries"
1940 );
1941
1942 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None)?;
1943 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
1944
1945 builder
1946 .add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES, None)?;
1947 assert_extension_builtin(&builder, &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES);
1948
1949 Ok(())
1950 }
1951
1952 #[test]
1953 fn test_macos_distribution_extensions() -> Result<()> {
1954 for target_triple in MACOS_TARGET_TRIPLES.iter() {
1955 for libpython_link_mode in vec![
1956 BinaryLibpythonLinkMode::Static,
1957 BinaryLibpythonLinkMode::Dynamic,
1958 ] {
1959 let options = StandalonePythonExecutableBuilderOptions {
1960 target_triple: target_triple.to_string(),
1961 libpython_link_mode,
1962 extension_module_filter: Some(ExtensionModuleFilter::All),
1963 ..StandalonePythonExecutableBuilderOptions::default()
1964 };
1965
1966 let builder = options.new_builder()?;
1967
1968 let builtin_names = builder.extension_build_contexts.keys().collect::<Vec<_>>();
1969
1970 for (name, _) in builder.target_distribution.extension_modules.iter() {
1972 if builder
1973 .python_packaging_policy()
1974 .broken_extensions_for_triple(&builder.target_triple)
1975 .unwrap_or(&vec![])
1976 .contains(name)
1977 {
1978 assert!(!builtin_names.contains(&name))
1979 } else {
1980 assert!(builtin_names.contains(&name));
1981 }
1982 }
1983 }
1984 }
1985
1986 Ok(())
1987 }
1988
1989 #[test]
1990 fn test_macos_distribution_extension_static() -> Result<()> {
1991 for target_triple in MACOS_TARGET_TRIPLES.iter() {
1992 for libpython_link_mode in vec![
1993 BinaryLibpythonLinkMode::Static,
1994 BinaryLibpythonLinkMode::Dynamic,
1995 ] {
1996 let options = StandalonePythonExecutableBuilderOptions {
1997 target_triple: target_triple.to_string(),
1998 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
1999 libpython_link_mode,
2000 ..StandalonePythonExecutableBuilderOptions::default()
2001 };
2002
2003 let mut builder = options.new_builder()?;
2004
2005 let sqlite = builder
2009 .target_distribution
2010 .extension_modules
2011 .get("_sqlite3")
2012 .unwrap()
2013 .default_variant()
2014 .clone();
2015
2016 builder.add_python_extension_module(&sqlite, None)?;
2017
2018 assert_eq!(
2019 builder.extension_build_contexts.get("_sqlite3"),
2020 Some(&LibPythonBuildContext {
2021 object_files: sqlite.object_file_data.clone(),
2022 static_libraries: ["sqlite3".to_string()].iter().cloned().collect(),
2023 init_functions: [("_sqlite3".to_string(), "PyInit__sqlite3".to_string())]
2024 .iter()
2025 .cloned()
2026 .collect(),
2027 licensed_components: licensed_components_from_extension(&sqlite),
2028 ..LibPythonBuildContext::default()
2029 })
2030 );
2031
2032 assert_eq!(
2033 builder
2034 .iter_resources()
2035 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
2036 Some(&PrePackagedResource {
2037 is_builtin_extension_module: true,
2038 name: "_sqlite3".to_string(),
2039 ..PrePackagedResource::default()
2040 })
2041 );
2042 }
2043 }
2044
2045 Ok(())
2046 }
2047
2048 #[test]
2049 fn test_macos_distribution_extension_filesystem_relative_only() -> Result<()> {
2050 for target_triple in MACOS_TARGET_TRIPLES.iter() {
2051 for libpython_link_mode in vec![
2052 BinaryLibpythonLinkMode::Static,
2053 BinaryLibpythonLinkMode::Dynamic,
2054 ] {
2055 let options = StandalonePythonExecutableBuilderOptions {
2056 target_triple: target_triple.to_string(),
2057 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2058 libpython_link_mode,
2059 resources_location: Some(ConcreteResourceLocation::RelativePath(
2060 "prefix_policy".to_string(),
2061 )),
2062 resources_location_fallback: Some(None),
2063 ..StandalonePythonExecutableBuilderOptions::default()
2064 };
2065
2066 let mut builder = options.new_builder()?;
2067
2068 let ext = builder
2069 .target_distribution
2070 .extension_modules
2071 .get("_sqlite3")
2072 .unwrap()
2073 .default_variant()
2074 .clone();
2075
2076 builder.add_python_extension_module(&ext, None)?;
2078
2079 assert_eq!(
2080 builder.extension_build_contexts.get("_sqlite3"),
2081 Some(&LibPythonBuildContext {
2082 object_files: ext.object_file_data.clone(),
2083 static_libraries: ["sqlite3".to_string()].iter().cloned().collect(),
2084 init_functions: [("_sqlite3".to_string(), "PyInit__sqlite3".to_string())]
2085 .iter()
2086 .cloned()
2087 .collect(),
2088 licensed_components: licensed_components_from_extension(&ext),
2089 ..LibPythonBuildContext::default()
2090 })
2091 );
2092
2093 assert_eq!(
2094 builder
2095 .iter_resources()
2096 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
2097 Some(&PrePackagedResource {
2098 is_builtin_extension_module: true,
2099 name: "_sqlite3".to_string(),
2100 ..PrePackagedResource::default()
2101 })
2102 );
2103 }
2104 }
2105
2106 Ok(())
2107 }
2108
2109 #[test]
2110 fn test_macos_extension_in_memory_only() -> Result<()> {
2111 for target_triple in MACOS_TARGET_TRIPLES.iter() {
2112 for libpython_link_mode in vec![
2113 BinaryLibpythonLinkMode::Static,
2114 BinaryLibpythonLinkMode::Dynamic,
2115 ] {
2116 let options = StandalonePythonExecutableBuilderOptions {
2117 target_triple: target_triple.to_string(),
2118 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2119 libpython_link_mode: libpython_link_mode.clone(),
2120 resources_location: Some(ConcreteResourceLocation::InMemory),
2121 resources_location_fallback: Some(None),
2122 ..StandalonePythonExecutableBuilderOptions::default()
2123 };
2124
2125 let mut builder = options.new_builder()?;
2126
2127 let res = builder
2128 .add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None);
2129 assert!(res.is_err());
2130 assert_eq!(
2131 res.err().unwrap().to_string(),
2132 "extension module shared_only cannot be loaded from memory but memory loading required"
2133 );
2134
2135 let res =
2136 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
2137 match libpython_link_mode {
2138 BinaryLibpythonLinkMode::Static => {
2139 assert!(res.is_ok());
2140 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
2141 }
2142 BinaryLibpythonLinkMode::Dynamic => {
2143 assert!(res.is_err());
2144 assert_eq!(res.err().unwrap().to_string(), "extension module object_files_only cannot be loaded from memory but memory loading required");
2145 }
2146 BinaryLibpythonLinkMode::Default => {
2147 panic!("should not get here");
2148 }
2149 }
2150
2151 let res = builder.add_python_extension_module(
2152 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2153 None,
2154 );
2155 match libpython_link_mode {
2156 BinaryLibpythonLinkMode::Static => {
2157 assert!(res.is_ok());
2158 assert_extension_builtin(
2159 &builder,
2160 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2161 );
2162 }
2163 BinaryLibpythonLinkMode::Dynamic => {
2164 assert!(res.is_err());
2165 assert_eq!(res.err().unwrap().to_string(), "extension module shared_and_object_files cannot be loaded from memory but memory loading required")
2166 }
2167 BinaryLibpythonLinkMode::Default => {
2168 panic!("should not get here");
2169 }
2170 }
2171 }
2172 }
2173
2174 Ok(())
2175 }
2176
2177 #[test]
2178 fn test_macos_extension_filesystem_relative_only() -> Result<()> {
2179 for target_triple in MACOS_TARGET_TRIPLES.iter() {
2180 for libpython_link_mode in vec![
2181 BinaryLibpythonLinkMode::Static,
2182 BinaryLibpythonLinkMode::Dynamic,
2183 ] {
2184 let options = StandalonePythonExecutableBuilderOptions {
2185 target_triple: target_triple.to_string(),
2186 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2187 libpython_link_mode: libpython_link_mode.clone(),
2188 resources_location: Some(ConcreteResourceLocation::RelativePath(
2189 "prefix_policy".to_string(),
2190 )),
2191 resources_location_fallback: Some(None),
2192 ..StandalonePythonExecutableBuilderOptions::default()
2193 };
2194
2195 let mut builder = options.new_builder()?;
2196
2197 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None)?;
2198 assert_extension_shared_library(
2199 &builder,
2200 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
2201 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
2202 );
2203
2204 let res =
2205 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
2206 match libpython_link_mode {
2207 BinaryLibpythonLinkMode::Static => {
2208 assert!(res.is_ok());
2209 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
2210 }
2211 BinaryLibpythonLinkMode::Dynamic => {
2212 assert!(res.is_err());
2213 assert_eq!(res.err().unwrap().to_string(), "extension module object_files_only cannot be materialized as a shared library extension but filesystem loading required");
2214 }
2215 BinaryLibpythonLinkMode::Default => {
2216 panic!("should not get here");
2217 }
2218 }
2219
2220 builder.add_python_extension_module(
2221 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2222 None,
2223 )?;
2224 assert_extension_shared_library(
2225 &builder,
2226 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2227 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
2228 );
2229 }
2230 }
2231
2232 Ok(())
2233 }
2234
2235 #[test]
2236 fn test_macos_extension_prefer_in_memory() -> Result<()> {
2237 for target_triple in MACOS_TARGET_TRIPLES.iter() {
2238 for libpython_link_mode in vec![
2239 BinaryLibpythonLinkMode::Static,
2240 BinaryLibpythonLinkMode::Dynamic,
2241 ] {
2242 let options = StandalonePythonExecutableBuilderOptions {
2243 target_triple: target_triple.to_string(),
2244 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2245 libpython_link_mode: libpython_link_mode.clone(),
2246 resources_location: Some(ConcreteResourceLocation::InMemory),
2247 resources_location_fallback: Some(Some(
2248 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
2249 )),
2250 ..StandalonePythonExecutableBuilderOptions::default()
2251 };
2252
2253 let mut builder = options.new_builder()?;
2254
2255 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None)?;
2256 assert_extension_shared_library(
2257 &builder,
2258 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
2259 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
2260 );
2261
2262 let res =
2263 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
2264 match libpython_link_mode {
2265 BinaryLibpythonLinkMode::Static => {
2266 assert!(res.is_ok());
2267 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
2268 }
2269 BinaryLibpythonLinkMode::Dynamic => {
2270 assert!(res.is_err());
2271 assert_eq!(
2272 res.err().unwrap().to_string(),
2273 "no shared library data present"
2274 );
2275 }
2276 BinaryLibpythonLinkMode::Default => {
2277 panic!("should not get here");
2278 }
2279 }
2280
2281 builder.add_python_extension_module(
2282 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2283 None,
2284 )?;
2285 match libpython_link_mode {
2286 BinaryLibpythonLinkMode::Static => {
2287 assert_extension_builtin(
2288 &builder,
2289 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2290 );
2291 }
2292 BinaryLibpythonLinkMode::Dynamic => {
2293 assert_extension_shared_library(
2294 &builder,
2295 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2296 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
2297 );
2298 }
2299 BinaryLibpythonLinkMode::Default => {
2300 panic!("should not get here");
2301 }
2302 }
2303 }
2304 }
2305
2306 Ok(())
2307 }
2308
2309 #[test]
2310 fn test_windows_dynamic_static_mismatch() {
2311 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2312 let options = StandalonePythonExecutableBuilderOptions {
2313 target_triple: target_triple.to_string(),
2314 distribution_flavor: DistributionFlavor::StandaloneDynamic,
2315 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2316 libpython_link_mode: BinaryLibpythonLinkMode::Static,
2317 ..StandalonePythonExecutableBuilderOptions::default()
2318 };
2319
2320 let err = options.new_builder().err();
2322 assert!(err.is_some());
2323 assert_eq!(
2324 err.unwrap().to_string(),
2325 "Python distribution does not support statically linking libpython"
2326 );
2327 }
2328 }
2329
2330 #[test]
2331 fn test_windows_static_dynamic_mismatch() {
2332 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2333 let options = StandalonePythonExecutableBuilderOptions {
2334 target_triple: target_triple.to_string(),
2335 distribution_flavor: DistributionFlavor::StandaloneStatic,
2336 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2337 libpython_link_mode: BinaryLibpythonLinkMode::Dynamic,
2338 ..StandalonePythonExecutableBuilderOptions::default()
2339 };
2340
2341 assert!(options.new_builder().is_err());
2343 }
2344 }
2345
2346 #[test]
2347 fn test_windows_dynamic_distribution_extensions() -> Result<()> {
2348 for target in WINDOWS_TARGET_TRIPLES.iter() {
2349 let options = StandalonePythonExecutableBuilderOptions {
2350 target_triple: target.to_string(),
2351 distribution_flavor: DistributionFlavor::StandaloneDynamic,
2352 extension_module_filter: Some(ExtensionModuleFilter::All),
2353 ..StandalonePythonExecutableBuilderOptions::default()
2354 };
2355
2356 let builder = options.new_builder()?;
2357
2358 let builtin_names = builder.extension_build_contexts.keys().collect::<Vec<_>>();
2359 let relative_path_extension_names = builder
2360 .iter_resources()
2361 .filter_map(|(name, r)| {
2362 if r.relative_path_extension_module_shared_library.is_some() {
2363 Some(name)
2364 } else {
2365 None
2366 }
2367 })
2368 .collect::<Vec<_>>();
2369 let in_memory_extension_names = builder
2370 .iter_resources()
2371 .filter_map(|(name, r)| {
2372 if r.in_memory_extension_module_shared_library.is_some() {
2373 Some(name)
2374 } else {
2375 None
2376 }
2377 })
2378 .collect::<Vec<_>>();
2379
2380 for (name, variants) in builder.target_distribution.extension_modules.iter() {
2384 if variants.iter().any(|e| e.required) {
2386 assert!(builtin_names.contains(&name));
2387 }
2388 }
2389
2390 for (name, variants) in builder.target_distribution.extension_modules.iter() {
2392 if variants.iter().any(|e| e.builtin_default) {
2393 assert!(builtin_names.contains(&name));
2394 }
2395 }
2396
2397 for (name, variants) in builder.target_distribution.extension_modules.iter() {
2399 if variants.iter().all(|e| !e.builtin_default) {
2400 assert!(!builtin_names.contains(&name));
2401 assert!(relative_path_extension_names.contains(&name));
2402 assert!(!in_memory_extension_names.contains(&name));
2403 }
2404 }
2405 }
2406
2407 Ok(())
2408 }
2409
2410 #[test]
2411 fn test_windows_distribution_extension_static() -> Result<()> {
2412 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2413 let options = StandalonePythonExecutableBuilderOptions {
2414 target_triple: target_triple.to_string(),
2415 distribution_flavor: DistributionFlavor::StandaloneStatic,
2416 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2417 libpython_link_mode: BinaryLibpythonLinkMode::Static,
2418 ..StandalonePythonExecutableBuilderOptions::default()
2419 };
2420
2421 let mut builder = options.new_builder()?;
2422
2423 let sqlite = builder
2427 .target_distribution
2428 .extension_modules
2429 .get("_sqlite3")
2430 .unwrap()
2431 .default_variant()
2432 .clone();
2433
2434 builder.add_python_extension_module(&sqlite, None)?;
2435
2436 assert_eq!(
2437 builder.extension_build_contexts.get("_sqlite3"),
2438 Some(&LibPythonBuildContext {
2439 object_files: sqlite.object_file_data.clone(),
2440 static_libraries: ["sqlite3".to_string()].iter().cloned().collect(),
2441 init_functions: [("_sqlite3".to_string(), "PyInit__sqlite3".to_string())]
2442 .iter()
2443 .cloned()
2444 .collect(),
2445 licensed_components: licensed_components_from_extension(&sqlite),
2446 ..LibPythonBuildContext::default()
2447 })
2448 );
2449
2450 assert_eq!(
2451 builder
2452 .iter_resources()
2453 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
2454 Some(&PrePackagedResource {
2455 is_builtin_extension_module: true,
2456 name: "_sqlite3".to_string(),
2457 ..PrePackagedResource::default()
2458 })
2459 );
2460 }
2461
2462 Ok(())
2463 }
2464
2465 #[test]
2466 fn test_windows_distribution_extension_dynamic() -> Result<()> {
2467 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2468 let options = StandalonePythonExecutableBuilderOptions {
2469 target_triple: target_triple.to_string(),
2470 distribution_flavor: DistributionFlavor::StandaloneDynamic,
2471 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2472 libpython_link_mode: BinaryLibpythonLinkMode::Dynamic,
2473 ..StandalonePythonExecutableBuilderOptions::default()
2474 };
2475
2476 let mut builder = options.new_builder()?;
2477
2478 let sqlite = builder
2483 .target_distribution
2484 .extension_modules
2485 .get("_sqlite3")
2486 .unwrap()
2487 .default_variant()
2488 .clone();
2489
2490 builder.add_python_extension_module(&sqlite, None)?;
2491
2492 assert!(!builder.extension_build_contexts.contains_key("_sqlite3"));
2493
2494 assert_eq!(
2495 builder
2496 .iter_resources()
2497 .find_map(|(name, r)| if *name == "_sqlite3" { Some(r) } else { None }),
2498 Some(&PrePackagedResource {
2499 name: "_sqlite3".to_string(),
2500 is_extension_module: true,
2501 relative_path_extension_module_shared_library: Some((
2502 PathBuf::from("lib/_sqlite3.pyd"),
2503 sqlite.shared_library.as_ref().unwrap().to_memory()?
2504 )),
2505 shared_library_dependency_names: Some(vec!["sqlite3".to_string()]),
2506 ..PrePackagedResource::default()
2507 })
2508 );
2509
2510 let library = builder
2511 .iter_resources()
2512 .find_map(|(name, r)| if *name == "sqlite3" { Some(r) } else { None })
2513 .unwrap();
2514 assert!(library.is_shared_library);
2515 assert!(library.relative_path_shared_library.is_some());
2516 }
2517
2518 Ok(())
2519 }
2520
2521 #[test]
2522 fn test_windows_dynamic_distribution_dynamic_extension_files() -> Result<()> {
2523 let env = get_env()?;
2524
2525 for target in WINDOWS_TARGET_TRIPLES.iter() {
2526 let options = StandalonePythonExecutableBuilderOptions {
2527 target_triple: target.to_string(),
2528 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2529 resources_location: Some(ConcreteResourceLocation::RelativePath("lib".to_string())),
2530 resources_location_fallback: Some(None),
2531 ..StandalonePythonExecutableBuilderOptions::default()
2532 };
2533
2534 let mut builder = options.new_builder()?;
2535
2536 let ssl_extension = builder
2541 .target_distribution
2542 .extension_modules
2543 .get("_ssl")
2544 .unwrap()
2545 .default_variant()
2546 .clone();
2547 assert_eq!(ssl_extension.extension_file_suffix, ".pyd");
2548 builder.add_python_extension_module(&ssl_extension, None)?;
2549
2550 let extensions = builder
2551 .iter_resources()
2552 .filter_map(|(_, r)| {
2553 if r.relative_path_extension_module_shared_library.is_some() {
2554 Some(r)
2555 } else {
2556 None
2557 }
2558 })
2559 .collect::<Vec<_>>();
2560
2561 assert_eq!(
2562 extensions.len(),
2563 1,
2564 "only manually added extension present when using minimal extension mode"
2565 );
2566 let ssl = &extensions[0];
2567 assert_eq!(ssl.name, "_ssl");
2568
2569 let (path, _) = ssl
2570 .relative_path_extension_module_shared_library
2571 .as_ref()
2572 .unwrap();
2573 assert_eq!(path, &PathBuf::from("lib/_ssl.pyd"));
2574
2575 let shared_libraries = builder
2576 .iter_resources()
2577 .filter_map(|(_, r)| {
2578 if r.relative_path_shared_library.is_some() {
2579 Some(r)
2580 } else {
2581 None
2582 }
2583 })
2584 .collect::<Vec<_>>();
2585
2586 assert_eq!(
2587 shared_libraries.len(),
2588 2,
2589 "pulled in shared library dependencies for _ssl"
2590 );
2591
2592 let lib_suffix = match *target {
2593 "i686-pc-windows-msvc" => "",
2594 "x86_64-pc-windows-msvc" => "-x64",
2595 _ => panic!("unexpected target: {}", target),
2596 };
2597
2598 assert_eq!(
2599 shared_libraries[0].name,
2600 format!("libcrypto-1_1{}", lib_suffix)
2601 );
2602 assert_eq!(
2603 shared_libraries[0]
2604 .relative_path_shared_library
2605 .as_ref()
2606 .unwrap()
2607 .0,
2608 "lib"
2609 );
2610
2611 assert_eq!(
2612 shared_libraries[1].name,
2613 format!("libssl-1_1{}", lib_suffix)
2614 );
2615
2616 let mut compiler = builder.host_distribution.create_bytecode_compiler(&env)?;
2617
2618 let resources = shared_libraries
2619 .iter()
2620 .map(|r| r.to_resource(compiler.deref_mut()))
2621 .collect::<Result<Vec<_>>>()?;
2622 assert_eq!(resources.len(), 2);
2623
2624 assert_eq!(
2625 &resources[0].1,
2626 &vec![(
2627 PathBuf::from(format!("lib/libcrypto-1_1{}.dll", lib_suffix)),
2628 FileData::Path(
2629 builder
2630 .target_distribution
2631 .base_dir
2632 .join("python")
2633 .join("install")
2634 .join("DLLs")
2635 .join(format!("libcrypto-1_1{}.dll", lib_suffix))
2636 ),
2637 true
2638 )]
2639 );
2640 assert_eq!(
2641 &resources[1].1,
2642 &vec![(
2643 PathBuf::from(format!("lib/libssl-1_1{}.dll", lib_suffix)),
2644 FileData::Path(
2645 builder
2646 .target_distribution
2647 .base_dir
2648 .join("python")
2649 .join("install")
2650 .join("DLLs")
2651 .join(format!("libssl-1_1{}.dll", lib_suffix))
2652 ),
2653 true
2654 )]
2655 );
2656 }
2657
2658 Ok(())
2659 }
2660
2661 #[test]
2662 fn test_windows_static_distribution_extensions() -> Result<()> {
2663 for target in WINDOWS_TARGET_TRIPLES.iter() {
2664 let options = StandalonePythonExecutableBuilderOptions {
2665 target_triple: target.to_string(),
2666 distribution_flavor: DistributionFlavor::StandaloneStatic,
2667 extension_module_filter: Some(ExtensionModuleFilter::All),
2668 ..StandalonePythonExecutableBuilderOptions::default()
2669 };
2670
2671 let builder = options.new_builder()?;
2672
2673 for name in builder.target_distribution.extension_modules.keys() {
2676 assert!(builder.extension_build_contexts.keys().any(|x| x == name));
2677 }
2678 }
2679
2680 Ok(())
2681 }
2682
2683 #[test]
2684 fn test_windows_dynamic_extension_in_memory_only() -> Result<()> {
2685 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2686 let options = StandalonePythonExecutableBuilderOptions {
2687 target_triple: target_triple.to_string(),
2688 distribution_flavor: DistributionFlavor::StandaloneDynamic,
2689 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2690 libpython_link_mode: BinaryLibpythonLinkMode::Dynamic,
2691 resources_location: Some(ConcreteResourceLocation::InMemory),
2692 resources_location_fallback: Some(None),
2693 allow_in_memory_shared_library_loading: Some(true),
2694 ..StandalonePythonExecutableBuilderOptions::default()
2695 };
2696
2697 let mut builder = options.new_builder()?;
2698
2699 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None)?;
2700 assert_extension_shared_library(
2701 &builder,
2702 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
2703 ConcreteResourceLocation::InMemory,
2704 );
2705
2706 let res =
2707 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
2708 assert!(res.is_err());
2709 assert_eq!(
2710 res.err().unwrap().to_string(),
2711 "no shared library data present"
2712 );
2713
2714 builder.add_python_extension_module(
2715 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2716 None,
2717 )?;
2718 assert_extension_shared_library(
2719 &builder,
2720 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2721 ConcreteResourceLocation::InMemory,
2722 );
2723 }
2724
2725 Ok(())
2726 }
2727
2728 #[test]
2729 fn test_windows_static_extension_in_memory_only() -> Result<()> {
2730 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2731 let options = StandalonePythonExecutableBuilderOptions {
2732 target_triple: target_triple.to_string(),
2733 distribution_flavor: DistributionFlavor::StandaloneStatic,
2734 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2735 libpython_link_mode: BinaryLibpythonLinkMode::Static,
2736 resources_location: Some(ConcreteResourceLocation::InMemory),
2737 resources_location_fallback: Some(None),
2738 ..StandalonePythonExecutableBuilderOptions::default()
2739 };
2740
2741 let mut builder = options.new_builder()?;
2742
2743 let res =
2744 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None);
2745 assert!(res.is_err());
2746 assert_eq!(
2747 res.err().unwrap().to_string(),
2748 "extension module shared_only cannot be loaded from memory but memory loading required"
2749 );
2750
2751 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None)?;
2752 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
2753
2754 builder.add_python_extension_module(
2755 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2756 None,
2757 )?;
2758 assert_extension_builtin(&builder, &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES);
2759 }
2760
2761 Ok(())
2762 }
2763
2764 #[test]
2765 fn test_windows_dynamic_extension_filesystem_relative_only() -> Result<()> {
2766 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2767 let options = StandalonePythonExecutableBuilderOptions {
2768 target_triple: target_triple.to_string(),
2769 distribution_flavor: DistributionFlavor::StandaloneDynamic,
2770 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2771 libpython_link_mode: BinaryLibpythonLinkMode::Dynamic,
2772 resources_location: Some(ConcreteResourceLocation::RelativePath(
2773 "prefix_policy".to_string(),
2774 )),
2775 resources_location_fallback: Some(None),
2776 ..StandalonePythonExecutableBuilderOptions::default()
2777 };
2778
2779 let mut builder = options.new_builder()?;
2780
2781 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None)?;
2782 assert_extension_shared_library(
2783 &builder,
2784 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
2785 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
2786 );
2787
2788 let res =
2789 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
2790 assert!(res.is_err());
2791 assert_eq!(res.err().unwrap().to_string(), "extension module object_files_only cannot be materialized as a shared library extension but filesystem loading required");
2792
2793 builder.add_python_extension_module(
2794 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2795 None,
2796 )?;
2797 assert_extension_shared_library(
2798 &builder,
2799 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
2800 ConcreteResourceLocation::RelativePath("prefix_policy".to_string()),
2801 );
2802 }
2803
2804 Ok(())
2805 }
2806
2807 #[test]
2808 fn test_windows_static_extension_filesystem_relative_only() -> Result<()> {
2809 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2810 let options = StandalonePythonExecutableBuilderOptions {
2811 target_triple: target_triple.to_string(),
2812 distribution_flavor: DistributionFlavor::StandaloneStatic,
2813 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2814 libpython_link_mode: BinaryLibpythonLinkMode::Static,
2815 resources_location: Some(ConcreteResourceLocation::RelativePath(
2816 "prefix_policy".to_string(),
2817 )),
2818 resources_location_fallback: Some(None),
2819 ..StandalonePythonExecutableBuilderOptions::default()
2820 };
2821
2822 let mut builder = options.new_builder()?;
2823
2824 let res =
2825 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None);
2826 assert!(res.is_err());
2827 assert_eq!(res.err().unwrap().to_string(),
2828 "extension module shared_only cannot be materialized as a shared library because distribution does not support loading extension module shared libraries"
2829 );
2830
2831 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None)?;
2832 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
2833
2834 builder.add_python_extension_module(
2835 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2836 None,
2837 )?;
2838 assert_extension_builtin(&builder, &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES);
2839 }
2840
2841 Ok(())
2842 }
2843
2844 #[test]
2845 fn test_windows_dynamic_extension_prefer_in_memory() -> Result<()> {
2846 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2847 let options = StandalonePythonExecutableBuilderOptions {
2848 target_triple: target_triple.to_string(),
2849 distribution_flavor: DistributionFlavor::StandaloneDynamic,
2850 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2851 libpython_link_mode: BinaryLibpythonLinkMode::Dynamic,
2852 resources_location: Some(ConcreteResourceLocation::InMemory),
2853 resources_location_fallback: Some(Some(ConcreteResourceLocation::RelativePath(
2854 "prefix_policy".to_string(),
2855 ))),
2856 allow_in_memory_shared_library_loading: Some(true),
2857 ..StandalonePythonExecutableBuilderOptions::default()
2858 };
2859
2860 let mut builder = options.new_builder()?;
2861
2862 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None)?;
2863 assert_extension_shared_library(
2864 &builder,
2865 &EXTENSION_MODULE_SHARED_LIBRARY_ONLY,
2866 ConcreteResourceLocation::InMemory,
2867 );
2868
2869 let res =
2871 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None);
2872 assert!(res.is_err());
2873 assert_eq!(
2874 res.err().unwrap().to_string(),
2875 "no shared library data present"
2876 );
2877
2878 builder.add_python_extension_module(
2879 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2880 None,
2881 )?;
2882 assert_extension_shared_library(
2883 &builder,
2884 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2885 ConcreteResourceLocation::InMemory,
2886 );
2887 }
2888
2889 Ok(())
2890 }
2891
2892 #[test]
2893 fn test_windows_static_extension_prefer_in_memory() -> Result<()> {
2894 for target_triple in WINDOWS_TARGET_TRIPLES.iter() {
2895 let options = StandalonePythonExecutableBuilderOptions {
2896 target_triple: target_triple.to_string(),
2897 distribution_flavor: DistributionFlavor::StandaloneStatic,
2898 extension_module_filter: Some(ExtensionModuleFilter::Minimal),
2899 libpython_link_mode: BinaryLibpythonLinkMode::Static,
2900 resources_location: Some(ConcreteResourceLocation::InMemory),
2901 resources_location_fallback: Some(Some(ConcreteResourceLocation::RelativePath(
2902 "prefix_policy".to_string(),
2903 ))),
2904 ..StandalonePythonExecutableBuilderOptions::default()
2905 };
2906
2907 let mut builder = options.new_builder()?;
2908
2909 let res =
2910 builder.add_python_extension_module(&EXTENSION_MODULE_SHARED_LIBRARY_ONLY, None);
2911 assert!(res.is_err());
2912 assert_eq!(res.err().unwrap().to_string(),
2913 "extension module shared_only cannot be materialized as a shared library because distribution does not support loading extension module shared libraries"
2914 );
2915
2916 builder.add_python_extension_module(&EXTENSION_MODULE_OBJECT_FILES_ONLY, None)?;
2917 assert_extension_builtin(&builder, &EXTENSION_MODULE_OBJECT_FILES_ONLY);
2918
2919 builder.add_python_extension_module(
2920 &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES,
2921 None,
2922 )?;
2923 assert_extension_builtin(&builder, &EXTENSION_MODULE_SHARED_LIBRARY_AND_OBJECT_FILES);
2924 }
2925
2926 Ok(())
2927 }
2928
2929 #[cfg(target_os = "linux")]
2930 #[test]
2931 fn test_linux_extension_build_with_library() -> Result<()> {
2932 let env = get_env()?;
2933
2934 for libpython_link_mode in vec![
2939 BinaryLibpythonLinkMode::Static,
2940 BinaryLibpythonLinkMode::Dynamic,
2941 ] {
2942 let options = StandalonePythonExecutableBuilderOptions {
2943 target_triple: "x86_64-unknown-linux-gnu".to_string(),
2944 distribution_version: Some("3.9".into()),
2945 extension_module_filter: Some(ExtensionModuleFilter::All),
2946 libpython_link_mode: libpython_link_mode.clone(),
2947 resources_location: Some(ConcreteResourceLocation::InMemory),
2948 ..StandalonePythonExecutableBuilderOptions::default()
2949 };
2950
2951 let mut builder = options.new_builder()?;
2952
2953 let resources = builder.pip_install(
2954 &env,
2955 false,
2956 &["pyyaml==5.3.1".to_string()],
2957 &HashMap::new(),
2958 )?;
2959
2960 let extensions = resources
2961 .iter()
2962 .filter_map(|r| match r {
2963 PythonResource::ExtensionModule(e) => Some(e),
2964 _ => None,
2965 })
2966 .collect::<Vec<_>>();
2967
2968 assert_eq!(extensions.len(), 1);
2969
2970 let mut orig = extensions[0].clone();
2971 assert!(orig.shared_library.is_some());
2972
2973 let (objects_len, link_libraries) = match libpython_link_mode {
2974 BinaryLibpythonLinkMode::Dynamic => (0, vec![]),
2975 BinaryLibpythonLinkMode::Static => (
2976 1,
2977 vec![LibraryDependency {
2978 name: "yaml".to_string(),
2979 static_library: None,
2980 static_filename: None,
2981 dynamic_library: None,
2982 dynamic_filename: None,
2983 framework: false,
2984 system: false,
2985 }],
2986 ),
2987 BinaryLibpythonLinkMode::Default => {
2988 panic!("should not get here");
2989 }
2990 };
2991
2992 assert_eq!(orig.object_file_data.len(), objects_len);
2993
2994 let mut e = orig.to_mut();
2996 e.shared_library = None;
2997 e.object_file_data = vec![];
2998
2999 assert_eq!(
3000 e,
3001 &PythonExtensionModule {
3002 name: "_yaml".to_string(),
3003 init_fn: Some("PyInit__yaml".to_string()),
3004 extension_file_suffix: ".cpython-39-x86_64-linux-gnu.so".to_string(),
3005 shared_library: None,
3006 object_file_data: vec![],
3007 is_package: false,
3008 link_libraries,
3009 is_stdlib: false,
3010 builtin_default: false,
3011 required: false,
3012 variant: None,
3013 license: None,
3014 },
3015 "PythonExtensionModule for {:?}",
3016 libpython_link_mode
3017 );
3018 }
3019
3020 Ok(())
3021 }
3022
3023 #[test]
3024 fn test_vcruntime_requirements() -> Result<()> {
3025 for dist in get_all_standalone_distributions()? {
3026 let host_distribution = get_host_distribution_from_target(&dist)?;
3027
3028 let builder = StandalonePythonExecutableBuilder::from_distribution(
3029 host_distribution.clone(),
3030 dist.clone(),
3031 host_distribution.target_triple().to_string(),
3032 dist.target_triple().to_string(),
3033 "myapp".to_string(),
3034 BinaryLibpythonLinkMode::Default,
3035 dist.create_packaging_policy()?,
3036 dist.create_python_interpreter_config()?,
3037 )?;
3038
3039 let reqs = builder.vc_runtime_requirements();
3040
3041 if dist.target_triple().contains("windows") && dist.libpython_shared_library.is_some() {
3042 let platform = match dist.target_triple() {
3043 "i686-pc-windows-msvc" => VcRedistributablePlatform::X86,
3044 "x86_64-pc-windows-msvc" => VcRedistributablePlatform::X64,
3045 triple => {
3046 return Err(anyhow!("unexpected distribution triple: {}", triple));
3047 }
3048 };
3049
3050 assert_eq!(reqs, Some(("14".to_string(), platform)));
3051 } else {
3052 assert!(reqs.is_none());
3053 }
3054 }
3055
3056 Ok(())
3057 }
3058
3059 #[test]
3060 fn test_install_windows_runtime_dlls() -> Result<()> {
3061 for dist in get_all_standalone_distributions()? {
3062 let host_distribution = get_host_distribution_from_target(&dist)?;
3063
3064 let mut builder = StandalonePythonExecutableBuilder::from_distribution(
3065 host_distribution.clone(),
3066 dist.clone(),
3067 host_distribution.target_triple().to_string(),
3068 dist.target_triple().to_string(),
3069 "myapp".to_string(),
3070 BinaryLibpythonLinkMode::Default,
3071 dist.create_packaging_policy()?,
3072 dist.create_python_interpreter_config()?,
3073 )?;
3074
3075 builder.set_windows_runtime_dlls_mode(WindowsRuntimeDllsMode::Never);
3077 let manifest = builder.resolve_windows_runtime_dll_files()?;
3078 assert!(
3079 manifest.is_empty(),
3080 "target triple: {}",
3081 dist.target_triple()
3082 );
3083
3084 builder.set_windows_runtime_dlls_mode(WindowsRuntimeDllsMode::WhenPresent);
3087
3088 if let Some((version, platform)) = builder.vc_runtime_requirements() {
3089 let can_locate_runtime =
3090 find_visual_cpp_redistributable(&version, platform).is_ok();
3091
3092 let manifest = builder.resolve_windows_runtime_dll_files()?;
3093
3094 if can_locate_runtime {
3095 assert!(
3096 !manifest.is_empty(),
3097 "target triple: {}",
3098 dist.target_triple()
3099 );
3100 } else {
3101 assert!(
3102 manifest.is_empty(),
3103 "target triple: {}",
3104 dist.target_triple()
3105 );
3106 }
3107 } else {
3108 assert!(
3109 builder.resolve_windows_runtime_dll_files()?.is_empty(),
3110 "target triple: {}",
3111 dist.target_triple()
3112 );
3113 }
3114
3115 builder.set_windows_runtime_dlls_mode(WindowsRuntimeDllsMode::Always);
3117
3118 if let Some((version, platform)) = builder.vc_runtime_requirements() {
3119 let can_locate_runtime =
3120 find_visual_cpp_redistributable(&version, platform).is_ok();
3121
3122 let res = builder.resolve_windows_runtime_dll_files();
3123
3124 if can_locate_runtime {
3125 assert!(!res?.is_empty(), "target triple: {}", dist.target_triple());
3126 } else {
3127 assert!(res.is_err());
3128 }
3129 } else {
3130 assert!(
3131 builder.resolve_windows_runtime_dll_files()?.is_empty(),
3132 "target triple: {}",
3133 dist.target_triple()
3134 );
3135 }
3136 }
3137
3138 Ok(())
3139 }
3140
3141 fn test_stdlib(dist: Arc<StandaloneDistribution>) -> Result<()> {
3143 let host_distribution = get_host_distribution_from_target(&dist)?;
3144
3145 let mut policy = dist.create_packaging_policy()?;
3146 policy.set_include_test(true);
3147
3148 let mut builder = StandalonePythonExecutableBuilder::from_distribution(
3149 host_distribution.clone(),
3150 dist.clone(),
3151 host_distribution.target_triple().to_string(),
3152 dist.target_triple().to_string(),
3153 "myapp".to_string(),
3154 BinaryLibpythonLinkMode::Default,
3155 policy,
3156 dist.create_python_interpreter_config()?,
3157 )?;
3158
3159 builder.add_distribution_resources(None)?;
3160
3161 let temp_dir = get_env()?.temporary_directory("pyoxidizer-test")?;
3162
3163 let mut compiler =
3164 BytecodeCompiler::new(host_distribution.python_exe_path(), temp_dir.path())?;
3165
3166 builder
3168 .resources_collector
3169 .compile_resources(&mut compiler)?;
3170
3171 temp_dir.close()?;
3172
3173 Ok(())
3174 }
3175
3176 #[test]
3177 fn test_stdlib_tests_0() -> Result<()> {
3178 for dist in get_all_standalone_distributions_chunk(0, 5)? {
3179 test_stdlib(dist)?;
3180 }
3181
3182 Ok(())
3183 }
3184
3185 #[test]
3186 fn test_stdlib_tests_1() -> Result<()> {
3187 for dist in get_all_standalone_distributions_chunk(1, 5)? {
3188 test_stdlib(dist)?;
3189 }
3190
3191 Ok(())
3192 }
3193
3194 #[test]
3195 fn test_stdlib_tests_2() -> Result<()> {
3196 for dist in get_all_standalone_distributions_chunk(2, 5)? {
3197 test_stdlib(dist)?;
3198 }
3199
3200 Ok(())
3201 }
3202
3203 #[test]
3204 fn test_stdlib_tests_3() -> Result<()> {
3205 for dist in get_all_standalone_distributions_chunk(3, 5)? {
3206 test_stdlib(dist)?;
3207 }
3208
3209 Ok(())
3210 }
3211
3212 #[test]
3213 fn test_stdlib_tests_4() -> Result<()> {
3214 for dist in get_all_standalone_distributions_chunk(4, 5)? {
3215 test_stdlib(dist)?;
3216 }
3217
3218 Ok(())
3219 }
3220}