77from enum import Enum
88from functools import partial
99from pathlib import Path
10+ from threading import Lock
1011from typing import (
1112 TYPE_CHECKING ,
1213 Any ,
@@ -354,6 +355,16 @@ class Posix(OperatingSystem, BaseClassMixin):
354355 def __init__ (self , node : Any ) -> None :
355356 super ().__init__ (node , is_posix = True )
356357 self ._first_time_installation : bool = True
358+ # instance variables to check if source repositories are enabled
359+ # some distros make this easier than others,
360+ # but all allow use of source code packages which are compiled
361+ # on the local machine instead of binary packages.
362+ # slower, but allows local optimization and auditing.
363+ # lucky for us! those packages contain build dependency metadata.
364+ # we can use this to avoid keeping long lists
365+ # of constantly changing package names for each distro.
366+ self ._source_installation_initialized = False
367+ self ._lock = Lock ()
357368
358369 @classmethod
359370 def type_name (cls ) -> str :
@@ -452,7 +463,6 @@ def enable_package_build_deps(self) -> None:
452463 turning on deb-src or srpm repositories.
453464 """
454465 self ._enable_package_build_deps ()
455- self ._src_repo_initialized = True
456466
457467 def install_build_deps (
458468 self ,
@@ -462,8 +472,12 @@ def install_build_deps(
462472 Install apt-get/yum build-deps package build dependency installation by
463473 turning on deb-src or srpm repositories.
464474 """
465- if not getattr (self , "_src_repo_initialized" , False ):
466- self .enable_package_build_deps ()
475+ # lock this to prevent double initialization;
476+ # some methods involve directly writing to a file
477+ with self ._lock :
478+ if not self ._source_installation_initialized :
479+ self .enable_package_build_deps ()
480+ self ._source_installation_initialized = True
467481
468482 package_names = self ._get_package_list (packages )
469483 for package in package_names :
@@ -964,14 +978,19 @@ def _get_package_information(self, package_name: str) -> LisaVersionInfo:
964978 return self ._cache_and_return_version_info (package_name , version_info )
965979
966980 def _enable_package_build_deps (self ) -> None :
967- if not getattr (self , "_src_repo_initialized" , False ):
981+ # debian uses apt sources.list.
982+ # we only need to uncomment the standard source repos
983+ # in that file
984+ sources_list = self ._node .get_pure_path ("/etc/apt/sources.list" )
985+ if self ._node .shell .exists (sources_list ):
968986 self ._node .tools [Sed ].substitute (
969- "# deb-src" , "deb-src" , "/etc/apt/sources.list" , sudo = True
987+ "# deb-src" , "deb-src" , str ( sources_list ) , sudo = True
970988 )
971989 self .get_repositories ()
972- self ._src_repo_initialized = True
973990
974991 def _install_build_deps (self , packages : str ) -> None :
992+ # apt-get build-dep installs the listed build dependencies
993+ # from the src .deb for a given package.
975994 self ._node .execute (
976995 f"apt-get build-dep -y { packages } " ,
977996 sudo = True ,
@@ -1501,11 +1520,12 @@ def _enable_package_build_deps(self) -> None:
15011520 # file in place of the sources.list file. It uses a fancier format, so
15021521 # requires special handling.
15031522 node = self ._node
1504- if node .shell .exists (node .get_pure_path ("/etc/apt/ubuntu.sources" )):
1523+ ubuntu_sources = node .get_pure_path ("/etc/apt/ubuntu.sources" )
1524+ if node .shell .exists (ubuntu_sources ):
15051525 node .tools [Sed ].substitute (
15061526 regexp = r"Types\: deb" ,
15071527 replacement = r"Types\: deb deb-src" ,
1508- file = "/etc/apt/ubuntu.sources" ,
1528+ file = str ( ubuntu_sources ) ,
15091529 sudo = True ,
15101530 )
15111531 else :
@@ -1888,6 +1908,7 @@ def _get_information(self) -> OsInformation:
18881908 return information
18891909
18901910 def _enable_package_build_deps (self ) -> None :
1911+ # epel enbables source repos by default
18911912 self .install_epel ()
18921913
18931914
@@ -2091,13 +2112,16 @@ def _dnf_tool(self) -> str:
20912112 def _enable_package_build_deps (self ) -> None :
20922113 # enable epel first
20932114 super ()._enable_package_build_deps ()
2115+ # almalinux has a few different source repos, they are easy to enable
20942116 self ._node .execute ("dnf install -y almalinux-release-devel" )
20952117 # then enable crb (code ready builder) using alma tool
20962118 # this enables some needed package build dependency srpms
2119+ # this was formerly known as the 'powertools' repo.
20972120 if int (self .information .version .major ) == 8 :
20982121 pkg = "powertools"
20992122 elif int (self .information .version .major ) == 9 :
21002123 pkg = "crb"
2124+ # set the repo as enabled
21012125 self ._node .execute (
21022126 f"dnf config-manager --set-enabled { pkg } " ,
21032127 sudo = True ,
@@ -2417,28 +2441,42 @@ def add_azure_core_repo(
24172441 )
24182442
24192443 def _enable_package_build_deps (self ) -> None :
2444+ # zypper seems more suited to interactive use
2445+ # to enable source repos, list the defaults and select the source repos
24202446 repos = self .get_repositories ()
24212447 for repo in repos :
24222448 if isinstance (repo , SuseRepositoryInfo ) and "Source-Pool" in repo .name :
2449+ # if it's a source repo, enable it
24232450 self ._node .execute (
24242451 f"zypper mr -e { repo .alias } " ,
24252452 sudo = True ,
24262453 expected_exit_code = 0 ,
2427- expected_exit_code_failure_message = f"Could not enable source pool for repo: { repo .alias } " ,
2454+ expected_exit_code_failure_message = (
2455+ f"Could not enable source pool for repo: { repo .alias } "
2456+ ),
24282457 )
2458+ # then refresh the repo status to fetch the new metadata
24292459 self ._node .execute (
24302460 "zypper refresh -y" ,
24312461 sudo = True ,
24322462 expected_exit_code = 0 ,
2433- expected_exit_code_failure_message = "Failure to zypper refresh after enabling source repos." ,
2463+ expected_exit_code_failure_message = (
2464+ "Failure to zypper refresh after enabling source repos."
2465+ ),
24342466 )
24352467
24362468 def _install_build_deps (self , packages : str ) -> None :
2469+ # zypper sourceinstall the packages
2470+ # if there are missing dependencies, you can attempt to
2471+ # force zypper to work out the problem. It seems to assume
2472+ # interactive usage for these even with the -n flag so YMMV.
24372473 self ._node .execute (
24382474 f"zypper si --build-deps-only --force-resolution { packages } " ,
24392475 sudo = True ,
24402476 expected_exit_code = 0 ,
2441- expected_exit_code_failure_message = f"failed to source install package: { packages } " ,
2477+ expected_exit_code_failure_message = (
2478+ f"failed to source install package: { packages } "
2479+ ),
24422480 )
24432481
24442482 def _initialize_package_installation (self ) -> None :
0 commit comments