Add anoncvs gentoo-x86 example in documentation of repos.conf.
[portage.git] / bin / install.py
1 #!/usr/bin/python
2 # Copyright 2013 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 import os
6 import stat
7 import sys
8 import subprocess
9 import traceback
10
11 from portage.util.movefile import _copyxattr
12 from portage.exception import OperationNotSupported
13
14 try:
15         from argparse import ArgumentParser
16 except ImportError:
17         # Compatibility with Python 2.6 and 3.1
18         from optparse import OptionParser
19
20         class ArgumentParser(object):
21                 def __init__(self, **kwargs):
22                         add_help = kwargs.pop("add_help", None)
23                         if add_help is not None:
24                                 kwargs["add_help_option"] = add_help
25                         parser = OptionParser(**kwargs)
26                         self.add_argument = parser.add_option
27                         self.parse_known_args = parser.parse_args
28
29 # Change back to original cwd _after_ all imports (bug #469338).
30 os.chdir(os.environ["__PORTAGE_HELPER_CWD"])
31
32 def parse_args(args):
33         """
34         Parse the command line arguments using optparse for python 2.6 compatibility
35         Args:
36           args: a list of the white space delimited command line
37         Returns:
38           tuple of the Namespace of parsed options, and a list of order parameters
39         """
40         parser = ArgumentParser(add_help=False)
41
42         parser.add_argument(
43                 "-b",
44                 action="store_true",
45                 dest="shortopt_b"
46         )
47         parser.add_argument(
48                 "--backup",
49                 action="store",
50                 dest="backup"
51                 )
52         parser.add_argument(
53                 "-c",
54                 action="store_true",
55                 dest="shortopt_c"
56         )
57         parser.add_argument(
58                 "--compare",
59                 "-C",
60                 action="store_true",
61                 dest="compare"
62         )
63         parser.add_argument(
64                 "--directory",
65                         "-d",
66                 action="store_true",
67                 dest="directory"
68         )
69         parser.add_argument(
70                 "-D",
71                 action="store_true",
72                 dest="shortopt_D"
73         )
74         parser.add_argument(
75                 "--owner",
76                 "-o",
77                 action="store", 
78                 dest="owner"
79         )
80         parser.add_argument(
81                 "--group",
82                 "-g",
83                 action="store",
84                 dest="group"
85         )
86         parser.add_argument(
87                 "--mode",
88                 "-m",
89                 action="store",
90                 dest="mode"
91         )
92         parser.add_argument(
93                 "--preserve-timestamps",
94                 "-p",
95                 action="store_true",
96                 dest="preserve_timestamps"
97         )
98         parser.add_argument(
99                 "--strip",
100                 "-s",
101                 action="store_true",
102                 dest="strip"
103         )
104         parser.add_argument(
105                 "--strip-program",
106                 action="store",
107                 dest="strip_program"
108         )
109         parser.add_argument(
110                 "--suffix",
111                 "-S",
112                 action="store",
113                 dest="suffix"
114         )
115         parser.add_argument(
116                 "--target-directory",
117                 "-t",
118                 action="store",
119                 dest="target_directory"
120         )
121         parser.add_argument(
122                 "--no-target-directory",
123                 "-T",
124                 action="store_true",
125                 dest="no_target_directory"
126         )
127         parser.add_argument(
128                 "--context",
129                 "-Z",
130                 action="store",
131                 dest="context"
132         )
133         parser.add_argument(
134                 "--verbose",
135                 "-v",
136                 action="store_true",
137                 dest="verbose"
138         )
139         parser.add_argument(
140                 "--help",
141                 action="store_true",
142                 dest="help"
143         )
144         parser.add_argument(
145                 "--version",
146                 action="store_true",
147                 dest="version"
148         )
149
150         # Use parse_known_args for maximum compatibility with
151         # getopt handling of non-option file arguments. Note
152         # that parser.add_argument("files", nargs='+') would
153         # be subtly incompatible because it requires that all
154         # of the file arguments be grouped sequentially. Also
155         # note that we have to explicitly call add_argument
156         # for known options in order for argparse to correctly
157         # separate option arguments from file arguments in all
158         # cases (it also allows for optparse compatibility).
159         parsed_args = parser.parse_known_args()
160
161         opts  = parsed_args[0]
162         files = parsed_args[1]
163         files = [f for f in files if f != "--"] # filter out "--"
164
165         return (opts, files)
166
167
168 def copy_xattrs(opts, files):
169         """
170         Copy the extended attributes using portage.util.movefile._copyxattr
171         Args:
172           opts:  Namespace of the parsed command line otions
173           files: list of ordered command line parameters which should be files/directories
174         Returns:
175           system exit code
176         """
177         if opts.directory:
178                 return os.EX_OK
179
180         if opts.target_directory is None:
181                 source, target = files[:-1], files[-1]
182                 target_is_directory = os.path.isdir(target)
183         else:
184                 source, target = files, opts.target_directory
185                 target_is_directory = True
186
187         exclude = os.environ.get("PORTAGE_XATTR_EXCLUDE", "security.* system.nfs4_acl")
188
189         try:
190                 if target_is_directory:
191                         for s in source:
192                                 abs_path = os.path.join(target, os.path.basename(s))
193                                 _copyxattr(s, abs_path, exclude=exclude)
194                 else:
195                         _copyxattr(source[0], target, exclude=exclude)
196                 return os.EX_OK
197
198         except OperationNotSupported:
199                 traceback.print_exc()
200                 return os.EX_OSERR
201
202
203 def Which(filename, path=None, exclude=None):
204         """
205         Find the absolute path of 'filename' in a given search 'path'
206         Args:
207           filename: basename of the file
208           path: colon delimited search path
209           exclude: path of file to exclude
210         """
211         if path is None:
212                 path = os.environ.get('PATH', '')
213
214         if exclude is not None:
215                 st = os.stat(exclude)
216                 exclude = (st.st_ino, st.st_dev)
217
218         for p in path.split(':'):
219                 p = os.path.join(p, filename)
220                 if os.access(p, os.X_OK):
221                         try:
222                                 st = os.stat(p)
223                         except OSError:
224                                 # file disappeared?
225                                 pass
226                         else:
227                                 if stat.S_ISREG(st.st_mode) and \
228                                         (exclude is None or exclude != (st.st_ino, st.st_dev)):
229                                         return p
230
231         return None
232
233
234 def main(args):
235         opts, files = parse_args(args)
236         install_binary = Which('install', exclude=os.environ["__PORTAGE_HELPER_PATH"])
237         if install_binary is None:
238                 sys.stderr.write("install: command not found\n")
239                 return 127
240
241         cmdline = [install_binary]
242         cmdline += args
243
244         if sys.hexversion >= 0x3000000:
245                 # We can't trust that the filesystem encoding (locale dependent)
246                 # correctly matches the arguments, so use surrogateescape to
247                 # pass through the original argv bytes for Python 3.
248                 fs_encoding = sys.getfilesystemencoding()
249                 cmdline = [x.encode(fs_encoding, 'surrogateescape') for x in cmdline]
250                 files = [x.encode(fs_encoding, 'surrogateescape') for x in files]
251                 if opts.target_directory is not None:
252                         opts.target_directory = \
253                                 opts.target_directory.encode(fs_encoding, 'surrogateescape')
254
255         returncode = subprocess.call(cmdline)
256         if returncode == os.EX_OK:
257                 returncode = copy_xattrs(opts, files)
258         return returncode
259
260
261 if __name__ == "__main__":
262         sys.exit(main(sys.argv[1:]))