5 # The contents of this file are subject to the terms of the
6 # Common Development and Distribution License (the "License").
7 # You may not use this file except in compliance with the License.
9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 # or http://www.opensolaris.org/os/licensing.
11 # See the License for the specific language governing permissions
12 # and limitations under the License.
14 # When distributing Covered Code, include this CDDL HEADER in each
15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 # If applicable, add the following below this CDDL HEADER, with the
17 # fields enclosed by brackets "[]" replaced with your own identifying
18 # information: Portions Copyright [yyyy] [name of copyright owner]
22 # This is all very naive and will hurt pythonists' eyes.
30 from urllib
.request
import urlopen
32 from .component
import Component
35 class Keywords(object):
62 "COMPONENT_VERSION": [],
63 "COMPONENT_REVISION": [],
65 "COMPONENT_CLASSIFICATION": [],
66 "COMPONENT_SUMMARY": [],
67 "COMPONENT_PROJECT_URL": [],
68 "COMPONENT_SRC": ["$(COMPONENT_NAME)-$(COMPONENT_VERSION)"],
69 "COMPONENT_ARCHIVE": [],
70 "COMPONENT_ARCHIVE_URL": [],
71 "COMPONENT_ARCHIVE_HASH": [],
72 "COMPONENT_LICENSE": [],
73 "COMPONENT_LICENSE_FILE": []
76 "build": [ "BUILD_$(MK_BITS)"],
77 "install": ["INSTALL_$(MK_BITS)"],
78 "test": ["TEST_$(MK_BITS)", "NO_TESTS"],
79 "system-test": ["SYSTEM_TEST_$(MK_BITS)", "SYSTEM_TESTS_NOT_IMPLEMENTED"]
83 def assignment(name
, value
):
84 return name
+ "=" + value
+ "\n"
87 def target_variable_assignment(name
, value
):
88 return Keywords
.assignment(name
.upper()+"_TARGET", value
)
92 def __init__(self
, line
=None, content
=[]):
95 for l
in iter(self
.str):
98 def append(self
, line
):
99 self
.str.append(line
.strip())
101 def extend(self
, content
):
112 return "".join(self
.str).replace("\n","").strip()
114 def set_value(self
, value
):
115 self
.str = [ i
.strip()+"\n" for i
in value
.split("\n") ]
117 def include_line(self
):
118 return "include "+self
.str[0]
120 def variable_assignment(self
, variable
):
121 if self
.length() == 1:
122 return ["{0:<24}{1}".format(variable
+"=",self
.str[0])]
123 # Handle continuation lines
124 lines
= ["{0:<24}{1}".format(variable
+"=",self
.str[0])]
125 for l
in self
.str[1:]:
131 def target_definition(self
, target
):
132 lines
= ["{0:<24}{1}".format(target
+":", self
.str[0])]
133 for l
in self
.str[1:]:
138 class Makefile(object):
139 def __init__(self
, path
=None, debug
=False):
142 self
.component
= Component()
146 makefile
= os
.path
.join(path
, 'Makefile')
147 with
open(makefile
, 'r') as f
:
148 self
.contents
= f
.readlines()
151 def update(self
, contents
=None):
155 if contents
is not None:
156 self
.contents
= contents
157 # Construct list of keywords
166 for idx
, line
in enumerate(self
.contents
):
167 # Continuation of target line
169 r
= re
.match(r
"^[\s]*(.*)[\s]*([\\]?)[\s]*$", line
)
171 self
.targets
[t
].str[0] += "\\".join(r
.group(1))
172 # Check for continuation or move to definition
179 r
= re
.match(r
"^[\t][\s]*(.*)[\s]*$", line
)
184 self
.targets
[d
].append(r
.group(1))
185 # Continuation line of variable
187 r
= re
.match(r
"^[\s]*(.*)[\s]*([\\]?)[\s]*$", line
)
188 self
.variables
[m
].append(r
.group(1))
192 if re
.match(r
"^#", line
):
195 r
= re
.match(r
"^include[\s]+(.*)", line
)
197 self
.includes
.append(Item(idx
, [r
.group(1)]))
200 # Collect known variables
201 for k
in list(kw
.variables
.keys()):
203 r
"^[\s]*("+k
+r
")[\s]*=[\s]*([^\\]*)[\s]*([\\]?)[\s]*$", line
)
207 if v
in self
.variables
.keys():
208 warnings
.warn("Variable '"+v
+"' redefined line "+idx
)
209 self
.variables
[k
] = Item(idx
, [v
])
215 # Collect known targets
216 for k
in list(kw
.targets
.keys()):
218 "^"+k
+r
"[\s]*:[\s]*(.*)[\s]*([\\]?)[\s]*$", line
)
222 if v
in self
.targets
.keys():
223 warnings
.warn("Target '"+v
+"' redefined line "+idx
)
224 self
.targets
[k
] = Item(idx
, [v
])
236 with
open(os
.path
.join(self
.path
, "Makefile"), 'w') as f
:
237 for line
in self
.contents
:
246 for i
in iter(self
.includes
):
247 print("{0:>3}: {1}".format(i
.line(), i
.value()))
252 for k
,i
in iter(sorted(self
.variables
.items())):
253 print("{0:>3}: {1:<24}= {2}".format(i
.line(), k
, i
.value()))
258 for k
,i
in iter(sorted(self
.targets
.items())):
259 print("{0:>3}: {1:<24}= {2}".format(i
.line(), k
, i
.value()))
263 def run(self
, targets
):
268 logger
.debug('Executing \'gmake %s\' in %s', targets
, path
)
270 proc
= subprocess
.Popen(['gmake', '-s', targets
],
271 stdout
=subprocess
.PIPE
,
272 stderr
=subprocess
.PIPE
,
274 universal_newlines
=True)
275 stdout
, stderr
= proc
.communicate()
277 for out
in stdout
.splitlines():
278 result
.append(out
.rstrip())
281 if proc
.returncode
!= 0:
282 logger
.debug('exit: %d, %s', proc
.returncode
, stderr
)
286 def print_value(self
, name
):
287 return self
.run('print-value-'+name
)[0]
289 def build_style(self
):
290 return self
.variables
['BUILD_STYLE'].value()
292 def build_bits(self
):
293 return self
.variables
['BUILD_BITS'].value()
295 def has_mk_include(self
, name
):
296 for i
in iter(self
.includes
):
297 if re
.match('^.*/'+name
+'.mk$', i
.value()):
301 def get_mk_include(self
, name
):
302 for i
in iter(self
.includes
):
303 if re
.match('^.*/'+name
+'.mk$', i
.value()):
307 def has_variable(self
, variable
):
308 return variable
in self
.variables
310 def variable(self
, variable
):
311 return self
.variables
[variable
]
313 def remove_variable(self
, variable
):
314 idx
= self
.variable(variable
).line()
315 del self
.contents
[idx
]
318 def set_variable(self
, variable
, value
, line
=None):
319 if not self
.has_variable(variable
):
320 self
.variables
[variable
] = Item(None)
321 self
.variables
[variable
].set_value(str(value
))
323 contents
= self
.contents
[0:line
]
324 contents
.extend(self
.variable_assignment(variable
))
325 contents
.extend(self
.contents
[line
:])
326 self
.update(contents
)
329 idx
= self
.variables
[variable
].line()
330 oln
= self
.variables
[variable
].length()
331 self
.variables
[variable
].set_value(str(value
))
332 nln
= self
.variables
[variable
].length()
336 if line
== idx
and nln
== 1 and oln
== 1:
337 self
.contents
[idx
] = self
.variable_assignment(variable
)[0]
339 contents
= self
.contents
[0:line
]
340 contents
.extend(self
.variable_assignment(variable
))
341 contents
.extend(self
.contents
[line
:idx
])
342 contents
.extend(self
.contents
[idx
+oln
:])
343 self
.update(contents
)
345 contents
= self
.contents
[0:idx
]
346 contents
.extend(self
.contents
[idx
+oln
:line
])
347 contents
.extend(self
.variable_assignment(variable
))
348 contents
.extend(self
.contents
[line
:])
349 self
.update(contents
)
350 # Add variable at given line
351 elif line
is not None:
352 contents
= self
.contents
[0:line
]
353 contents
.extend(self
.variable_assignment(variable
))
354 contents
.extend(self
.contents
[line
:])
355 self
.update(contents
)
357 def set_archive_hash(self
, checksum
):
358 self
.set_variable('COMPONENT_ARCHIVE_HASH', "sha256:"+str(checksum
))
360 def variable_assignment(self
, variable
):
361 return self
.variables
[variable
].variable_assignment(variable
)
363 def has_target(self
, target
):
364 return target
in self
.targets
366 def target(self
, target
):
367 return self
.targets
[target
]
369 def target_definition(self
, target
):
370 return self
.targets
[target
].target_definition(target
)
372 def required_packages(self
):
373 return self
.run("print-value-REQUIRED_PACKAGES")[0]
376 # Default build style is configure
377 if not self
.has_variable('BUILD_STYLE'):
379 is_py
= (self
.build_style() == 'setup.py')
380 urlnone
= (not self
.has_variable('COMPONENT_ARCHIVE_URL'))
381 urlpipy
= urlnone
or (self
.variable('COMPONENT_ARCHIVE_URL').value() == '$(call pypi_url)')
382 return is_py
and urlpipy
384 def get_pypi_data(self
):
385 name
= self
.print_value('COMPONENT_PYPI')
386 jsurl
= "https://pypi.python.org/pypi/%s/json" % name
388 f
= urlopen(jsurl
, data
=None)
389 except HTTPError
as e
:
390 if e
.getcode() == 404:
391 print("Unknown component '%s'" % name
)
393 printIOError(e
, "Can't open PyPI JSON url %s" % jsurl
)
396 printIOError(e
, "Can't open PyPI JSON url %s" % jsurl
)
398 content
= f
.read().decode("utf-8")
399 return json
.loads(content
)
403 return "$("+variable
+")"
406 def directory_variable(subdir
):
407 return Makefile
.value("WS_"+subdir
.upper().replace("-","_"))
410 def makefile_path(name
):
411 return os
.path
.join("$(WS_MAKE_RULES)", name
+".mk")
414 def target_value(name
, bits
):
415 return Makefile
.value(name
.upper()+"_"+bits
)