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 # Copyright (c) 2008, 2015, Oracle and/or its affiliates. All rights reserved.
25 from __future__
import print_function
43 from distutils
.errors
import DistutilsError
, DistutilsFileError
44 from distutils
.core
import setup
, Extension
45 from distutils
.cmd
import Command
46 from distutils
.command
.install
import install
as _install
47 from distutils
.command
.install_data
import install_data
as _install_data
48 from distutils
.command
.install_lib
import install_lib
as _install_lib
49 from distutils
.command
.build
import build
as _build
50 from distutils
.command
.build_ext
import build_ext
as _build_ext
51 from distutils
.command
.build_py
import build_py
as _build_py
52 from distutils
.command
.bdist
import bdist
as _bdist
53 from distutils
.command
.clean
import clean
as _clean
54 from distutils
.dist
import Distribution
55 from distutils
import log
57 from distutils
.sysconfig
import get_python_inc
58 import distutils
.dep_util
as dep_util
59 import distutils
.dir_util
as dir_util
60 import distutils
.file_util
as file_util
61 import distutils
.util
as util
62 import distutils
.ccompiler
63 from distutils
.unixccompiler
import UnixCCompiler
65 osname
= platform
.uname()[0].lower()
66 ostype
= arch
= 'unknown'
67 if osname
== 'sunos' or osname
== 'unleashed':
68 arch
= platform
.processor()
70 elif osname
== 'linux':
71 arch
= "linux_" + platform
.machine()
73 elif osname
== 'windows':
76 elif osname
== 'darwin':
83 pwd
= os
.path
.normpath(sys
.path
[0])
85 # the version of pylint that we must have in order to run the pylint checks.
86 req_pylint_version
= "0.25.2"
89 # Unbuffer stdout and stderr. This helps to ensure that subprocess output
90 # is properly interleaved with output from this program.
92 sys
.stdout
= os
.fdopen(sys
.stdout
.fileno(), "w", 0)
93 sys
.stderr
= os
.fdopen(sys
.stderr
.fileno(), "w", 0)
95 dist_dir
= os
.path
.normpath(os
.path
.join(pwd
, os
.pardir
, "proto", "dist_" + arch
))
96 build_dir
= os
.path
.normpath(os
.path
.join(pwd
, os
.pardir
, "proto", "build_" + arch
))
97 if "ROOT" in os
.environ
and os
.environ
["ROOT"] != "":
98 root_dir
= os
.environ
["ROOT"]
100 root_dir
= os
.path
.normpath(os
.path
.join(pwd
, os
.pardir
, "proto", "root_" + arch
))
101 pkgs_dir
= os
.path
.normpath(os
.path
.join(pwd
, os
.pardir
, "packages", arch
))
102 extern_dir
= os
.path
.normpath(os
.path
.join(pwd
, "extern"))
104 # Extract Python minor version.
105 py_version
= '.'.join(platform
.python_version_tuple()[:2])
106 assert py_version
in ('2.6', '2.7')
107 py_install_dir
= 'usr/lib/python' + py_version
+ '/vendor-packages'
109 scripts_dir
= 'usr/bin'
111 svc_method_dir
= 'lib/svc/method'
112 svc_share_dir
= 'lib/svc/share'
114 man1_dir
= 'usr/share/man/man1'
115 man1m_dir
= 'usr/share/man/man1m'
116 man5_dir
= 'usr/share/man/man5'
117 man1_ja_JP_dir
= 'usr/share/man/ja_JP.UTF-8/man1'
118 man1m_ja_JP_dir
= 'usr/share/man/ja_JP.UTF-8/man1m'
119 man5_ja_JP_dir
= 'usr/share/man/ja_JP.UTF-8/man5'
120 man1_zh_CN_dir
= 'usr/share/man/zh_CN.UTF-8/man1'
121 man1m_zh_CN_dir
= 'usr/share/man/zh_CN.UTF-8/man1m'
122 man5_zh_CN_dir
= 'usr/share/man/zh_CN.UTF-8/man5'
124 ignored_deps_dir
= 'usr/share/pkg/ignored_deps'
125 rad_dir
= 'usr/share/lib/pkg'
126 resource_dir
= 'usr/share/lib/pkg'
127 transform_dir
= 'usr/share/pkg/transforms'
128 smf_app_dir
= 'lib/svc/manifest/application/pkg'
129 execattrd_dir
= 'etc/security/exec_attr.d'
130 authattrd_dir
= 'etc/security/auth_attr.d'
131 userattrd_dir
= 'etc/user_attr.d'
132 sysrepo_dir
= 'etc/pkg/sysrepo'
133 sysrepo_logs_dir
= 'var/log/pkg/sysrepo'
134 sysrepo_cache_dir
= 'var/cache/pkg/sysrepo'
135 depot_dir
= 'etc/pkg/depot'
136 depot_conf_dir
= 'etc/pkg/depot/conf.d'
137 depot_logs_dir
= 'var/log/pkg/depot'
138 depot_cache_dir
= 'var/cache/pkg/depot'
139 locale_dir
= 'usr/share/locale'
140 mirror_logs_dir
= 'var/log/pkg/mirror'
141 mirror_cache_dir
= 'var/cache/pkg/mirror'
144 # A list of source, destination tuples of modules which should be hardlinked
145 # together if the os supports it and otherwise copied.
146 hardlink_modules
= []
150 ['client.py', 'pkg'],
151 ['pkgdep.py', 'pkgdepend'],
152 ['pkgrepo.py', 'pkgrepo'],
153 ['util/publish/pkgdiff.py', 'pkgdiff'],
154 ['util/publish/pkgfmt.py', 'pkgfmt'],
155 ['util/publish/pkglint.py', 'pkglint'],
156 ['util/publish/pkgmerge.py', 'pkgmerge'],
157 ['util/publish/pkgmogrify.py', 'pkgmogrify'],
158 ['util/publish/pkgsurf.py', 'pkgsurf'],
159 ['publish.py', 'pkgsend'],
160 ['pull.py', 'pkgrecv'],
161 ['sign.py', 'pkgsign'],
164 ['depot.py', 'pkg.depotd'],
165 ['sysrepo.py', 'pkg.sysrepo'],
166 ['depot-config.py', "pkg.depot-config"]
169 ['svc/svc-pkg-depot', 'svc-pkg-depot'],
170 ['svc/svc-pkg-mirror', 'svc-pkg-mirror'],
171 ['svc/svc-pkg-repositories-setup',
172 'svc-pkg-repositories-setup'],
173 ['svc/svc-pkg-server', 'svc-pkg-server'],
174 ['svc/svc-pkg-sysrepo', 'svc-pkg-sysrepo'],
177 ['svc/pkg5_include.sh', 'pkg5_include.sh'],
180 ["rad-invoke.py", "rad-invoke"],
186 ['client.py', 'client.py'],
187 ['pkgrepo.py', 'pkgrepo.py'],
188 ['publish.py', 'publish.py'],
189 ['pull.py', 'pull.py'],
190 ['scripts/pkg.bat', 'pkg.bat'],
191 ['scripts/pkgsend.bat', 'pkgsend.bat'],
192 ['scripts/pkgrecv.bat', 'pkgrecv.bat'],
195 ['depot.py', 'depot.py'],
196 ['scripts/pkg.depotd.bat', 'pkg.depotd.bat'],
200 scripts_other_unix
= {
202 ['client.py', 'client.py'],
203 ['pkgdep.py', 'pkgdep'],
204 ['util/publish/pkgdiff.py', 'pkgdiff'],
205 ['util/publish/pkgfmt.py', 'pkgfmt'],
206 ['util/publish/pkgmogrify.py', 'pkgmogrify'],
207 ['pull.py', 'pull.py'],
208 ['publish.py', 'publish.py'],
209 ['scripts/pkg.sh', 'pkg'],
210 ['scripts/pkgsend.sh', 'pkgsend'],
211 ['scripts/pkgrecv.sh', 'pkgrecv'],
214 ['depot.py', 'depot.py'],
215 ['scripts/pkg.depotd.sh', 'pkg.depotd'],
218 ["rad-invoke.py", "rad-invoke"],
222 # indexed by 'osname'
224 "sunos": scripts_sunos
,
225 "unleashed": scripts_sunos
,
226 "linux": scripts_other_unix
,
227 "windows": scripts_windows
,
228 "darwin": scripts_other_unix
,
229 "aix" : scripts_other_unix
,
230 "unknown": scripts_sunos
,
249 'man/pkg.depot-config.1m',
258 'man/ja_JP/pkgdepend.1',
259 'man/ja_JP/pkgdiff.1',
260 'man/ja_JP/pkgfmt.1',
261 'man/ja_JP/pkglint.1',
262 'man/ja_JP/pkgmerge.1',
263 'man/ja_JP/pkgmogrify.1',
264 'man/ja_JP/pkgsend.1',
265 'man/ja_JP/pkgsign.1',
266 'man/ja_JP/pkgrecv.1',
267 'man/ja_JP/pkgrepo.1',
270 'man/ja_JP/pkg.depotd.1m',
271 'man/ja_JP/pkg.sysrepo.1m'
279 'man/zh_CN/pkgdepend.1',
280 'man/zh_CN/pkgdiff.1',
281 'man/zh_CN/pkgfmt.1',
282 'man/zh_CN/pkglint.1',
283 'man/zh_CN/pkgmerge.1',
284 'man/zh_CN/pkgmogrify.1',
285 'man/zh_CN/pkgsend.1',
286 'man/zh_CN/pkgsign.1',
287 'man/zh_CN/pkgrecv.1',
288 'man/zh_CN/pkgrepo.1',
290 man1m_zh_CN_files
= [
291 'man/zh_CN/pkg.depotd.1m',
292 'man/zh_CN/pkg.sysrepo.1m'
303 'pkg.client.linkedimage',
304 'pkg.client.transport',
315 'pkg.client.__init__',
317 'pkg.client.linkedimage',
318 'pkg.client.pkg_solver',
319 'pkg.client.pkgdefs',
320 'pkg.client.pkgremote',
321 'pkg.client.plandesc',
322 'pkg.client.printengine',
323 'pkg.client.progress',
329 for entry
in os
.walk("web"):
330 web_dir
, dirs
, files
= entry
333 web_files
.append((os
.path
.join(resource_dir
, web_dir
), [
334 os
.path
.join(web_dir
, f
) for f
in files
337 # install same set of files in "en/" in "__LOCALE__/ as well"
338 # for localizable file package (regarding themes, install
339 # theme "oracle.com" only)
340 if os
.path
.basename(web_dir
) == "en" and \
341 os
.path
.dirname(web_dir
) in ("web", "web/_themes/oracle.com"):
342 web_files
.append((os
.path
.join(resource_dir
,
343 os
.path
.dirname(web_dir
), "__LOCALE__"), [
344 os
.path
.join(web_dir
, f
) for f
in files
350 'svc/pkg-mirror.xml',
351 'svc/pkg-repositories-setup.xml',
352 'svc/pkg-server.xml',
353 'svc/pkg-system-repository.xml',
354 'svc/zoneproxy-client.xml',
358 'util/opensolaris.org.sections',
362 'util/publish/transforms/developer',
363 'util/publish/transforms/documentation',
364 'util/publish/transforms/locale',
365 'util/publish/transforms/smf-manifests'
368 'util/apache2/sysrepo/sysrepo_p5p.py',
369 'util/apache2/sysrepo/sysrepo_httpd.conf.mako',
370 'util/apache2/sysrepo/sysrepo_publisher_response.mako',
372 sysrepo_log_stubs
= [
373 'util/apache2/sysrepo/logs/access_log',
374 'util/apache2/sysrepo/logs/error_log',
375 'util/apache2/sysrepo/logs/rewrite.log',
378 'util/apache2/depot/depot.conf.mako',
379 'util/apache2/depot/depot_httpd.conf.mako',
380 'util/apache2/depot/depot_index.py',
381 'util/apache2/depot/depot_httpd_ssl_protocol.conf',
384 'util/apache2/depot/logs/access_log',
385 'util/apache2/depot/logs/error_log',
386 'util/apache2/depot/logs/rewrite.log',
388 ignored_deps_files
= []
390 # The apache-based depot includes an shtml file we add to the resource dir
391 web_files
.append((os
.path
.join(resource_dir
, "web"),
392 ["util/apache2/depot/repos.shtml"]))
394 'util/misc/exec_attr.d/package:pkg',
396 authattrd_files
= ['util/misc/auth_attr.d/package:pkg']
397 userattrd_files
= ['util/misc/user_attr.d/package:pkg']
399 'ar ca cs de es fr he hu id it ja ko nl pl pt_BR ru sk sv zh_CN zh_HK zh_TW'.split()
405 'modules/syscallat.c'
412 'modules/elfextract.c',
419 'modules/actions/_actions.c'
422 'modules/actions/_common.c'
428 'modules/solver/solver.c',
429 'modules/solver/py_solver.c'
431 solver_link_args
= ["-lm", "-lc"]
432 if osname
== 'sunos' or osname
== 'unleashed':
433 solver_link_args
= ["-ztext"] + solver_link_args
435 # Runs lint on the extension module source code
436 class pylint_func(Command
):
437 description
= "Runs pylint tools over IPS python source code"
440 def initialize_options(self
):
443 def finalize_options(self
):
446 # Make string shell-friendly
449 return astring
.replace(' ', '\\ ')
451 def run(self
, quiet
=False):
453 def supported_pylint_ver(version
):
454 """Compare the installed version against the version
455 we require to build with, returning False if the version
456 is too old. It's tempting to use pkg.version.Version
457 here, but since that's a build artifact, we'll do it
459 inst_pylint_ver
= version
.split(".")
460 req_pylint_ver
= req_pylint_version
.split(".")
462 # if the lists are of different lengths, we just
463 # compare with the precision we have.
464 vers_comp
= zip(inst_pylint_ver
, req_pylint_ver
)
465 for inst
, req
in vers_comp
:
467 if int(inst
) < int(req
):
470 # if we somehow get non-numeric version
471 # components, we ignore them.
475 # it's fine to default to the required version - the build will
476 # break if the installed version is incompatible and $PYLINT_VER
477 # didn't get set, somehow.
478 pylint_ver_str
= os
.environ
.get("PYLINT_VER",
480 if pylint_ver_str
== "":
481 pylint_ver_str
= req_pylint_version
483 if os
.environ
.get("PKG_SKIP_PYLINT"):
484 log
.warn("WARNING: skipping pylint checks: "
485 "$PKG_SKIP_PYLINT was set")
487 elif not pylint_ver_str
or \
488 not supported_pylint_ver(pylint_ver_str
):
489 log
.warn("WARNING: skipping pylint checks: the "
490 "installed version {0} is older than version {1}".format(
491 pylint_ver_str
, req_pylint_version
))
494 proto
= os
.path
.join(root_dir
, py_install_dir
)
495 sys
.path
.insert(0, proto
)
497 # Insert tests directory onto sys.path so any custom checkers
499 sys
.path
.insert(0, os
.path
.join(pwd
, 'tests'))
500 # assumes pylint is accessible on the sys.path
501 from pylint
import lint
504 # For some reason, the load-plugins option, when used in the
505 # rcfile, does not work, so we put it here instead, to load
506 # our custom checkers.
508 # Unfortunately, pylint seems pretty fragile and will crash if
509 # we try to run it over all the current pkg source. Hence for
510 # now we only run it over a subset of the source. As source
511 # files are made pylint clean they should be added to the
512 # pylint_targets list.
514 args
= ['--load-plugins=multiplatform']
516 args
+= ['--reports=no']
517 args
+= ['--rcfile', os
.path
.join(pwd
, 'tests', 'pylintrc')]
518 args
+= pylint_targets
522 class pylint_func_quiet(pylint_func
):
524 def run(self
, quiet
=False):
525 pylint_func
.run(self
, quiet
=True)
528 include_dirs
= [ 'modules' ]
529 lint_flags
= [ '-u', '-axms', '-erroff=E_NAME_DEF_NOT_USED2' ]
531 # Runs lint on the extension module source code
532 class clint_func(Command
):
533 description
= "Runs lint tools over IPS C extension source code"
536 def initialize_options(self
):
539 def finalize_options(self
):
542 # Make string shell-friendly
545 return astring
.replace(' ', '\\ ')
548 if "LINT" in os
.environ
and os
.environ
["LINT"] != "":
549 lint
= [os
.environ
["LINT"]]
552 if osname
in ['sunos', 'unleashed', 'linux']:
553 archcmd
= lint
+ lint_flags
+ \
554 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
555 ['-I' + self
.escape(get_python_inc())] + \
557 elfcmd
= lint
+ lint_flags
+ \
558 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
559 ['-I' + self
.escape(get_python_inc())] + \
560 ["{0}{1}".format("-l", k
) for k
in elf_libraries
] + \
562 _actionscmd
= lint
+ lint_flags
+ \
563 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
564 ['-I' + self
.escape(get_python_inc())] + \
566 _actcommcmd
= lint
+ lint_flags
+ \
567 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
568 ['-I' + self
.escape(get_python_inc())] + \
570 _varcetcmd
= lint
+ lint_flags
+ \
571 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
572 ['-I' + self
.escape(get_python_inc())] + \
574 pspawncmd
= lint
+ lint_flags
+ \
575 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
576 ['-I' + self
.escape(get_python_inc())] + \
578 syscallatcmd
= lint
+ lint_flags
+ \
579 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
580 ['-I' + self
.escape(get_python_inc())] + \
582 sysattrcmd
= lint
+ lint_flags
+ \
583 ["{0}{1}".format("-I", k
) for k
in include_dirs
] + \
584 ['-I' + self
.escape(get_python_inc())] + \
585 ["{0}{1}".format("-l", k
) for k
in sysattr_libraries
] + \
588 print(" ".join(archcmd
))
589 os
.system(" ".join(archcmd
))
590 print(" ".join(elfcmd
))
591 os
.system(" ".join(elfcmd
))
592 print(" ".join(_actionscmd
))
593 os
.system(" ".join(_actionscmd
))
594 print(" ".join(_actcommcmd
))
595 os
.system(" ".join(_actcommcmd
))
596 print(" ".join(_varcetcmd
))
597 os
.system(" ".join(_varcetcmd
))
598 print(" ".join(pspawncmd
))
599 os
.system(" ".join(pspawncmd
))
600 print(" ".join(syscallatcmd
))
601 os
.system(" ".join(syscallatcmd
))
602 print(" ".join(sysattrcmd
))
603 os
.system(" ".join(sysattrcmd
))
606 # Runs both C and Python lint
607 class lint_func(Command
):
608 description
= "Runs C and Python lint checkers"
611 def initialize_options(self
):
614 def finalize_options(self
):
617 # Make string shell-friendly
620 return astring
.replace(' ', '\\ ')
623 clint_func(Distribution()).run()
624 pylint_func(Distribution()).run()
626 class install_func(_install
):
627 def initialize_options(self
):
628 _install
.initialize_options(self
)
630 # PRIVATE_BUILD set in the environment tells us to put the build
631 # directory into the .pyc files, rather than the final
632 # installation directory.
633 private_build
= os
.getenv("PRIVATE_BUILD", None)
635 if private_build
is None:
636 self
.install_lib
= py_install_dir
637 self
.install_data
= os
.path
.sep
640 self
.install_lib
= os
.path
.join(root_dir
, py_install_dir
)
641 self
.install_data
= root_dir
643 # This is used when installing scripts, below, but it isn't a
644 # standard distutils variable.
645 self
.root_dir
= root_dir
648 """At the end of the install function, we need to rename some
649 files because distutils provides no way to rename files as they
650 are placed in their install locations.
655 for o_src
, o_dest
in hardlink_modules
:
656 for e
in [".py", ".pyc"]:
657 src
= util
.change_root(self
.root_dir
, o_src
+ e
)
658 dest
= util
.change_root(
659 self
.root_dir
, o_dest
+ e
)
660 if ostype
== "posix":
661 if os
.path
.exists(dest
) and \
662 os
.stat(src
)[stat
.ST_INO
] != \
663 os
.stat(dest
)[stat
.ST_INO
]:
665 file_util
.copy_file(src
, dest
,
666 link
="hard", update
=1)
668 file_util
.copy_file(src
, dest
, update
=1)
670 # Don't install the scripts for python 2.6.
671 if py_version
== '2.6':
673 for d
, files
in scripts
[osname
].iteritems():
674 for (srcname
, dstname
) in files
:
675 dst_dir
= util
.change_root(self
.root_dir
, d
)
676 dst_path
= util
.change_root(self
.root_dir
,
677 os
.path
.join(d
, dstname
))
678 dir_util
.mkpath(dst_dir
, verbose
=True)
679 file_util
.copy_file(srcname
, dst_path
, update
=True)
680 # make scripts executable
682 os
.stat(dst_path
).st_mode
683 | stat
.S_IXUSR | stat
.S_IXGRP | stat
.S_IXOTH
)
685 class install_lib_func(_install_lib
):
686 """Remove the target files prior to the standard install_lib procedure
687 if the build_py module has determined that they've actually changed.
688 This may be needed when a module's timestamp goes backwards in time, if
689 a working-directory change is reverted, or an older changeset is checked
694 build_py
= self
.get_finalized_command("build_py")
695 prefix_len
= len(self
.build_dir
) + 1
696 for p
in build_py
.copied
:
697 id_p
= os
.path
.join(self
.install_dir
, p
[prefix_len
:])
701 if self
.optimize
> 0:
703 return _install_lib
.install(self
)
705 class install_data_func(_install_data
):
706 """Enhance the standard install_data subcommand to take not only a list
707 of filenames, but a list of source and destination filename tuples, for
708 the cases where a filename needs to be renamed between the two
712 self
.mkpath(self
.install_dir
)
713 for f
in self
.data_files
:
715 dir = util
.convert_path(dir)
716 if not os
.path
.isabs(dir):
717 dir = os
.path
.join(self
.install_dir
, dir)
719 dir = change_root(self
.root
, dir)
723 self
.outfiles
.append(dir)
726 if isinstance(file, basestring
):
728 outfile
= os
.path
.join(dir,
729 os
.path
.basename(file))
731 infile
, outfile
= file
732 infile
= util
.convert_path(infile
)
733 outfile
= util
.convert_path(outfile
)
734 if os
.path
.sep
not in outfile
:
735 outfile
= os
.path
.join(dir,
737 self
.copy_file(infile
, outfile
)
738 self
.outfiles
.append(outfile
)
740 def run_cmd(args
, swdir
, updenv
=None, ignerr
=False, savestderr
=None):
742 # use temp environment modified with the given dict
743 env
= os
.environ
.copy()
746 # just use environment of this (parent) process as is
749 # send stderr to devnull
750 stderr
= open(os
.devnull
)
754 # just use stderr of this (parent) process
756 ret
= subprocess
.Popen(args
, cwd
=swdir
, env
=env
,
757 stderr
=stderr
).wait()
761 print("install failed and returned {0:d}.".format(ret
),
763 print("Command was: {0}".format(" ".join(args
)),
770 def _copy_file_contents(src
, dst
, buffer_size
=16*1024):
771 """A clone of distutils.file_util._copy_file_contents() that strips the
772 CDDL text. For Python files, we replace the CDDL text with an equal
773 number of empty comment lines so that line numbers match between the
774 source and destination files."""
776 # Match the lines between and including the CDDL header signposts, as
777 # well as empty comment lines before and after, if they exist.
778 cddl_re
= re
.compile("\n(#\s*\n)?^[^\n]*CDDL HEADER START.+"
779 "CDDL HEADER END[^\n]*$(\n#\s*$)?", re
.MULTILINE|re
.DOTALL
)
781 with
file(src
, "r") as sfp
:
784 except EnvironmentError as e
:
785 if e
.errno
!= errno
.ENOENT
:
786 raise DistutilsFileError("could not delete "
787 "'{0}': {1}".format(dst
, e
))
789 with
file(dst
, "w") as dfp
:
791 buf
= sfp
.read(buffer_size
)
794 if src
.endswith(".py"):
795 match
= cddl_re
.search(buf
)
797 # replace the CDDL expression
798 # with the same number of empty
799 # comment lines as the cddl_re
802 match
.start():match
.end()]
804 substr
.split("\n")) - 2
805 blanks
= "#\n" * count
806 buf
= cddl_re
.sub("\n" + blanks
,
809 buf
= cddl_re
.sub("", buf
)
812 # Make file_util use our version of _copy_file_contents
813 file_util
._copy
_file
_contents
= _copy_file_contents
815 def intltool_update_maintain():
816 """Check if scope of localization looks up-to-date or possibly not,
817 by comparing file set described in po/POTFILES.{in,skip} and
818 actual source files (e.g. .py) detected.
824 "/usr/bin/intltool-update", "--maintain"
826 print(" ".join(args
))
827 podir
= os
.path
.join(os
.getcwd(), "po")
828 run_cmd(args
, podir
, updenv
={"LC_ALL": "C"}, ignerr
=True)
830 if os
.path
.exists("po/missing"):
831 print("New file(s) with translatable strings detected:",
833 missing
= open("po/missing", "r")
834 print("--------", file=sys
.stderr
)
836 print("{0}".format(fn
.strip()), file=sys
.stderr
)
837 print("--------", file=sys
.stderr
)
840 Please evaluate whether any of the above file(s) needs localization.
841 If so, please add its name to po/POTFILES.in. If not (e.g., it's not
842 delivered), please add its name to po/POTFILES.skip.
843 Please be sure to maintain alphabetical ordering in both files.""", file=sys
.stderr
)
846 if os
.path
.exists("po/notexist"):
848 The following files are listed in po/POTFILES.in, but no longer exist
849 in the workspace:""", file=sys
.stderr
)
850 notexist
= open("po/notexist", "r")
851 print("--------", file=sys
.stderr
)
853 print("{0}".format(fn
.strip()), file=sys
.stderr
)
854 print("--------", file=sys
.stderr
)
857 print("Please remove the file names from po/POTFILES.in",
861 def intltool_update_pot():
862 """Generate pkg.pot by extracting localizable strings from source
868 "/usr/bin/intltool-update", "--pot"
870 print(" ".join(args
))
871 podir
= os
.path
.join(os
.getcwd(), "po")
873 updenv
={"LC_ALL": "C", "XGETTEXT": "/usr/gnu/bin/xgettext"})
875 if not os
.path
.exists("po/pkg.pot"):
876 print("Failed in generating pkg.pot.", file=sys
.stderr
)
879 def intltool_merge(src
, dst
):
880 if not dep_util
.newer(src
, dst
):
884 "/usr/bin/intltool-merge", "-d", "-u",
885 "-c", "po/.intltool-merge-cache", "po", src
, dst
887 print(" ".join(args
))
888 run_cmd(args
, os
.getcwd(), updenv
={"LC_ALL": "C"})
891 """Checks for common i18n messaging bugs in the source."""
894 # A list of the i18n errors we check for in the code
895 common_i18n_errors
= [
896 # This checks that messages with multiple parameters are always
897 # written using "{name}" format, rather than just "{0}"
898 "format string with unnamed arguments cannot be properly localized"
901 for line
in open("po/POTFILES.in", "r").readlines():
902 if line
.startswith("["):
904 if line
.startswith("#"):
906 src_files
.append(line
.rstrip())
909 "/usr/gnu/bin/xgettext", "--from-code=UTF-8", "-o", "/dev/null"]
912 xgettext_output_path
= tempfile
.mkstemp()[1]
913 xgettext_output
= open(xgettext_output_path
, "w")
914 run_cmd(args
, os
.getcwd(), updenv
={"LC_ALL": "C"},
915 savestderr
=xgettext_output
)
918 i18n_errs
= open("po/i18n_errs.txt", "w")
919 for line
in open(xgettext_output_path
, "r").readlines():
920 for err
in common_i18n_errors
:
922 i18n_errs
.write(line
)
927 The following i18n errors were detected and should be corrected:
928 (this list is saved in po/i18n_errs.txt)
929 """, file=sys
.stderr
)
930 for line
in open("po/i18n_errs.txt", "r"):
931 print(line
.rstrip(), file=sys
.stderr
)
933 os
.remove(xgettext_output_path
)
935 def msgfmt(src
, dst
):
936 if not dep_util
.newer(src
, dst
):
939 args
= ["/usr/bin/msgfmt", "-o", dst
, src
]
940 print(" ".join(args
))
941 run_cmd(args
, os
.getcwd())
943 def localizablexml(src
, dst
):
944 """create XML help for localization, where French part of legalnotice
947 if not dep_util
.newer(src
, dst
):
950 fsrc
= open(src
, "r")
951 fdst
= open(dst
, "w")
953 # indicates currently in French part of legalnotice
957 if in_fr
: # in French part
958 if l
.startswith('</legalnotice>'):
959 # reached end of legalnotice
962 elif l
.startswith('<para lang="fr"/>') or \
963 l
.startswith('<para lang="fr"></para>'):
972 def xml2po_gen(src
, dst
):
973 """Input is English XML file. Output is pkg_help.pot, message
974 source for next translation update.
976 if not dep_util
.newer(src
, dst
):
979 args
= ["/usr/bin/xml2po", "-o", dst
, src
]
980 print(" ".join(args
))
981 run_cmd(args
, os
.getcwd())
983 def xml2po_merge(src
, dst
, mofile
):
984 """Input is English XML file and <lang>.po file (which contains
985 translations). Output is translated XML file.
987 msgfmt(mofile
[:-3] + ".po", mofile
)
989 monewer
= dep_util
.newer(mofile
, dst
)
990 srcnewer
= dep_util
.newer(src
, dst
)
992 if not srcnewer
and not monewer
:
995 args
= ["/usr/bin/xml2po", "-t", mofile
, "-o", dst
, src
]
996 print(" ".join(args
))
997 run_cmd(args
, os
.getcwd())
999 class installfile(Command
):
1001 ("file=", "f", "source file to copy"),
1002 ("dest=", "d", "destination directory"),
1003 ("mode=", "m", "file mode"),
1006 description
= "De-CDDLing file copy"
1008 def initialize_options(self
):
1013 def finalize_options(self
):
1014 if self
.mode
is None:
1016 elif isinstance(self
.mode
, basestring
):
1018 self
.mode
= int(self
.mode
, 8)
1023 dest_file
= os
.path
.join(self
.dest
, os
.path
.basename(self
.file))
1024 ret
= self
.copy_file(self
.file, dest_file
)
1026 os
.chmod(dest_file
, self
.mode
)
1027 os
.utime(dest_file
, None)
1031 class build_func(_build
):
1032 sub_commands
= _build
.sub_commands
+ [('build_data', None)]
1034 def initialize_options(self
):
1035 _build
.initialize_options(self
)
1036 self
.build_base
= build_dir
1038 def get_hg_version():
1040 cmd
= 'hg id -i 2>/dev/null || git log --pretty=format:\'%h\' -1'
1041 p
= subprocess
.Popen(cmd
, shell
=True, stdout
= subprocess
.PIPE
)
1042 return p
.communicate()[0].strip()
1044 print("ERROR: unable to obtain mercurial/git version",
1048 def syntax_check(filename
):
1049 """ Run python's compiler over the file, and discard the results.
1050 Arrange to generate an exception if the file does not compile.
1051 This is needed because distutil's own use of pycompile (in the
1052 distutils.utils module) is broken, and doesn't stop on error. """
1054 py_compile
.compile(filename
, os
.devnull
, doraise
=True)
1055 except py_compile
.PyCompileError
as e
:
1057 for err
in e
.exc_value
:
1058 if isinstance(err
, basestring
):
1062 # Assume it's a tuple of (filename, lineno, col, code)
1063 fname
, line
, col
, code
= err
1064 res
+= "line {0:d}, column {1}, in {2}:\n{3}".format(
1065 line
, col
or "unknown", fname
, code
)
1067 raise DistutilsError(res
)
1069 # On Solaris, ld inserts the full argument to the -o option into the symbol
1070 # table. This means that the resulting object will be different depending on
1071 # the path at which the workspace lives, and not just on the interesting content
1074 # In order to work around that bug (7076871), we create a new compiler class
1075 # that looks at the argument indicating the output file, chdirs to its
1076 # directory, and runs the real link with the output file set to just the base
1079 # Unfortunately, distutils isn't too customizable in this regard, so we have to
1080 # twiddle with a couple of the names in the distutils.ccompiler namespace: we
1081 # have to add a new entry to the compiler_class dict, and we have to override
1082 # the new_compiler() function to point to our own. Luckily, our copy of
1083 # new_compiler() gets to be very simple, since we always know what we want to
1085 class MyUnixCCompiler(UnixCCompiler
):
1087 def link(self
, *args
, **kwargs
):
1089 output_filename
= args
[2]
1090 output_dir
= kwargs
.get('output_dir')
1093 assert(not output_dir
)
1094 output_dir
= os
.path
.join(cwd
, os
.path
.dirname(output_filename
))
1095 output_filename
= os
.path
.basename(output_filename
)
1096 nargs
= args
[:2] + (output_filename
,) + args
[3:]
1097 if not os
.path
.exists(output_dir
):
1098 os
.mkdir(output_dir
, 0o755)
1099 os
.chdir(output_dir
)
1101 UnixCCompiler
.link(self
, *nargs
, **kwargs
)
1105 distutils
.ccompiler
.compiler_class
['myunix'] = (
1106 'unixccompiler', 'MyUnixCCompiler',
1107 'standard Unix-style compiler with a link stage modified for Solaris'
1110 def my_new_compiler(plat
=None, compiler
=None, verbose
=0, dry_run
=0, force
=0):
1111 return MyUnixCCompiler(None, dry_run
, force
)
1113 if osname
== 'sunos' or osname
== 'unleashed':
1114 distutils
.ccompiler
.new_compiler
= my_new_compiler
1116 class build_ext_func(_build_ext
):
1118 def initialize_options(self
):
1119 _build_ext
.initialize_options(self
)
1121 if osname
== 'sunos' or osname
== 'unleashed':
1122 self
.compiler
= 'myunix'
1124 def build_extension(self
, ext
):
1125 _build_ext
.build_extension(self
, ext
)
1127 def get_ext_fullpath(self
, ext_name
):
1128 return _build_ext
.get_ext_fullpath(self
, ext_name
)
1130 class build_py_func(_build_py
):
1132 def __init__(self
, dist
):
1133 ret
= _build_py
.__init
__(self
, dist
)
1137 # Gather the timestamps of the .py files in the gate, so we can
1138 # force the mtimes of the built and delivered copies to be
1139 # consistent across builds, causing their corresponding .pyc
1140 # files to be unchanged unless the .py file content changed.
1142 self
.timestamps
= {}
1146 if os
.path
.isdir(os
.path
.join(pwd
, "../.git")):
1147 pydates
= "pydates.git"
1149 p
= subprocess
.Popen(
1150 os
.path
.join(pwd
, pydates
),
1151 stdout
=subprocess
.PIPE
)
1153 for line
in p
.stdout
:
1154 stamp
, path
= line
.split()
1155 stamp
= float(stamp
)
1156 self
.timestamps
[path
] = stamp
1159 print("ERROR: unable to gather .py timestamps",
1165 # override the build_module method to do VERSION substitution on
1167 def build_module (self
, module
, module_file
, package
):
1169 if module
== "__init__" and package
== "pkg":
1170 versionre
= '(?m)^VERSION[^"]*"([^"]*)"'
1171 # Grab the previously-built version out of the build
1175 file(self
.get_module_outfile(self
.build_lib
,
1176 [package
], module
)).read()
1177 ov
= re
.search(versionre
, ocontent
).group(1)
1180 v
= get_hg_version()
1181 vstr
= 'VERSION = "{0}"'.format(v
)
1182 # If the versions haven't changed, there's no need to
1187 mcontent
= file(module_file
).read()
1188 mcontent
= re
.sub(versionre
, vstr
, mcontent
)
1189 tmpfd
, tmp_file
= tempfile
.mkstemp()
1190 os
.write(tmpfd
, mcontent
)
1192 print("doing version substitution: ", v
)
1193 rv
= _build_py
.build_module(self
, module
, tmp_file
, package
)
1197 # Will raise a DistutilsError on failure.
1198 syntax_check(module_file
)
1200 return _build_py
.build_module(self
, module
, module_file
, package
)
1202 def copy_file(self
, infile
, outfile
, preserve_mode
=1, preserve_times
=1,
1203 link
=None, level
=1):
1205 # If the timestamp on the source file (coming from mercurial if
1206 # unchanged, or from the filesystem if changed) doesn't match
1207 # the filesystem timestamp on the destination, then force the
1208 # copy to make sure the right data is in place.
1211 dst_mtime
= os
.stat(outfile
).st_mtime
1212 except OSError as e
:
1213 if e
.errno
!= errno
.ENOENT
:
1215 dst_mtime
= time
.time()
1217 # The timestamp for __init__.py is the timestamp for the
1219 if outfile
.endswith("/pkg/__init__.py"):
1220 src_mtime
= self
.timestamps
["."]
1222 src_mtime
= self
.timestamps
.get(
1223 os
.path
.join("src", infile
), self
.timestamps
["."])
1225 # Force a copy of the file if the source timestamp is different
1226 # from that of the destination, not just if it's newer. This
1227 # allows timestamps in the working directory to regress (for
1228 # instance, following the reversion of a change).
1229 if dst_mtime
!= src_mtime
:
1232 dst
, copied
= _build_py
.copy_file(self
, infile
, outfile
,
1233 preserve_mode
, preserve_times
, link
, level
)
1236 dst
, copied
= outfile
, 0
1238 # If we copied the file, then we need to go and readjust the
1239 # timestamp on the file to match what we have in our database.
1240 # Save the filename aside for our version of install_lib.
1241 if copied
and dst
.endswith(".py"):
1242 os
.utime(dst
, (src_mtime
, src_mtime
))
1243 self
.copied
.append(dst
)
1247 def manpage_input_dir(path
):
1248 """Convert a manpage output path to the directory where its source lives."""
1250 patharr
= path
.split("/")
1251 if len(patharr
) == 4:
1253 elif len(patharr
) == 5:
1254 loc
= patharr
[-3].split(".")[0]
1256 raise RuntimeError("bad manpage path")
1257 return os
.path
.join(patharr
[0], loc
).rstrip("/")
1259 def xml2roff(files
):
1260 """Convert XML manpages to ROFF for delivery.
1262 The input should be a list of the output file paths. The corresponding
1263 inputs will be generated from this. We do it in this way so that we can
1264 share the paths with the install code.
1266 All paths should have a common manpath root. In particular, pages
1267 belonging to different localizations should be run through this function
1271 input_dir
= manpage_input_dir(files
[0])
1273 os
.path
.join(input_dir
, os
.path
.basename(f
))
1275 if dep_util
.newer(os
.path
.join(input_dir
, os
.path
.basename(f
)), f
)
1278 # Get the output dir by removing the filename and the manX
1280 output_dir
= os
.path
.join(*files
[0].split("/")[:-2])
1281 args
= ["/usr/share/xml/xsolbook/python/xml2roff.py", "-o", output_dir
]
1283 print(" ".join(args
))
1284 run_cmd(args
, os
.getcwd())
1286 class build_data_func(Command
):
1287 description
= "build data files whose source isn't in deliverable form"
1290 # As a subclass of distutils.cmd.Command, these methods are required to
1292 def initialize_options(self
):
1295 def finalize_options(self
):
1302 """Remove a file without caring whether it exists."""
1306 except OSError as e
:
1307 if e
.errno
!= errno
.ENOENT
:
1310 class clean_func(_clean
):
1311 def initialize_options(self
):
1312 _clean
.initialize_options(self
)
1313 self
.build_base
= build_dir
1318 rm_f("po/.intltool-merge-cache")
1320 for l
in pkg_locales
:
1321 rm_f("po/{0}.mo".format(l
))
1325 rm_f("po/i18n_errs.txt")
1327 #shutil.rmtree(MANPAGE_OUTPUT_ROOT, True)
1329 class clobber_func(Command
):
1331 description
= "Deletes any and all files created by setup"
1333 def initialize_options(self
):
1335 def finalize_options(self
):
1339 print("deleting " + dist_dir
)
1340 shutil
.rmtree(dist_dir
, True)
1341 print("deleting " + build_dir
)
1342 shutil
.rmtree(build_dir
, True)
1343 print("deleting " + root_dir
)
1344 shutil
.rmtree(root_dir
, True)
1345 print("deleting " + pkgs_dir
)
1346 shutil
.rmtree(pkgs_dir
, True)
1347 print("deleting " + extern_dir
)
1348 shutil
.rmtree(extern_dir
, True)
1350 class test_func(Command
):
1351 # NOTE: these options need to be in sync with tests/run.py and the
1352 # list of options stored in initialize_options below. The first entry
1353 # in each tuple must be the exact name of a member variable.
1355 ("archivedir=", 'a', "archive failed tests <dir>"),
1356 ("baselinefile=", 'b', "baseline file <file>"),
1357 ("coverage", "c", "collect code coverage data"),
1358 ("genbaseline", 'g', "generate test baseline"),
1359 ("only=", "o", "only <regex>"),
1360 ("parseable", 'p', "parseable output"),
1361 ("port=", "z", "lowest port to start a depot on"),
1362 ("timing", "t", "timing file <file>"),
1363 ("verbosemode", 'v', "run tests in verbose mode"),
1364 ("stoponerr", 'x', "stop when a baseline mismatch occurs"),
1365 ("debugoutput", 'd', "emit debugging output"),
1366 ("showonexpectedfail", 'f',
1367 "show all failure info, even for expected fails"),
1368 ("startattest=", 's', "start at indicated test"),
1369 ("jobs=", 'j', "number of parallel processes to use"),
1370 ("quiet", "q", "use the dots as the output format"),
1371 ("livesystem", 'l', "run tests on live system"),
1373 description
= "Runs unit and functional tests"
1375 def initialize_options(self
):
1377 self
.baselinefile
= ""
1378 self
.verbosemode
= 0
1380 self
.genbaseline
= 0
1384 self
.debugoutput
= 0
1385 self
.showonexpectedfail
= 0
1386 self
.startattest
= ""
1387 self
.archivedir
= ""
1391 self
.livesystem
= False
1393 def finalize_options(self
):
1398 os
.putenv('PYEXE', sys
.executable
)
1399 os
.chdir(os
.path
.join(pwd
, "tests"))
1401 # Reconstruct the cmdline and send that to run.py
1402 cmd
= [sys
.executable
, "run.py"]
1404 if "test" in sys
.argv
:
1405 args
= sys
.argv
[sys
.argv
.index("test")+1:]
1407 subprocess
.call(cmd
)
1409 class dist_func(_bdist
):
1410 def initialize_options(self
):
1411 _bdist
.initialize_options(self
)
1412 self
.dist_dir
= dist_dir
1414 # These are set to real values based on the platform, down below
1416 if osname
in ("sunos", "unleashed", "linux", "darwin"):
1417 compile_args
= [ "-O3" ]
1418 if osname
== "sunos" or osname
== "unleashed":
1427 include_dirs
= include_dirs
,
1428 extra_compile_args
= compile_args
,
1429 extra_link_args
= link_args
,
1434 include_dirs
= include_dirs
,
1435 extra_compile_args
= compile_args
,
1436 extra_link_args
= link_args
,
1441 include_dirs
= include_dirs
,
1442 extra_compile_args
= compile_args
,
1443 extra_link_args
= link_args
,
1448 include_dirs
= include_dirs
+ ["."],
1449 extra_compile_args
= compile_args
,
1450 extra_link_args
= link_args
+ solver_link_args
,
1453 elf_libraries
= None
1454 sysattr_libraries
= None
1455 data_files
= web_files
1457 'install': install_func
,
1458 'install_data': install_data_func
,
1459 'install_lib': install_lib_func
,
1460 'build': build_func
,
1461 'build_data': build_data_func
,
1462 'build_ext': build_ext_func
,
1463 'build_py': build_py_func
,
1466 'clint': clint_func
,
1467 'pylint': pylint_func
,
1468 'pylint_quiet': pylint_func_quiet
,
1469 'clean': clean_func
,
1470 'clobber': clobber_func
,
1472 'installfile': installfile
,
1475 # all builds of IPS should have manpages
1477 (man1_dir
, man1_files
),
1478 (man1m_dir
, man1m_files
),
1479 (man5_dir
, man5_files
),
1480 (man1_ja_JP_dir
, man1_ja_files
),
1481 (man1m_ja_JP_dir
, man1m_ja_files
),
1482 (man5_ja_JP_dir
, man5_ja_files
),
1483 (man1_zh_CN_dir
, man1_zh_CN_files
),
1484 (man1m_zh_CN_dir
, man1m_zh_CN_files
),
1485 (man5_zh_CN_dir
, man5_zh_CN_files
),
1486 (resource_dir
, resource_files
),
1490 (transform_dir
, transform_files
)
1494 (ignored_deps_dir
, ignored_deps_files
)
1496 if osname
== 'sunos' or osname
== 'unleashed':
1497 # Solaris-specific extensions are added here
1499 (smf_app_dir
, smf_app_files
),
1500 (execattrd_dir
, execattrd_files
),
1501 (authattrd_dir
, authattrd_files
),
1502 (userattrd_dir
, userattrd_files
),
1503 (sysrepo_dir
, sysrepo_files
),
1504 (sysrepo_logs_dir
, sysrepo_log_stubs
),
1505 (sysrepo_cache_dir
, {}),
1506 (depot_dir
, depot_files
),
1507 (depot_conf_dir
, {}),
1508 (depot_logs_dir
, depot_log_stubs
),
1509 (depot_cache_dir
, {}),
1510 (mirror_cache_dir
, {}),
1511 (mirror_logs_dir
, {}),
1514 if osname
in ['sunos', 'unleashed', 'linux']:
1515 # Unix platforms which the elf extension has been ported to
1516 # are specified here, so they are built automatically
1517 elf_libraries
= ['elf']
1522 include_dirs
= include_dirs
,
1523 libraries
= elf_libraries
,
1524 extra_compile_args
= compile_args
,
1525 extra_link_args
= link_args
,
1529 # Solaris has built-in md library and Solaris-specific arch extension
1530 # All others use OpenSSL and cross-platform arch module
1531 if osname
== 'sunos' or osname
== 'unleashed':
1532 elf_libraries
+= [ 'md' ]
1533 sysattr_libraries
= [ 'nvpair' ]
1538 include_dirs
= include_dirs
,
1539 extra_compile_args
= compile_args
,
1540 extra_link_args
= link_args
,
1545 include_dirs
= include_dirs
,
1546 extra_compile_args
= compile_args
,
1547 extra_link_args
= link_args
,
1552 include_dirs
= include_dirs
,
1553 extra_compile_args
= compile_args
,
1554 extra_link_args
= link_args
,
1559 include_dirs
= include_dirs
,
1560 libraries
= sysattr_libraries
,
1561 extra_compile_args
= compile_args
,
1562 extra_link_args
= link_args
,
1566 elf_libraries
+= [ 'ssl' ]
1568 setup(cmdclass
= cmdclasses
,
1571 package_dir
= {'pkg':'modules'},
1572 packages
= packages
,
1573 data_files
= data_files
,
1574 ext_package
= 'pkg',
1575 ext_modules
= ext_modules
,