Skip to content

Commit a5923cc

Browse files
committed
feat: add support for multiple astyle versions; add 3.4.7
1 parent 832d452 commit a5923cc

21 files changed

+205
-26
lines changed

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
tmp
22
dist
3-
build
43
*.egg-info
54
__pycache__
65
.venv
76
.idea
7+
.pytest_cache

CONTRIBUTING.md

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,23 @@ Contributions in the form of pull requests, issue reports, and feature requests
66

77
Please do the following before submitting a PR:
88

9-
- [ ] Install pre-commit hooks: `pip install pre-commit && pre-commit install`
109
- [ ] Create a virtual environment: `python3 -m venv .venv` and activate it: `. .venv/bin/activate`
1110
- [ ] Install testing prerequisites: `pip install -e ".[dev]"`
11+
- [ ] Install pre-commit hooks: `pre-commit install`
1212
- [ ] Run the tests: `pytest`
1313

1414
## Building Astyle
1515

16-
This Python package uses Astyle compiled into WebAssembly ([astyle_py/libastyle.wasm](astyle_py/libastyle.wasm)). To rebuild libastyle.wasm, run `build.sh` in the `builder/` directory.
16+
This Python package uses Astyle compiled into WebAssembly (astyle_py/lib/<VERSION>/libastyle.wasm). To build libastyle.wasm for a new Astyle version, run `build.sh <VERSION>` in the `build-scripts/3.3+` directory. For example, to build the library for Astyle 3.4.7:
1717

18-
Updating Astyle to a newer version might require tweaking the patch applied in [builder/build_inner.sh](builder/build_inner.sh).
18+
```bash
19+
cd build-scripts/3.3+
20+
./build.sh 3.4.7
21+
```
22+
23+
You need Docker installed to do the build.
24+
25+
A build script for the older Astyle 3.1 is also provided in `build-scripts/3.1`.
1926

2027
## Tagging a new release
2128

README.md

+22-4
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ The main reason to use this Python wrapper, rather than native `astyle` binaries
1919
rev: v1.0.0
2020
hooks:
2121
- id: astyle_py
22-
args: [--style=linux]
22+
args: [--astyle-version 3.4.7 --style=linux]
2323
```
2424
2525
Place the required astyle formatting options to the `args` array. See the next section for details.
@@ -48,6 +48,7 @@ astyle_py [options] <files to format>
4848
### Common options
4949

5050
* `--version` — print the version and exit.
51+
* `--astyle-version <VER>` — choose the version of Astyle to use.
5152
* `--quiet` — don't print diagnostic messages; by default, the list of files which are formatted is printed to `stderr`.
5253
* `--dry-run` — don't format the files, only check the formatting. Returns non-zero exit code if any file would change after formatting.
5354

@@ -86,6 +87,8 @@ Here is an example of a rules file:
8687
```yml
8788
8889
DEFAULT:
90+
# Version of Astyle to use
91+
version: "3.4.7"
8992
# These formatting options will be used by default
9093
options: "--style=otbs --indent=spaces=4 --convert-tabs"
9194
@@ -104,14 +107,27 @@ code_to_ignore_for_now:
104107
- "tests/" # matches a subdirectory 'tests' anywhere in the source tree
105108
```
106109

107-
## Implementation notes
110+
## Supported Astyle versions
111+
112+
This python wrapper bundles multiple copies of Astyle, you can choose which one to use:
113+
- In the CLI: via `--astyle-version` argument
114+
- If you are using a rules file: using `version` key
115+
- When using astyle_py as a library: by passing the version to `Astyle()` constructor
116+
117+
The following versions are supported:
108118

119+
- 3.1 — used by default, unless a different version is specified
120+
- 3.4.7
121+
122+
## Implementation notes
109123

110124
To simplify distribution of astyle, it is compiled to WebAssembly ([astyle_py/libastyle.wasm](astyle_py/libastyle.wasm)) and executed using [wasmtime runtime](https://github.com/bytecodealliance/wasmtime) via its [Python bindings](https://github.com/bytecodealliance/wasmtime-py). This package should work on all operating systems supported by wasmtime — at the time of writing these are:
111125
- x86_64 (amd64) Windows, Linux, macOS
112126
- aarch64 (arm64) Linux and macOS
113127

114-
There is another project which wraps astyle into a Python package, without using WebAssembly: https://github.com/timonwong/pyastyle. At the time of writing, it is unmaintained.
128+
Other project which wraps astyle into a Python package include:
129+
- https://github.com/timonwong/pyastyle — unmaintained at the time of writing, uses native Astyle binaries
130+
- https://github.com/Freed-Wu/astyle-wheel/ — actively maintained, uses native Astyle binaries
115131

116132
## Contributing
117133

@@ -120,4 +136,6 @@ Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.
120136
## Copyright and License
121137

122138
* The source code in this repository is Copyright (c) 2020-2022 Ivan Grokhotkov and licensed under the [MIT license](LICENSE).
123-
* `libastyle.wasm` binary bundled herein is built from Artistic Style project, Copyright (c) 2018 by Jim Pattee <[email protected]>, also licensed under the MIT license. See http://astyle.sourceforge.net/ for details.
139+
* `libastyle.wasm` binaries bundled under [astyle_py/lib](astyle_py/lib) directory are built from [Artistic Style project](https://gitlab.com/saalen/astyle), Copyright (c) 2018 by Jim Pattee <[email protected]>, also licensed under the MIT license. See http://astyle.sourceforge.net/ for details.
140+
141+
Thanks to André Simon for maintaining Astyle project!

astyle_py/__main__.py

+11-6
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,27 @@
44

55
from . import __version__
66
from .args import parse_args
7-
from .astyle_wrapper import Astyle
7+
from .astyle_wrapper import ASTYLE_COMPAT_VERSION, Astyle
88
from .files_iter import iterate_files
99

1010

1111
def main():
12-
if '--version' in sys.argv:
13-
print('astyle_py v{} with astyle v{}'.format(__version__, Astyle().version()))
14-
raise SystemExit(0)
15-
1612
try:
1713
args = parse_args(sys.argv[1:])
1814
except ValueError as e:
1915
print(str(e), file=sys.stderr)
2016
raise SystemExit(1)
2117

22-
astyle = Astyle()
18+
if args.astyle_version:
19+
astyle_version = args.astyle_version
20+
else:
21+
astyle_version = ASTYLE_COMPAT_VERSION
22+
23+
astyle = Astyle(version=astyle_version)
24+
25+
if args.version:
26+
print('astyle-py {} with Astyle v{}'.format(__version__, astyle.version()))
27+
raise SystemExit(0)
2328

2429
def diag(*args_):
2530
if not args.quiet:

astyle_py/args.py

+23-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,16 @@
66

77
AstyleArgs = namedtuple(
88
'AstyleArgs',
9-
['rules', 'options', 'files', 'exclude_list', 'fix_formatting', 'quiet'],
9+
[
10+
'rules',
11+
'options',
12+
'files',
13+
'exclude_list',
14+
'fix_formatting',
15+
'quiet',
16+
'version',
17+
'astyle_version',
18+
],
1019
)
1120

1221

@@ -25,6 +34,8 @@ def parse_args(args) -> AstyleArgs:
2534
quiet = False
2635
rules = None
2736
options_to_remove = []
37+
version = False
38+
astyle_version = None
2839

2940
for o in options:
3041
o_trimmed = o[2:] if o.startswith('--') else o
@@ -64,6 +75,15 @@ def ensure_value():
6475
ensure_value()
6576
rules = value
6677

78+
elif opt == 'version':
79+
options_to_remove.append(o)
80+
version = True
81+
82+
elif opt == 'astyle-version':
83+
options_to_remove.append(o)
84+
ensure_value()
85+
astyle_version = value
86+
6787
for o in options_to_remove:
6888
options.remove(o)
6989

@@ -81,4 +101,6 @@ def ensure_value():
81101
exclude_list=exclude_list,
82102
fix_formatting=fix_formatting,
83103
quiet=quiet,
104+
version=version,
105+
astyle_version=astyle_version,
84106
)

astyle_py/astyle_wrapper.py

+15-2
Original file line numberDiff line numberDiff line change
@@ -78,14 +78,27 @@ def from_addr(context: WasmContext, addr: int) -> 'WasmString':
7878
return WasmString(context, addr, n_bytes, string)
7979

8080

81+
ASTYLE_COMPAT_VERSION = '3.1'
82+
ASTYLE_SUPPORTED_VERSIONS = os.listdir(os.path.join(os.path.dirname(__file__), 'lib'))
83+
84+
8185
class Astyle:
82-
def __init__(self):
86+
def __init__(self, version: str = ASTYLE_COMPAT_VERSION):
87+
if version not in ASTYLE_SUPPORTED_VERSIONS:
88+
raise ValueError(
89+
'Unsupported astyle version: {}. Available versions: {}'.format(
90+
version, ', '.join(ASTYLE_SUPPORTED_VERSIONS)
91+
)
92+
)
93+
8394
self.context = WasmContext()
8495
err_handler_type = FuncType([ValType.i32(), ValType.i32()], [])
8596
err_handler_func = Func(self.context.store, err_handler_type, self._err_handler)
8697
self.context.linker.define('env', 'AStyleErrorHandler', err_handler_func)
8798

88-
wasm_file = os.path.join(os.path.dirname(__file__), 'libastyle.wasm')
99+
wasm_file = os.path.join(
100+
os.path.dirname(__file__), 'lib', version, 'libastyle.wasm'
101+
)
89102
module = Module.from_file(self.context.store.engine, wasm_file)
90103
self.context.inst = self.context.linker.instantiate(self.context.store, module)
91104
self.context.call_func('_initialize')

astyle_py/files_iter.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from .utils import pattern_to_regex
1212

1313
FileItem = namedtuple('FileItem', ['filename', 'astyle_options'])
14-
Rule = namedtuple('Rule', ['check', 'include', 'options'])
14+
Rule = namedtuple('Rule', ['check', 'include', 'options', 'version'])
1515

1616

1717
def file_matches_patterns(fname: str, patterns: List[Pattern]) -> bool:
@@ -52,7 +52,9 @@ def iterate_files_rules(
5252
rules_dict = yaml.safe_load(rf)
5353

5454
# set the default rule
55-
default_rule = Rule(check=True, include=[pattern_to_regex('*')], options=[])
55+
default_rule = Rule(
56+
check=True, include=[pattern_to_regex('*')], options=[], version=None
57+
)
5658

5759
# if 'DEFAULT' key is in the YAML file, use it to update the default rule
5860
default_rule_dict = rules_dict.get('DEFAULT')
@@ -77,6 +79,7 @@ def get_rule_from_dict(rule_name: str, rule_dict, defaults: Rule) -> Rule:
7779
options = defaults.options
7880
check = defaults.check
7981
include = defaults.include
82+
version = defaults.version
8083

8184
for k, v in rule_dict.items():
8285
if k == 'options':
@@ -97,12 +100,18 @@ def get_rule_from_dict(rule_name: str, rule_dict, defaults: Rule) -> Rule:
97100
f'Unexpected value of \'include\' in rule {rule_name}, expected a list of strings, found {v}'
98101
)
99102
include = [re.compile(pattern_to_regex(x)) for x in v]
103+
elif k == 'version':
104+
if not isinstance(v, str):
105+
raise ValueError(
106+
f'Unexpected value of \'version\' in rule {rule_name}, expected a string, found {v}'
107+
)
108+
version = v
100109
else:
101110
raise ValueError(
102111
f'Unexpected key \'{k}\' in rule {rule_name}, expected one of: options, check, include'
103112
)
104113

105-
return Rule(check, include, options)
114+
return Rule(check, include, options, version)
106115

107116

108117
def process_rules_for_file(
@@ -113,4 +122,8 @@ def process_rules_for_file(
113122
if file_matches_patterns(fname, rule.include):
114123
selected_rule = rule
115124
if selected_rule.check:
116-
yield FileItem(filename=fname, astyle_options=selected_rule.options)
125+
options = []
126+
if selected_rule.version:
127+
options.append(f'--astyle-version={selected_rule.version}')
128+
options += selected_rule.options
129+
yield FileItem(filename=fname, astyle_options=options)

astyle_py/lib/3.1/libastyle.wasm

377 KB
Binary file not shown.

astyle_py/lib/3.4.7/libastyle.wasm

383 KB
Binary file not shown.

astyle_py/libastyle.wasm

-386 KB
Binary file not shown.

build-scripts/3.1/README.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Script to build WASM library for Astyle 3.1.
2+
3+
Run:
4+
5+
```bash
6+
./build.sh
7+
```
8+
9+
This should build the docker image with Emscripten SDK, download Astyle and build it with Emscripten. The resulting library will be copied to `astyle_py/lib/3.1/libastyle.wasm`.

builder/build.sh renamed to build-scripts/3.1/build.sh

+3-2
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@
22
set -euo pipefail
33

44
TMP_DIR=$PWD/../tmp
5-
DST_DIR=${PWD}/../astyle_py
5+
DST_DIR=${PWD}/../../astyle_py/lib/3.1
6+
DOCKER_DIR=${PWD}/../docker
67

7-
docker build -t emsdk .
8+
docker build -t emsdk ${DOCKER_DIR}
89
mkdir -p ${TMP_DIR}
910
cp build_inner.sh ${TMP_DIR}
1011
docker run --rm -v ${TMP_DIR}:/work -w /work emsdk ./build_inner.sh

builder/build_inner.sh renamed to build-scripts/3.1/build_inner.sh

+1
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ emmake make static
4747
emcc -o bin/libastyle.wasm \
4848
-s EXPORTED_FUNCTIONS=["_AStyleGetVersion","_AStyleWrapper","_malloc","_free"] \
4949
-s STANDALONE_WASM=1 \
50+
-s ERROR_ON_UNDEFINED_SYMBOLS=0 \
5051
--no-entry \
5152
bin/libastyle.a
5253

build-scripts/3.3+/CMakeLists.txt

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
cmake_minimum_required(VERSION 3.15)
2+
3+
if(NOT DEFINED ENV{EMSDK_PATH})
4+
message(FATAL_ERROR "EMSDK_PATH environment variable not set")
5+
endif()
6+
7+
if(NOT DEFINED ASTYLE_VER)
8+
message(FATAL_ERROR "ASTYLE_VER cache variable not set")
9+
endif()
10+
11+
set(CMAKE_TOOLCHAIN_FILE $ENV{EMSDK_PATH}/upstream/emscripten/cmake/Modules/Platform/Emscripten.cmake)
12+
13+
set(BUILD_STATIC_LIBS 1 CACHE BOOL "Build static libraries")
14+
15+
project(astyle-wasm)
16+
17+
include(FetchContent)
18+
FetchContent_Declare(
19+
astyle
20+
GIT_REPOSITORY https://gitlab.com/saalen/astyle.git
21+
GIT_TAG ${ASTYLE_VER}
22+
)
23+
FetchContent_MakeAvailable(astyle)
24+
25+
26+
add_executable(astyle-wasm wrapper.c)
27+
target_link_libraries(astyle-wasm PRIVATE astyle)
28+
29+
target_link_libraries(astyle-wasm PRIVATE "-s EXPORTED_FUNCTIONS=[\"_AStyleGetVersion\",\"_AStyleWrapper\",\"_malloc\",\"_free\",\"_AStyleErrorHandler\"]")
30+
target_link_libraries(astyle-wasm PRIVATE "-s STANDALONE_WASM=1")
31+
target_link_libraries(astyle-wasm PRIVATE "-s ERROR_ON_UNDEFINED_SYMBOLS=0")
32+
target_link_libraries(astyle-wasm PRIVATE "-s WARN_ON_UNDEFINED_SYMBOLS=1")
33+
target_link_libraries(astyle-wasm PRIVATE --no-entry)
34+
target_link_libraries(astyle-wasm PRIVATE -Wl,--import-undefined)

build-scripts/3.3+/README.md

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
Build script for Astyle 3.3 and later.
2+
3+
Run:
4+
5+
```
6+
./build.sh <version>
7+
```
8+
9+
where `<version>` is the version of Astyle you want to build. For example, `3.4.7`.
10+
11+
The script will build a Docker image with Emscripten SDK, download the required version of Astyle and build it with Emscripten. Since Astyle >=3.3 has a CMake build system, everything is conveniently handled in CMakeLists.txt.
12+
13+
The resulting library will be copied to `astyle_py/lib/<version>/libastyle.wasm`.

build-scripts/3.3+/build.sh

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#!/bin/bash
2+
set -euo pipefail
3+
4+
# argument is the version number
5+
if [ $# -ne 1 ]; then
6+
echo "Usage: $0 <astyle_version>"
7+
exit 1
8+
fi
9+
10+
ASTYLE_VERSION=$1
11+
12+
DOCKER_DIR=${PWD}/../docker
13+
docker build -t emsdk ${DOCKER_DIR}
14+
15+
TMP_DIR=${PWD}/../tmp/${ASTYLE_VERSION}
16+
mkdir -p ${TMP_DIR}
17+
DST_DIR=${PWD}/../../astyle_py/lib/${ASTYLE_VERSION}
18+
mkdir -p ${DST_DIR}
19+
20+
docker run --rm -v ${PWD}:/src -v ${TMP_DIR}:/build emsdk bash -c "cmake -S /src -B /build -DASTYLE_VER=${ASTYLE_VERSION} && cmake --build /build"
21+
cp ${TMP_DIR}/astyle-wasm.wasm ${DST_DIR}/libastyle.wasm

build-scripts/3.3+/wrapper.c

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include <stdlib.h>
2+
void AStyleErrorHandler(int errorNumber, const char* errorMessage);
3+
char* AStyleMain(const char* pSourceIn, const char* pOptions, void (*fpError)(int, const char*), void* (*fpAlloc)(unsigned long));
4+
5+
char* AStyleWrapper(const char* pSourceIn, const char* pOptions)
6+
{
7+
return AStyleMain(pSourceIn, pOptions, &AStyleErrorHandler, malloc);
8+
}

0 commit comments

Comments
 (0)