Skip to content

Commit 729cc86

Browse files
committed
Add interconnectivity between bite components
Signed-off-by: Alireza Poodineh <[email protected]>
1 parent 911d358 commit 729cc86

File tree

4 files changed

+74
-30
lines changed

4 files changed

+74
-30
lines changed

Directory.Build.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@
22
<Project>
33
<PropertyGroup>
44
<BiteScriptInstance Condition=" '$(BiteScriptInstance)' == '' ">false</BiteScriptInstance>
5-
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
5+
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
66

77
<BiteRoot>$(MSBuildThisFileDirectory)</BiteRoot>
88
<BiteArtifactsPath>$(BiteRoot)artifacts\</BiteArtifactsPath>
9-
<BiteModulesPath>$(BiteRoot)build\modules\</BiteModulesPath>
10-
9+
<BiteModulesPath Condition=" '$(BiteModulesPath)' == '' ">$(BiteRoot)build\modules\</BiteModulesPath>
10+
1111
<BaseIntermediateOutputPath>$(BiteArtifactsPath)obj\$([MSBuild]::MakeRelative($(BiteRoot), $(MSBuildProjectDirectory)))\</BaseIntermediateOutputPath>
1212
<BaseOutputPath Condition=" '$(BaseOutputPath)' == '' ">$(BiteArtifactsPath)bin\$(MSBuildProjectName)\</BaseOutputPath>
1313
<PublishDir>$(BiteArtifactsPath)publish\$(MSBuildProjectName)\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\</PublishDir>

build.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
host = config.pybite.Host(
1313
app=app_name,
1414
description='Bite build engine command line interface',
15-
usage=f'{app_name} command [options]',
16-
epilog="Any unrecognized options will be passed to the command handler.",
1715
)
1816

1917
host.load_modules()

config.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,9 @@
11
import tools.pybite as pybite
22

3+
# This is a configuration file for the PyBite build engine.
4+
5+
# Set if you have more than one sln file in your project.
6+
#pybite.Host.SOLUTION_PATH = 'ProjectName.sln'
7+
8+
# Change bite modules folder
9+
#pybite.Host.MODULES_DIR = 'tools/bite'

tools/pybite/host.py

Lines changed: 64 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,17 @@ class Host:
1919
BASE_DIR: str = os.getcwd()
2020
"""Base directory for the project (current working directory)."""
2121

22-
MODULES_DIR: str = os.path.join(BASE_DIR, 'build', 'modules')
23-
"""Directory where .bite.py modules are located."""
22+
MODULES_DIR: str = os.path.join('build', 'modules')
23+
"""Directory where the bite modules are located."""
2424

25-
DOTNET_DIR: str = os.path.join(BASE_DIR, '.dotnet')
25+
DOTNET_DIR: str = '.dotnet'
2626
"""Directory where the .NET SDK is or will be installed."""
2727

2828
SOLUTION_PATH: Optional[str] = None
2929
"""Optional override for the solution path."""
30+
31+
BITE_PROJ_PATH: str = 'bite.proj'
32+
"""Path to the bite.core file."""
3033

3134
DEFAULT_ARGS: List[str] = ['--nologo']
3235
"""Default arguments to pass to dotnet CLI commands."""
@@ -41,32 +44,41 @@ class Host:
4144

4245
DOTNET_COMMANDS: List[Dict[str, str]] = [
4346
{'name': 'restore', 'help': 'Restore NuGet packages for the solution'},
44-
{'name': 'clean', 'help': 'Clean the solution'},
45-
{'name': 'build', 'help': 'Build the solution'},
46-
{'name': 'test', 'help': 'Test the solution'}
47+
{'name': 'build', 'help': 'Build the solution (default)'},
48+
{'name': 'pack', 'help': 'Pack the solution'},
49+
{'name': 'clean', 'help': 'Clean the solution'},
50+
{'name': 'test', 'help': 'Test the solution'},
4751
]
4852
"""List of default dotnet commands and their help descriptions."""
4953

5054
def __init__(
5155
self,
5256
app: str,
5357
description: Optional[str] = None,
54-
epilog: Optional[str] = None,
55-
usage: Optional[str] = None,
5658
) -> None:
5759
"""
5860
Initialize the Host instance and prepare CLI argument parser configuration.
5961
6062
Args:
6163
app: The name of the application.
6264
description: Description of the application.
63-
usage: Usage string for the argument parser.
64-
epilog: Text to display at the end of the help message.
6565
"""
6666
self.name: str = app
6767
self.description = description
68-
self.argparser_usage = usage
69-
self.argparser_epilog = epilog
68+
self.argparser_usage = f'{self.name} command [options]'
69+
self.argparser_epilog = "Any unrecognized options will be passed to the command handler."
70+
71+
# Only join with BASE_DIR if the path is relative
72+
if not os.path.isabs(self.MODULES_DIR):
73+
self.MODULES_DIR = os.path.join(self.BASE_DIR, self.MODULES_DIR)
74+
if not os.path.isabs(self.DOTNET_DIR):
75+
self.DOTNET_DIR = os.path.join(self.BASE_DIR, self.DOTNET_DIR)
76+
if self.SOLUTION_PATH and not os.path.isabs(self.SOLUTION_PATH):
77+
self.SOLUTION_PATH = os.path.join(self.BASE_DIR, self.SOLUTION_PATH)
78+
if not os.path.isabs(self.BITE_PROJ_PATH):
79+
self.BITE_PROJ_PATH = os.path.join(self.BASE_DIR, self.BITE_PROJ_PATH)
80+
81+
self.DEFAULT_ARGS.append(f'/p:BiteModulesPath={self.msbuild_path(self.MODULES_DIR)}')
7082

7183
try:
7284
self.global_json: Optional[GlobalJson] = GlobalJson(os.path.join(self.BASE_DIR, 'global.json'))
@@ -107,21 +119,32 @@ def get_argparser(self) -> argparse.ArgumentParser:
107119
required=False, # Allow default
108120
metavar='', # This hides the {restore,clean,...} line in help
109121
)
122+
110123
subparsers.required = False # Explicitly allow no subcommand
111124
parser.set_defaults(command='build', func=self._handle_dotnet_command)
112125
self._subparsers_action = subparsers
113126

114127
# Register built-in dotnet commands
115128
for cmd in self.DOTNET_COMMANDS:
116-
sub = subparsers.add_parser(cmd['name'], help=cmd['help'])
129+
sub = subparsers.add_parser(
130+
cmd['name'],
131+
help=cmd['help'],
132+
epilog=self.argparser_epilog + ' (Dotnet CLI)',
133+
usage=self.argparser_usage.replace('command', cmd['name']),
134+
)
117135
sub.set_defaults(func=self._handle_dotnet_command)
118136
self.handlers[cmd['name']] = self._handle_dotnet_command
119137

120-
# Bite command
121-
bite_parser = subparsers.add_parser('bite', help='Run a custom bite target')
122-
bite_parser.add_argument('target', nargs='?', default='help', help='Bite target to run')
123-
bite_parser.set_defaults(func=self._handle_bite)
124-
self.handlers['bite'] = self._handle_bite
138+
if os.path.isfile(self.BITE_PROJ_PATH):
139+
bite_parser = subparsers.add_parser(
140+
'bite',
141+
help='Run a bite.core target',
142+
epilog=self.argparser_epilog + ' (MSBuild)',
143+
usage=self.argparser_usage.replace('command', 'bite') + ' [target]',
144+
)
145+
bite_parser.add_argument('target', nargs='?', default='help', help='bite.core target to run, default is "help"')
146+
bite_parser.set_defaults(func=self._handle_bite)
147+
self.handlers['bite'] = self._handle_bite
125148

126149
self.argparser = parser
127150
return self.argparser
@@ -139,7 +162,7 @@ def add_command(
139162
140163
Args:
141164
name: The command name (e.g., 'deploy').
142-
handler: The function to handle the command (called with parsed args).
165+
handler: The function to handle the command (called with parsed args and unknown args).
143166
description: Optional description for the command.
144167
help: Help string for the command.
145168
arguments: List of dicts with keys for add_argument (optional).
@@ -148,7 +171,7 @@ def add_command(
148171
subparsers = self._subparsers_action
149172
if subparsers is None:
150173
raise RuntimeError("Subparsers not found in parser")
151-
sub = subparsers.add_parser(name, help=help, description=description or help)
174+
sub = subparsers.add_parser(name, help=help, description=description or help, epilog=self.argparser_epilog)
152175
if arguments:
153176
for arg in arguments:
154177
sub.add_argument(*arg.get('args', ()), **arg.get('kwargs', {}))
@@ -170,7 +193,7 @@ def _handle_bite(self, args: argparse.Namespace, *extras: str) -> None:
170193
Passes any extra arguments to msbuild.
171194
"""
172195
target = getattr(args, 'target', 'help')
173-
self.msbuild(target, *extras)
196+
self.run_bite(target, *extras)
174197

175198
def register_handler(self, command: str, handler: Callable[[argparse.Namespace], None]) -> None:
176199
"""
@@ -317,17 +340,31 @@ def run(self, command: str, *args: str) -> None:
317340
cmd = ['dotnet', command] + [self.solution] + self.DEFAULT_ARGS + list(args)
318341
subprocess.call(cmd)
319342

320-
def msbuild(self, target: str, *args: str) -> None:
343+
def run_bite(self, target: str, *args: str) -> None:
321344
"""
322-
Run msbuild with a specific target using dotnet CLI.
345+
Run bite.core with the specified target.
323346
324347
Args:
325-
target: The msbuild target to run.
348+
target: The bite.core target to run.
326349
*args: Additional arguments to pass to msbuild.
327350
"""
328-
cmd = ['dotnet', 'msbuild'] + self.DEFAULT_ARGS + [f'-t:{target}', 'bite.proj'] + list(args)
351+
cmd = ['dotnet', 'msbuild'] + self.DEFAULT_ARGS + [f'-t:{target}', self.BITE_PROJ_PATH] + list(args)
329352
subprocess.call(cmd)
330353

354+
@staticmethod
355+
def msbuild_path(path: str) -> str:
356+
"""
357+
Convert a Python path string to an MSBuild-acceptable path for directory properties.
358+
Ensures absolute path, uses backslashes, and ends with a backslash.
359+
"""
360+
abs_path = os.path.abspath(path)
361+
msbuild_path = abs_path
362+
if not msbuild_path.endswith('\\'):
363+
msbuild_path += '\\'
364+
if ' ' in msbuild_path:
365+
msbuild_path = f'"{msbuild_path}"'
366+
return msbuild_path
367+
331368
def load_modules(self) -> Dict[str, Any]:
332369
"""
333370
Load all .bite.py modules from the modules directory.
@@ -342,8 +379,10 @@ def load_modules(self) -> Dict[str, Any]:
342379
spec = importlib.util.spec_from_file_location(name, path)
343380
if spec is None or spec.loader is None:
344381
continue
382+
345383
mod = importlib.util.module_from_spec(spec)
346384
spec.loader.exec_module(mod)
385+
347386
if hasattr(mod, 'load'):
348387
mods[name] = mod.load(self)
349388
return mods

0 commit comments

Comments
 (0)