4 # The contents of this file are subject to the terms of the
5 # Common Development and Distribution License (the "License").
6 # You may not use this file except in compliance with the License.
8 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 # or http://www.opensolaris.org/os/licensing.
10 # See the License for the specific language governing permissions
11 # and limitations under the License.
13 # When distributing Covered Code, include this CDDL HEADER in each
14 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 # If applicable, add the following below this CDDL HEADER, with the
16 # fields enclosed by brackets "[]" replaced with your own identifying
17 # information: Portions Copyright [yyyy] [name of copyright owner]
21 # This is all very naive and will hurt pythonists' eyes.
29 from urllib
.request
import urlopen
31 from .component
import Component
34 class Keywords(object):
61 "COMPONENT_VERSION": [],
62 "COMPONENT_REVISION": [],
64 "COMPONENT_CLASSIFICATION": [],
65 "COMPONENT_SUMMARY": [],
66 "COMPONENT_PROJECT_URL": [],
67 "COMPONENT_SRC": ["$(COMPONENT_NAME)-$(COMPONENT_VERSION)"],
68 "COMPONENT_ARCHIVE": [],
69 "COMPONENT_ARCHIVE_URL": [],
70 "COMPONENT_ARCHIVE_HASH": [],
71 "COMPONENT_LICENSE": [],
72 "COMPONENT_LICENSE_FILE": []
75 "build": [ "BUILD_$(MK_BITS)"],
76 "install": ["INSTALL_$(MK_BITS)"],
77 "test": ["TEST_$(MK_BITS)", "NO_TESTS"],
78 "system-test": ["SYSTEM_TEST_$(MK_BITS)", "SYSTEM_TESTS_NOT_IMPLEMENTED"]
82 def assignment(name
, value
):
83 return name
+ "=" + value
+ "\n"
86 def target_variable_assignment(name
, value
):
87 return Keywords
.assignment(name
.upper()+"_TARGET", value
)
91 def __init__(self
, line
=None, content
=[]):
94 for l
in iter(self
.str):
97 def append(self
, line
):
98 self
.str.append(line
.strip())
100 def extend(self
, content
):
111 return "".join(self
.str).replace("\n","").strip()
113 def set_value(self
, value
):
114 self
.str = [ i
.strip()+"\n" for i
in value
.split("\n") ]
116 def include_line(self
):
117 return "include "+self
.str[0]
119 def variable_assignment(self
, variable
):
120 if self
.length() == 1:
121 return ["{0:<24}{1}".format(variable
+"=",self
.str[0])]
122 # Handle continuation lines
123 lines
= ["{0:<24}{1}".format(variable
+"=",self
.str[0])]
124 for l
in self
.str[1:]:
130 def target_definition(self
, target
):
131 lines
= ["{0:<24}{1}".format(target
+":", self
.str[0])]
132 for l
in self
.str[1:]:
137 class Makefile(object):
138 def __init__(self
, path
=None, debug
=False):
141 self
.component
= Component()
145 makefile
= os
.path
.join(path
, 'Makefile')
146 with
open(makefile
, 'r') as f
:
147 self
.contents
= f
.readlines()
150 def update(self
, contents
=None):
154 if contents
is not None:
155 self
.contents
= contents
156 # Construct list of keywords
165 for idx
, line
in enumerate(self
.contents
):
166 # Continuation of target line
168 r
= re
.match(r
"^[\s]*(.*)[\s]*([\\]?)[\s]*$", line
)
170 self
.targets
[t
].str[0] += "\\".join(r
.group(1))
171 # Check for continuation or move to definition
178 r
= re
.match(r
"^[\t][\s]*(.*)[\s]*$", line
)
183 self
.targets
[d
].append(r
.group(1))
184 # Continuation line of variable
186 r
= re
.match(r
"^[\s]*(.*)[\s]*([\\]?)[\s]*$", line
)
187 self
.variables
[m
].append(r
.group(1))
191 if re
.match(r
"^#", line
):
194 r
= re
.match(r
"^include[\s]+(.*)", line
)
196 self
.includes
.append(Item(idx
, [r
.group(1)]))
199 # Collect known variables
200 for k
in list(kw
.variables
.keys()):
202 r
"^[\s]*("+k
+r
")[\s]*=[\s]*([^\\]*)[\s]*([\\]?)[\s]*$", line
)
206 if v
in self
.variables
.keys():
207 warnings
.warn("Variable '"+v
+"' redefined line "+idx
)
208 self
.variables
[k
] = Item(idx
, [v
])
214 # Collect known targets
215 for k
in list(kw
.targets
.keys()):
217 "^"+k
+r
"[\s]*:[\s]*(.*)[\s]*([\\]?)[\s]*$", line
)
221 if v
in self
.targets
.keys():
222 warnings
.warn("Target '"+v
+"' redefined line "+idx
)
223 self
.targets
[k
] = Item(idx
, [v
])
235 with
open(os
.path
.join(self
.path
, "Makefile"), 'w') as f
:
236 for line
in self
.contents
:
245 for i
in iter(self
.includes
):
246 print("{0:>3}: {1}".format(i
.line(), i
.value()))
251 for k
,i
in iter(sorted(self
.variables
.items())):
252 print("{0:>3}: {1:<24}= {2}".format(i
.line(), k
, i
.value()))
257 for k
,i
in iter(sorted(self
.targets
.items())):
258 print("{0:>3}: {1:<24}= {2}".format(i
.line(), k
, i
.value()))
262 def run(self
, targets
):
267 logger
.debug('Executing \'gmake %s\' in %s', targets
, path
)
269 proc
= subprocess
.Popen(['gmake', '-s', targets
],
270 stdout
=subprocess
.PIPE
,
271 stderr
=subprocess
.PIPE
,
273 universal_newlines
=True)
274 stdout
, stderr
= proc
.communicate()
276 for out
in stdout
.splitlines():
277 result
.append(out
.rstrip())
280 if proc
.returncode
!= 0:
281 logger
.debug('exit: %d, %s', proc
.returncode
, stderr
)
285 def print_value(self
, name
):
286 return self
.run('print-value-'+name
)[0]
288 def build_style(self
):
289 return self
.variables
['BUILD_STYLE'].value()
291 def build_bits(self
):
292 return self
.variables
['BUILD_BITS'].value()
294 def has_mk_include(self
, name
):
295 for i
in iter(self
.includes
):
296 if re
.match('^.*/'+name
+'.mk$', i
.value()):
300 def get_mk_include(self
, name
):
301 for i
in iter(self
.includes
):
302 if re
.match('^.*/'+name
+'.mk$', i
.value()):
306 def has_variable(self
, variable
):
307 return variable
in self
.variables
309 def variable(self
, variable
):
310 return self
.variables
[variable
]
312 def remove_variable(self
, variable
):
313 idx
= self
.variable(variable
).line()
314 del self
.contents
[idx
]
317 def set_variable(self
, variable
, value
, line
=None):
318 if not self
.has_variable(variable
):
319 self
.variables
[variable
] = Item(None)
320 self
.variables
[variable
].set_value(str(value
))
322 contents
= self
.contents
[0:line
]
323 contents
.extend(self
.variable_assignment(variable
))
324 contents
.extend(self
.contents
[line
:])
325 self
.update(contents
)
328 idx
= self
.variables
[variable
].line()
329 oln
= self
.variables
[variable
].length()
330 self
.variables
[variable
].set_value(str(value
))
331 nln
= self
.variables
[variable
].length()
335 if line
== idx
and nln
== 1 and oln
== 1:
336 self
.contents
[idx
] = self
.variable_assignment(variable
)[0]
338 contents
= self
.contents
[0:line
]
339 contents
.extend(self
.variable_assignment(variable
))
340 contents
.extend(self
.contents
[line
:idx
])
341 contents
.extend(self
.contents
[idx
+oln
:])
342 self
.update(contents
)
344 contents
= self
.contents
[0:idx
]
345 contents
.extend(self
.contents
[idx
+oln
:line
])
346 contents
.extend(self
.variable_assignment(variable
))
347 contents
.extend(self
.contents
[line
:])
348 self
.update(contents
)
349 # Add variable at given line
350 elif line
is not None:
351 contents
= self
.contents
[0:line
]
352 contents
.extend(self
.variable_assignment(variable
))
353 contents
.extend(self
.contents
[line
:])
354 self
.update(contents
)
356 def set_archive_hash(self
, checksum
):
357 self
.set_variable('COMPONENT_ARCHIVE_HASH', "sha256:"+str(checksum
))
359 def variable_assignment(self
, variable
):
360 return self
.variables
[variable
].variable_assignment(variable
)
362 def has_target(self
, target
):
363 return target
in self
.targets
365 def target(self
, target
):
366 return self
.targets
[target
]
368 def target_definition(self
, target
):
369 return self
.targets
[target
].target_definition(target
)
371 def required_packages(self
):
372 return self
.run("print-value-REQUIRED_PACKAGES")[0]
375 # Default build style is configure
376 if not self
.has_variable('BUILD_STYLE'):
378 is_py
= (self
.build_style() == 'setup.py')
379 urlnone
= (not self
.has_variable('COMPONENT_ARCHIVE_URL'))
380 urlpipy
= urlnone
or (self
.variable('COMPONENT_ARCHIVE_URL').value() == '$(call pypi_url)')
381 return is_py
and urlpipy
383 def get_pypi_data(self
):
384 name
= self
.print_value('COMPONENT_PYPI')
385 jsurl
= "https://pypi.python.org/pypi/%s/json" % name
387 f
= urlopen(jsurl
, data
=None)
388 except HTTPError
as e
:
389 if e
.getcode() == 404:
390 print("Unknown component '%s'" % name
)
392 printIOError(e
, "Can't open PyPI JSON url %s" % jsurl
)
395 printIOError(e
, "Can't open PyPI JSON url %s" % jsurl
)
397 content
= f
.read().decode("utf-8")
398 return json
.loads(content
)
402 return "$("+variable
+")"
405 def directory_variable(subdir
):
406 return Makefile
.value("WS_"+subdir
.upper().replace("-","_"))
409 def makefile_path(name
):
410 return os
.path
.join("$(WS_MAKE_RULES)", name
+".mk")
413 def target_value(name
, bits
):
414 return Makefile
.value(name
.upper()+"_"+bits
)