Skip to content

Commit 14a5bee

Browse files
committed
src: allow local pymod_do_main modules
1 parent 2276af7 commit 14a5bee

10 files changed

+341
-117
lines changed

gyp/common.py

+15
Original file line numberDiff line numberDiff line change
@@ -604,3 +604,18 @@ def CrossCompileRequested():
604604
os.environ.get('AR_target') or
605605
os.environ.get('CC_target') or
606606
os.environ.get('CXX_target'))
607+
608+
609+
def IsStrCanonicalInt(string):
610+
"""
611+
Returns True if |string| is a canonical integer form str.
612+
The canonical form is such that str(int(string)) == string.
613+
"""
614+
if type(string) is not str:
615+
return False
616+
# noinspection PyBroadException
617+
try:
618+
i = int(string)
619+
return str(i) == string
620+
except:
621+
return False

gyp/input.py

+20-117
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,21 @@
55
from __future__ import print_function
66

77
import ast
8-
from collections import OrderedDict
98
import os.path
109
import re
1110
import shlex
12-
import signal
1311
import subprocess
1412
import sys
1513
import traceback
14+
from collections import OrderedDict
15+
1616
import gyp.common
1717
import gyp.lib.simple_copy
1818
from gyp.common import GypError, OrderedSet
1919

2020
if not 'unicode' in __builtins__:
2121
unicode = str
22+
basestring = str
2223

2324
# A list of types that are treated as linkable.
2425
linkable_types = [
@@ -434,96 +435,6 @@ def LoadTargetBuildFile(build_file_path, data, aux_data, variables, includes, de
434435
return (build_file_path, dependencies)
435436

436437

437-
def CallLoadTargetBuildFile(global_flags, build_file_path, variables, includes, depth, generator_input_info):
438-
"""Wrapper around LoadTargetBuildFile for parallel processing.
439-
440-
This wrapper is used when LoadTargetBuildFile is executed in
441-
a worker process.
442-
"""
443-
444-
try:
445-
signal.signal(signal.SIGINT, signal.SIG_IGN)
446-
447-
# Apply globals so that the worker process behaves the same.
448-
for key, value in global_flags.items():
449-
globals()[key] = value
450-
451-
SetGeneratorGlobals(generator_input_info)
452-
result = LoadTargetBuildFile(build_file_path, per_process_data, per_process_aux_data, variables, includes, depth, False)
453-
if not result:
454-
return result
455-
456-
(build_file_path, dependencies) = result
457-
458-
# We can safely pop the build_file_data from per_process_data because it
459-
# will never be referenced by this process again, so we don't need to keep
460-
# it in the cache.
461-
build_file_data = per_process_data.pop(build_file_path)
462-
463-
# This gets serialized and sent back to the main process via a pipe.
464-
# It's handled in LoadTargetBuildFileCallback.
465-
return (build_file_path, build_file_data, dependencies)
466-
except GypError as e:
467-
sys.stderr.write("gyp: %s\n" % e)
468-
return None
469-
except Exception as e:
470-
print('Exception:', e, file=sys.stderr)
471-
print(traceback.format_exc(), file=sys.stderr)
472-
return None
473-
474-
475-
class ParallelProcessingError(Exception):
476-
pass
477-
478-
479-
class ParallelState(object):
480-
"""Class to keep track of state when processing input files in parallel.
481-
482-
If build files are loaded in parallel, use this to keep track of
483-
state during farming out and processing parallel jobs. It's stored
484-
in a global so that the callback function can have access to it.
485-
"""
486-
487-
def __init__(self):
488-
# The multiprocessing pool.
489-
self.pool = None
490-
# The condition variable used to protect this object and notify
491-
# the main loop when there might be more data to process.
492-
self.condition = None
493-
# The "data" dict that was passed to LoadTargetBuildFileParallel
494-
self.data = None
495-
# The number of parallel calls outstanding; decremented when a response
496-
# was received.
497-
self.pending = 0
498-
# The set of all build files that have been scheduled, so we don't
499-
# schedule the same one twice.
500-
self.scheduled = set()
501-
# A list of dependency build file paths that haven't been scheduled yet.
502-
self.dependencies = []
503-
# Flag to indicate if there was an error in a child process.
504-
self.error = False
505-
506-
def LoadTargetBuildFileCallback(self, result):
507-
"""Handle the results of running LoadTargetBuildFile in another process.
508-
"""
509-
self.condition.acquire()
510-
if not result:
511-
self.error = True
512-
self.condition.notify()
513-
self.condition.release()
514-
return
515-
(build_file_path0, build_file_data0, dependencies0) = result
516-
self.data[build_file_path0] = build_file_data0
517-
self.data['target_build_files'].add(build_file_path0)
518-
for new_dependency in dependencies0:
519-
if new_dependency not in self.scheduled:
520-
self.scheduled.add(new_dependency)
521-
self.dependencies.append(new_dependency)
522-
self.pending -= 1
523-
self.condition.notify()
524-
self.condition.release()
525-
526-
527438
# Look for the bracket that matches the first bracket seen in a
528439
# string, and return the start and end as a tuple. For example, if
529440
# the input is something like "<(foo <(bar)) blah", then it would
@@ -773,30 +684,25 @@ def ExpandVariables(input, phase, variables, build_file):
773684
gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Executing command '%s' in directory '%s'", contents, build_file_dir)
774685

775686
if command_string == 'pymod_do_main':
776-
# <!pymod_do_main(modulename param eters) loads |modulename| as a
777-
# python module and then calls that module's DoMain() function,
778-
# passing ["param", "eters"] as a single list argument. For modules
779-
# that don't load quickly, this can be faster than
780-
# <!(python modulename param eters). Do this in |build_file_dir|.
781-
oldwd = os.getcwd() # Python doesn't like os.open('.'): no fchdir.
782-
if build_file_dir is not None: # build_file_dir may be None (see above).
783-
# noinspection PyTypeChecker
784-
os.chdir(build_file_dir)
687+
# <!pymod_do_main(modulename foo bar) loads |modulename| as a python module and then calls that module's DoMain() function, passing ["foo", "bar"] as a single list argument.
688+
# For modules that don't load quickly, this can be faster than <!(python modulename foo bar). Do this in |build_file_dir|.
689+
old_cwd = os.getcwd()
690+
# Wrap the following with `try` for unwinding state in `finally`
785691
try:
786-
parsed_contents = shlex.split(contents)
692+
# changing process state, to be unwind in the `finally`
693+
build_file_dir and os.chdir(build_file_dir)
694+
sys.path.append(os.getcwd())
695+
parts = shlex.split(contents)
696+
modulename, params = parts[0], parts[1:]
787697
try:
788-
py_module = __import__(parsed_contents[0])
698+
py_module = __import__(modulename)
789699
except ImportError as e:
790-
raise GypError("Error importing pymod_do_main module (%s): %s" % (parsed_contents[0], e))
791-
replacement = str(py_module.DoMain(parsed_contents[1:])).rstrip()
792-
except Exception as e:
793-
err_info = traceback.format_exc()
794-
print(e, file=sys.stderr)
795-
print(err_info, file=sys.stderr)
796-
797-
700+
raise GypError("Error importing pymod_do_main module (%s): %s" % (modulename, e))
701+
mod_ret = py_module.DoMain(params)
702+
replacement = str(mod_ret).rstrip()
798703
finally:
799-
os.chdir(oldwd)
704+
sys.path.pop()
705+
os.chdir(old_cwd)
800706
assert replacement is not None
801707
elif command_string:
802708
raise GypError("Unknown command string '%s' in '%s'." % (command_string, contents))
@@ -888,14 +794,11 @@ def ExpandVariables(input, phase, variables, build_file):
888794
if output == input:
889795
gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found only identity matches on %r, avoiding infinite recursion.", output)
890796
else:
891-
# Look for more matches now that we've replaced some, to deal with
892-
# expanding local variables (variables defined in the same
893-
# variables block as this one).
797+
# Look for more matches now that we've replaced some, to deal with expanding local variables (variables defined in the same variables block as this one).
894798
gyp.DebugOutput(gyp.DEBUG_VARIABLES, "Found output %r, recursing.", output)
895799
if type(output) is list:
896800
if output and type(output[0]) is list:
897-
# Leave output alone if it's a list of lists.
898-
# We don't want such lists to be stringified.
801+
# Leave output alone if it's a list of lists. We don't want such lists to be stringified.
899802
pass
900803
else:
901804
new_output = []
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Copyright (c) 2019 Refael Ackeramnn<[email protected]>. All rights reserved.
2+
# Use of this source code is governed by an MIT-style license.
3+
def DoMain(args):
4+
format_str = args.pop(0)
5+
return ' '.join(format_str % a for a in args)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Copyright (c) 2019 Refael Ackeramnn<[email protected]>. All rights reserved.
2+
# Use of this source code is governed by an MIT-style license.
3+
4+
# GetConstantValue
5+
6+
def DoMain(args):
7+
return 'Lorem ipsum. %s' % tuple(args)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
{
2+
'variables': {
3+
'names': [
4+
"arguments",
5+
"array",
6+
"array-copywithin",
7+
"array-filter",
8+
"array-find",
9+
"array-findindex",
10+
"array-foreach",
11+
"array-join",
12+
"array-map",
13+
"array-of",
14+
"array-reverse",
15+
"array-slice",
16+
"array-splice",
17+
"array-unshift",
18+
"array-lastindexof",
19+
"base",
20+
"collections",
21+
"data-view",
22+
"extras-utils",
23+
"growable-fixed-array",
24+
"iterator",
25+
"object",
26+
"string",
27+
"typed-array",
28+
"typed-array-createtypedarray",
29+
"typed-array-filter",
30+
"typed-array-foreach",
31+
"typed-array-reduce",
32+
"typed-array-reduceright",
33+
"typed-array-slice",
34+
"typed-array-subarray",
35+
],
36+
'output_root': 'real-output-root',
37+
'ret1': [
38+
'<!@pymod_do_main(ForEachFormat "<(output_root)/%s-from-name.cc" <@(names))',
39+
'<!@pymod_do_main(ForEachFormat "<(output_root)/%s-from-name.hpp" <@(names))'
40+
],
41+
'ret1+': [ '<!@pymod_do_main(ForEachFormat "<(output_root)/%s-from-name.h" <@(names))' ],
42+
},
43+
'includes': [
44+
'commands-pymod_do_main.gypi',
45+
],
46+
'targets': [
47+
{
48+
'target_name': 'foo',
49+
'type': 'none',
50+
'variables': {
51+
'ret2': '<!pymod_do_main(GetConstantValue 2)',
52+
},
53+
# 'actions': [
54+
# {
55+
# 'action_name': 'test_action',
56+
# 'variables': {
57+
# 'ret3': '<!pymod_do_main(GetConstantValue 3)',
58+
# },
59+
# 'inputs' : [
60+
# '<@(ret1)',
61+
# ],
62+
# 'outputs': [
63+
# '<(ret2)',
64+
# '<(ret3)',
65+
# '<(ret4)',
66+
# ],
67+
# 'action': [
68+
# 'echo',
69+
# '<@(_inputs)',
70+
# '<@(_outputs)',
71+
# ],
72+
# },
73+
# ],
74+
},
75+
],
76+
}

0 commit comments

Comments
 (0)