├── COPYING
├── Makefile
├── README.md
├── VERSION
├── osm4routing.py
├── osmreader.cc
├── osmreader.h
├── parameters.cc
├── parse.cc
├── parse.h
├── parse.i
├── setup.py
└── virtualenv.py
/COPYING:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | CXX=g++
2 | CXXFLAGS=-W -Wall -ansi -pedantic -O3 -Wno-deprecated
3 | LDFLAGS=-lexpat -lbz2 -lpq -lboost_program_options -lboost_filesystem
4 | EXEC=osm4routing
5 |
6 | all: $(EXEC)
7 |
8 | osm4routing: parameters.o main.o osmreader.o bz2reader.o stdinreader.o csvwriter.o pqwriter.o
9 | $(CXX) -o $@ $^ $(LDFLAGS)
10 |
11 | main.o: main.cc main.h
12 | $(CXX) -o $@ -c $< $(CXXFLAGS)
13 |
14 | parameters.o: parameters.cc main.h
15 | $(CXX) -o $@ -c $< $(CXXFLAGS)
16 |
17 | osmreader.o: osmreader.cc osmreader.h reader.h
18 | $(CXX) -o $@ -c $< $(CXXFLAGS)
19 |
20 | bz2reader.o: bz2reader.cc bz2reader.h reader.h
21 | $(CXX) -o $@ -c $< $(CXXFLAGS)
22 |
23 | stdinreader.o: stdinreader.cc stdinreader.h reader.h
24 | $(CXX) -o $@ -c $< $(CXXFLAGS)
25 |
26 | csvwriter.o: csvwriter.cc csvwriter.h writer.h
27 | $(CXX) -o $@ -c $< $(CXXFLAGS)
28 |
29 | pqwriter.o: pqwriter.cc pqwriter.h writer.h
30 | $(CXX) -o $@ -c $< $(CXXFLAGS)
31 |
32 | clean:
33 | rm -rf *.o
34 |
35 | mrproper: clean
36 | rm -rf $(EXEC)
37 |
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # This project is DEPRECATED
2 |
3 | Please have a look at https://github.com/Tristramg/osm4routing2
4 |
5 | ---
6 |
7 | This tool provides an OpenStreetMap data parser to turn them into a nodes-edges
8 | adapted for routing applications.
9 |
10 | # INPUT FORMAT
11 | The input is an OpenStreetMap XML file. The file can be read:
12 | * from a plain .osm file
13 | * from a bzip2 file
14 | * from a gzip file
15 |
16 | # OUTPUT FORMAT
17 | The output can be:
18 | * a csv file
19 | * database (postgres, mysql, sqlite, postgis)
20 |
21 | In both output you'll get two files/tables:
22 | * nodes that contain
23 | * id (64 bit integer)
24 | * longitude (decimal real)
25 | * latitude (decimal real)
26 | * geometry (only in postgis)
27 |
28 | * edges that contain
29 | * id (64 bit integer)
30 | * source node id (64 bit integer)
31 | * target node id (64 bit integer)
32 | * length (real in meters),
33 | * car accessibility (integer)
34 | * car reverse accessibility (integer)
35 | * bike accessibility (integer)
36 | * bike reverse accessibility (integer)
37 | * foot accessibility (integer)
38 | * geometry (string representing a linestring in the WKT format)
39 |
40 | The accessibility is an integer describing the edge for every mean of transport.
41 | As for cars an bikes the driving direction might change those properties, the
42 | are direct (source->target direction) an reverse (target->source direction)
43 | information.
44 |
45 | The integers mean:
46 | * cars
47 | * 0 forbiden
48 | * 1 residential street
49 | * 2 tertiary road
50 | * 3 secondary road
51 | * 4 primary road
52 | * 5 trunk
53 | * 6 motorway
54 | * bike
55 | * 0 forbiden
56 | * 1 cycling lane in the opposite direction of the car flow
57 | * 2 allowed without specific equipment
58 | * 3 cycling lane
59 | * 4 bus lane allowed for cycles
60 | * 5 cycling track
61 | * foot (no distinction in made on the direction)
62 | * 0 forbiden
63 | * 1 allowed
64 |
65 |
66 | # INSTALL
67 | You need:
68 | * Boost (only for unordered_map that will be included in C++1X)
69 | Read bellow if you have troubles with it
70 | * expat (XML parser) (Ubuntu users install libexpat1-dev)
71 | * python (Ubunut users install python-dev)
72 | * swig
73 | * Python connectors for the database you wan
74 | * sqlite and text output is by default
75 | * python-psycopy2 for postgresql
76 |
77 | ```
78 | # Just run the following command
79 | sudo python setup.py install
80 | # Run it
81 | osm4routing --help
82 | ```
83 |
84 | ## Alternative:
85 | ```
86 | # If you don't have the rights to install it system-wide, or don't want to, use virtualenv:
87 | # Create a virtual environment and activate it
88 | python virtualenv.py env
89 | source env/bin/activate
90 | python setup.py install
91 | osm4routing --help
92 | ```
93 |
94 | * Installing boost:
95 | You only need the headers. If you have a linux distribution, just install it.
96 | Under windows or macOSX, grab the sources and unzip them. Place the boost directory
97 | containing the headers in the same directory as setup.py
98 | An alternative is to edit setup.py to tell where the directory is located.
99 |
100 | # USAGE
101 | Get the .osm XML file of the region that interests you.
102 | For limited regions, use the export tools from the web interface.
103 | For bigger regions you might find what you want at http://download.geofabrik.de/osm/
104 | Osmosis can help you to have a smaller region from a big dump http://wiki.openstreetmap.org/wiki/Osmosis
105 |
106 | To know the options, run:
107 | osm4routing --help
108 |
109 | # PERFORMANCE
110 | OSM data can get very big and can be very consuming, don't try to parse the whole world ;)
111 | On my laptop from 2006 (core2duo 1.66Ghz, 2Gb Ram, slow hard drive),
112 | it takes 20 minutes to parse 8Gb uncompressed (0.5Gb as bzip2) and represents France in June 2010
113 |
114 | # Postgis output
115 | Only if you want to use the spatial abilities of postgis, please read those extra informations
116 | in order to a have spatial database
117 | The usual way to get is to execute the following commands (the location of
118 | lwpostgis.sql and spatial_ref_sys.sql depend on your installation).
119 |
120 | ```
121 | createdb yourdatabase
122 | createlang plpgsql yourdatabase
123 | psql -d yourdatabase -f lwpostgis.sql
124 | psql -d yourdatabase -f spatial_ref_sys.sql
125 | ```
126 |
--------------------------------------------------------------------------------
/VERSION:
--------------------------------------------------------------------------------
1 | * 0.0
2 | First public release
3 |
--------------------------------------------------------------------------------
/osm4routing.py:
--------------------------------------------------------------------------------
1 | from osm4routing_xml import *
2 | import os
3 | import bz2, gzip
4 | import sys
5 | from optparse import OptionParser
6 | from sqlalchemy import Table, Column, MetaData, Integer, BigInteger, String, Float, SmallInteger, create_engine
7 | from sqlalchemy.orm import mapper, sessionmaker
8 | from geoalchemy import *
9 |
10 | class Node(object):
11 | def __init__(self, id, lon, lat, elevation = 0, the_geom = 0, spatial=False):
12 | wkt_geom = 'POINT({0} {1})'.format(lon, lat)
13 | self.original_id = id
14 | self.lon = lon
15 | self.lat = lat
16 | self.elevation = elevation
17 | if spatial:
18 | self.the_geom = WKTSpatialElement(wkt_geom)
19 | else:
20 | self.the_geom = wkt_geom
21 |
22 | class Edge(object):
23 | def __init__(self, id, source, target, length, car, car_rev, bike, bike_rev, foot, the_geom, spatial=False):
24 | wkt_geom = 'LINESTRING({0})'.format(the_geom)
25 | self.id = id
26 | self.source = source
27 | self.target = target
28 | self.length = length
29 | self.car = car
30 | self.car_rev = car_rev
31 | self.bike = bike
32 | self.bike_rev = bike
33 | self.foot = foot
34 | if spatial:
35 | self.the_geom = WKTSpatialElement(wkt_geom)
36 | else:
37 | self.the_geom = wkt_geom
38 |
39 |
40 | def parse(file, output="csv", edges_name="edges", nodes_name="nodes", spatial=False):
41 | if not os.path.exists(file):
42 | raise IOError("File {0} not found".format(file))
43 |
44 | if output != "csv":
45 | metadata = MetaData()
46 | if(spatial):
47 | node_geom = Point(2)
48 | edge_geom = LineString(2)
49 | else:
50 | node_geom = String
51 | edge_geom = String
52 |
53 | nodes_table = Table(nodes_name, metadata,
54 | Column('id', Integer, primary_key = True),
55 | Column('original_id', BigInteger, index = True),
56 | Column('elevation', Integer),
57 | Column('lon', Float, index = True),
58 | Column('lat', Float, index = True),
59 | Column('the_geom', node_geom)
60 | )
61 |
62 | edges_table = Table(edges_name, metadata,
63 | Column('id', Integer, primary_key=True),
64 | Column('source', BigInteger, index=True),
65 | Column('target', BigInteger, index=True),
66 | Column('length', Float),
67 | Column('car', SmallInteger),
68 | Column('car_rev', SmallInteger),
69 | Column('bike', SmallInteger),
70 | Column('bike_rev', SmallInteger),
71 | Column('foot', SmallInteger),
72 | Column('the_geom', edge_geom)
73 | )
74 |
75 | GeometryDDL(nodes_table)
76 | GeometryDDL(edges_table)
77 |
78 |
79 | engine = create_engine(output)
80 | metadata.drop_all(engine)
81 | metadata.create_all(engine)
82 | mapper(Node, nodes_table)
83 | mapper(Edge, edges_table)
84 | Session = sessionmaker(bind=engine)
85 | session = Session()
86 |
87 | extension = os.path.splitext(file)[1]
88 | if extension == '.bz2':
89 | print "Recognized as bzip2 file"
90 | f = bz2.BZ2File(file, 'r')
91 |
92 | elif extension == '.gz':
93 | print "Recognized as gzip2 file"
94 | f = gzip.open(file, 'r')
95 |
96 | else:
97 | print "Supposing OSM/xml file"
98 | filesize = os.path.getsize(file)
99 | f = open(file, 'r')
100 |
101 | buffer_size = 4096
102 | p = Parser()
103 | eof = False
104 | print "Step 1: reading file {0}".format(file)
105 | read = 0
106 | while not eof:
107 | s = f.read(buffer_size)
108 | eof = len(s) != buffer_size
109 | p.read(s, len(s), eof)
110 | read += len(s)
111 |
112 | print " Read {0} nodes and {1} ways\n".format(p.get_osm_nodes(), p.get_osm_ways())
113 |
114 | print "Step 2: saving the nodes"
115 | nodes = p.get_nodes()
116 | if output == "csv":
117 | n = open(nodes_name + '.csv', 'w')
118 | n.write('"node_id","longitude","latitude"\n')
119 |
120 | count = 0
121 | for node in nodes:
122 | if output == "csv":
123 | n.write("{0},{1},{2}\n".format(node.id, node.lon, node.lat))
124 | else:
125 | session.add(Node(node.id, node.lon, node.lat, spatial=spatial))
126 | count += 1
127 | if output == "csv":
128 | n.close()
129 | else:
130 | session.commit()
131 |
132 | print " Wrote {0} nodes\n".format(count)
133 |
134 | print "Step 3: saving the edges"
135 | edges = p.get_edges()
136 | count = 0
137 | if output == "csv":
138 | e = open(edges_name + '.csv', 'w')
139 | e.write('"edge_id","source","target","length","car","car reverse","bike","bike reverse","foot","WKT"\n')
140 | for edge in edges:
141 | if output == "csv":
142 | e.write('{0},{1},{2},{3},{4},{5},{6},{7},{8},LINESTRING({9})\n'.format(edge.edge_id, edge.source, edge.target, edge.length, edge.car, edge.car_d, edge.bike, edge.bike_d, edge.foot, edge.geom))
143 | else:
144 | session.add(Edge(edge.edge_id, edge.source, edge.target, edge.length, edge.car, edge.car_d, edge.bike, edge.bike_d, edge.foot, edge.geom, spatial=spatial))
145 | count += 1
146 | if output == "csv":
147 | e.close()
148 | else:
149 | session.commit()
150 | print " Wrote {0} edges\n".format(count)
151 |
152 | print "Happy routing :) and please give some feedback!"
153 |
154 | def main():
155 | usage = """Usage: %prog [options] input_file
156 |
157 | input_file must be an OSM/XML file. It can be compressed with gzip (.gz) or bzip2 (.bz2)"""
158 |
159 |
160 | parser = OptionParser(usage)
161 | parser.add_option("-o", "--output", dest="output", default="csv",
162 | help="""'csv' if you want a simple file,
163 | a connection string to use a database (Example: sqlite:///foo.db postgresql://john@localhost/my_database)
164 | [default: %default]""")
165 | parser.add_option("-n", "--nodes_name", dest="nodes_name", default="nodes", help="Name of the file or table where nodes are stored [default: %default]")
166 | parser.add_option("-e", "--edges_name", dest="edges_name", default="edges", help="Name of the file or table where edges are stored [default: %default]")
167 | parser.add_option("-s", "--spatial", dest="spatial", default=False, action="store_true", help="Is the database spatial? If yes, it creates spatial indexes on the column the_geom. Read about geoalchemy to know what databases are supported (only spatial was tested)")
168 | (options, args) = parser.parse_args()
169 |
170 | if len(args) != 1:
171 | sys.stderr.write("Wrong number of arguments. Expected 1, got {0}\n".format(len(args)))
172 | sys.exit(1)
173 |
174 | try:
175 | parse(args[0], options.output, options.edges_name, options.nodes_name, options.spatial)
176 | except IOError as e:
177 | sys.stderr.write("I/O error: {0}\n".format(e))
178 | except Exception as e:
179 | sys.stderr.write("Woops... an error occured: {0}\n".format(e))
180 |
181 | if __name__ == "__main__":
182 | main()
183 |
--------------------------------------------------------------------------------
/osmreader.cc:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of osm4routing.
3 |
4 | osm4routing is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Mumoro is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with osm4routing. If not, see .
16 | */
17 |
18 | #include "osmreader.h"
19 | #include
20 | #include
21 |
22 | using namespace std;
23 |
24 | OsmReader::OsmReader(const std::string & filename)
25 | {
26 | file.open(filename.c_str(), ifstream::in);
27 | if( !file.good() )
28 | {
29 | std::cout << endl;
30 | std::cerr << "Unable to open file " << filename << std::endl;
31 | exit(1);
32 | }
33 | }
34 |
35 | int OsmReader::read(char * buff, int buffsize)
36 | {
37 | file.read(buff, buffsize);
38 | return file.gcount();
39 | }
40 |
41 | bool OsmReader::eof() const
42 | {
43 | return file.eof();
44 | }
45 |
--------------------------------------------------------------------------------
/osmreader.h:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of osm4routing.
3 |
4 | osm4routing is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Mumoro is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with osm4routing. If not, see .
16 | */
17 |
18 | #include "reader.h"
19 | #include
20 |
21 | #ifndef _OSMREADER_H
22 | #define _OSMREADER_H
23 | class OsmReader : public Reader
24 | {
25 | std::ifstream file;
26 | public:
27 | OsmReader(const std::string & filename);
28 | int read(char * buff, int buffsize);
29 | bool eof() const;
30 | };
31 | #endif
32 |
--------------------------------------------------------------------------------
/parameters.cc:
--------------------------------------------------------------------------------
1 | /*
2 | This file is part of osm4routing.
3 |
4 | osm4routing is free software: you can redistribute it and/or modify
5 | it under the terms of the GNU General Public License as published by
6 | the Free Software Foundation, either version 3 of the License, or
7 | (at your option) any later version.
8 |
9 | Mumoro is distributed in the hope that it will be useful,
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | GNU General Public License for more details.
13 |
14 | You should have received a copy of the GNU General Public License
15 | along with osm4routing. If not, see .
16 | */
17 |
18 | #include "parse.h"
19 | #include
20 | Edge_property::Edge_property() :
21 | car_direct(unknown),
22 | car_reverse(unknown),
23 | bike_direct(unknown),
24 | bike_reverse(unknown),
25 | foot(unknown)
26 | {
27 | }
28 |
29 | bool Edge_property::accessible()
30 | {
31 | return direct_accessible() || reverse_accessible();
32 | }
33 |
34 | bool Edge_property::direct_accessible()
35 | {
36 | return car_direct > 0 || bike_direct > 0 || foot > 0;
37 | }
38 |
39 | bool Edge_property::reverse_accessible()
40 | {
41 | return car_reverse > 0 || bike_reverse > 0 || foot > 0;
42 | }
43 |
44 |
45 |
46 | void Edge_property::reset()
47 | {
48 | car_direct = unknown;
49 | car_reverse = unknown;
50 | bike_direct = unknown;
51 | bike_reverse = unknown;
52 | foot = unknown;
53 | }
54 |
55 | void Edge_property::normalize()
56 | {
57 | if(car_reverse == unknown && car_direct != unknown)
58 | car_reverse = car_direct;
59 | if(bike_reverse == unknown && bike_direct != unknown)
60 | bike_reverse = bike_direct;
61 | if(car_direct == unknown) car_direct = car_forbiden;
62 | if(bike_direct == unknown) bike_direct = bike_forbiden;
63 | if(car_reverse == unknown) car_reverse = car_forbiden;
64 | if(bike_reverse == unknown) bike_reverse = bike_forbiden;
65 | if(foot == unknown) foot = foot_forbiden;
66 | }
67 |
68 | bool Edge_property::update(const std::string & key, const std::string & val)
69 | {
70 | if(key == "highway")
71 | {
72 | if(val == "cycleway" || val == "path" || val == "footway" ||
73 | val == "steps" || val == "pedestrian")
74 | {
75 | bike_direct = bike_track;
76 | foot = foot_allowed;
77 | }
78 | else if(val == "primary" || val == "primary_link")
79 | {
80 | car_direct = car_primary;
81 | foot = foot_allowed;
82 | bike_direct = bike_allowed;
83 | }
84 | else if(val == "secondary")
85 | {
86 | car_direct = car_secondary;
87 | foot = foot_allowed;
88 | bike_direct = bike_allowed;
89 | }
90 | else if(val == "tertiary")
91 | {
92 | car_direct = car_tertiary;
93 | foot = foot_allowed;
94 | bike_direct = bike_allowed;
95 | }
96 | else if(val == "unclassified" || val == "residential" || val == "living_street" ||
97 | val == "road" || val == "service" || val == "track")
98 | {
99 | car_direct = car_residential;
100 | foot = foot_allowed;
101 | bike_direct = bike_allowed;
102 | }
103 | else if(val == "motorway" || val == "motorway_link")
104 | {
105 | car_direct = car_motorway;
106 | foot = foot_forbiden;
107 | bike_direct = bike_forbiden;
108 | }
109 | else if(val == "trunk" || val == "trunk_link")
110 | {
111 | car_direct = car_trunk;
112 | foot = foot_forbiden;
113 | bike_direct = bike_forbiden;
114 | }
115 | }
116 |
117 | else if(key == "pedestrian" || key == "foot")
118 | {
119 | if(val == "yes" || val == "designated" || val == "permissive")
120 | foot = foot_allowed;
121 | else if(val == "no")
122 | foot = foot_forbiden;
123 | else
124 | std::cerr << "I don't know what to do with: " << key << "=" << val << std::endl;
125 | }
126 |
127 | // http://wiki.openstreetmap.org/wiki/Cycleway
128 | // http://wiki.openstreetmap.org/wiki/Map_Features#Cycleway
129 | else if(key == "cycleway")
130 | {
131 | if(val == "lane" || val == "yes" || val == "true" || val == "lane_in_the_middle")
132 | bike_direct = bike_lane;
133 | else if(val == "track")
134 | bike_direct = bike_track;
135 | else if(val == "opposite_lane")
136 | bike_reverse = bike_lane;
137 | else if(val == "opposite_track")
138 | bike_reverse = bike_track;
139 | else if(val == "opposite")
140 | bike_reverse = bike_allowed;
141 | else if(val == "share_busway")
142 | bike_direct = bike_busway;
143 | else if(val == "lane_left")
144 | bike_reverse = bike_lane;
145 | else
146 | bike_direct = bike_lane;
147 | }
148 |
149 | else if(key == "bicycle")
150 | {
151 | if(val == "yes" || val == "permissive" || val == "destination" || val == "designated" || val == "private" || val == "true")
152 | bike_direct = bike_allowed;
153 | else if(val == "no" || val == "true")
154 | bike_direct = bike_forbiden;
155 | else
156 | std::cerr << "I don't know what to do with: " << key << "=" << val << std::endl;
157 | }
158 |
159 | else if(key == "busway")
160 | {
161 | if(val == "yes" || val == "track" || val == "lane")
162 | bike_direct = bike_busway;
163 | else if(val == "opposite_lane" || val == "opposite_track")
164 | bike_reverse = bike_busway;
165 | else
166 | bike_direct = bike_busway;
167 | }
168 |
169 | else if(key == "oneway")
170 | {
171 | if(val == "yes" || val == "true" || val == "1")
172 | {
173 | car_reverse = car_forbiden;
174 | if(bike_reverse == unknown)
175 | bike_reverse = bike_forbiden;
176 | }
177 | }
178 |
179 | else if(key == "junction")
180 | {
181 | if(val == "roundabout")
182 | {
183 | car_reverse = car_forbiden;
184 | if(bike_reverse == unknown)
185 | bike_reverse = bike_forbiden;
186 | }
187 | }
188 | return this->accessible();
189 | }
190 |
--------------------------------------------------------------------------------
/parse.cc:
--------------------------------------------------------------------------------
1 | #include "parse.h"
2 | #include "cmath"
3 |
4 | #include
5 | #include
6 | #include
7 | using namespace std;
8 | typedef boost::unordered_map NodeMapType;
9 |
10 |
11 | double rad(double deg)
12 | {
13 | return deg * 3.14159265 / 180;
14 | }
15 |
16 | double distance(double lon1, double lat1, double lon2, double lat2)
17 | {
18 | const double r = 6371000;
19 |
20 | return acos( sin( rad(lat1) ) * sin( rad(lat2) ) +
21 | cos( rad(lat1) ) * cos( rad(lat2) ) * cos( rad(lon2-lon1 ) )
22 | ) * r;
23 | }
24 |
25 | void
26 | start(void * data, const char *el, const char **attr)
27 | {
28 | Parser * d = (Parser*) data;
29 | if (strcmp(el, "node") == 0)
30 | {
31 | node_t id = 0;
32 | double lat = 0, lon = 0;
33 | while (*attr != NULL)
34 | {
35 | const char* name = *attr++;
36 | const char* value = *attr++;
37 |
38 | if (strcmp(name, "id") == 0)
39 | {
40 | id = atoll(value);
41 | }
42 | else if (strcmp(name, "lat") == 0)
43 | {
44 | lat = atof(value);
45 | }
46 | else if (strcmp(name, "lon") == 0)
47 | {
48 | lon = atof(value);
49 | }
50 | }
51 | d->nodes[id] = Node(lon, lat, id);
52 | }
53 |
54 | else if (strcmp(el, "nd") == 0)
55 | {
56 | const char* name = *attr++;
57 | const char* value = *attr++;
58 | if (strcmp(name, "ref") == 0)
59 | {
60 | node_t node_id = atoll(value);
61 | d->way_nodes.push_back(node_id);
62 | }
63 | }
64 |
65 | else if(strcmp(el, "way") == 0)
66 | {
67 | d->way_nodes.clear();
68 | d->ep.reset();
69 | const char* name = *attr++;
70 | const char* value = *attr++;
71 | if( !strcmp(name, "id") == 0 )
72 | {
73 | cout << "fuck" << std::endl;
74 | }
75 | else
76 | {
77 | d->current_way = atoll(value);
78 | }
79 | d->ways_count++;
80 | }
81 |
82 | else if(strcmp(el, "tag") == 0)
83 | {
84 | string key;
85 | while (*attr != NULL)
86 | {
87 | const char* name = *attr++;
88 | const char* value = *attr++;
89 |
90 | if ( strcmp(name, "k") == 0 )
91 | key = value;
92 | else if ( strcmp(name, "v") == 0 )
93 | {
94 | d->ep.update(key, value);
95 | }
96 | }
97 | }
98 |
99 | }
100 |
101 | void end(void * data, const char * el)
102 | {
103 | Parser * d = (Parser*) data;
104 | if(strcmp(el, "way") == 0)
105 | {
106 | if(d->ep.accessible())
107 | {
108 | d->ep.normalize();
109 | vector::const_iterator it;
110 | d->temp_edges << d->ep.foot << " "
111 | << d->ep.car_direct << " " << d->ep.car_reverse << " "
112 | << d->ep.bike_direct << " " << d->ep.bike_reverse << " "
113 | << d->way_nodes.size();
114 | for(it = d->way_nodes.begin(); it < d->way_nodes.end(); ++it)
115 | {
116 | d->nodes[*it].uses++;
117 | d->temp_edges << " " << *it;
118 | }
119 | d->temp_edges << endl;
120 |
121 | d->nodes[d->way_nodes.front()].uses++;
122 | d->nodes[d->way_nodes.back()].uses++;
123 | }
124 | }
125 | }
126 |
127 |
128 | Parser::Parser()
129 | {
130 | temp_edges.open("temp_ways");
131 | parser = XML_ParserCreate(NULL);
132 | XML_SetElementHandler(parser, start, end);
133 | // ios_base::sync_with_stdio(false);
134 | XML_SetUserData(parser, this);
135 | }
136 |
137 | void Parser::read(char * buf, int size, bool end)
138 | {
139 | if( !XML_Parse(parser, buf, size, end) )
140 | {
141 | cerr << XML_ErrorString(XML_GetErrorCode(parser)) <<
142 | " at line " <<
143 | XML_GetCurrentLineNumber(parser) << endl;
144 | }
145 |
146 | if(end)
147 | {
148 | XML_ParserFree(parser);
149 | temp_edges.close();
150 | }
151 | }
152 |
153 | vector Parser::get_nodes() const
154 | {
155 | vector ret;
156 | ret.reserve(nodes.size()/5); //Simply heuristical...
157 | for(NodeMapType::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
158 | {
159 | if( (*i).second.uses > 1 )
160 | {
161 | ret.push_back(Node((*i).second.lon, (*i).second.lat, (*i).first));
162 | }
163 | }
164 | return ret;
165 |
166 | }
167 |
168 | vector Parser::get_edges() const
169 | {
170 | vector ret;
171 | ret.reserve(nodes.size()/5); //Simply heuristical...
172 | ifstream tmp;
173 | tmp.open("temp_ways");
174 | node_t id, source=0;
175 | stringstream geom;
176 | geom.precision(10);
177 | double length = 0, pred_lon = 0, pred_lat = 0;
178 | char car_direct, car_rev, foot, bike_direct, bike_rev;
179 | int nb;
180 | int edges_inserted = 0;
181 | Node n;
182 | string line;
183 |
184 |
185 | while(getline(tmp, line))
186 | {
187 | stringstream way(line);
188 | way >> foot >> car_direct >> car_rev >> bike_direct >> bike_rev >> nb;
189 | bool first_node = true;
190 |
191 | // We skip the edge if there is an invalid node
192 | bool skip_edge = false;
193 | for(int i=0; i> id;
196 | n = nodes.at(id);
197 |
198 | if(n.valid)
199 | {
200 | if(first_node)
201 | {
202 | geom.str("");
203 | source = id;
204 | first_node = false;
205 | }
206 | else
207 | {
208 | length += distance(n.lon, n.lat, pred_lon, pred_lat);
209 | if(geom.str() != "")
210 | geom << ",";
211 | }
212 |
213 | pred_lon = n.lon;
214 | pred_lat = n.lat;
215 |
216 | geom << n.lon << " " << n.lat;
217 | if( !first_node && n.uses > 1 && id != source)
218 | {
219 | if(!skip_edge)
220 | {
221 | ret.push_back(Edge(edges_inserted, source, id, length, car_direct, car_rev, bike_direct, bike_rev, foot, geom.str()));
222 | }
223 |
224 | edges_inserted++;
225 | length = 0;
226 | geom.str("");
227 | geom << n.lon << " " << n.lat;
228 | source = id;
229 | skip_edge = false;
230 | }
231 | }
232 | else
233 | {
234 | skip_edge = true;
235 | }
236 | }
237 | }
238 |
239 | tmp.close();
240 | return ret;
241 | }
242 |
243 | unsigned long Parser::get_osm_nodes() const
244 | {
245 | return nodes.size();
246 | }
247 |
248 | unsigned long long Parser::get_osm_ways() const
249 | {
250 | return ways_count;
251 | }
252 |
--------------------------------------------------------------------------------
/parse.h:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 |
8 | typedef uint64_t node_t;
9 | const int unknown = -1;
10 | const int foot_forbiden = 0;
11 | const int foot_allowed = 1;
12 |
13 | const int car_forbiden = 0;
14 | const int car_residential = 1;
15 | const int car_tertiary = 2;
16 | const int car_secondary = 3;
17 | const int car_primary = 4;
18 | const int car_trunk = 5;
19 | const int car_motorway = 6;
20 |
21 | const int bike_forbiden = 0;
22 | const int bike_opposite_lane = 1;
23 | const int bike_allowed = 2;
24 | const int bike_lane = 3;
25 | const int bike_busway = 4;
26 | const int bike_track = 5;
27 |
28 | class Edge_property
29 | {
30 | public:
31 | int car_direct;
32 | int car_reverse;
33 | int bike_direct;
34 | int bike_reverse;
35 | int foot;
36 |
37 | Edge_property();
38 |
39 | // Can at least one mean use that edge
40 | bool accessible();
41 | bool direct_accessible();
42 | bool reverse_accessible();
43 |
44 | // Update the properties given new information
45 | bool update(const std::string & tag, const std::string & val);
46 |
47 | // Infer unknown data
48 | void normalize();
49 |
50 | void reset();
51 | };
52 |
53 | struct Node
54 | {
55 | node_t id;
56 | double lon;
57 | double lat;
58 |
59 | // By how many ways has this node been used
60 | // This allows to detect a street intersection
61 | char uses;
62 |
63 | // Is this node valid:
64 | // In some exports, a way references a node that isn’t in the data
65 | // This causes bad coordinates
66 | bool valid;
67 |
68 | Node() : uses(0), valid(false) {};
69 |
70 | Node(double _lon, double _lat, node_t _id) :
71 | id(_id), lon(_lon), lat(_lat), uses(0), valid(true)
72 | {};
73 | };
74 |
75 | struct Edge
76 | {
77 | node_t edge_id;
78 | node_t source;
79 | node_t target;
80 | float length;
81 | char car;
82 | char car_d;
83 | char bike;
84 | char bike_d;
85 | char foot;
86 | std::string geom;
87 | Edge() {}
88 | Edge(node_t e, node_t s, node_t t, float l, char c, char cd, char b, char bd, char f, const std::string & str) :
89 | edge_id(e), source(s), target(t), length(l),
90 | car(c), car_d(cd), bike(b), bike_d(bd), foot(f),
91 | geom(str)
92 | {}
93 | };
94 |
95 |
96 | typedef boost::unordered_map NodeMapType;
97 |
98 | struct Parser
99 | {
100 | Node * source;
101 | Node * prev;
102 | NodeMapType nodes;
103 | node_t ways_count;
104 | int edge_length;
105 | node_t ways_progress;
106 | Edge_property ep;
107 | double length;
108 | XML_Parser parser;
109 | std::ofstream temp_edges;
110 | std::vector way_nodes;
111 | node_t current_way;
112 |
113 | Parser();
114 | void read(char *, int, bool);
115 | std::vector get_nodes() const;
116 | std::vector get_edges() const;
117 | unsigned long get_osm_nodes() const;
118 | unsigned long long get_osm_ways() const;
119 |
120 | };
121 |
--------------------------------------------------------------------------------
/parse.i:
--------------------------------------------------------------------------------
1 | %module osm4routing_xml
2 | %include "std_vector.i"
3 | %include "std_string.i"
4 | %{
5 | #include "parse.h"
6 | %}
7 |
8 | %template(Nodes) std::vector;
9 | %template(Edges) std::vector;
10 |
11 | class Edge_property
12 | {
13 | public:
14 | int car_direct;
15 | int car_reverse;
16 | int bike_direct;
17 | int bike_reverse;
18 | int foot;
19 |
20 | Edge_property();
21 |
22 | // Can at least one mean use that edge
23 | bool accessible();
24 | bool direct_accessible();
25 | bool reverse_accessible();
26 |
27 | // Update the properties given new information
28 | bool update(const std::string & tag, const std::string & val);
29 |
30 | // Infer unknown data
31 | void normalize();
32 |
33 | void reset();
34 | };
35 |
36 | struct Edge
37 | {
38 | unsigned long long edge_id;
39 | unsigned long long source;
40 | unsigned long long target;
41 | float length;
42 | char car;
43 | char car_d;
44 | char bike;
45 | char bike_d;
46 | char foot;
47 | std::string geom;
48 | };
49 |
50 | struct Node
51 | {
52 | unsigned long id;
53 | double lon;
54 | double lat;
55 | char uses;
56 | };
57 |
58 |
59 | struct Parser
60 | {
61 | Parser();
62 | void read(char *, int, bool);
63 | std::vector get_nodes() const;
64 | std::vector get_edges() const;
65 | int get_osm_nodes() const;
66 | int get_osm_ways() const;
67 | };
68 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | # Update this if needed
2 | # It has to point to the directory containing the boost headers
3 | boost_path = "."
4 |
5 |
6 | from setuptools import setup, find_packages, Extension
7 | setup(name='Osm4routing',
8 | version='1.0.6',
9 | author= 'Tristram Graebener',
10 | author_email = 'tristramg@gmail.com',
11 | description = 'A simple tool to parse OpenStreetMap data to use them for routing',
12 | license = 'GPLv3',
13 | url = 'http://github.com/Tristramg/osm4routing/',
14 | install_requires = ['sqlalchemy <0.9', 'setuptools-git', 'geoalchemy'],
15 | py_modules = ['osm4routing', 'osm4routing_xml'],
16 |
17 | ext_modules = [
18 | Extension("_osm4routing_xml",
19 | sources=["parse.cc", "parameters.cc", "parse.i"],
20 | swig_opts=['-c++'],
21 | include_dirs=[boost_path],
22 | libraries=['expat'])
23 | ],
24 | entry_points = {
25 | 'console_scripts': ['osm4routing = osm4routing:main'],
26 | }
27 |
28 | )
29 |
30 |
--------------------------------------------------------------------------------
/virtualenv.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 | """Create a "virtual" Python installation
3 | """
4 |
5 | virtualenv_version = "1.4.8"
6 |
7 | import sys
8 | import os
9 | import optparse
10 | import re
11 | import shutil
12 | import logging
13 | import distutils.sysconfig
14 | try:
15 | import subprocess
16 | except ImportError, e:
17 | if sys.version_info <= (2, 3):
18 | print 'ERROR: %s' % e
19 | print 'ERROR: this script requires Python 2.4 or greater; or at least the subprocess module.'
20 | print 'If you copy subprocess.py from a newer version of Python this script will probably work'
21 | sys.exit(101)
22 | else:
23 | raise
24 | try:
25 | set
26 | except NameError:
27 | from sets import Set as set
28 |
29 | join = os.path.join
30 | py_version = 'python%s.%s' % (sys.version_info[0], sys.version_info[1])
31 | is_jython = sys.platform.startswith('java')
32 | expected_exe = is_jython and 'jython' or 'python'
33 |
34 | REQUIRED_MODULES = ['os', 'posix', 'posixpath', 'nt', 'ntpath', 'genericpath',
35 | 'fnmatch', 'locale', 'encodings', 'codecs',
36 | 'stat', 'UserDict', 'readline', 'copy_reg', 'types',
37 | 're', 'sre', 'sre_parse', 'sre_constants', 'sre_compile',
38 | 'lib-dynload', 'config', 'zlib']
39 |
40 | if sys.version_info[:2] >= (2, 6):
41 | REQUIRED_MODULES.extend(['warnings', 'linecache', '_abcoll', 'abc'])
42 | if sys.version_info[:2] <= (2, 3):
43 | REQUIRED_MODULES.extend(['sets', '__future__'])
44 |
45 | class Logger(object):
46 |
47 | """
48 | Logging object for use in command-line script. Allows ranges of
49 | levels, to avoid some redundancy of displayed information.
50 | """
51 |
52 | DEBUG = logging.DEBUG
53 | INFO = logging.INFO
54 | NOTIFY = (logging.INFO+logging.WARN)/2
55 | WARN = WARNING = logging.WARN
56 | ERROR = logging.ERROR
57 | FATAL = logging.FATAL
58 |
59 | LEVELS = [DEBUG, INFO, NOTIFY, WARN, ERROR, FATAL]
60 |
61 | def __init__(self, consumers):
62 | self.consumers = consumers
63 | self.indent = 0
64 | self.in_progress = None
65 | self.in_progress_hanging = False
66 |
67 | def debug(self, msg, *args, **kw):
68 | self.log(self.DEBUG, msg, *args, **kw)
69 | def info(self, msg, *args, **kw):
70 | self.log(self.INFO, msg, *args, **kw)
71 | def notify(self, msg, *args, **kw):
72 | self.log(self.NOTIFY, msg, *args, **kw)
73 | def warn(self, msg, *args, **kw):
74 | self.log(self.WARN, msg, *args, **kw)
75 | def error(self, msg, *args, **kw):
76 | self.log(self.WARN, msg, *args, **kw)
77 | def fatal(self, msg, *args, **kw):
78 | self.log(self.FATAL, msg, *args, **kw)
79 | def log(self, level, msg, *args, **kw):
80 | if args:
81 | if kw:
82 | raise TypeError(
83 | "You may give positional or keyword arguments, not both")
84 | args = args or kw
85 | rendered = None
86 | for consumer_level, consumer in self.consumers:
87 | if self.level_matches(level, consumer_level):
88 | if (self.in_progress_hanging
89 | and consumer in (sys.stdout, sys.stderr)):
90 | self.in_progress_hanging = False
91 | sys.stdout.write('\n')
92 | sys.stdout.flush()
93 | if rendered is None:
94 | if args:
95 | rendered = msg % args
96 | else:
97 | rendered = msg
98 | rendered = ' '*self.indent + rendered
99 | if hasattr(consumer, 'write'):
100 | consumer.write(rendered+'\n')
101 | else:
102 | consumer(rendered)
103 |
104 | def start_progress(self, msg):
105 | assert not self.in_progress, (
106 | "Tried to start_progress(%r) while in_progress %r"
107 | % (msg, self.in_progress))
108 | if self.level_matches(self.NOTIFY, self._stdout_level()):
109 | sys.stdout.write(msg)
110 | sys.stdout.flush()
111 | self.in_progress_hanging = True
112 | else:
113 | self.in_progress_hanging = False
114 | self.in_progress = msg
115 |
116 | def end_progress(self, msg='done.'):
117 | assert self.in_progress, (
118 | "Tried to end_progress without start_progress")
119 | if self.stdout_level_matches(self.NOTIFY):
120 | if not self.in_progress_hanging:
121 | # Some message has been printed out since start_progress
122 | sys.stdout.write('...' + self.in_progress + msg + '\n')
123 | sys.stdout.flush()
124 | else:
125 | sys.stdout.write(msg + '\n')
126 | sys.stdout.flush()
127 | self.in_progress = None
128 | self.in_progress_hanging = False
129 |
130 | def show_progress(self):
131 | """If we are in a progress scope, and no log messages have been
132 | shown, write out another '.'"""
133 | if self.in_progress_hanging:
134 | sys.stdout.write('.')
135 | sys.stdout.flush()
136 |
137 | def stdout_level_matches(self, level):
138 | """Returns true if a message at this level will go to stdout"""
139 | return self.level_matches(level, self._stdout_level())
140 |
141 | def _stdout_level(self):
142 | """Returns the level that stdout runs at"""
143 | for level, consumer in self.consumers:
144 | if consumer is sys.stdout:
145 | return level
146 | return self.FATAL
147 |
148 | def level_matches(self, level, consumer_level):
149 | """
150 | >>> l = Logger()
151 | >>> l.level_matches(3, 4)
152 | False
153 | >>> l.level_matches(3, 2)
154 | True
155 | >>> l.level_matches(slice(None, 3), 3)
156 | False
157 | >>> l.level_matches(slice(None, 3), 2)
158 | True
159 | >>> l.level_matches(slice(1, 3), 1)
160 | True
161 | >>> l.level_matches(slice(2, 3), 1)
162 | False
163 | """
164 | if isinstance(level, slice):
165 | start, stop = level.start, level.stop
166 | if start is not None and start > consumer_level:
167 | return False
168 | if stop is not None or stop <= consumer_level:
169 | return False
170 | return True
171 | else:
172 | return level >= consumer_level
173 |
174 | #@classmethod
175 | def level_for_integer(cls, level):
176 | levels = cls.LEVELS
177 | if level < 0:
178 | return levels[0]
179 | if level >= len(levels):
180 | return levels[-1]
181 | return levels[level]
182 |
183 | level_for_integer = classmethod(level_for_integer)
184 |
185 | def mkdir(path):
186 | if not os.path.exists(path):
187 | logger.info('Creating %s', path)
188 | os.makedirs(path)
189 | else:
190 | logger.info('Directory %s already exists', path)
191 |
192 | def copyfile(src, dest, symlink=True):
193 | if not os.path.exists(src):
194 | # Some bad symlink in the src
195 | logger.warn('Cannot find file %s (bad symlink)', src)
196 | return
197 | if os.path.exists(dest):
198 | logger.debug('File %s already exists', dest)
199 | return
200 | if not os.path.exists(os.path.dirname(dest)):
201 | logger.info('Creating parent directories for %s' % os.path.dirname(dest))
202 | os.makedirs(os.path.dirname(dest))
203 | if symlink and hasattr(os, 'symlink'):
204 | logger.info('Symlinking %s', dest)
205 | os.symlink(os.path.abspath(src), dest)
206 | else:
207 | logger.info('Copying to %s', dest)
208 | if os.path.isdir(src):
209 | shutil.copytree(src, dest, True)
210 | else:
211 | shutil.copy2(src, dest)
212 |
213 | def writefile(dest, content, overwrite=True):
214 | if not os.path.exists(dest):
215 | logger.info('Writing %s', dest)
216 | f = open(dest, 'wb')
217 | f.write(content)
218 | f.close()
219 | return
220 | else:
221 | f = open(dest, 'rb')
222 | c = f.read()
223 | f.close()
224 | if c != content:
225 | if not overwrite:
226 | logger.notify('File %s exists with different content; not overwriting', dest)
227 | return
228 | logger.notify('Overwriting %s with new content', dest)
229 | f = open(dest, 'wb')
230 | f.write(content)
231 | f.close()
232 | else:
233 | logger.info('Content %s already in place', dest)
234 |
235 | def rmtree(dir):
236 | if os.path.exists(dir):
237 | logger.notify('Deleting tree %s', dir)
238 | shutil.rmtree(dir)
239 | else:
240 | logger.info('Do not need to delete %s; already gone', dir)
241 |
242 | def make_exe(fn):
243 | if hasattr(os, 'chmod'):
244 | oldmode = os.stat(fn).st_mode & 07777
245 | newmode = (oldmode | 0555) & 07777
246 | os.chmod(fn, newmode)
247 | logger.info('Changed mode of %s to %s', fn, oct(newmode))
248 |
249 | def _find_file(filename, dirs):
250 | for dir in dirs:
251 | if os.path.exists(join(dir, filename)):
252 | return join(dir, filename)
253 | return filename
254 |
255 | def _install_req(py_executable, unzip=False, distribute=False):
256 | if not distribute:
257 | setup_fn = 'setuptools-0.6c11-py%s.egg' % sys.version[:3]
258 | project_name = 'setuptools'
259 | bootstrap_script = EZ_SETUP_PY
260 | source = None
261 | else:
262 | setup_fn = None
263 | source = 'distribute-0.6.8.tar.gz'
264 | project_name = 'distribute'
265 | bootstrap_script = DISTRIBUTE_SETUP_PY
266 | try:
267 | # check if the global Python has distribute installed or plain
268 | # setuptools
269 | import pkg_resources
270 | if not hasattr(pkg_resources, '_distribute'):
271 | location = os.path.dirname(pkg_resources.__file__)
272 | logger.notify("A globally installed setuptools was found (in %s)" % location)
273 | logger.notify("Use the --no-site-packages option to use distribute in "
274 | "the virtualenv.")
275 | except ImportError:
276 | pass
277 |
278 | search_dirs = file_search_dirs()
279 |
280 | if setup_fn is not None:
281 | setup_fn = _find_file(setup_fn, search_dirs)
282 |
283 | if source is not None:
284 | source = _find_file(source, search_dirs)
285 |
286 | if is_jython and os._name == 'nt':
287 | # Jython's .bat sys.executable can't handle a command line
288 | # argument with newlines
289 | import tempfile
290 | fd, ez_setup = tempfile.mkstemp('.py')
291 | os.write(fd, bootstrap_script)
292 | os.close(fd)
293 | cmd = [py_executable, ez_setup]
294 | else:
295 | cmd = [py_executable, '-c', bootstrap_script]
296 | if unzip:
297 | cmd.append('--always-unzip')
298 | env = {}
299 | if logger.stdout_level_matches(logger.DEBUG):
300 | cmd.append('-v')
301 |
302 | old_chdir = os.getcwd()
303 | if setup_fn is not None and os.path.exists(setup_fn):
304 | logger.info('Using existing %s egg: %s' % (project_name, setup_fn))
305 | cmd.append(setup_fn)
306 | if os.environ.get('PYTHONPATH'):
307 | env['PYTHONPATH'] = setup_fn + os.path.pathsep + os.environ['PYTHONPATH']
308 | else:
309 | env['PYTHONPATH'] = setup_fn
310 | else:
311 | # the source is found, let's chdir
312 | if source is not None and os.path.exists(source):
313 | os.chdir(os.path.dirname(source))
314 | else:
315 | logger.info('No %s egg found; downloading' % project_name)
316 | cmd.extend(['--always-copy', '-U', project_name])
317 | logger.start_progress('Installing %s...' % project_name)
318 | logger.indent += 2
319 | cwd = None
320 | if project_name == 'distribute':
321 | env['DONT_PATCH_SETUPTOOLS'] = 'true'
322 |
323 | def _filter_ez_setup(line):
324 | return filter_ez_setup(line, project_name)
325 |
326 | if not os.access(os.getcwd(), os.W_OK):
327 | cwd = '/tmp'
328 | if source is not None and os.path.exists(source):
329 | # the current working dir is hostile, let's copy the
330 | # tarball to /tmp
331 | target = os.path.join(cwd, os.path.split(source)[-1])
332 | shutil.copy(source, target)
333 | try:
334 | call_subprocess(cmd, show_stdout=False,
335 | filter_stdout=_filter_ez_setup,
336 | extra_env=env,
337 | cwd=cwd)
338 | finally:
339 | logger.indent -= 2
340 | logger.end_progress()
341 | if os.getcwd() != old_chdir:
342 | os.chdir(old_chdir)
343 | if is_jython and os._name == 'nt':
344 | os.remove(ez_setup)
345 |
346 | def file_search_dirs():
347 | here = os.path.dirname(os.path.abspath(__file__))
348 | dirs = ['.', here,
349 | join(here, 'virtualenv_support')]
350 | if os.path.splitext(os.path.dirname(__file__))[0] != 'virtualenv':
351 | # Probably some boot script; just in case virtualenv is installed...
352 | try:
353 | import virtualenv
354 | except ImportError:
355 | pass
356 | else:
357 | dirs.append(os.path.join(os.path.dirname(virtualenv.__file__), 'virtualenv_support'))
358 | return [d for d in dirs if os.path.isdir(d)]
359 |
360 | def install_setuptools(py_executable, unzip=False):
361 | _install_req(py_executable, unzip)
362 |
363 | def install_distribute(py_executable, unzip=False):
364 | _install_req(py_executable, unzip, distribute=True)
365 |
366 | _pip_re = re.compile(r'^pip-.*(zip|tar.gz|tar.bz2|tgz|tbz)$', re.I)
367 | def install_pip(py_executable):
368 | filenames = []
369 | for dir in file_search_dirs():
370 | filenames.extend([join(dir, fn) for fn in os.listdir(dir)
371 | if _pip_re.search(fn)])
372 | filenames.sort(key=lambda x: os.path.basename(x).lower())
373 | if not filenames:
374 | filename = 'pip'
375 | else:
376 | filename = filenames[-1]
377 | easy_install_script = 'easy_install'
378 | if sys.platform == 'win32':
379 | easy_install_script = 'easy_install-script.py'
380 | cmd = [py_executable, join(os.path.dirname(py_executable), easy_install_script), filename]
381 | if filename == 'pip':
382 | logger.info('Installing pip from network...')
383 | else:
384 | logger.info('Installing %s' % os.path.basename(filename))
385 | logger.indent += 2
386 | def _filter_setup(line):
387 | return filter_ez_setup(line, 'pip')
388 | try:
389 | call_subprocess(cmd, show_stdout=False,
390 | filter_stdout=_filter_setup)
391 | finally:
392 | logger.indent -= 2
393 |
394 | def filter_ez_setup(line, project_name='setuptools'):
395 | if not line.strip():
396 | return Logger.DEBUG
397 | if project_name == 'distribute':
398 | for prefix in ('Extracting', 'Now working', 'Installing', 'Before',
399 | 'Scanning', 'Setuptools', 'Egg', 'Already',
400 | 'running', 'writing', 'reading', 'installing',
401 | 'creating', 'copying', 'byte-compiling', 'removing',
402 | 'Processing'):
403 | if line.startswith(prefix):
404 | return Logger.DEBUG
405 | return Logger.DEBUG
406 | for prefix in ['Reading ', 'Best match', 'Processing setuptools',
407 | 'Copying setuptools', 'Adding setuptools',
408 | 'Installing ', 'Installed ']:
409 | if line.startswith(prefix):
410 | return Logger.DEBUG
411 | return Logger.INFO
412 |
413 | def main():
414 | parser = optparse.OptionParser(
415 | version=virtualenv_version,
416 | usage="%prog [OPTIONS] DEST_DIR")
417 |
418 | parser.add_option(
419 | '-v', '--verbose',
420 | action='count',
421 | dest='verbose',
422 | default=0,
423 | help="Increase verbosity")
424 |
425 | parser.add_option(
426 | '-q', '--quiet',
427 | action='count',
428 | dest='quiet',
429 | default=0,
430 | help='Decrease verbosity')
431 |
432 | parser.add_option(
433 | '-p', '--python',
434 | dest='python',
435 | metavar='PYTHON_EXE',
436 | help='The Python interpreter to use, e.g., --python=python2.5 will use the python2.5 '
437 | 'interpreter to create the new environment. The default is the interpreter that '
438 | 'virtualenv was installed with (%s)' % sys.executable)
439 |
440 | parser.add_option(
441 | '--clear',
442 | dest='clear',
443 | action='store_true',
444 | help="Clear out the non-root install and start from scratch")
445 |
446 | parser.add_option(
447 | '--no-site-packages',
448 | dest='no_site_packages',
449 | action='store_true',
450 | help="Don't give access to the global site-packages dir to the "
451 | "virtual environment")
452 |
453 | parser.add_option(
454 | '--unzip-setuptools',
455 | dest='unzip_setuptools',
456 | action='store_true',
457 | help="Unzip Setuptools or Distribute when installing it")
458 |
459 | parser.add_option(
460 | '--relocatable',
461 | dest='relocatable',
462 | action='store_true',
463 | help='Make an EXISTING virtualenv environment relocatable. '
464 | 'This fixes up scripts and makes all .pth files relative')
465 |
466 | parser.add_option(
467 | '--distribute',
468 | dest='use_distribute',
469 | action='store_true',
470 | help='Use Distribute instead of Setuptools. Set environ variable'
471 | 'VIRTUALENV_USE_DISTRIBUTE to make it the default ')
472 |
473 | if 'extend_parser' in globals():
474 | extend_parser(parser)
475 |
476 | options, args = parser.parse_args()
477 |
478 | global logger
479 |
480 | if 'adjust_options' in globals():
481 | adjust_options(options, args)
482 |
483 | verbosity = options.verbose - options.quiet
484 | logger = Logger([(Logger.level_for_integer(2-verbosity), sys.stdout)])
485 |
486 | if options.python and not os.environ.get('VIRTUALENV_INTERPRETER_RUNNING'):
487 | env = os.environ.copy()
488 | interpreter = resolve_interpreter(options.python)
489 | if interpreter == sys.executable:
490 | logger.warn('Already using interpreter %s' % interpreter)
491 | else:
492 | logger.notify('Running virtualenv with interpreter %s' % interpreter)
493 | env['VIRTUALENV_INTERPRETER_RUNNING'] = 'true'
494 | file = __file__
495 | if file.endswith('.pyc'):
496 | file = file[:-1]
497 | os.execvpe(interpreter, [interpreter, file] + sys.argv[1:], env)
498 |
499 | if not args:
500 | print 'You must provide a DEST_DIR'
501 | parser.print_help()
502 | sys.exit(2)
503 | if len(args) > 1:
504 | print 'There must be only one argument: DEST_DIR (you gave %s)' % (
505 | ' '.join(args))
506 | parser.print_help()
507 | sys.exit(2)
508 |
509 | home_dir = args[0]
510 |
511 | if os.environ.get('WORKING_ENV'):
512 | logger.fatal('ERROR: you cannot run virtualenv while in a workingenv')
513 | logger.fatal('Please deactivate your workingenv, then re-run this script')
514 | sys.exit(3)
515 |
516 | if 'PYTHONHOME' in os.environ:
517 | logger.warn('PYTHONHOME is set. You *must* activate the virtualenv before using it')
518 | del os.environ['PYTHONHOME']
519 |
520 | if options.relocatable:
521 | make_environment_relocatable(home_dir)
522 | return
523 |
524 | create_environment(home_dir, site_packages=not options.no_site_packages, clear=options.clear,
525 | unzip_setuptools=options.unzip_setuptools,
526 | use_distribute=options.use_distribute)
527 | if 'after_install' in globals():
528 | after_install(options, home_dir)
529 |
530 | def call_subprocess(cmd, show_stdout=True,
531 | filter_stdout=None, cwd=None,
532 | raise_on_returncode=True, extra_env=None):
533 | cmd_parts = []
534 | for part in cmd:
535 | if len(part) > 40:
536 | part = part[:30]+"..."+part[-5:]
537 | if ' ' in part or '\n' in part or '"' in part or "'" in part:
538 | part = '"%s"' % part.replace('"', '\\"')
539 | cmd_parts.append(part)
540 | cmd_desc = ' '.join(cmd_parts)
541 | if show_stdout:
542 | stdout = None
543 | else:
544 | stdout = subprocess.PIPE
545 | logger.debug("Running command %s" % cmd_desc)
546 | if extra_env:
547 | env = os.environ.copy()
548 | env.update(extra_env)
549 | else:
550 | env = None
551 | try:
552 | proc = subprocess.Popen(
553 | cmd, stderr=subprocess.STDOUT, stdin=None, stdout=stdout,
554 | cwd=cwd, env=env)
555 | except Exception, e:
556 | logger.fatal(
557 | "Error %s while executing command %s" % (e, cmd_desc))
558 | raise
559 | all_output = []
560 | if stdout is not None:
561 | stdout = proc.stdout
562 | while 1:
563 | line = stdout.readline()
564 | if not line:
565 | break
566 | line = line.rstrip()
567 | all_output.append(line)
568 | if filter_stdout:
569 | level = filter_stdout(line)
570 | if isinstance(level, tuple):
571 | level, line = level
572 | logger.log(level, line)
573 | if not logger.stdout_level_matches(level):
574 | logger.show_progress()
575 | else:
576 | logger.info(line)
577 | else:
578 | proc.communicate()
579 | proc.wait()
580 | if proc.returncode:
581 | if raise_on_returncode:
582 | if all_output:
583 | logger.notify('Complete output from command %s:' % cmd_desc)
584 | logger.notify('\n'.join(all_output) + '\n----------------------------------------')
585 | raise OSError(
586 | "Command %s failed with error code %s"
587 | % (cmd_desc, proc.returncode))
588 | else:
589 | logger.warn(
590 | "Command %s had error code %s"
591 | % (cmd_desc, proc.returncode))
592 |
593 |
594 | def create_environment(home_dir, site_packages=True, clear=False,
595 | unzip_setuptools=False, use_distribute=False):
596 | """
597 | Creates a new environment in ``home_dir``.
598 |
599 | If ``site_packages`` is true (the default) then the global
600 | ``site-packages/`` directory will be on the path.
601 |
602 | If ``clear`` is true (default False) then the environment will
603 | first be cleared.
604 | """
605 | home_dir, lib_dir, inc_dir, bin_dir = path_locations(home_dir)
606 |
607 | py_executable = os.path.abspath(install_python(
608 | home_dir, lib_dir, inc_dir, bin_dir,
609 | site_packages=site_packages, clear=clear))
610 |
611 | install_distutils(lib_dir, home_dir)
612 |
613 | if use_distribute or os.environ.get('VIRTUALENV_USE_DISTRIBUTE'):
614 | install_distribute(py_executable, unzip=unzip_setuptools)
615 | else:
616 | install_setuptools(py_executable, unzip=unzip_setuptools)
617 |
618 | install_pip(py_executable)
619 |
620 | install_activate(home_dir, bin_dir)
621 |
622 | def path_locations(home_dir):
623 | """Return the path locations for the environment (where libraries are,
624 | where scripts go, etc)"""
625 | # XXX: We'd use distutils.sysconfig.get_python_inc/lib but its
626 | # prefix arg is broken: http://bugs.python.org/issue3386
627 | if sys.platform == 'win32':
628 | # Windows has lots of problems with executables with spaces in
629 | # the name; this function will remove them (using the ~1
630 | # format):
631 | mkdir(home_dir)
632 | if ' ' in home_dir:
633 | try:
634 | import win32api
635 | except ImportError:
636 | print 'Error: the path "%s" has a space in it' % home_dir
637 | print 'To handle these kinds of paths, the win32api module must be installed:'
638 | print ' http://sourceforge.net/projects/pywin32/'
639 | sys.exit(3)
640 | home_dir = win32api.GetShortPathName(home_dir)
641 | lib_dir = join(home_dir, 'Lib')
642 | inc_dir = join(home_dir, 'Include')
643 | bin_dir = join(home_dir, 'Scripts')
644 | elif is_jython:
645 | lib_dir = join(home_dir, 'Lib')
646 | inc_dir = join(home_dir, 'Include')
647 | bin_dir = join(home_dir, 'bin')
648 | else:
649 | lib_dir = join(home_dir, 'lib', py_version)
650 | inc_dir = join(home_dir, 'include', py_version)
651 | bin_dir = join(home_dir, 'bin')
652 | return home_dir, lib_dir, inc_dir, bin_dir
653 |
654 | def install_python(home_dir, lib_dir, inc_dir, bin_dir, site_packages, clear):
655 | """Install just the base environment, no distutils patches etc"""
656 | if sys.executable.startswith(bin_dir):
657 | print 'Please use the *system* python to run this script'
658 | return
659 |
660 | if clear:
661 | rmtree(lib_dir)
662 | ## FIXME: why not delete it?
663 | ## Maybe it should delete everything with #!/path/to/venv/python in it
664 | logger.notify('Not deleting %s', bin_dir)
665 |
666 | if hasattr(sys, 'real_prefix'):
667 | logger.notify('Using real prefix %r' % sys.real_prefix)
668 | prefix = sys.real_prefix
669 | else:
670 | prefix = sys.prefix
671 | mkdir(lib_dir)
672 | fix_lib64(lib_dir)
673 | stdlib_dirs = [os.path.dirname(os.__file__)]
674 | if sys.platform == 'win32':
675 | stdlib_dirs.append(join(os.path.dirname(stdlib_dirs[0]), 'DLLs'))
676 | elif sys.platform == 'darwin':
677 | stdlib_dirs.append(join(stdlib_dirs[0], 'site-packages'))
678 | for stdlib_dir in stdlib_dirs:
679 | if not os.path.isdir(stdlib_dir):
680 | continue
681 | if hasattr(os, 'symlink'):
682 | logger.info('Symlinking Python bootstrap modules')
683 | else:
684 | logger.info('Copying Python bootstrap modules')
685 | logger.indent += 2
686 | try:
687 | for fn in os.listdir(stdlib_dir):
688 | if fn != 'site-packages' and os.path.splitext(fn)[0] in REQUIRED_MODULES:
689 | copyfile(join(stdlib_dir, fn), join(lib_dir, fn))
690 | finally:
691 | logger.indent -= 2
692 | mkdir(join(lib_dir, 'site-packages'))
693 | writefile(join(lib_dir, 'site.py'), SITE_PY)
694 | writefile(join(lib_dir, 'orig-prefix.txt'), prefix)
695 | site_packages_filename = join(lib_dir, 'no-global-site-packages.txt')
696 | if not site_packages:
697 | writefile(site_packages_filename, '')
698 | else:
699 | if os.path.exists(site_packages_filename):
700 | logger.info('Deleting %s' % site_packages_filename)
701 | os.unlink(site_packages_filename)
702 |
703 | stdinc_dir = join(prefix, 'include', py_version)
704 | if os.path.exists(stdinc_dir):
705 | copyfile(stdinc_dir, inc_dir)
706 | else:
707 | logger.debug('No include dir %s' % stdinc_dir)
708 |
709 | if sys.exec_prefix != prefix:
710 | if sys.platform == 'win32':
711 | exec_dir = join(sys.exec_prefix, 'lib')
712 | elif is_jython:
713 | exec_dir = join(sys.exec_prefix, 'Lib')
714 | else:
715 | exec_dir = join(sys.exec_prefix, 'lib', py_version)
716 | for fn in os.listdir(exec_dir):
717 | copyfile(join(exec_dir, fn), join(lib_dir, fn))
718 |
719 | if is_jython:
720 | # Jython has either jython-dev.jar and javalib/ dir, or just
721 | # jython.jar
722 | for name in 'jython-dev.jar', 'javalib', 'jython.jar':
723 | src = join(prefix, name)
724 | if os.path.exists(src):
725 | copyfile(src, join(home_dir, name))
726 | # XXX: registry should always exist after Jython 2.5rc1
727 | src = join(prefix, 'registry')
728 | if os.path.exists(src):
729 | copyfile(src, join(home_dir, 'registry'), symlink=False)
730 | copyfile(join(prefix, 'cachedir'), join(home_dir, 'cachedir'),
731 | symlink=False)
732 |
733 | mkdir(bin_dir)
734 | py_executable = join(bin_dir, os.path.basename(sys.executable))
735 | if 'Python.framework' in prefix:
736 | if re.search(r'/Python(?:-32|-64)*$', py_executable):
737 | # The name of the python executable is not quite what
738 | # we want, rename it.
739 | py_executable = os.path.join(
740 | os.path.dirname(py_executable), 'python')
741 |
742 | logger.notify('New %s executable in %s', expected_exe, py_executable)
743 | if sys.executable != py_executable:
744 | ## FIXME: could I just hard link?
745 | executable = sys.executable
746 | if sys.platform == 'cygwin' and os.path.exists(executable + '.exe'):
747 | # Cygwin misreports sys.executable sometimes
748 | executable += '.exe'
749 | py_executable += '.exe'
750 | logger.info('Executable actually exists in %s' % executable)
751 | shutil.copyfile(executable, py_executable)
752 | make_exe(py_executable)
753 | if sys.platform == 'win32' or sys.platform == 'cygwin':
754 | pythonw = os.path.join(os.path.dirname(sys.executable), 'pythonw.exe')
755 | if os.path.exists(pythonw):
756 | logger.info('Also created pythonw.exe')
757 | shutil.copyfile(pythonw, os.path.join(os.path.dirname(py_executable), 'pythonw.exe'))
758 |
759 | if os.path.splitext(os.path.basename(py_executable))[0] != expected_exe:
760 | secondary_exe = os.path.join(os.path.dirname(py_executable),
761 | expected_exe)
762 | py_executable_ext = os.path.splitext(py_executable)[1]
763 | if py_executable_ext == '.exe':
764 | # python2.4 gives an extension of '.4' :P
765 | secondary_exe += py_executable_ext
766 | if os.path.exists(secondary_exe):
767 | logger.warn('Not overwriting existing %s script %s (you must use %s)'
768 | % (expected_exe, secondary_exe, py_executable))
769 | else:
770 | logger.notify('Also creating executable in %s' % secondary_exe)
771 | shutil.copyfile(sys.executable, secondary_exe)
772 | make_exe(secondary_exe)
773 |
774 | if 'Python.framework' in prefix:
775 | logger.debug('MacOSX Python framework detected')
776 |
777 | # Make sure we use the the embedded interpreter inside
778 | # the framework, even if sys.executable points to
779 | # the stub executable in ${sys.prefix}/bin
780 | # See http://groups.google.com/group/python-virtualenv/
781 | # browse_thread/thread/17cab2f85da75951
782 | shutil.copy(
783 | os.path.join(
784 | prefix, 'Resources/Python.app/Contents/MacOS/%s' % os.path.basename(sys.executable)),
785 | py_executable)
786 |
787 | # Copy the framework's dylib into the virtual
788 | # environment
789 | virtual_lib = os.path.join(home_dir, '.Python')
790 |
791 | if os.path.exists(virtual_lib):
792 | os.unlink(virtual_lib)
793 | copyfile(
794 | os.path.join(prefix, 'Python'),
795 | virtual_lib)
796 |
797 | # And then change the install_name of the copied python executable
798 | try:
799 | call_subprocess(
800 | ["install_name_tool", "-change",
801 | os.path.join(prefix, 'Python'),
802 | '@executable_path/../.Python',
803 | py_executable])
804 | except:
805 | logger.fatal(
806 | "Could not call install_name_tool -- you must have Apple's development tools installed")
807 | raise
808 |
809 | # Some tools depend on pythonX.Y being present
810 | py_executable_version = '%s.%s' % (
811 | sys.version_info[0], sys.version_info[1])
812 | if not py_executable.endswith(py_executable_version):
813 | # symlinking pythonX.Y > python
814 | pth = py_executable + '%s.%s' % (
815 | sys.version_info[0], sys.version_info[1])
816 | if os.path.exists(pth):
817 | os.unlink(pth)
818 | os.symlink('python', pth)
819 | else:
820 | # reverse symlinking python -> pythonX.Y (with --python)
821 | pth = join(bin_dir, 'python')
822 | if os.path.exists(pth):
823 | os.unlink(pth)
824 | os.symlink(os.path.basename(py_executable), pth)
825 |
826 | if sys.platform == 'win32' and ' ' in py_executable:
827 | # There's a bug with subprocess on Windows when using a first
828 | # argument that has a space in it. Instead we have to quote
829 | # the value:
830 | py_executable = '"%s"' % py_executable
831 | cmd = [py_executable, '-c', 'import sys; print sys.prefix']
832 | logger.info('Testing executable with %s %s "%s"' % tuple(cmd))
833 | proc = subprocess.Popen(cmd,
834 | stdout=subprocess.PIPE)
835 | proc_stdout, proc_stderr = proc.communicate()
836 | proc_stdout = os.path.normcase(os.path.abspath(proc_stdout.strip()))
837 | if proc_stdout != os.path.normcase(os.path.abspath(home_dir)):
838 | logger.fatal(
839 | 'ERROR: The executable %s is not functioning' % py_executable)
840 | logger.fatal(
841 | 'ERROR: It thinks sys.prefix is %r (should be %r)'
842 | % (proc_stdout, os.path.normcase(os.path.abspath(home_dir))))
843 | logger.fatal(
844 | 'ERROR: virtualenv is not compatible with this system or executable')
845 | if sys.platform == 'win32':
846 | logger.fatal(
847 | 'Note: some Windows users have reported this error when they installed Python for "Only this user". The problem may be resolvable if you install Python "For all users". (See https://bugs.launchpad.net/virtualenv/+bug/352844)')
848 | sys.exit(100)
849 | else:
850 | logger.info('Got sys.prefix result: %r' % proc_stdout)
851 |
852 | pydistutils = os.path.expanduser('~/.pydistutils.cfg')
853 | if os.path.exists(pydistutils):
854 | logger.notify('Please make sure you remove any previous custom paths from '
855 | 'your %s file.' % pydistutils)
856 | ## FIXME: really this should be calculated earlier
857 | return py_executable
858 |
859 | def install_activate(home_dir, bin_dir):
860 | if sys.platform == 'win32' or is_jython and os._name == 'nt':
861 | files = {'activate.bat': ACTIVATE_BAT,
862 | 'deactivate.bat': DEACTIVATE_BAT}
863 | if os.environ.get('OS') == 'Windows_NT' and os.environ.get('OSTYPE') == 'cygwin':
864 | files['activate'] = ACTIVATE_SH
865 | else:
866 | files = {'activate': ACTIVATE_SH}
867 | files['activate_this.py'] = ACTIVATE_THIS
868 | for name, content in files.items():
869 | content = content.replace('__VIRTUAL_ENV__', os.path.abspath(home_dir))
870 | content = content.replace('__VIRTUAL_NAME__', os.path.basename(os.path.abspath(home_dir)))
871 | content = content.replace('__BIN_NAME__', os.path.basename(bin_dir))
872 | writefile(os.path.join(bin_dir, name), content)
873 |
874 | def install_distutils(lib_dir, home_dir):
875 | distutils_path = os.path.join(lib_dir, 'distutils')
876 | mkdir(distutils_path)
877 | ## FIXME: maybe this prefix setting should only be put in place if
878 | ## there's a local distutils.cfg with a prefix setting?
879 | home_dir = os.path.abspath(home_dir)
880 | ## FIXME: this is breaking things, removing for now:
881 | #distutils_cfg = DISTUTILS_CFG + "\n[install]\nprefix=%s\n" % home_dir
882 | writefile(os.path.join(distutils_path, '__init__.py'), DISTUTILS_INIT)
883 | writefile(os.path.join(distutils_path, 'distutils.cfg'), DISTUTILS_CFG, overwrite=False)
884 |
885 | def fix_lib64(lib_dir):
886 | """
887 | Some platforms (particularly Gentoo on x64) put things in lib64/pythonX.Y
888 | instead of lib/pythonX.Y. If this is such a platform we'll just create a
889 | symlink so lib64 points to lib
890 | """
891 | if [p for p in distutils.sysconfig.get_config_vars().values()
892 | if isinstance(p, basestring) and 'lib64' in p]:
893 | logger.debug('This system uses lib64; symlinking lib64 to lib')
894 | assert os.path.basename(lib_dir) == 'python%s' % sys.version[:3], (
895 | "Unexpected python lib dir: %r" % lib_dir)
896 | lib_parent = os.path.dirname(lib_dir)
897 | assert os.path.basename(lib_parent) == 'lib', (
898 | "Unexpected parent dir: %r" % lib_parent)
899 | copyfile(lib_parent, os.path.join(os.path.dirname(lib_parent), 'lib64'))
900 |
901 | def resolve_interpreter(exe):
902 | """
903 | If the executable given isn't an absolute path, search $PATH for the interpreter
904 | """
905 | if os.path.abspath(exe) != exe:
906 | paths = os.environ.get('PATH', '').split(os.pathsep)
907 | for path in paths:
908 | if os.path.exists(os.path.join(path, exe)):
909 | exe = os.path.join(path, exe)
910 | break
911 | if not os.path.exists(exe):
912 | logger.fatal('The executable %s (from --python=%s) does not exist' % (exe, exe))
913 | sys.exit(3)
914 | return exe
915 |
916 | ############################################################
917 | ## Relocating the environment:
918 |
919 | def make_environment_relocatable(home_dir):
920 | """
921 | Makes the already-existing environment use relative paths, and takes out
922 | the #!-based environment selection in scripts.
923 | """
924 | activate_this = os.path.join(home_dir, 'bin', 'activate_this.py')
925 | if not os.path.exists(activate_this):
926 | logger.fatal(
927 | 'The environment doesn\'t have a file %s -- please re-run virtualenv '
928 | 'on this environment to update it' % activate_this)
929 | fixup_scripts(home_dir)
930 | fixup_pth_and_egg_link(home_dir)
931 | ## FIXME: need to fix up distutils.cfg
932 |
933 | OK_ABS_SCRIPTS = ['python', 'python%s' % sys.version[:3],
934 | 'activate', 'activate.bat', 'activate_this.py']
935 |
936 | def fixup_scripts(home_dir):
937 | # This is what we expect at the top of scripts:
938 | shebang = '#!%s/bin/python' % os.path.normcase(os.path.abspath(home_dir))
939 | # This is what we'll put:
940 | new_shebang = '#!/usr/bin/env python%s' % sys.version[:3]
941 | activate = "import os; activate_this=os.path.join(os.path.dirname(__file__), 'activate_this.py'); execfile(activate_this, dict(__file__=activate_this)); del os, activate_this"
942 | bin_dir = os.path.join(home_dir, 'bin')
943 | for filename in os.listdir(bin_dir):
944 | filename = os.path.join(bin_dir, filename)
945 | if not os.path.isfile(filename):
946 | # ignore subdirs, e.g. .svn ones.
947 | continue
948 | f = open(filename, 'rb')
949 | lines = f.readlines()
950 | f.close()
951 | if not lines:
952 | logger.warn('Script %s is an empty file' % filename)
953 | continue
954 | if not lines[0].strip().startswith(shebang):
955 | if os.path.basename(filename) in OK_ABS_SCRIPTS:
956 | logger.debug('Cannot make script %s relative' % filename)
957 | elif lines[0].strip() == new_shebang:
958 | logger.info('Script %s has already been made relative' % filename)
959 | else:
960 | logger.warn('Script %s cannot be made relative (it\'s not a normal script that starts with %s)'
961 | % (filename, shebang))
962 | continue
963 | logger.notify('Making script %s relative' % filename)
964 | lines = [new_shebang+'\n', activate+'\n'] + lines[1:]
965 | f = open(filename, 'wb')
966 | f.writelines(lines)
967 | f.close()
968 |
969 | def fixup_pth_and_egg_link(home_dir, sys_path=None):
970 | """Makes .pth and .egg-link files use relative paths"""
971 | home_dir = os.path.normcase(os.path.abspath(home_dir))
972 | if sys_path is None:
973 | sys_path = sys.path
974 | for path in sys_path:
975 | if not path:
976 | path = '.'
977 | if not os.path.isdir(path):
978 | continue
979 | path = os.path.normcase(os.path.abspath(path))
980 | if not path.startswith(home_dir):
981 | logger.debug('Skipping system (non-environment) directory %s' % path)
982 | continue
983 | for filename in os.listdir(path):
984 | filename = os.path.join(path, filename)
985 | if filename.endswith('.pth'):
986 | if not os.access(filename, os.W_OK):
987 | logger.warn('Cannot write .pth file %s, skipping' % filename)
988 | else:
989 | fixup_pth_file(filename)
990 | if filename.endswith('.egg-link'):
991 | if not os.access(filename, os.W_OK):
992 | logger.warn('Cannot write .egg-link file %s, skipping' % filename)
993 | else:
994 | fixup_egg_link(filename)
995 |
996 | def fixup_pth_file(filename):
997 | lines = []
998 | prev_lines = []
999 | f = open(filename)
1000 | prev_lines = f.readlines()
1001 | f.close()
1002 | for line in prev_lines:
1003 | line = line.strip()
1004 | if (not line or line.startswith('#') or line.startswith('import ')
1005 | or os.path.abspath(line) != line):
1006 | lines.append(line)
1007 | else:
1008 | new_value = make_relative_path(filename, line)
1009 | if line != new_value:
1010 | logger.debug('Rewriting path %s as %s (in %s)' % (line, new_value, filename))
1011 | lines.append(new_value)
1012 | if lines == prev_lines:
1013 | logger.info('No changes to .pth file %s' % filename)
1014 | return
1015 | logger.notify('Making paths in .pth file %s relative' % filename)
1016 | f = open(filename, 'w')
1017 | f.write('\n'.join(lines) + '\n')
1018 | f.close()
1019 |
1020 | def fixup_egg_link(filename):
1021 | f = open(filename)
1022 | link = f.read().strip()
1023 | f.close()
1024 | if os.path.abspath(link) != link:
1025 | logger.debug('Link in %s already relative' % filename)
1026 | return
1027 | new_link = make_relative_path(filename, link)
1028 | logger.notify('Rewriting link %s in %s as %s' % (link, filename, new_link))
1029 | f = open(filename, 'w')
1030 | f.write(new_link)
1031 | f.close()
1032 |
1033 | def make_relative_path(source, dest, dest_is_directory=True):
1034 | """
1035 | Make a filename relative, where the filename is dest, and it is
1036 | being referred to from the filename source.
1037 |
1038 | >>> make_relative_path('/usr/share/something/a-file.pth',
1039 | ... '/usr/share/another-place/src/Directory')
1040 | '../another-place/src/Directory'
1041 | >>> make_relative_path('/usr/share/something/a-file.pth',
1042 | ... '/home/user/src/Directory')
1043 | '../../../home/user/src/Directory'
1044 | >>> make_relative_path('/usr/share/a-file.pth', '/usr/share/')
1045 | './'
1046 | """
1047 | source = os.path.dirname(source)
1048 | if not dest_is_directory:
1049 | dest_filename = os.path.basename(dest)
1050 | dest = os.path.dirname(dest)
1051 | dest = os.path.normpath(os.path.abspath(dest))
1052 | source = os.path.normpath(os.path.abspath(source))
1053 | dest_parts = dest.strip(os.path.sep).split(os.path.sep)
1054 | source_parts = source.strip(os.path.sep).split(os.path.sep)
1055 | while dest_parts and source_parts and dest_parts[0] == source_parts[0]:
1056 | dest_parts.pop(0)
1057 | source_parts.pop(0)
1058 | full_parts = ['..']*len(source_parts) + dest_parts
1059 | if not dest_is_directory:
1060 | full_parts.append(dest_filename)
1061 | if not full_parts:
1062 | # Special case for the current directory (otherwise it'd be '')
1063 | return './'
1064 | return os.path.sep.join(full_parts)
1065 |
1066 |
1067 |
1068 | ############################################################
1069 | ## Bootstrap script creation:
1070 |
1071 | def create_bootstrap_script(extra_text, python_version=''):
1072 | """
1073 | Creates a bootstrap script, which is like this script but with
1074 | extend_parser, adjust_options, and after_install hooks.
1075 |
1076 | This returns a string that (written to disk of course) can be used
1077 | as a bootstrap script with your own customizations. The script
1078 | will be the standard virtualenv.py script, with your extra text
1079 | added (your extra text should be Python code).
1080 |
1081 | If you include these functions, they will be called:
1082 |
1083 | ``extend_parser(optparse_parser)``:
1084 | You can add or remove options from the parser here.
1085 |
1086 | ``adjust_options(options, args)``:
1087 | You can change options here, or change the args (if you accept
1088 | different kinds of arguments, be sure you modify ``args`` so it is
1089 | only ``[DEST_DIR]``).
1090 |
1091 | ``after_install(options, home_dir)``:
1092 |
1093 | After everything is installed, this function is called. This
1094 | is probably the function you are most likely to use. An
1095 | example would be::
1096 |
1097 | def after_install(options, home_dir):
1098 | subprocess.call([join(home_dir, 'bin', 'easy_install'),
1099 | 'MyPackage'])
1100 | subprocess.call([join(home_dir, 'bin', 'my-package-script'),
1101 | 'setup', home_dir])
1102 |
1103 | This example immediately installs a package, and runs a setup
1104 | script from that package.
1105 |
1106 | If you provide something like ``python_version='2.4'`` then the
1107 | script will start with ``#!/usr/bin/env python2.4`` instead of
1108 | ``#!/usr/bin/env python``. You can use this when the script must
1109 | be run with a particular Python version.
1110 | """
1111 | filename = __file__
1112 | if filename.endswith('.pyc'):
1113 | filename = filename[:-1]
1114 | f = open(filename, 'rb')
1115 | content = f.read()
1116 | f.close()
1117 | py_exe = 'python%s' % python_version
1118 | content = (('#!/usr/bin/env %s\n' % py_exe)
1119 | + '## WARNING: This file is generated\n'
1120 | + content)
1121 | return content.replace('##EXT' 'END##', extra_text)
1122 |
1123 | ##EXTEND##
1124 |
1125 | ##file site.py
1126 | SITE_PY = """
1127 | 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
1257 | yBXo8FFND74JkgILcmKn2vJbYxD8H2/QG9E=
1258 | """.decode("base64").decode("zlib")
1259 |
1260 | ##file ez_setup.py
1261 | EZ_SETUP_PY = """
1262 | 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
1328 | 178BYnMUkw==
1329 | """.decode("base64").decode("zlib")
1330 |
1331 | ##file distribute_setup.py
1332 | DISTRIBUTE_SETUP_PY = """
1333 | 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
1410 | """.decode("base64").decode("zlib")
1411 |
1412 | ##file activate.sh
1413 | ACTIVATE_SH = """
1414 | eJytVFFv2jAQfs+vuIU+QDWK+tqKB6oigdRC1bBOW1sZk1yIpWAj2yGj0/77ziFAUijStPIA2Hc+
1415 | f/7u+64Bk0QYiEWKsMiMhRlCZjCCXNgEfKMyHSLMhOzw0IoVt+jDeazVAmbcJOdeA9Yqg5BLqSzo
1416 | TIKwEAmNoU3Xnhfh9hQ0W/DbA/o0QKNBCyqNAOVKaCUXKC2suBZ8lqIpskQMz9CW4J+x8d0texo+
1417 | Tr717thDbzLw4RWuwSYoi0z3cdvdY6m7DPy1VNoWibu9TDocB4eKeCxOwvgxGYxHg/F9/xiYXfAA
1418 | 0v7YAbBd6CS8ehaBLCktmmgSlRGpEVqiv+gPcBnBm0m+Qp6IMIGErxA4/VAoVIuFC9uE26L1ZSkS
1419 | QMjTlCRgFcwJAXWU/sVKu8WSk0bKo+YC4DvJRGW2DFsh52WZWqIjCM4cuRAmXM7RQE5645H7WoPT
1420 | Dl1LulgScozeUX/TC6jpbbVZ/QwG7Kn/GAzHoyPkF09r6xo9HzUxuDzWveDyoG2UeNCv4PJko8rw
1421 | FsImZRvtj572wL4QLgLSBV8qGaGxOnOewXfYGhBgGsM24cu729sutDXb9uo/HvlzExdaY0rdrxmt
1422 | Ys/63Z5Xgdr1GassGfO9koTqe7wDHxGNGw+Wi0p2h7Gb4YiNevd9xq7KtKpFd7j3inds0Q5FrBN7
1423 | LtIUYi5St1/NMi7LKdZpDhdLuwZ6FwkTmhsTUMaMR2SNdc7XLaoXFrahqQdTqtUs6Myu4YoUu6vb
1424 | guspCFm4ytsL6sNB8IFtu7UjFWlUnO00s7nhDWqssdth0Lu567OHx/H9w+TkjYWKd8ItyvlTAo+S
1425 | LxBeanVf/GmhP+rsoR8a4EwpeEpTgRgin0OPdiQZdy7CctYrLcq5XR5BhMTa5VWnk+f5xRtasvrq
1426 | gsZBx6jY5lxjh7sqnbrvnisQp1T6KNiX6fQV9m/D1GC9SvPEQ1v7g+WIrxjaMf9Js/QT5uh/ztB/
1427 | n5/b2Uk0/AXm/2MV
1428 | """.decode("base64").decode("zlib")
1429 |
1430 | ##file activate.bat
1431 | ACTIVATE_BAT = """
1432 | eJyFUssKgzAQvAfyD3swYH+hItSiVKlGsalQKOyhauvFHOr/U+MzFcWc9jEzO7vkVLw+EmRZUvIt
1433 | GsiCVNydED2e2YhahkgJJVUJtWwgL8qqLnJI0jhKBJiUQPsUv6/YRmJcKDkMlBGOcehOmptctgJj
1434 | e2IP4cfcjyNvFOwVp/JSdWqMygq+MthmkwHNojmfhjuRh3iAGffncsPYhpl2mm5sbY+9QzjC7ylt
1435 | sFy6LTEL3rKRcLsGicrXV++4HVz1jzN4Vta+BnsingM+nMLSiB53KfkBsnmnEA==
1436 | """.decode("base64").decode("zlib")
1437 |
1438 | ##file deactivate.bat
1439 | DEACTIVATE_BAT = """
1440 | eJxzSE3OyFfIT0vj4spMU0hJTcvMS01RiPf3cYkP8wwKCXX0iQ8I8vcNCFHQ4FIAguLUEgWIgK0q
1441 | FlWqXJpcICVYpGzx2BAZ4uHv5+Hv6wq1BWINXBTdKriEKkI1DhW2QAfhttcxxANiFZCBbglQSJUL
1442 | i2dASrm4rFz9XLgAwJNbyQ==
1443 | """.decode("base64").decode("zlib")
1444 |
1445 | ##file distutils-init.py
1446 | DISTUTILS_INIT = """
1447 | eJytVl2L6zYQffevGBKK7XavKe3bhVBo78uFSyml0IdlEVpbTtR1JCMpm6S/vjOSY0v+uO1DDbs4
1448 | 0tF8nJk5sjz32jjQNpPhzd7H1ys3SqqjhcfCL1q18vgbN1YY2Kc/pQWlHXB4l8ZdeCfUO5x1c+nE
1449 | E1gNVwE1V3CxAqQDp6GVqgF3EmBd08nXLGukUfws4IDBVD13p2pYoS3rLk52ltF6hPhLS1XM4EUc
1450 | VsVYKzvBWPkE+WgmLzPZjkaUNmd6KVI3JRwWoRSLM6P98mMG+Dw4q+il8Ev07P7ATCNmRlfQ8/qN
1451 | HwVwB99Y4H0vMHAi6BWZUoEhoqXTNXdSK+A2LN6tE+fJ0E+7MhOdFSEM5lNgrJIKWXDF908wy87D
1452 | xE3UoHsxkegZTaHIHGNSSYfm+ntelpURvCnK7NEWBI/ap/b8Z1m232N2rj7B60V2DRM3B5NpaLSw
1453 | KnfwpvQVTviHOR+F88lhQyBAGlE7be6DoRNg9ldsG3218IHa6MRNU+tGBEYIggwafRk6yzsXDcVU
1454 | 9Ua08kYxt+F3x12LRaQi52j0xx/ywFxrdMRqVevzmaummlIYEp0WsCAaX8cFb6buuLUTqEgQQ6/Q
1455 | 04iWRoF38m/BdE8VtlBY0bURiB6KG1crpMZwc2fIjqWh+1UrkSLpWUIP8PySwLKv4qPGSVqDuMPy
1456 | dywQ+gS7L1irXVkm5pJsq3l+Ib1lMOvUrxI+/mBBY4KB+WpUtcO06RtzckNvQ6vYj1lGoZM2sdDG
1457 | fryJPYJVn/Cfka8XSqNaoLKhmOlqXMzW9+YBVp1EtIThZtOwzCRvMaARa+0xD0b2kcaJGwJsMbc7
1458 | hLUfY4vKvsCOBdvDnyfuRbzmXRdGTZgPF7oGQkJACWVD22IMQdhx0npt5S2f+pXO+OwH6d+hwiS5
1459 | 7IJOjcK2emj1zBy1aONHByfAMoraw6WlrSIFTbGghqASoRCjVncYROFpXM4uYSqhGnuVeGvks4jz
1460 | cjnCoR5GnPW7KOh4maVbdFeoplgJ3wh3MSrAsv/QuMjOspnTKRl1fTYqqNisv7uTVnhF1GhoBFbp
1461 | lh+OcXN2riA5ZrYXtWxlfcDuC8U5kLoN3CCJYXGpesO6dx6rU0zGMtjU6cNlmW0Fid8Sja4ZG+Z3
1462 | fTPbyj+mZnZ2wSQK8RaT9Km0ySRuLpm0DkUUL0ra3WQ2BgGJ7v9I9SKqNKZ/IR4R28RHm+vEz5ic
1463 | nZ2IH7bfub8pU1PR3gr10W7xLTfHh6Z6bgZ7K14G7Mj/1z5J6MFo6V5e07H0Ou78dTyeI+mxKOpI
1464 | eC2KMSj6HKxd6Uudf/n886fPv+f++x1lbASlmjQuPz8OvGA0j7j2eCu/4bcW6SFeCuNJ0W1GQHI5
1465 | iwC9Ey0bjtHd9P4dPA++XxLnZDVuxvFEtlm3lf5a2c02u2LRYXHH/AOs8pIa
1466 | """.decode("base64").decode("zlib")
1467 |
1468 | ##file distutils.cfg
1469 | DISTUTILS_CFG = """
1470 | eJxNj00KwkAMhfc9xYNuxe4Ft57AjYiUtDO1wXSmNJnK3N5pdSEEAu8nH6lxHVlRhtDHMPATA4uH
1471 | xJ4EFmGbvfJiicSHFRzUSISMY6hq3GLCRLnIvSTnEefN0FIjw5tF0Hkk9Q5dRunBsVoyFi24aaLg
1472 | 9FDOlL0FPGluf4QjcInLlxd6f6rqkgPu/5nHLg0cXCscXoozRrP51DRT3j9QNl99AP53T2Q=
1473 | """.decode("base64").decode("zlib")
1474 |
1475 | ##file activate_this.py
1476 | ACTIVATE_THIS = """
1477 | eJyNUk2L3DAMvftXiCxLEphmSvc2MIcu9NaWHnopwxCcRNlRN7GD7clM/n0lp5mPZQs1JLb8pKcn
1478 | WUmSPE9w9GReAM9Yt9RhFg7kSzmtoKE6ZGU0ynJ7AfIcJnuEE3Wd0nWgUQcEQWEkF466QzMCf+Ss
1479 | 6dGEQqmfgtbaQIWcDxs4HdBElv7og1wBg3gmH0TMjykcrAEyAd3gkP8rMDaocMDbHBWZ9RBdVZIk
1480 | SgU3bRTwWjQrPNc4BPiue/zinHUz7DRxws/eowtkTUSyiMhKfi2y3NHMdXX0itcOpYMOh3Ww61g8
1481 | luJSDFP6tmH3ftyki2eeJ7mifrAugJ/8crReqUqztC0fC4kuGnKGxWf/snXlZb8kzXMmboW0GDod
1482 | Wut62G4hPZF5+pTO5XtiKYOuX/UL+ptcvy2ZTPKvIP1KFdeTiuuHxTXNFXYe/5+km0nmJ3r0KTxG
1483 | YSM6z23fbZ7276Tg9x5LdiuFjok7noks1sP2tWscpeRX6KaRnRuT3WnKlQQ51F3JlC2dmSvSRENd
1484 | j3wvetUDfLOjDDLPYtPwjDJb7yHYeNXyMPMLtdEQKRtl8HQrdLdX3O4YxZP7RvfcNH6ZCPMsi8td
1485 | qZvLAN7yFnoY0DSZhOUXj4WWy+tZ8190ud1tPu5Zzy2N+gOGaVfA
1486 | """.decode("base64").decode("zlib")
1487 |
1488 | if __name__ == '__main__':
1489 | main()
1490 |
1491 | ## TODO:
1492 | ## Copy python.exe.manifest
1493 | ## Monkeypatch distutils.sysconfig
1494 |
--------------------------------------------------------------------------------