Skip to content

Commit 2490477

Browse files
author
Justin Boswell
authored
Fixes found porting java/cpp/python (#14)
* Removed toolchain dependence on env, and simplified arg parsing, always work from source_dir * Make sure docker images are re-generated for every PR to builder * base dirs off source_dir, not launch_dir * Fixed config mashing * Added output when selecting git branch * Reduced redundant cmake args * Removed redundant cmake_binaries option * Ensure that download script in docker images busts the cloudfront cache * Added util to API * Moved libcrypto to using python for download/tarball * Prevent GC from destroying our late loaded classes * Fixed env in cross-compile container * All S3 uploads have .pyz extension * Containers must download with .pyz extension * Scoped build_env, pre_build_env, and post_build_env to just those steps
1 parent 4fc2c78 commit 2490477

File tree

20 files changed

+427
-325
lines changed

20 files changed

+427
-325
lines changed

.github/docker-images/entrypoint.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ if [ "${args[0]}" == "--version=*" ]; then
1111
fi
1212

1313
# download the version of builder requested
14-
curl -sSL -o /usr/local/bin/builder.pyz --retry 3 --retry-delay 3 --retry-max-time 30 https://d19elf31gohf1l.cloudfront.net/${version}/builder
14+
curl -sSL -o /usr/local/bin/builder.pyz --retry 3 --retry-delay 3 --retry-max-time 30 https://d19elf31gohf1l.cloudfront.net/${version}/builder.pyz?date=`date +%s`
1515
builder=/usr/local/bin/builder.pyz
1616
chmod a+x $builder
1717

.github/docker-images/ubuntu-16-x64/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ RUN apt-get update -qq \
2323
###############################################################################
2424
# Python/AWS CLI
2525
###############################################################################
26-
RUN python3 -m pip install --upgrade pip setuptools virtualenv \
26+
RUN python3 -m pip install --upgrade pip setuptools \
2727
&& python3 -m pip install --upgrade awscli \
2828
&& aws --version
2929

.github/workflows/docker-images.yml

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,11 @@ on:
1010
- '.github/docker-images/entrypoint.sh'
1111
- '.github/workflows/docker-images.yml'
1212
- '.github/workflows/*.sh'
13+
# need images created at least once per branch, even if there are no docker changes
14+
# so that downstream projects can use the branch channel
15+
pull_request:
16+
types: [opened]
17+
# Make new images for every published release
1318
release:
1419
types: [published]
1520

@@ -82,7 +87,7 @@ jobs:
8287
run: |
8388
mkdir -p build
8489
python3 -m zipapp --python="/usr/bin/env python3" --output=build/builder builder
85-
aws s3 cp build/builder s3://$AWS_S3_BUCKET/${{ steps.release.outputs.release_tag }}/builder
90+
aws s3 cp build/builder s3://$AWS_S3_BUCKET/${{ steps.release.outputs.release_tag }}/builder.pyz
8691
8792
- name: Artifact builder
8893
uses: actions/upload-artifact@v1
@@ -102,7 +107,7 @@ jobs:
102107
run: |
103108
mkdir -p build
104109
python3 -m zipapp --python="/usr/bin/env python3" --output=build/builder builder
105-
aws s3 cp build/builder s3://$AWS_S3_BUCKET/LATEST/builder
110+
aws s3 cp build/builder s3://$AWS_S3_BUCKET/LATEST/builder.pyz
106111
107112
linux-x86-x64:
108113
name: ${{ matrix.variant }}

README.md

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,16 @@
11
# AWS CRT Builder
22

3-
This is a central repository for build scripts and docker images for the [AWS Common Runtime Team](https://github.com/orgs/awslabs/teams/aws-sdk-common-runtime-team)
3+
This is a central repository for the build tool and docker images for the [AWS Common Runtime Team](https://github.com/orgs/awslabs/teams/aws-sdk-common-runtime-team)
44

5-
## Builder
5+
## Using Builder
66
Builder is bundled into a zipapp. Within a given project using builder, builder.json in the root of the project will provide configuration data.
7-
If you wish to add custom actions or programmatically generate data you can add python scripts in the <root>/.builder/actions directory. All
8-
scripts in this directory will be loaded and scanned for classes.
7+
If you wish to add custom actions or programmatically generate data you can add python scripts in the <root>/.builder/ directory. All
8+
scripts in this directory will be loaded and scanned for subclasses of Project, Import, and Action.
99

1010
### Requirements
11-
* Python 3.4+
11+
* Python 3.5+
1212
* docker (if cross compiling with dockcross)
1313
* CMake 3.1+ (if compiling native code)
14-
* curl (linux only)
15-
* tar (linux only)
1614

1715
### CLI Arguments
1816
Usage: ```builder.pyz [build|inspect|<action-name>] [spec] [OPTIONS]```
@@ -33,29 +31,6 @@ Usage: ```builder.pyz [build|inspect|<action-name>] [spec] [OPTIONS]```
3331
### Example build
3432
```builder.pyz build --project=aws-c-common downstream```
3533

36-
### Example action
37-
```python
38-
import Builder
39-
40-
class MyAction(Builder.Action):
41-
def run(self, env):
42-
print('My Action did the thing')
43-
```
44-
45-
This can be run with ```builder.pyz my-action``` or ```builder.pyz myaction``` or ```builder.pyz MyAction```
46-
47-
See api.py for the available API to actions.
48-
49-
#### Action chaining
50-
The ```run(self, env)``` method of any action can return an Action or list of Actions to run before considering this action complete.
51-
The ```Builder.Script``` class can encapsulate a list of python functions, actions, or shell commands to run. Most compound actions
52-
return ```Builder.Script([additional, commands, to, run])```
53-
54-
#### The Virtual Shell
55-
There is a virtual shell available via ```env.shell```. It abstracts away dry run behavior, and allows for cross-platform implementations
56-
of common shell operations (cd, cwd, pushd, popd, setenv, getenv, pushenv, popenv, where) and the ```exec()``` function for running
57-
arbitrary commands.
58-
5934
## Projects
6035
Each project is represented at minimum by its name, path on disk, and github repo url. If there is no builder.json file in the project, nor
6136
any python scripts to describe it, then this is enough for the builder to at least build the project, assuming it will build with defaults
@@ -78,6 +53,8 @@ that instead. There are a few external dependencies (s2n and libcrypto, for inst
7853
```
7954

8055
#### Detailed config:
56+
NOTE: Any key can be prefixed with a ```!``` to overwrite the config value, rather than to add to it.
57+
8158
```json
8259
{
8360
"name": "my-project",
@@ -128,6 +105,8 @@ that instead. There are a few external dependencies (s2n and libcrypto, for inst
128105
"al2": {},
129106
"al2012": {},
130107
"alpine": {},
108+
"raspbian": {},
109+
"manylinux": {},
131110
"macos": {},
132111
"windows": {}
133112
},
@@ -161,14 +140,47 @@ that instead. There are a few external dependencies (s2n and libcrypto, for inst
161140
// example, disable on clang 3
162141
"3": {
163142
"enabled": false
164-
}
143+
},
165144
}
166145
}
167146
}
168147
}
169148
```
170149

171-
## Docker Images
150+
### Cross-compiling
151+
On linux and macos, builder supports cross compiling via [dockcross](https://github.com/dockcross/dockcross). It installs a small docker
152+
container locally that contains the toolchain and creates a shell to run commands in this environment. Builder then wraps build commands
153+
with that shell.
154+
155+
## Actions
156+
Actions are just arbitrary python code that can be run in place of shell commands. Minimally, Actions derive from ```Builder.Action```
157+
and provide a ```run(self, env)``` method.
158+
159+
### Example action
160+
```python
161+
import Builder
162+
163+
class MyAction(Builder.Action):
164+
def run(self, env):
165+
print('My Action did the thing')
166+
```
167+
168+
This can be run with ```builder.pyz my-action``` or ```builder.pyz myaction``` or ```builder.pyz MyAction```
169+
170+
See api.py for the available API to actions.
171+
172+
#### Action chaining
173+
The ```run(self, env)``` method of any action can return an Action or list of Actions to run before considering this action complete.
174+
The ```Builder.Script``` class can encapsulate a list of python functions, actions, or shell commands to run. Most compound actions
175+
return ```Builder.Script([additional, commands, to, run])```
176+
177+
#### The Virtual Shell
178+
There is a virtual shell available via ```env.shell```. It abstracts away dry run behavior, and allows for cross-platform implementations
179+
of common shell operations (cd, cwd, pushd, popd, setenv, getenv, pushenv, popenv, where) and the ```exec()``` function for running
180+
arbitrary commands.
181+
182+
## Developing on builder
183+
### Docker Images
172184
Each docker image has a script which will fetch the builder app baked into it, and will then call the builder with the arguments provided.
173185
Any push to the .github/docker-images directory will cause a rebuild of all of the docker images (see docker-images.yml). The
174186
image layers are cached, so this should be quick unless you made a fundamental modification. Any push to the builder source, or any

builder/__main__.py

Lines changed: 36 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@ def run_action(action, env):
4343
config = env.config
4444
# Set build environment from config
4545
env.shell.pushenv()
46-
for var, value in config.get('build_env', {}).items():
47-
env.shell.setenv(var, value)
4846
for var, value in getattr(env, 'env', {}).items():
4947
env.shell.setenv(var, value)
5048

@@ -104,53 +102,29 @@ def build_consumers(env):
104102

105103

106104
def default_spec(env):
107-
target, arch = env.platform.split('-')
105+
target, arch = current_platform().split('-')
108106
host = current_host()
109-
compiler, version = Toolchain.default_compiler(env, target, arch)
110-
print('Using Spec:')
111-
print(' Host: {} {}'.format(host, current_arch()))
112-
print(' Target: {} {}'.format(target, arch))
113-
print(' Compiler: {} {}'.format(compiler, version))
107+
compiler, version = Toolchain.default_compiler(target, arch)
114108
return BuildSpec(host=host, compiler=compiler, compiler_version='{}'.format(version), target=target, arch=arch)
115109

116110

117111
def inspect_host(env):
118112
spec = env.spec
119-
toolchain = Toolchain(env, spec=spec)
113+
toolchain = Toolchain(spec=spec)
120114
print('Host Environment:')
121115
print(' Host: {} {}'.format(spec.host, spec.arch))
122116
print(' Default Target: {} {}'.format(spec.target, spec.arch))
123-
compiler_path = toolchain.compiler_path(env)
117+
compiler_path = toolchain.compiler_path()
124118
if not compiler_path:
125119
compiler_path = '(Will Install)'
126120
print(' Compiler: {} (version: {}) {}'.format(
127121
spec.compiler, toolchain.compiler_version, compiler_path))
128122
compilers = ['{} {}'.format(c[0], c[1])
129-
for c in Toolchain.all_compilers(env)]
123+
for c in Toolchain.all_compilers()]
130124
print(' Available Compilers: {}'.format(', '.join(compilers)))
131125
print(' Available Projects: {}'.format(', '.join(Project.projects())))
132126

133127

134-
def parse_extra_args(env):
135-
args = getattr(env.args, 'args', [])
136-
parser = argparse.ArgumentParser()
137-
parser.add_argument('--compiler', type=str,
138-
help="The compiler to use for this build")
139-
parser.add_argument(
140-
'--target', type=str, help="The target to cross-compile for (e.g. android, linux-x86, aarch64)")
141-
# parse the args we know, pass the rest on to actions to figure out
142-
args, env.args.args = parser.parse_known_args(args)
143-
144-
if args.compiler or args.target:
145-
compiler, version = (None, None)
146-
if args.compiler:
147-
compiler, version = args.compiler.split('-')
148-
spec = str(env.spec) if hasattr(
149-
env, 'spec') else getattr(env.args, 'spec', None)
150-
env.spec = BuildSpec(compiler=compiler,
151-
compiler_version=version, target=args.target, spec=spec)
152-
153-
154128
def parse_args():
155129
parser = argparse.ArgumentParser()
156130
parser.add_argument('-d', '--dry-run', action='store_true',
@@ -165,10 +139,12 @@ def parse_args():
165139
parser.add_argument('--build-dir', type=str,
166140
help='Directory to work in', default='.')
167141
parser.add_argument('-b', '--branch', help='Branch to build from')
168-
parser.add_argument(
169-
'--platform', help='Target platform to compile/cross-compile for', default='{}-{}'.format(current_os(), current_arch()),
170-
choices=data.PLATFORMS)
171142
parser.add_argument('--cli_config', action='append', type=list)
143+
parser.add_argument('--compiler', type=str,
144+
help="The compiler to use for this build")
145+
parser.add_argument('--target', type=str, help="The target to cross-compile for (e.g. android-armv7, linux-x86, linux-aarch64)",
146+
default='{}-{}'.format(current_os(), current_arch()),
147+
choices=data.PLATFORMS)
172148
parser.add_argument('args', nargs=argparse.REMAINDER)
173149

174150
# hand parse command and spec from within the args given
@@ -201,19 +177,33 @@ def parse_args():
201177
args, extras = parser.parse_known_args(argv)
202178
args.command = command
203179
args.cli_config = cli_config
204-
if not args.spec:
205-
args.spec = spec
206-
args.args += extras
180+
args.spec = args.spec if args.spec else spec
207181
# Backwards compat for `builder run $action`
208182
if args.command == 'run':
209183
args.command = args.spec
210184
args.spec = None
211185

212-
return args
186+
if args.spec:
187+
spec = BuildSpec(spec=args.spec, target=args.target)
188+
189+
if args.compiler or args.target:
190+
compiler, version = (None, None)
191+
if args.compiler:
192+
compiler, version = args.compiler.split('-')
193+
spec = str(spec) if spec else None
194+
spec = BuildSpec(compiler=compiler,
195+
compiler_version=version, target=args.target, spec=spec)
196+
197+
if not spec:
198+
spec = default_spec()
199+
# Save unknown args for actions to parse later
200+
args.args += extras
201+
202+
return args, spec
213203

214204

215205
if __name__ == '__main__':
216-
args = parse_args()
206+
args, spec = parse_args()
217207

218208
if args.build_dir != '.':
219209
if not os.path.isdir(args.build_dir):
@@ -226,19 +216,10 @@ def parse_args():
226216
'args': args,
227217
'project': args.project,
228218
'branch': args.branch,
229-
'platform': args.platform,
219+
'spec': spec,
230220
})
231221

232-
parse_extra_args(env)
233-
234-
if not getattr(env, 'spec', None):
235-
build_name = getattr(args, 'spec', getattr(args, 'build', None))
236-
if build_name:
237-
env.spec = BuildSpec(spec=build_name, platform=env.platform)
238-
else:
239-
env.spec = default_spec(env)
240-
241-
if env.platform == current_platform():
222+
if env.spec.target == current_os() and env.spec.arch == current_arch():
242223
inspect_host(env)
243224
if args.command == 'inspect':
244225
sys.exit(0)
@@ -247,20 +228,16 @@ def parse_args():
247228
print('No project specified and no project found in current directory')
248229
sys.exit(1)
249230

250-
# Build the config object
251-
env.config = env.project.get_config(
252-
env.spec,
253-
env.args.cli_config,
254-
source_dir=env.source_dir,
255-
build_dir=env.build_dir,
256-
install_dir=env.install_dir,
257-
project_dir=env.project.path)
231+
print('Using Spec:')
232+
print(' Host: {} {}'.format(spec.host, current_arch()))
233+
print(' Target: {} {}'.format(spec.target, spec.arch))
234+
print(' Compiler: {} {}'.format(spec.compiler, spec.compiler_version))
258235

259236
if not env.config.get('enabled', True):
260237
raise Exception("The project is disabled in this configuration")
261238

262239
if env.config.get('needs_compiler', True):
263-
env.toolchain = Toolchain(env, spec=env.spec)
240+
env.toolchain = Toolchain(spec=env.spec)
264241

265242
if args.dump_config:
266243
from pprint import pprint

0 commit comments

Comments
 (0)