PyProd 0.3.0__py3-none-any.whl → 0.4.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- pyprod/main.py +19 -7
- pyprod/prod.py +168 -104
- pyprod/utils.py +2 -2
- pyprod/venv.py +3 -3
- {pyprod-0.3.0.dist-info → pyprod-0.4.0.dist-info}/METADATA +12 -6
- pyprod-0.4.0.dist-info/RECORD +11 -0
- pyprod-0.3.0.dist-info/RECORD +0 -11
- {pyprod-0.3.0.dist-info → pyprod-0.4.0.dist-info}/WHEEL +0 -0
- {pyprod-0.3.0.dist-info → pyprod-0.4.0.dist-info}/entry_points.txt +0 -0
- {pyprod-0.3.0.dist-info → pyprod-0.4.0.dist-info}/licenses/LICENSE +0 -0
pyprod/main.py
CHANGED
@@ -14,6 +14,13 @@ parser = argparse.ArgumentParser(
|
|
14
14
|
description="""PyProd - More makable than make""",
|
15
15
|
)
|
16
16
|
|
17
|
+
parser.add_argument(
|
18
|
+
"-C",
|
19
|
+
"--directory",
|
20
|
+
dest="directory",
|
21
|
+
help="Change to DIRECTORY before performing any operations",
|
22
|
+
)
|
23
|
+
|
17
24
|
parser.add_argument(
|
18
25
|
"-f", "--file", help="Use FILE as the Prodfile (default: 'PRODFILE.py')"
|
19
26
|
)
|
@@ -27,12 +34,8 @@ parser.add_argument(
|
|
27
34
|
)
|
28
35
|
|
29
36
|
parser.add_argument(
|
30
|
-
"-
|
31
|
-
"--directory",
|
32
|
-
dest="directory",
|
33
|
-
help="Change to DIRECTORY before performing any operations",
|
37
|
+
"-r", "--rebuild", dest="rebuild", action="store_true", help="Rebuild all"
|
34
38
|
)
|
35
|
-
|
36
39
|
parser.add_argument(
|
37
40
|
"-v",
|
38
41
|
dest="verbose",
|
@@ -55,8 +58,13 @@ def print_exc(e):
|
|
55
58
|
logger.exception("Terminated by exception")
|
56
59
|
|
57
60
|
|
61
|
+
def init_args(args=None):
|
62
|
+
args = pyprod.args = parser.parse_args(args)
|
63
|
+
return args
|
64
|
+
|
65
|
+
|
58
66
|
def main():
|
59
|
-
args =
|
67
|
+
args = init_args()
|
60
68
|
pyprod.verbose = args.verbose
|
61
69
|
chdir = args.directory
|
62
70
|
if chdir:
|
@@ -89,6 +97,7 @@ def main():
|
|
89
97
|
|
90
98
|
params = {}
|
91
99
|
targets = []
|
100
|
+
|
92
101
|
for target in args.targets:
|
93
102
|
if "=" in target:
|
94
103
|
name, value = target.split("=", 1)
|
@@ -107,7 +116,10 @@ def main():
|
|
107
116
|
sys.exit("No default target")
|
108
117
|
targets = [target]
|
109
118
|
|
110
|
-
ret =
|
119
|
+
ret = 0
|
120
|
+
for target in targets:
|
121
|
+
ret += asyncio.run(prod.start([target]))
|
122
|
+
|
111
123
|
if not ret:
|
112
124
|
print(f"Nothing to be done for {targets}")
|
113
125
|
|
pyprod/prod.py
CHANGED
@@ -37,6 +37,10 @@ class RuleError(Exception):
|
|
37
37
|
pass
|
38
38
|
|
39
39
|
|
40
|
+
class TargetError(Exception):
|
41
|
+
pass
|
42
|
+
|
43
|
+
|
40
44
|
class HandledExceptionError(Exception):
|
41
45
|
pass
|
42
46
|
|
@@ -109,7 +113,7 @@ def glob(path, dir="."):
|
|
109
113
|
|
110
114
|
def rule_to_re(rule):
|
111
115
|
if not isinstance(rule, (str, Path)):
|
112
|
-
raise
|
116
|
+
raise TypeError(f"str or Path required: {rule}")
|
113
117
|
|
114
118
|
srule = str(rule)
|
115
119
|
srule = translate(srule)
|
@@ -156,47 +160,48 @@ def _check_pattern(pattern):
|
|
156
160
|
raise RuleError(f"{pattern}: Pattern should contain a '%'.")
|
157
161
|
|
158
162
|
|
159
|
-
def _strip_dot(path):
|
160
|
-
if not path:
|
161
|
-
return path
|
162
|
-
path = Path(path) # ./aaa/ -> aaa
|
163
|
-
parts = path.parts
|
164
|
-
if ".." in parts:
|
165
|
-
raise RuleError(f"{path}: '..' directory is not allowed")
|
166
|
-
return str(path)
|
167
|
-
|
168
|
-
|
169
163
|
def _check_wildcard(path):
|
170
164
|
if "*" in path:
|
171
165
|
raise RuleError(f"{path}: '*' directory is not allowed")
|
172
166
|
|
173
167
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
168
|
+
def _name_to_str(name):
|
169
|
+
match name:
|
170
|
+
case Task():
|
171
|
+
return name.name
|
172
|
+
case _TaskFunc():
|
173
|
+
return name.name
|
174
|
+
case Path():
|
175
|
+
return str(name)
|
176
|
+
case str():
|
177
|
+
return name
|
183
178
|
|
184
|
-
|
185
|
-
for target in flatten(targets or ()):
|
186
|
-
target = str(target)
|
187
|
-
if not target:
|
188
|
-
continue
|
179
|
+
return name
|
189
180
|
|
190
|
-
if "*" in target:
|
191
|
-
continue
|
192
181
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
182
|
+
class Rule:
|
183
|
+
def __init__(self, targets, pattern=None, depends=(), uses=(), builder=None):
|
184
|
+
self.targets = []
|
185
|
+
self.first_target = None
|
186
|
+
if targets:
|
187
|
+
for target in flatten(targets):
|
188
|
+
if not target:
|
189
|
+
continue
|
190
|
+
target = str(target)
|
191
|
+
if not target:
|
192
|
+
continue
|
193
|
+
|
194
|
+
if not self.first_target:
|
195
|
+
if "*" not in target:
|
196
|
+
if _check_pattern_count(target) == 0:
|
197
|
+
# not contain one %
|
198
|
+
self.first_target = target
|
199
|
+
|
200
|
+
target = rule_to_re(target)
|
201
|
+
self.targets.append(target)
|
197
202
|
|
198
203
|
if pattern:
|
199
|
-
pattern =
|
204
|
+
pattern = str(pattern)
|
200
205
|
if _check_pattern_count(pattern) != 1:
|
201
206
|
raise RuleError(f"{pattern}: Pattern should contain a '%'")
|
202
207
|
|
@@ -206,18 +211,16 @@ class Rule:
|
|
206
211
|
|
207
212
|
self.depends = []
|
208
213
|
for depend in flatten(depends or ()):
|
209
|
-
depend =
|
214
|
+
depend = _name_to_str(depend)
|
210
215
|
_check_pattern_count(depend)
|
211
216
|
_check_wildcard(depend)
|
212
|
-
depend = _strip_dot(depend)
|
213
217
|
self.depends.append(depend)
|
214
218
|
|
215
219
|
self.uses = []
|
216
220
|
for use in flatten(uses or ()):
|
217
|
-
use =
|
221
|
+
use = _name_to_str(use)
|
218
222
|
_check_pattern_count(use)
|
219
223
|
_check_wildcard(use)
|
220
|
-
use = _strip_dot(use)
|
221
224
|
self.uses.append(use)
|
222
225
|
|
223
226
|
self.builder = builder
|
@@ -227,6 +230,49 @@ class Rule:
|
|
227
230
|
return f
|
228
231
|
|
229
232
|
|
233
|
+
class _TaskFunc:
|
234
|
+
def __init__(self, f, name):
|
235
|
+
self.f = f
|
236
|
+
self.name = name
|
237
|
+
|
238
|
+
def __call__(self, *args, **kwargs):
|
239
|
+
return self.f(*args, **kwargs)
|
240
|
+
|
241
|
+
|
242
|
+
def default_builder(self, *args, **kwargs):
|
243
|
+
# default builder
|
244
|
+
pass
|
245
|
+
|
246
|
+
|
247
|
+
class Task(Rule):
|
248
|
+
def __init__(self, name, depends, uses, func=None):
|
249
|
+
super().__init__((), pattern=None, depends=depends, uses=uses, builder=func)
|
250
|
+
self.name = _name_to_str(name)
|
251
|
+
if name:
|
252
|
+
self.targets = [name]
|
253
|
+
self.first_target = self.name
|
254
|
+
if func:
|
255
|
+
self._set_funcname(func)
|
256
|
+
if not self.builder:
|
257
|
+
self.builder = default_builder
|
258
|
+
|
259
|
+
def _set_funcname(self, f):
|
260
|
+
if not self.name:
|
261
|
+
if not f.__name__ or f.__name__ == "<lambda>":
|
262
|
+
raise RuleError(
|
263
|
+
"Task function should have a name. Use @task(name='name')"
|
264
|
+
)
|
265
|
+
self.name = f.__name__
|
266
|
+
self.targets = [f.__name__]
|
267
|
+
|
268
|
+
self.first_target = self.name
|
269
|
+
|
270
|
+
def __call__(self, f):
|
271
|
+
self.builder = f
|
272
|
+
self._set_funcname(f)
|
273
|
+
return _TaskFunc(f, self.name)
|
274
|
+
|
275
|
+
|
230
276
|
class Rules:
|
231
277
|
def __init__(self):
|
232
278
|
self.rules = []
|
@@ -234,40 +280,52 @@ class Rules:
|
|
234
280
|
self._detect_loop = set()
|
235
281
|
self.frozen = False
|
236
282
|
|
237
|
-
def add_rule(self, targets, pattern, depends, uses, builder):
|
283
|
+
def add_rule(self, targets, pattern=None, depends=(), uses=(), builder=None):
|
284
|
+
if builder:
|
285
|
+
if not callable(builder):
|
286
|
+
raise ValueError(f"{builder} is not callable")
|
287
|
+
|
238
288
|
if self.frozen:
|
239
289
|
raise RuntimeError("No new rule can be added after initialization")
|
240
290
|
|
241
|
-
dep = Rule(
|
242
|
-
targets,
|
243
|
-
pattern,
|
244
|
-
depends,
|
245
|
-
uses,
|
246
|
-
builder,
|
247
|
-
)
|
291
|
+
dep = Rule(targets, pattern, depends, uses, builder)
|
248
292
|
self.rules.append(dep)
|
249
293
|
return dep
|
250
294
|
|
251
|
-
def
|
252
|
-
if
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
dep
|
295
|
+
def add_task(self, name=None, depends=(), uses=(), func=None):
|
296
|
+
if self.frozen:
|
297
|
+
raise RuntimeError("No new rule can be added after initialization")
|
298
|
+
dep = Task(name, depends, uses, func)
|
299
|
+
self.rules.append(dep)
|
300
|
+
return dep
|
301
|
+
|
302
|
+
def rule(self, targets, *, pattern=None, depends=(), uses=()):
|
303
|
+
if not targets:
|
304
|
+
raise ValueError("No target specified")
|
305
|
+
|
306
|
+
dep = self.add_rule([targets], pattern, depends, uses, None)
|
307
|
+
return dep
|
308
|
+
|
309
|
+
def task(self, func=None, *, name=None, depends=(), uses=()):
|
310
|
+
if func:
|
311
|
+
if not callable(func):
|
312
|
+
raise ValueError(f"{func} is not callable")
|
313
|
+
|
314
|
+
dep = self.add_task(name, depends, uses, func)
|
257
315
|
return dep
|
258
316
|
|
259
317
|
def iter_rule(self, name):
|
260
|
-
name =
|
318
|
+
name = _name_to_str(name)
|
261
319
|
for dep in self.rules:
|
262
320
|
for target in dep.targets:
|
263
|
-
m = re.fullmatch(
|
321
|
+
m = re.fullmatch(target, name)
|
264
322
|
if m:
|
265
323
|
stem = None
|
266
324
|
d = m.groupdict().get("stem", None)
|
267
325
|
if d is not None:
|
268
326
|
stem = d
|
269
327
|
elif dep.pattern:
|
270
|
-
m = re.fullmatch(
|
328
|
+
m = re.fullmatch(dep.pattern, name)
|
271
329
|
if m:
|
272
330
|
stem = m.groupdict().get("stem", None)
|
273
331
|
|
@@ -282,7 +340,6 @@ class Rules:
|
|
282
340
|
break
|
283
341
|
|
284
342
|
def get_dep_names(self, name):
|
285
|
-
assert name
|
286
343
|
ret_depends = []
|
287
344
|
ret_uses = []
|
288
345
|
|
@@ -307,11 +364,13 @@ class Rules:
|
|
307
364
|
return depends, uses, dep
|
308
365
|
|
309
366
|
def build_tree(self, name, lv=1):
|
367
|
+
assert name
|
310
368
|
self.frozen = True
|
311
369
|
|
312
|
-
name =
|
370
|
+
name = _name_to_str(name)
|
313
371
|
if name in self._detect_loop:
|
314
|
-
raise CircularReferenceError(name)
|
372
|
+
raise CircularReferenceError(f"Circular reference detected: {name}")
|
373
|
+
|
315
374
|
self._detect_loop.add(name)
|
316
375
|
try:
|
317
376
|
if name in self.tree:
|
@@ -326,7 +385,7 @@ class Rules:
|
|
326
385
|
depends.extend(build_uses)
|
327
386
|
|
328
387
|
depends = unique_list(depends)
|
329
|
-
self.tree[name].update(
|
388
|
+
self.tree[name].update(depends)
|
330
389
|
for dep in depends:
|
331
390
|
self.build_tree(dep, lv=lv + 1)
|
332
391
|
|
@@ -339,17 +398,19 @@ class Checkers:
|
|
339
398
|
self.checkers = []
|
340
399
|
|
341
400
|
def get_checker(self, name):
|
401
|
+
name = _name_to_str(name)
|
342
402
|
for targets, f in self.checkers:
|
343
403
|
for target in targets:
|
344
|
-
if fnmatch(name,
|
404
|
+
if fnmatch(name, target):
|
345
405
|
return f
|
346
406
|
|
347
|
-
def add_check(self,
|
348
|
-
|
407
|
+
def add_check(self, targets, f):
|
408
|
+
targets = list(map(_name_to_str, flatten(targets or ())))
|
409
|
+
self.checkers.append((targets, f))
|
349
410
|
|
350
|
-
def check(self,
|
411
|
+
def check(self, targets):
|
351
412
|
def deco(f):
|
352
|
-
self.add_check(
|
413
|
+
self.add_check(targets, f)
|
353
414
|
return f
|
354
415
|
|
355
416
|
return deco
|
@@ -421,22 +482,27 @@ def write(filename, s, append=False):
|
|
421
482
|
f.write(s)
|
422
483
|
|
423
484
|
|
424
|
-
def quote(s):
|
425
|
-
return shlex.quote(str(s))
|
426
|
-
|
427
|
-
|
428
|
-
def squote(*s):
|
485
|
+
def quote(*s):
|
429
486
|
ret = [shlex.quote(str(x)) for x in flatten(s)]
|
430
487
|
return ret
|
431
488
|
|
432
489
|
|
490
|
+
def squote(s):
|
491
|
+
s = " ".join(str(e) for e in flatten(s))
|
492
|
+
return shlex.quote(s)
|
493
|
+
|
494
|
+
|
433
495
|
def makedirs(path):
|
434
496
|
os.makedirs(path, exist_ok=True)
|
435
497
|
|
436
498
|
|
437
499
|
class Prod:
|
438
500
|
def __init__(self, modulefile, njobs=1, params=None):
|
439
|
-
|
501
|
+
if modulefile:
|
502
|
+
self.modulefile = Path(modulefile)
|
503
|
+
else:
|
504
|
+
self.modulefile = None
|
505
|
+
|
440
506
|
self.rules = Rules()
|
441
507
|
self.checkers = Checkers()
|
442
508
|
if njobs > 1:
|
@@ -445,11 +511,14 @@ class Prod:
|
|
445
511
|
self.executor = None
|
446
512
|
self.params = Params(params)
|
447
513
|
self.buildings = {}
|
448
|
-
self.module =
|
514
|
+
self.module = None
|
515
|
+
if self.modulefile:
|
516
|
+
self.module = self.load_pyprodfile(self.modulefile)
|
449
517
|
self.built = 0 # number of build execused
|
450
518
|
|
451
519
|
def get_module_globals(self):
|
452
520
|
globals = {
|
521
|
+
"build": self.build,
|
453
522
|
"capture": capture,
|
454
523
|
"check": self.checkers.check,
|
455
524
|
"environ": Envs(),
|
@@ -466,6 +535,7 @@ class Prod:
|
|
466
535
|
"rule": self.rules.rule,
|
467
536
|
"run": run,
|
468
537
|
"shutil": shutil,
|
538
|
+
"task": self.rules.task,
|
469
539
|
"write": write,
|
470
540
|
"MAX_TS": MAX_TS,
|
471
541
|
"Path": Path,
|
@@ -489,47 +559,28 @@ class Prod:
|
|
489
559
|
async def run_in_executor(self, func, *args, **kwargs):
|
490
560
|
if self.executor:
|
491
561
|
loop = asyncio.get_running_loop()
|
492
|
-
|
562
|
+
ret = await loop.run_in_executor(
|
493
563
|
self.executor, lambda: func(*args, **kwargs)
|
494
564
|
)
|
495
565
|
else:
|
496
|
-
|
566
|
+
ret = func(*args, **kwargs)
|
567
|
+
|
568
|
+
return ret
|
497
569
|
|
498
570
|
def get_default_target(self):
|
499
571
|
return self.rules.select_first_target()
|
500
572
|
|
501
573
|
async def start(self, deps):
|
574
|
+
self.loop = asyncio.get_running_loop()
|
502
575
|
self.built = 0
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
if value:
|
508
|
-
names.append(value)
|
509
|
-
else:
|
510
|
-
names.append(name)
|
511
|
-
else:
|
512
|
-
names.append(name)
|
513
|
-
|
514
|
-
builds = []
|
515
|
-
waitings = []
|
516
|
-
for obj in flatten(names):
|
517
|
-
if isinstance(obj, str | Path):
|
518
|
-
builds.append(obj)
|
519
|
-
elif isinstance(obj, Rule):
|
520
|
-
raise RuleError(obj)
|
521
|
-
elif callable(obj):
|
522
|
-
self.built += 1
|
523
|
-
task = asyncio.create_task(self.run_in_executor(obj))
|
524
|
-
waitings.append(task)
|
525
|
-
else:
|
526
|
-
raise RuleError(obj)
|
576
|
+
self.deps = deps[:]
|
577
|
+
while self.deps:
|
578
|
+
dep = self.deps.pop(0)
|
579
|
+
await self.schedule([dep])
|
527
580
|
|
528
|
-
await self.build(builds)
|
529
|
-
await asyncio.gather(*waitings)
|
530
581
|
return self.built
|
531
582
|
|
532
|
-
async def
|
583
|
+
async def schedule(self, deps):
|
533
584
|
tasks = []
|
534
585
|
waits = []
|
535
586
|
for dep in deps:
|
@@ -581,9 +632,11 @@ class Prod:
|
|
581
632
|
ret = MAX_TS
|
582
633
|
return Exists(name, True, ret)
|
583
634
|
|
584
|
-
|
585
|
-
|
635
|
+
def build(self, *deps):
|
636
|
+
self.deps[0:0] = [_name_to_str(name) for name in flatten(deps)]
|
586
637
|
|
638
|
+
async def run(self, name): # -> Any | int:
|
639
|
+
name = _name_to_str(name)
|
587
640
|
self.rules.build_tree(name)
|
588
641
|
deps, uses = self.rules.get_dep_names(name)
|
589
642
|
selected = self.rules.select_builder(name)
|
@@ -594,9 +647,14 @@ class Prod:
|
|
594
647
|
|
595
648
|
ts = 0
|
596
649
|
if deps:
|
597
|
-
ts = await self.
|
650
|
+
ts = await self.schedule(deps)
|
598
651
|
if uses:
|
599
|
-
await self.
|
652
|
+
await self.schedule(uses)
|
653
|
+
|
654
|
+
if selected and isinstance(builder, Task):
|
655
|
+
self.built += 1
|
656
|
+
await self.run_in_executor(builder.builder, *build_deps)
|
657
|
+
return MAX_TS
|
600
658
|
|
601
659
|
exists = await self.is_exists(name)
|
602
660
|
|
@@ -608,8 +666,14 @@ class Prod:
|
|
608
666
|
logger.debug("%r already exists", name)
|
609
667
|
|
610
668
|
if not exists.exists and not selected:
|
611
|
-
raise NoRuleToMakeTargetError(name)
|
612
|
-
|
669
|
+
raise NoRuleToMakeTargetError(f"No rule to make target: {name}")
|
670
|
+
|
671
|
+
elif selected and (
|
672
|
+
(not exists.exists)
|
673
|
+
or (ts >= MAX_TS)
|
674
|
+
or (exists.ts < ts)
|
675
|
+
or pyprod.args.rebuild
|
676
|
+
):
|
613
677
|
logger.warning("building: %r", name)
|
614
678
|
await self.run_in_executor(builder.builder, name, *build_deps)
|
615
679
|
self.built += 1
|
pyprod/utils.py
CHANGED
pyprod/venv.py
CHANGED
@@ -11,15 +11,15 @@ import pyprod
|
|
11
11
|
from .utils import flatten
|
12
12
|
|
13
13
|
THREADID = threading.get_ident()
|
14
|
-
pyprodenv = ".pyprod"
|
15
14
|
venvdir = None
|
16
15
|
|
17
|
-
PYPRODVENV = "
|
16
|
+
PYPRODVENV = "pyprod"
|
18
17
|
|
19
18
|
|
20
19
|
def makevenv(conffile):
|
21
20
|
global venvdir
|
22
|
-
|
21
|
+
major, minor = sys.version_info[:2]
|
22
|
+
venvdir = Path(".") / f".{conffile.name}.{major}.{minor}.{PYPRODVENV}"
|
23
23
|
if not venvdir.is_dir():
|
24
24
|
venv.main([str(venvdir)])
|
25
25
|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: PyProd
|
3
|
-
Version: 0.
|
3
|
+
Version: 0.4.0
|
4
4
|
Summary: PyProd: More Makeable than Make
|
5
5
|
Project-URL: Homepage, https://github.com/atsuoishimoto/pyprod
|
6
6
|
Project-URL: Documentation, https://pyprod.readthedocs.io/en/latest/
|
@@ -17,7 +17,7 @@ Description-Content-Type: text/x-rst
|
|
17
17
|
PyProd - More Makeable than Make
|
18
18
|
=================================
|
19
19
|
|
20
|
-
PyProd is a Python script that can be used as an alternative to Makefile. By leveraging Python's versatility, it enables you to define build rules and dependencies programmatically, allowing for dynamic configurations, integration with existing Python libraries, and custom build logic not easily achievable with traditional Makefiles. For detailed documentation, please refer to the `official documentation <https://pyprod.readthedocs.io/en/
|
20
|
+
PyProd is a Python script that can be used as an alternative to Makefile. By leveraging Python's versatility, it enables you to define build rules and dependencies programmatically, allowing for dynamic configurations, integration with existing Python libraries, and custom build logic not easily achievable with traditional Makefiles. For detailed documentation, please refer to the `official documentation <https://pyprod.readthedocs.io/en/stable/>`_.
|
21
21
|
|
22
22
|
|
23
23
|
Features
|
@@ -42,22 +42,26 @@ With PyProd, a traditional Makefile for C can be expressed as a Python script li
|
|
42
42
|
.. code-block:: python
|
43
43
|
|
44
44
|
CC = "gcc"
|
45
|
-
CFLAGS = "-I."
|
45
|
+
CFLAGS = "-c -I."
|
46
46
|
DEPS = "hello.h"
|
47
47
|
OBJS = "hello.o main.o".split()
|
48
|
+
EXE = "hello.exe"
|
48
49
|
|
49
50
|
@rule("%.o", depends=("%.c", DEPS))
|
50
51
|
def compile(target, src, *deps):
|
51
|
-
run(CC, "-
|
52
|
+
run(CC, "-o", target, src, CFLAGS)
|
52
53
|
|
53
|
-
@rule(
|
54
|
+
@rule(EXE, depends=OBJS)
|
54
55
|
def link(target, *objs):
|
55
56
|
run(CC, "-o", target, objs)
|
56
57
|
|
58
|
+
@task
|
57
59
|
def clean():
|
58
60
|
run("rm -f", OBJS, "hello.exe")
|
59
61
|
|
60
|
-
|
62
|
+
@task
|
63
|
+
def rebuild():
|
64
|
+
build(clean, EXE)
|
61
65
|
|
62
66
|
|
63
67
|
To run the build script, simply execute:
|
@@ -67,6 +71,8 @@ To run the build script, simply execute:
|
|
67
71
|
$ cd project
|
68
72
|
$ pyprod
|
69
73
|
|
74
|
+
Other examples can be found in the `samples <https://github.com/atsuoishimoto/pyprod/tree/main/samples>`_ directory.
|
75
|
+
|
70
76
|
License
|
71
77
|
-------
|
72
78
|
PyProd is licensed under the MIT License. See the `LICENSE <LICENSE>`_ file for more details.
|
@@ -0,0 +1,11 @@
|
|
1
|
+
pyprod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
+
pyprod/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
|
3
|
+
pyprod/main.py,sha256=w8xm7Dz4eXF6D6FhZuxlPfU24KpnXciJLuAMT6_y3zI,2808
|
4
|
+
pyprod/prod.py,sha256=Vu376JyNqiieCMy8jVvBml3TAf32Pq4XxSdpC4bBdgY,18424
|
5
|
+
pyprod/utils.py,sha256=hvqeZhXyVAsHAdPUG6LhBOs2dMVXuDUWBYiIPBcsoKw,391
|
6
|
+
pyprod/venv.py,sha256=lRTYxvtX876FQ9-bTYmsOHYV3nuMkKHO5hTLog0iHro,1085
|
7
|
+
pyprod-0.4.0.dist-info/METADATA,sha256=zkpPbp07U627hgqWE6yDYGe_dZEDQTCiE3FidIfQ5mM,2652
|
8
|
+
pyprod-0.4.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
9
|
+
pyprod-0.4.0.dist-info/entry_points.txt,sha256=zFycf8BYSMRDTiI0jftmcvtkf9XM4MZ4BL3JaIer_ZM,44
|
10
|
+
pyprod-0.4.0.dist-info/licenses/LICENSE,sha256=OtPgwnlLrsVEYPnTraun5AqftAT5vUv4rIan-qYj7nE,1071
|
11
|
+
pyprod-0.4.0.dist-info/RECORD,,
|
pyprod-0.3.0.dist-info/RECORD
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
pyprod/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
2
|
-
pyprod/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
|
3
|
-
pyprod/main.py,sha256=nS0mcLy3Ix7ydnX1Pu5g7ceLzhh6DndwJWO5sjO_qWg,2580
|
4
|
-
pyprod/prod.py,sha256=r2e-JSHiTw2SOcv-tYFx4aRpVkQbOb5j9vEVrjT0meE,16694
|
5
|
-
pyprod/utils.py,sha256=oiiUkSbeqTazbtJ6gz7ZKqG1OvAeV-nV9u_3Y0DCOOM,401
|
6
|
-
pyprod/venv.py,sha256=_riw56YQvUOSd55u_1m9ElsqPdjM5qVvIZP6dr9Fzt4,1051
|
7
|
-
pyprod-0.3.0.dist-info/METADATA,sha256=t9TMYeBveYE1LFZNpcYJAtOdUo7gPARFHmIZ9p0bnag,2477
|
8
|
-
pyprod-0.3.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
9
|
-
pyprod-0.3.0.dist-info/entry_points.txt,sha256=zFycf8BYSMRDTiI0jftmcvtkf9XM4MZ4BL3JaIer_ZM,44
|
10
|
-
pyprod-0.3.0.dist-info/licenses/LICENSE,sha256=OtPgwnlLrsVEYPnTraun5AqftAT5vUv4rIan-qYj7nE,1071
|
11
|
-
pyprod-0.3.0.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|