Coverage for debputy/manifest_conditions.py: 0%

88 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2023-01-22 14:29 +0100

1import dataclasses 

2from typing import List, Callable, Optional 

3 

4from debian.debian_support import DpkgArchTable 

5 

6from debputy._deb_options_profiles import DebOptionsAndProfiles 

7from debputy.architecture_support import DpkgArchitectureBuildProcessValuesTable 

8from debputy.packages import BinaryPackage, SourcePackage 

9from debputy.substitution import Substitution 

10from debputy.util import active_profiles_match 

11 

12 

13@dataclasses.dataclass(slots=True, frozen=True) 

14class ConditionContext: 

15 binary_package: Optional[BinaryPackage] 

16 build_env: DebOptionsAndProfiles 

17 substitution: Substitution 

18 dpkg_architecture_variables: DpkgArchitectureBuildProcessValuesTable 

19 dpkg_arch_query_table: DpkgArchTable 

20 

21 

22class ManifestCondition: 

23 

24 def describe(self) -> str: 

25 raise NotImplementedError 

26 

27 def negated(self) -> 'ManifestCondition': 

28 return NegatedManifestCondition(self) 

29 

30 def evaluate(self, context: ConditionContext) -> bool: 

31 raise NotImplementedError 

32 

33 @classmethod 

34 def is_cross_building(cls) -> 'ManifestCondition': 

35 return _IS_CROSS_BUILDING 

36 

37 @classmethod 

38 def can_execute_compiled_binaries(cls): 

39 return _CAN_EXECUTE_COMPILED_BINARIES 

40 

41 @classmethod 

42 def run_build_time_tests(cls): 

43 return _RUN_BUILD_TIME_TESTS 

44 

45 

46class NegatedManifestCondition(ManifestCondition): 

47 

48 __slots__ = ('_condition',) 

49 

50 def __init__(self, condition: ManifestCondition) -> None: 

51 self._condition = condition 

52 

53 def negated(self) -> 'ManifestCondition': 

54 return self._condition 

55 

56 def describe(self) -> str: 

57 return f'not ({self._condition.describe()})' 

58 

59 def evaluate(self, *args, **kwargs) -> bool: 

60 return not self._condition.evaluate(*args, **kwargs) 

61 

62 

63class ArchMatchManifestCondition(ManifestCondition): 

64 

65 __slots__ = ('_arch_spec', '_is_negated') 

66 

67 def __init__(self, arch_spec: List[str], *, is_negated: bool = False) -> None: 

68 self._arch_spec = arch_spec 

69 self._is_negated = is_negated 

70 

71 def negated(self) -> 'ManifestCondition': 

72 return self.__class__(self._arch_spec, is_negated=not self._is_negated) 

73 

74 def describe(self) -> str: 

75 if self._is_negated: 

76 return f'architecture (for binary package) matches *none* of [{", ".join(self._arch_spec)}]' 

77 return f'architecture (for binary package) matches any of [{", ".join(self._arch_spec)}]' 

78 

79 def evaluate(self, context: ConditionContext) -> bool: 

80 binary_package = context.binary_package 

81 if binary_package is None: 

82 raise RuntimeError("Condition only applies in the context of a BinaryPackage, but was evaluated" 

83 " without one") 

84 arch = binary_package.resolved_architecture 

85 match = context.dpkg_arch_query_table.architecture_is_concerned(arch, self._arch_spec) 

86 return not match if self._is_negated else match 

87 

88 

89class BuildProfileMatch(ManifestCondition): 

90 

91 __slots__ = ('_profile_spec', '_is_negated') 

92 

93 def __init__(self, profile_spec: str, *, is_negated: bool = False) -> None: 

94 self._profile_spec = profile_spec 

95 self._is_negated = is_negated 

96 

97 def negated(self) -> 'ManifestCondition': 

98 return self.__class__(self._profile_spec, is_negated=not self._is_negated) 

99 

100 def describe(self) -> str: 

101 if self._is_negated: 

102 return f'DEB_BUILD_PROFILES matches *none* of [{", ".join(self._profile_spec)}]' 

103 return f'DEB_BUILD_PROFILES matches any of [{", ".join(self._profile_spec)}]' 

104 

105 def evaluate(self, context: ConditionContext) -> bool: 

106 match = active_profiles_match(self._profile_spec, context.build_env.deb_build_profiles) 

107 return not match if self._is_negated else match 

108 

109 

110@dataclasses.dataclass(frozen=True, slots=True) 

111class _SingletonCondition(ManifestCondition): 

112 

113 description: str 

114 implementation: Callable[[ConditionContext], bool] 

115 

116 def describe(self) -> str: 

117 return self.description 

118 

119 def evaluate(self, context: ConditionContext) -> bool: 

120 

121 return self.implementation(context) 

122 

123 

124def _can_run_built_binaries(context: ConditionContext) -> bool: 

125 if not context.dpkg_architecture_variables.is_cross_compiling: 

126 return True 

127 # User / Builder asserted that we could even though we are cross-compiling, so we have to assume it is true 

128 return 'crossbuildcanrunhostbinaries' in context.build_env.deb_build_options 

129 

130 

131_IS_CROSS_BUILDING = _SingletonCondition('Cross Compiling (i.e., DEB_HOST_GNU_TYPE != DEB_BUILD_GNU_TYPE)', 

132 lambda c: c.dpkg_architecture_variables.is_cross_compiling, 

133 ) 

134 

135_CAN_EXECUTE_COMPILED_BINARIES = _SingletonCondition('Can run built binaries (natively or via transparent emulation)', 

136 _can_run_built_binaries, 

137 ) 

138 

139_RUN_BUILD_TIME_TESTS = _SingletonCondition('Can run built binaries (natively or via transparent emulation)', 

140 lambda c: 'nocheck' in c.build_env.deb_build_options 

141 ) 

142 

143 

144del _SingletonCondition 

145del _can_run_built_binaries 

146