diff --git a/src/oebuild/app/conf/config.yaml b/src/oebuild/app/conf/config.yaml index f637d2e0fa41086697c127225298e2d4e9002f0c..f0ad9893ccc2f8951880d97fc0f088184014cabe 100644 --- a/src/oebuild/app/conf/config.yaml +++ b/src/oebuild/app/conf/config.yaml @@ -10,3 +10,4 @@ basic_repo: path: yocto-meta-openeuler remote_url: https://gitee.com/openeuler/yocto-meta-openeuler.git branch: master +feat_root_dir: nightly-features diff --git a/src/oebuild/app/plugins/neo_generate/neo_generate.py b/src/oebuild/app/plugins/neo_generate/neo_generate.py index 12a79050d4e846fa1cffe514e16986af47bd8071..e7ca644c7ae01d758025dae4de00ccd19f9ad77f 100644 --- a/src/oebuild/app/plugins/neo_generate/neo_generate.py +++ b/src/oebuild/app/plugins/neo_generate/neo_generate.py @@ -28,10 +28,10 @@ from oebuild.command import OebuildCommand from oebuild.configure import Configure from oebuild.m_log import logger from oebuild.nightly_features import ( - FeatureResolutionError, + ResolutionError, FeatureResolver, NeoFeatureError, - NightlyFeatureRegistry, + FeatureRegistry, ) from oebuild.parse_template import ( BaseParseTemplate, @@ -81,6 +81,7 @@ class NeoGenerate(OebuildCommand): if self.pre_parse_help(args, unknown): sys.exit(0) + unknown = unknown or [] parsed_args = args.parse_args(unknown) self._validate_environment() @@ -89,8 +90,14 @@ class NeoGenerate(OebuildCommand): self.list_info() return - if parsed_args.menuconfig: + interactive_invocation = parsed_args.menuconfig and not unknown + if interactive_invocation: menu_selection = self._run_menuconfig(parsed_args) + if menu_selection is None: + logger.info( + 'Menuconfig exited without saving a configuration; nothing generated.' + ) + return parsed_args.platform = menu_selection.platform parsed_args.features = menu_selection.features @@ -98,10 +105,22 @@ class NeoGenerate(OebuildCommand): resolution = self._resolve_features( parsed_args.platform, parsed_args.features or [] ) - except FeatureResolutionError as err: + except ResolutionError as err: logger.error(str(err)) sys.exit(1) + try: + parser_template = self._prepare_parser_template( + args=parsed_args, + resolved_features=resolution.features, + ) + except BaseParseTemplate as b_t: + logger.error(str(b_t)) + sys.exit(-1) + except ValueError as v_e: + logger.error(str(v_e)) + sys.exit(-1) + build_dir = self._init_build_dir(parsed_args) if build_dir is None: sys.exit(1) @@ -112,7 +131,7 @@ class NeoGenerate(OebuildCommand): self._generate_compile_conf( args=parsed_args, build_dir=build_dir, - resolved_features=resolution.features, + parser_template=parser_template, ) def _validate_environment(self): @@ -120,6 +139,7 @@ class NeoGenerate(OebuildCommand): logger.error('Your current directory had not finished init') sys.exit(-1) + oebuild_config = self.configure.parse_oebuild_config() yocto_dir = self.configure.source_yocto_dir() self.yocto_dir = yocto_dir if not self.check_support_oebuild(yocto_dir): @@ -130,14 +150,29 @@ class NeoGenerate(OebuildCommand): sys.exit(-1) try: - nightly_dir = pathlib.Path(yocto_dir, '.oebuild', 'nightly-features') - self.feature_registry = NightlyFeatureRegistry(nightly_dir) + feat_root_dir = ( + oebuild_config.feat_root_dir.strip() + if isinstance(oebuild_config.feat_root_dir, str) + else '' + ) + if not feat_root_dir: + feat_root_dir = 'nightly-features' + nightly_dir = pathlib.Path( + yocto_dir, '.oebuild', feat_root_dir + ) + self.feature_registry = FeatureRegistry(nightly_dir) except NeoFeatureError as err: logger.error(str(err)) sys.exit(-1) def _run_menuconfig(self, args): platform_dir = pathlib.Path(self.yocto_dir, '.oebuild', 'platform') + config_path = pathlib.Path(os.getcwd(), '.config') + if config_path.exists(): + try: + config_path.unlink() + except OSError: + pass try: generator = NeoMenuconfigGenerator( registry=self.feature_registry, @@ -192,32 +227,26 @@ class NeoGenerate(OebuildCommand): """) logger.info(summary) - def _resolve_features(self, platform, requested): - resolver = FeatureResolver(self.feature_registry, platform) - return resolver.resolve(requested) - - def _generate_compile_conf( - self, args, build_dir, resolved_features - ): + def _prepare_parser_template(self, args, resolved_features): parser_template = ParseTemplate(yocto_dir=self.yocto_dir) yocto_oebuild_dir = pathlib.Path(self.yocto_dir, '.oebuild') - try: - self._add_platform_template( - args=args, - yocto_oebuild_dir=yocto_oebuild_dir, - parser_template=parser_template, - ) - except BaseParseTemplate as b_t: - logger.error(str(b_t)) - sys.exit(-1) - except ValueError as v_e: - logger.error(str(v_e)) - sys.exit(-1) - + self._add_platform_template( + args=args, + yocto_oebuild_dir=yocto_oebuild_dir, + parser_template=parser_template, + ) self._add_nightly_features_template( parser_template, resolved_features ) + return parser_template + + def _resolve_features(self, platform, requested): + resolver = FeatureResolver(self.feature_registry, platform) + return resolver.resolve(requested) + def _generate_compile_conf( + self, args, build_dir, parser_template + ): compile_yaml_path = pathlib.Path(build_dir, 'compile.yaml') if compile_yaml_path.exists(): compile_yaml_path.unlink() diff --git a/src/oebuild/configure.py b/src/oebuild/configure.py index 95e5a1e1e6471b54108318ef249f09c754c86a70..30a1c72cddf7b42e035b4c991aeccc8b0554826a 100644 --- a/src/oebuild/configure.py +++ b/src/oebuild/configure.py @@ -64,6 +64,8 @@ class Config: basic_repo: dict + feat_root_dir: str = 'nightly-features' + class Configure: """ @@ -219,7 +221,18 @@ class Configure: branch=repo['branch'], ) - config = Config(docker=docker_config, basic_repo=basic_config) + raw_feat_root = config.get('feat_root_dir') + # nightly-features is the fallback + feat_root_dir = ( + raw_feat_root.strip() + if isinstance(raw_feat_root, str) and raw_feat_root.strip() + else 'nightly-features' + ) + config = Config( + docker=docker_config, + basic_repo=basic_config, + feat_root_dir=feat_root_dir, + ) return config @@ -247,6 +260,7 @@ class Configure: 'remote_url': repo.remote_url, 'branch': repo.branch, } + data['feat_root_dir'] = config.feat_root_dir try: oebuild_util.write_yaml( diff --git a/src/oebuild/nightly_features.py b/src/oebuild/nightly_features.py index a1136fe728f124915451a260a011197a3271f652..1ce6852e8b9bc09a506ca9a02dba738e9321d2ce 100644 --- a/src/oebuild/nightly_features.py +++ b/src/oebuild/nightly_features.py @@ -20,19 +20,19 @@ class NeoFeatureError(Exception): """Base error for nightly feature parsing and resolution.""" # noqa: D401 -class FeatureResolutionError(NeoFeatureError): +class ResolutionError(NeoFeatureError): """Raised when the resolver cannot satisfy a requested feature merge.""" # noqa: D401 -class FeatureConflictError(FeatureResolutionError): +class ConflictError(ResolutionError): """Raised when conflicting features are enabled simultaneously.""" -class FeatureNotFoundError(FeatureResolutionError): +class NotFountError(ResolutionError): """Raised when a requested feature identifier cannot be resolved.""" -class AmbiguousFeatureError(FeatureResolutionError): +class AmbiguourError(ResolutionError): """Raised when a feature identifier matches multiple candidates.""" @@ -74,8 +74,8 @@ class ResolutionResult: features: List[Feature] -class NightlyFeatureRegistry: - """Indexes features defined under .oebuild/nightly-features.""" +class FeatureRegistry: + """Indexes features defined under .oebuild/.""" def __init__(self, nightly_dir: pathlib.Path): self.features_dir = pathlib.Path(nightly_dir) @@ -367,7 +367,7 @@ class NightlyFeatureRegistry: return entry try: resolved = self.resolve_identifier(entry) - except FeatureResolutionError as err: + except ResolutionError as err: raise NeoFeatureError( f'{feature.full_id} references unknown feature {entry}' ) from err @@ -435,7 +435,7 @@ class NightlyFeatureRegistry: if len(top_level) == 1: return top_level[0] if top_level: - raise AmbiguousFeatureError( + raise AmbiguourError( f"Ambiguous feature ID: '{identifier}'. Candidates: " + ', '.join(f.full_id for f in top_level) ) @@ -445,7 +445,7 @@ class NightlyFeatureRegistry: if len(sub_candidates) == 1: return sub_candidates[0] if sub_candidates: - raise AmbiguousFeatureError( + raise AmbiguourError( f"Ambiguous feature ID: '{identifier}'. Candidates: " + ', '.join(f.full_id for f in sub_candidates) ) @@ -485,7 +485,7 @@ class NightlyFeatureRegistry: if alias_feature and not has_context and ( alias_feature.full_id != leaf_match.full_id ): - raise AmbiguousFeatureError( + raise AmbiguourError( f"Ambiguous feature ID: '{identifier}'. Candidates: " f"{alias_feature.full_id}, {leaf_match.full_id}" ) @@ -494,7 +494,7 @@ class NightlyFeatureRegistry: if alias_feature: return alias_feature - raise FeatureNotFoundError( + raise NotFountError( f"Unknown feature '{identifier}'. Use --list to see available features." ) @@ -507,7 +507,7 @@ class NightlyFeatureRegistry: class FeatureResolver: """Resolves machine-aware dependency trees for nightly features.""" - def __init__(self, registry: NightlyFeatureRegistry, machine: str): + def __init__(self, registry: FeatureRegistry, machine: str): self.registry = registry self.machine = machine.strip() self.enabled: Dict[str, Feature] = {} @@ -592,7 +592,7 @@ class FeatureResolver: *trace_lines, f' - Constraint: {feature.leaf_id} requires machine {machines}', ] - raise FeatureResolutionError('\n'.join(lines)) + raise ResolutionError('\n'.join(lines)) def _resolve_one_of_groups(self) -> None: for feature in self.registry.features_with_one_of: @@ -638,7 +638,7 @@ class FeatureResolver: if leaf_names else '' ) - raise FeatureConflictError( + raise ConflictError( f"[Error] Conflict in feature '{feature.full_id}':\n" f"{detail} cannot be enabled together (one_of)." )