Skip to content

Commit 5a274a5

Browse files
committed
Add pybite
Signed-off-by: Alireza Poodineh <[email protected]>
1 parent 8f1648a commit 5a274a5

File tree

8 files changed

+232
-294
lines changed

8 files changed

+232
-294
lines changed

build.cmd

Lines changed: 3 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,8 @@
11
@echo off
2-
REM ----------------------------------------------------------------
3-
REM Windows batch wrapper to invoke the PowerShell Bite script
4-
REM ----------------------------------------------------------------
52
setlocal
63

7-
REM Determine script folder
8-
set "SCRIPT_DIR=%~dp0"
4+
cd /d "%~dp0"
95

10-
REM Choose PowerShell host: prefer pwsh (PowerShell Core), fallback to Windows PowerShell
11-
where pwsh >nul 2>&1
12-
if %errorlevel%==0 (
13-
set "PS_CMD=pwsh"
14-
) else (
15-
set "PS_CMD=powershell"
16-
)
6+
python "%~dp0build.py" %*
177

18-
REM Check if arguments are provided
19-
if "%~1"=="" (
20-
REM No arguments provided, set default argument
21-
set args=build
22-
) else (
23-
REM Use provided arguments
24-
set args=%*
25-
)
26-
27-
REM Invoke the PowerShell wrapper with all passed arguments
28-
"%PS_CMD%" -NoProfile -ExecutionPolicy Bypass -File "%SCRIPT_DIR%build.ps1" %args%
29-
30-
endlocal
8+
endlocal

build.ps1

Lines changed: 0 additions & 97 deletions
This file was deleted.

build.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import sys
2+
3+
import config
4+
5+
if __name__ == '__main__':
6+
host = config.pybite.Host('build.py')
7+
host.load_modules()
8+
9+
if (host.requested_sdk is not None):
10+
print(f'Installing .NET SDK {host.requested_sdk}')
11+
host.install_sdk()
12+
13+
p = host.get_argparser()
14+
args = p.parse_args()
15+
16+
if args.action == 'help':
17+
p.print_help()
18+
sys.exit()
19+
elif args.action == 'bite':
20+
if args.extras and not args.extras[0].startswith('-'):
21+
target, extras = args.extras[0], args.extras[1:]
22+
host.msbuild(target, *extras)
23+
else:
24+
host.msbuild('help', *args.extras)
25+
else:
26+
host.run(args.action, *args.extras)

build.sh

100644100755
Lines changed: 3 additions & 172 deletions
Original file line numberDiff line numberDiff line change
@@ -1,175 +1,6 @@
1-
#!/usr/bin/env sh
2-
set -euo pipefail
3-
IFS=' \t\n'
1+
#!/usr/bin/env bash
42

5-
# -----------------------------------
6-
# Bite Build Engine Helper Script
7-
# -----------------------------------
8-
9-
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
3+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
104
cd "$SCRIPT_DIR"
11-
PROG="$(basename "$0")"
12-
LOCAL_DOTNET="$SCRIPT_DIR/.dotnet"
13-
INSTALL_SCRIPT_URL="https://dot.net/v1/dotnet-install.sh"
14-
15-
# Defaults
16-
LOG_FILE="${LOG_FILE:-$SCRIPT_DIR/${PROG%.sh}.log}"
17-
VERBOSE=0
18-
DRY_RUN=0
19-
20-
# -----------------
21-
# Utility Functions
22-
# -----------------
23-
echo_and_log() {
24-
echo "$*" | tee -a "$LOG_FILE"
25-
}
26-
27-
log() {
28-
echo "[$(date +'%Y-%m-%dT%H:%M:%S%z')] $*" >> "$LOG_FILE"
29-
[ "$VERBOSE" -eq 1 ] && echo "$*"
30-
}
31-
32-
debug_setup() {
33-
touch "$LOG_FILE"
34-
[ "$VERBOSE" -eq 1 ] && set -x
35-
trap 'err=$?; echo_and_log "[ERROR] line $LINENO exit $err"; exit $err' ERR
36-
}
37-
38-
usage() {
39-
cat <<EOF
40-
Usage: $PROG [options] <command> [additional dotnet args]
41-
42-
Options:
43-
-v, --verbose Enable verbose tracing
44-
-n, --dry-run Preview commands without executing
45-
--log-file <path> Write logs to specified file (default: $LOG_FILE)
46-
47-
Commands:
48-
build Build the solution (default)
49-
test Run unit tests in the solution
50-
restore Restore NuGet packages for the solution
51-
clean Clean outputs for the solution
52-
bite [target] Invoke bite target
53-
help Show this message and exit
54-
55-
Example:
56-
$PROG build -c Release
57-
EOF
58-
exit 0
59-
}
60-
61-
parse_global_flags() {
62-
while [ $# -gt 0 ]; do
63-
case "$1" in
64-
-v|--verbose)
65-
VERBOSE=1; shift ;;
66-
-n|--dry-run)
67-
DRY_RUN=1; shift ;;
68-
--log-file)
69-
LOG_FILE="$2"; shift 2 ;;
70-
--)
71-
shift; break ;;
72-
-h|--help)
73-
usage ;;
74-
-*?)
75-
echo "Unknown option: $1" >&2; usage ;;
76-
*)
77-
break ;;
78-
esac
79-
done
80-
}
81-
82-
run_cmd() {
83-
cmd_str="$*"
84-
log "CMD: $cmd_str"
85-
if [ "$DRY_RUN" -eq 1 ]; then
86-
echo "[DRY-RUN] $cmd_str"
87-
else
88-
eval "$cmd_str"
89-
fi
90-
}
91-
92-
# -------------------
93-
# SDK Installation
94-
# -------------------
95-
install_local_sdk() {
96-
version="$1"
97-
log "Installing SDK $version locally"
98-
mkdir -p "$LOCAL_DOTNET"
99-
curl -sSL "$INSTALL_SCRIPT_URL" -o "$LOCAL_DOTNET/dotnet-install.sh"
100-
run_cmd sh "$LOCAL_DOTNET/dotnet-install.sh" --version "$version" --install-dir "$LOCAL_DOTNET"
101-
DOTNET_CMD="$LOCAL_DOTNET/dotnet"
102-
export DOTNET_ROOT="$LOCAL_DOTNET"
103-
export PATH="$DOTNET_ROOT:$PATH"
104-
}
105-
106-
get_required_sdk() {
107-
if [ -f global.json ]; then
108-
grep -m1 '"version"' global.json | sed -E 's/.*"([0-9]+\.[0-9]+\.[0-9]+)".*/\1/'
109-
else
110-
echo "latest"
111-
fi
112-
}
113-
114-
ensure_dotnet() {
115-
required_sdk=$(get_required_sdk)
116-
if command -v dotnet >/dev/null 2>&1; then
117-
DOTNET_CMD="dotnet"
118-
log "Found system dotnet"
119-
if [ "$required_sdk" != "latest" ] && ! "$DOTNET_CMD" --list-sdks | awk '{print \$1}' | grep -q "^${required_sdk}"; then
120-
install_local_sdk "$required_sdk"
121-
fi
122-
else
123-
log "System dotnet not found, installing $required_sdk"
124-
install_local_sdk "$required_sdk"
125-
fi
126-
}
127-
128-
# -------------------
129-
# Solution Detection
130-
# -------------------
131-
detect_solution() {
132-
mapfile -t slns < <(find . -maxdepth 1 -type f -name '*.sln')
133-
case "${#slns[@]}" in
134-
1) SOLUTION="${slns[0]}" ;;
135-
0) echo_and_log "ERROR: no .sln file found"; exit 1 ;;
136-
*) echo_and_log "ERROR: multiple .sln files found: ${slns[*]}"; exit 1 ;;
137-
esac
138-
log "Using solution: $SOLUTION"
139-
}
140-
141-
# -------------------
142-
# Main Dispatch
143-
# -------------------
144-
main() {
145-
parse_global_flags "$@"
146-
debug_setup
147-
log "Started $PROG with args: $*"
148-
149-
ensure_dotnet
150-
detect_solution
151-
152-
if [ $# -eq 0 ]; then
153-
set -- build -- "$SOLUTION"
154-
fi
155-
156-
command="$1"; shift
157-
158-
case "$command" in
159-
restore|clean|build|test)
160-
run_cmd "$DOTNET_CMD" "$command" "$SOLUTION" "$@" ;;
161-
bite)
162-
if [ $# -gt 0 ] && case "$1" in -*) false;; /*) false;; *) true;; esac; then
163-
target="$1"; shift
164-
else
165-
target=help
166-
fi
167-
run_cmd "$DOTNET_CMD" msbuild -t:"$target" "bite.proj" "$@" ;;
168-
help|-h|--help)
169-
usage ;;
170-
*)
171-
echo "Unknown command: $command" >&2; usage ;;
172-
esac
173-
}
1745

175-
main "$@"
6+
exec python3 "$SCRIPT_DIR/build.py" "$@"

config.py

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

tools/pybite/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from .host import Host
2+
from .global_json import GlobalJson
3+
4+
__all__ = [
5+
'Host',
6+
'GlobalJson'
7+
]

tools/pybite/global_json.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import json, os
2+
3+
class GlobalJson:
4+
"""
5+
Represents the contents of a global.json file.
6+
"""
7+
def __init__(self, path=None):
8+
self.path = path
9+
self._load()
10+
11+
def _load(self):
12+
if not os.path.isfile(self.path):
13+
raise FileNotFoundError(self.path)
14+
with open(self.path, 'r', encoding='utf-8') as f:
15+
data = json.load(f)
16+
self.sdk = data.get('sdk', {})
17+
18+
@property
19+
def version(self):
20+
return self.sdk.get('version')
21+
22+
@property
23+
def roll_forward(self):
24+
return self.sdk.get('rollForward', 'patch')
25+
26+
@property
27+
def allow_prerelease(self):
28+
return self.sdk.get('allowPrerelease', False)
29+
30+
def is_compatible(self, given_version: str) -> bool:
31+
parts = given_version.split('-')[0].split('.')
32+
nums = [int(p) if p.isdigit() else 0 for p in parts]
33+
req = tuple(int(p) for p in self.version.split('.')) if self.version else (0, 0, 0)
34+
35+
if req[0] == 0:
36+
return True
37+
38+
if not self.allow_prerelease and '-' in given_version:
39+
return False
40+
41+
rf = self.roll_forward.lower()
42+
if rf == 'patch':
43+
return nums[:2] == req[:2] and nums[2] >= req[2]
44+
elif rf in ('feature', 'latestfeature', 'minor', 'latestminor'):
45+
return nums[0] == req[0] and (nums[1] > req[1] or (nums[1] == req[1] and nums[2] >= req[2]))
46+
elif rf in ('major', 'latestmajor'):
47+
return (nums[0] > req[0] or (nums[0] == req[0] and (nums[1] > req[1] or (nums[1] == req[1] and nums[2] >= req[2]))))
48+
return nums == req

0 commit comments

Comments
 (0)