Skip to content

Commit 2f86929

Browse files
authored
Plugin Config Table (#116)
1 parent 0c4e4f4 commit 2f86929

File tree

20 files changed

+1282
-189
lines changed

20 files changed

+1282
-189
lines changed

cppython/builder.py

Lines changed: 80 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
from cppython.data import Data, Plugins
3535
from cppython.defaults import DefaultSCM
3636
from cppython.utility.exception import PluginError
37+
from cppython.utility.utility import TypeName
3738

3839

3940
class Resolver:
@@ -59,14 +60,14 @@ def generate_plugins(
5960
raw_generator_plugins = self.find_generators()
6061
generator_plugins = self.filter_plugins(
6162
raw_generator_plugins,
62-
cppython_local_configuration.generator_name,
63+
self._get_effective_generator_name(cppython_local_configuration),
6364
'Generator',
6465
)
6566

6667
raw_provider_plugins = self.find_providers()
6768
provider_plugins = self.filter_plugins(
6869
raw_provider_plugins,
69-
cppython_local_configuration.provider_name,
70+
self._get_effective_provider_name(cppython_local_configuration),
7071
'Provider',
7172
)
7273

@@ -79,6 +80,74 @@ def generate_plugins(
7980

8081
return PluginBuildData(generator_type=generator_type, provider_type=provider_type, scm_type=scm_type)
8182

83+
def _get_effective_generator_name(self, config: CPPythonLocalConfiguration) -> str | None:
84+
"""Get the effective generator name from configuration
85+
86+
Args:
87+
config: The local configuration
88+
89+
Returns:
90+
The generator name to use, or None for auto-detection
91+
"""
92+
if config.generators:
93+
# For now, pick the first generator (in future, could support selection logic)
94+
return list(config.generators.keys())[0]
95+
96+
# No generators specified, use auto-detection
97+
return None
98+
99+
def _get_effective_provider_name(self, config: CPPythonLocalConfiguration) -> str | None:
100+
"""Get the effective provider name from configuration
101+
102+
Args:
103+
config: The local configuration
104+
105+
Returns:
106+
The provider name to use, or None for auto-detection
107+
"""
108+
if config.providers:
109+
# For now, pick the first provider (in future, could support selection logic)
110+
return list(config.providers.keys())[0]
111+
112+
# No providers specified, use auto-detection
113+
return None
114+
115+
def _get_effective_generator_config(
116+
self, config: CPPythonLocalConfiguration, generator_name: str
117+
) -> dict[str, Any]:
118+
"""Get the effective generator configuration
119+
120+
Args:
121+
config: The local configuration
122+
generator_name: The name of the generator being used
123+
124+
Returns:
125+
The configuration dict for the generator
126+
"""
127+
generator_type_name = TypeName(generator_name)
128+
if config.generators and generator_type_name in config.generators:
129+
return config.generators[generator_type_name]
130+
131+
# Return empty config if not found
132+
return {}
133+
134+
def _get_effective_provider_config(self, config: CPPythonLocalConfiguration, provider_name: str) -> dict[str, Any]:
135+
"""Get the effective provider configuration
136+
137+
Args:
138+
config: The local configuration
139+
provider_name: The name of the provider being used
140+
141+
Returns:
142+
The configuration dict for the provider
143+
"""
144+
provider_type_name = TypeName(provider_name)
145+
if config.providers and provider_type_name in config.providers:
146+
return config.providers[provider_type_name]
147+
148+
# Return empty config if not found
149+
return {}
150+
82151
@staticmethod
83152
def generate_cppython_plugin_data(plugin_build_data: PluginBuildData) -> PluginCPPythonData:
84153
"""Generates the CPPython plugin data from the resolved plugins
@@ -447,11 +516,18 @@ def build(
447516
pep621_data = self._resolver.generate_pep621_data(pep621_configuration, self._project_configuration, scm)
448517

449518
# Create the chosen plugins
519+
generator_config = self._resolver._get_effective_generator_config(
520+
cppython_local_configuration, plugin_build_data.generator_type.name()
521+
)
450522
generator = self._resolver.create_generator(
451-
core_data, pep621_data, cppython_local_configuration.generator, plugin_build_data.generator_type
523+
core_data, pep621_data, generator_config, plugin_build_data.generator_type
524+
)
525+
526+
provider_config = self._resolver._get_effective_provider_config(
527+
cppython_local_configuration, plugin_build_data.provider_type.name()
452528
)
453529
provider = self._resolver.create_provider(
454-
core_data, pep621_data, cppython_local_configuration.provider, plugin_build_data.provider_type
530+
core_data, pep621_data, provider_config, plugin_build_data.provider_type
455531
)
456532

457533
plugins = Plugins(generator=generator, provider=provider, scm=scm)

cppython/core/resolution.py

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -139,16 +139,22 @@ def resolve_cppython(
139139
if not modified_build_path.is_absolute():
140140
modified_build_path = root_directory / modified_build_path
141141

142-
modified_provider_name = local_configuration.provider_name
143-
modified_generator_name = local_configuration.generator_name
142+
modified_provider_name = plugin_build_data.provider_name
143+
modified_generator_name = plugin_build_data.generator_name
144144

145-
if modified_provider_name is None:
146-
modified_provider_name = plugin_build_data.provider_name
145+
modified_scm_name = plugin_build_data.scm_name
147146

148-
if modified_generator_name is None:
149-
modified_generator_name = plugin_build_data.generator_name
147+
# Extract provider and generator configuration data
148+
provider_type_name = TypeName(modified_provider_name)
149+
generator_type_name = TypeName(modified_generator_name)
150150

151-
modified_scm_name = plugin_build_data.scm_name
151+
provider_data = {}
152+
if local_configuration.providers and provider_type_name in local_configuration.providers:
153+
provider_data = local_configuration.providers[provider_type_name]
154+
155+
generator_data = {}
156+
if local_configuration.generators and generator_type_name in local_configuration.generators:
157+
generator_data = local_configuration.generators[generator_type_name]
152158

153159
# Construct dependencies from the local configuration only
154160
dependencies: list[Requirement] = []
@@ -173,6 +179,8 @@ def resolve_cppython(
173179
generator_name=modified_generator_name,
174180
scm_name=modified_scm_name,
175181
dependencies=dependencies,
182+
provider_data=provider_data,
183+
generator_data=generator_data,
176184
)
177185
return cppython_data
178186

@@ -200,6 +208,8 @@ def resolve_cppython_plugin(cppython_data: CPPythonData, plugin_type: type[Plugi
200208
generator_name=cppython_data.generator_name,
201209
scm_name=cppython_data.scm_name,
202210
dependencies=cppython_data.dependencies,
211+
provider_data=cppython_data.provider_data,
212+
generator_data=cppython_data.generator_data,
203213
)
204214

205215
return cast(CPPythonPluginData, plugin_data)

cppython/core/schema.py

Lines changed: 13 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,9 @@ class CPPythonData(CPPythonModel, extra='forbid'):
118118
scm_name: TypeName
119119
dependencies: list[Requirement]
120120

121+
provider_data: Annotated[dict[str, Any], Field(description='Resolved provider configuration data')]
122+
generator_data: Annotated[dict[str, Any], Field(description='Resolved generator configuration data')]
123+
121124
@field_validator('configuration_path', 'install_path', 'tool_path', 'build_path') # type: ignore
122125
@classmethod
123126
def validate_absolute_path(cls, value: Path) -> Path:
@@ -302,29 +305,21 @@ class CPPythonLocalConfiguration(CPPythonModel, extra='forbid'):
302305
),
303306
] = Path('build')
304307

305-
provider: Annotated[ProviderData, Field(description="Provider plugin data associated with 'provider_name")] = (
306-
ProviderData({})
307-
)
308-
309-
provider_name: Annotated[
310-
TypeName | None,
308+
providers: Annotated[
309+
dict[TypeName, ProviderData],
311310
Field(
312-
alias='provider-name',
313-
description='If empty, the provider will be automatically deduced.',
311+
description='Named provider configurations. Key is the provider name, value is the provider configuration.'
314312
),
315-
] = None
316-
317-
generator: Annotated[GeneratorData, Field(description="Generator plugin data associated with 'generator_name'")] = (
318-
GeneratorData({})
319-
)
313+
] = {}
320314

321-
generator_name: Annotated[
322-
TypeName | None,
315+
generators: Annotated[
316+
dict[TypeName, GeneratorData],
323317
Field(
324-
alias='generator-name',
325-
description='If empty, the generator will be automatically deduced.',
318+
description=(
319+
'Named generator configurations. Key is the generator name, value is the generator configuration.'
320+
)
326321
),
327-
] = None
322+
] = {}
328323

329324
dependencies: Annotated[
330325
list[str] | None,

cppython/plugins/conan/builder.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def _create_conanfile(conan_file: Path, dependencies: list[ConanDependency]) ->
125125
"""Creates a conanfile.py file with the necessary content."""
126126
template_string = """
127127
from conan import ConanFile
128-
from conan.tools.cmake import CMake, CMakeToolchain, cmake_layout
128+
from conan.tools.cmake import CMake, cmake_layout
129129
130130
class MyProject(ConanFile):
131131
name = "myproject"

cppython/plugins/conan/plugin.py

Lines changed: 4 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
import requests
1313
from conan.api.conan_api import ConanAPI
1414
from conan.api.model import ListPattern
15-
from conan.internal.model.profile import Profile
1615

1716
from cppython.core.plugin_schema.generator import SyncConsumer
1817
from cppython.core.plugin_schema.provider import Provider, ProviderPluginGroupData, SupportedProviderFeatures
@@ -109,8 +108,8 @@ def _install_dependencies(self, *, update: bool = False) -> None:
109108
all_remotes = conan_api.remotes.list()
110109
logger.debug('Available remotes: %s', [remote.name for remote in all_remotes])
111110

112-
# Get profiles with fallback to auto-detection
113-
profile_host, profile_build = self._get_profiles(conan_api)
111+
# Get profiles from resolved data
112+
profile_host, profile_build = self.data.host_profile, self.data.build_profile
114113

115114
path = str(conanfile_path)
116115
remotes = all_remotes
@@ -249,8 +248,8 @@ def publish(self) -> None:
249248
remotes=all_remotes, # Use all remotes for dependency resolution during export
250249
)
251250

252-
# Step 2: Get profiles with fallback to auto-detection
253-
profile_host, profile_build = self._get_profiles(conan_api)
251+
# Step 2: Get profiles from resolved data
252+
profile_host, profile_build = self.data.host_profile, self.data.build_profile
254253

255254
# Step 3: Build dependency graph for the package - prepare parameters
256255
path = str(conanfile_path)
@@ -305,68 +304,3 @@ def publish(self) -> None:
305304
)
306305
else:
307306
raise ProviderInstallationError('conan', 'No packages found to upload')
308-
309-
def _apply_profile_processing(self, profiles: list[Profile], conan_api: ConanAPI, cache_settings: Any) -> None:
310-
"""Apply profile plugin and settings processing to a list of profiles.
311-
312-
Args:
313-
profiles: List of profiles to process
314-
conan_api: The Conan API instance
315-
cache_settings: The settings configuration
316-
"""
317-
logger = logging.getLogger('cppython.conan')
318-
319-
# Apply profile plugin processing
320-
try:
321-
profile_plugin = conan_api.profiles._load_profile_plugin()
322-
if profile_plugin is not None:
323-
for profile in profiles:
324-
try:
325-
profile_plugin(profile)
326-
except Exception as plugin_error:
327-
logger.warning('Profile plugin failed for profile: %s', str(plugin_error))
328-
except (AttributeError, Exception):
329-
logger.debug('Profile plugin not available or failed to load')
330-
331-
# Process settings to initialize processed_settings
332-
for profile in profiles:
333-
try:
334-
profile.process_settings(cache_settings)
335-
except (AttributeError, Exception) as settings_error:
336-
logger.debug('Settings processing failed for profile: %s', str(settings_error))
337-
338-
def _get_profiles(self, conan_api: ConanAPI) -> tuple[Profile, Profile]:
339-
"""Get Conan profiles with fallback to auto-detection.
340-
341-
Args:
342-
conan_api: The Conan API instance
343-
344-
Returns:
345-
A tuple of (profile_host, profile_build) objects
346-
"""
347-
logger = logging.getLogger('cppython.conan')
348-
349-
try:
350-
# Gather default profile paths, these can raise exceptions if not available
351-
profile_host_path = conan_api.profiles.get_default_host()
352-
profile_build_path = conan_api.profiles.get_default_build()
353-
354-
# Load the actual profile objects, can raise if data is invalid
355-
profile_host = conan_api.profiles.get_profile([profile_host_path])
356-
profile_build = conan_api.profiles.get_profile([profile_build_path])
357-
358-
logger.debug('Using existing default profiles')
359-
return profile_host, profile_build
360-
361-
except Exception as e:
362-
logger.warning('Default profiles not available, using auto-detection. Conan message: %s', str(e))
363-
364-
# Create auto-detected profiles
365-
profiles = [conan_api.profiles.detect(), conan_api.profiles.detect()]
366-
cache_settings = conan_api.config.settings_yml
367-
368-
# Apply profile plugin processing to both profiles
369-
self._apply_profile_processing(profiles, conan_api, cache_settings)
370-
371-
logger.debug('Auto-detected profiles with plugin processing applied')
372-
return profiles[0], profiles[1]

0 commit comments

Comments
 (0)