Merge lp:~vila/bzr/385453-make-pyrex into lp:~bzr/bzr/trunk-old

Proposed by Vincent Ladeuil
Status: Rejected
Rejected by: Martin Pool
Proposed branch: lp:~vila/bzr/385453-make-pyrex
Merge into: lp:~bzr/bzr/trunk-old
Diff against target: 286 lines
To merge this branch: bzr merge lp:~vila/bzr/385453-make-pyrex
Reviewer Review Type Date Requested Status
Martin Pool Needs Fixing
Review via email: mp+7808@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Vincent Ladeuil (vila) wrote :

So this implements a simple-minded fix for the bug.

The idea is that setup.py is used, so far, to either build extensions or build the distribution which should always include the extensions anyway.

In short, setup is useless if you don't have pyrex or if you don't have the C files.

So the implemented fix make setup abort if pyrex is not available *and* the C files are not available.

It also starts implementing tests for that.

As discussed with Martin and John, splitting the build step into generation and compilation and
archiving the post-processed generated files can come later.

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Vincent Ladeuil wrote:
> Vincent Ladeuil has proposed merging lp:~vila/bzr/385453-make-pyrex into lp:bzr.
>
> Requested reviews:
> bzr-core (bzr-core)
>
> So this implements a simple-minded fix for the bug.
>
> The idea is that setup.py is used, so far, to either build extensions or build the distribution which should always include the extensions anyway.
>
> In short, setup is useless if you don't have pyrex or if you don't have the C files.
>
> So the implemented fix make setup abort if pyrex is not available *and* the C files are not available.
>
> It also starts implementing tests for that.
>
> As discussed with Martin and John, splitting the build step into generation and compilation and
> archiving the post-processed generated files can come later.
>

This seems to completely disable --allow-python-fallbacks which is how
we handle it in the past.

Am I missing something?

John
=:->

-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkpBCjYACgkQJdeBCYSNAAN7uQCcD1ZKQpqr7R4jOIKwyCU+EEe6
ucwAoMK3Eskll0CWf3J+4MzdOYjxSMF7
=1bEf
-----END PGP SIGNATURE-----

Revision history for this message
Vincent Ladeuil (vila) wrote :

>>>>> "jam" == John A Meinel <email address hidden> writes:

    jam> Vincent Ladeuil wrote:
    >> Vincent Ladeuil has proposed merging lp:~vila/bzr/385453-make-pyrex into lp:bzr.
    >>
    >> Requested reviews:
    >> bzr-core (bzr-core)
    >>
    >> So this implements a simple-minded fix for the bug.
    >>
    >> The idea is that setup.py is used, so far, to either build extensions or build the distribution which should always include the extensions anyway.
    >>
    >> In short, setup is useless if you don't have pyrex or if you don't have the C files.
    >>
    >> So the implemented fix make setup abort if pyrex is not available *and* the C files are not available.
    >>
    >> It also starts implementing tests for that.
    >>
    >> As discussed with Martin and John, splitting the build step into generation and compilation and
    >> archiving the post-processed generated files can come later.
    >>

    jam> This seems to completely disable --allow-python-fallbacks which is how
    jam> we handle it in the past.

    jam> Am I missing something?

Grr, no, I missed to ask the question that prompted me to submit:

What is the intended use of --allow-python-fallbacks ?

As far as I can tell, it is to allow compile errors to be
ignored, so failing to build the extensions is not seen as an
error to build a distribution. I think we want to get away from
such tolerance, the bug title is:
"make dist should fail if C files don't exist or can't be built"

That's why I summarize above: "the distribution should always
include the extensions".

I confess that the setup.py is a bit hard to analyze without
being able to test it :-/

I'd say that at least 2/3 of it is windows specific... It would
be nice to define more specific setup commands for each sub part
(even if not really needed from a pure setup point of view, if
only to be able to test them and use them for make purposes), the
current relationship between setup and Makefile are a bit hard to
follow. Anyway, that's outside the scope of this bug.

So this submission is more RFC than MERGE.

Revision history for this message
Martin Pool (mbp) wrote :

2009/6/24 Vincent Ladeuil <email address hidden>:
>>>>>> "jam" == John A Meinel <email address hidden> writes:
>
> Grr, no, I missed to ask the question that prompted me to submit:
>
> What is the intended use of --allow-python-fallbacks ?
>
> As far as I can tell, it is to allow compile errors to be
> ignored, so failing to build the extensions is not seen as an
> error to build a distribution. I think we want to get away from
> such tolerance, the bug title is:
> "make dist should fail if C files don't exist or can't be built"
>
> That's why I summarize above: "the distribution should always
> include the extensions".

As I understand it the goal is that you can run bzr from source even
without a compiler. I think this is a bit useful for people
installing on a minority or difficult operating system.

I don't think it needs to be a supported or even desirable case to
allow you to make a tarball with no .c files, or even to make a binary
package without the built libraries.

I could even see a case for saying in that extreme case you need to
just run from the source directory and not run setup.py at all. Maybe
it's not important at all; if you're running python at all obviously
someone somewhere has a compiler that can build Python.

--
Martin <http://launchpad.net/~mbp/>

Revision history for this message
Robert Collins (lifeless) wrote :

On Thu, 2009-06-25 at 08:57 +0000, Martin Pool wrote:
>
> I could even see a case for saying in that extreme case you need to
> just run from the source directory and not run setup.py at all. Maybe
> it's not important at all; if you're running python at all obviously
> someone somewhere has a compiler that can build Python.

The compiler may be a cross-compiler; theres no strong case for having a
native compiler. Perhaps this is overly pessimistic.

-Rob

Revision history for this message
John A Meinel (jameinel) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

...

> As I understand it the goal is that you can run bzr from source even
> without a compiler. I think this is a bit useful for people
> installing on a minority or difficult operating system.

The idea was that you could run/install bzr even without having a
compiler. This is somewhat useful for people who are deploying bzr
manually on machines that they don't want to install the compiler tool
chain.

>
> I don't think it needs to be a supported or even desirable case to
> allow you to make a tarball with no .c files, or even to make a binary
> package without the built libraries.
>
> I could even see a case for saying in that extreme case you need to
> just run from the source directory and not run setup.py at all. Maybe
> it's not important at all; if you're running python at all obviously
> someone somewhere has a compiler that can build Python.
>

If we want to change the story to "you can run from source without
compiling extensions, but we won't let you install" I can be okay with
that, but we should be clear that *that* is what we are now saying.

John
=:->
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.9 (Cygwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iEYEARECAAYFAkpDioEACgkQJdeBCYSNAAPkdgCfSgMVA5UNdWj5Yr2iIrIdme9N
Pr4AmQHfF5nvvRV9HRaAVB5Na3NnblPZ
=jTob
-----END PGP SIGNATURE-----

Revision history for this message
Martin Pool (mbp) wrote :

> So this implements a simple-minded fix for the bug.
>
> The idea is that setup.py is used, so far, to either build extensions or build
> the distribution which should always include the extensions anyway.
>
> In short, setup is useless if you don't have pyrex or if you don't have the C
> files.
>
> So the implemented fix make setup abort if pyrex is not available *and* the C
> files are not available.

So, to try to finish this review off:

I think we want this behaviour:

if allow_python_fallbacks:
  pass
else:
  if not have_current_c_files:
    must_have_pyrex
  must_have_cc

This should apply to installation, and to making source and binary distributions.

Then 'make dist' should not pass --allow-python-fallbacks, so someone trying to prepare an official release should always include them.

I think this is consistent with what John and Robert are saying.

I don't think this is what this patch does, so for the sake of keeping the queue clean I'm going to say resubmit. But if you think we should have different behaviour we can certainly talk about it.

70 # TODO: Run bzr from the installed copy to see if it works. Really we need to
71 # run something that exercises every module, just starting it may not detect
72 # some missing modules.
73 #
74 +# NOTE: Doing the above for every commit seems overkill. I'm not against
75 +# defining a separate test uite for precisely that purpose to be run at
76 +# *release* time, but then, our actual test suite sounds adequate
77 +# -- vila 20090623
78 +#
79 # TODO: Check that the version numbers are in sync. (Or avoid this...)

I think the previous comment is now mostly obsolete, as we make 'make dist-check-install', and running the test suite is a recommended part of the release process. I suggest you just delete the comment; if there are failures where things are not being properly installed we can look for cheaper ways to do it.

review: Needs Fixing

Unmerged revisions

4465. By Vincent Ladeuil

Rough fix for bug#385453.

* setup.py:
Some cleanup and exit early if pyrex generated C files are not
available.

* bzrlib/tests/test_setup.py:
(TestSetup.setUp): CleanupBetter protect the build dir.
(TestSetupBuild): Start adding tests for the build step.
(TestSetupInstall): Separate install step tests.

* bzrlib/tests/__init__.py:
(PyrexFeature): Most extensions use pyrex.

4464. By Vincent Ladeuil

Cleanup setup tests.

* bzrlib/tests/test_setup.py:
(TestSetup): Protect the existing build dir.
(TestSetup.test_build_and_install.rmtree): Use cleanup hooks and
don't clobber the existing build dir.

* .bzrignore:
All pyrex generated files follow the same pattern.

4463. By Vincent Ladeuil

Fixed as per John and Martin reviews.

* bzrlib/_dirstate_helpers_pyx.pyx:
(_cmp_path_by_dirblock_intern): Renamed as requested.

4462. By Vincent Ladeuil

Fix failing benchmarks for dirstate helpers.

4461. By Vincent Ladeuil

Use the same method or function names for _dirstate_helpers in pyrex and
python modules.

4460. By Vincent Ladeuil

Use a consistent scheme for naming pyrex source files.

4459. By Canonical.com Patch Queue Manager <email address hidden>

(jam) Remove a spurious call to _get_raw_record,
 speeds up get_record_stream(..., True)

4458. By Canonical.com Patch Queue Manager <email address hidden>

(andrew) Fix branch format upgrades triggered by default stacking
 policy on a smart server.

4457. By Canonical.com Patch Queue Manager <email address hidden>

(igc) fix ls DIR --from-root and improve ls performance

4456. By Canonical.com Patch Queue Manager <email address hidden>

(jam) Fix some failing tests on win32

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2009-06-22 12:52:39 +0000
3+++ .bzrignore 2009-07-01 22:35:49 +0000
4@@ -38,16 +38,8 @@
5 ./api
6 doc/**/*.html
7 doc/developers/performance.png
8-bzrlib/_bencode_pyx.c
9-bzrlib/_btree_serializer_pyx.c
10-bzrlib/_chk_map_pyx.c
11-bzrlib/_chunks_to_lines_pyx.c
12-bzrlib/_dirstate_helpers_pyx.c
13-bzrlib/_groupcompress_pyx.c
14-bzrlib/_knit_load_data_pyx.c
15-bzrlib/_known_graph_pyx.c
16-bzrlib/_readdir_pyx.c
17-bzrlib/_rio_pyx.c
18+# Pyrex generated C files
19+bzrlib/_*_pyx.c
20 bzrlib/_walkdirs_win32.c
21 doc/en/release-notes/NEWS.txt
22 doc/en/developer-guide/HACKING.txt
23
24=== modified file 'bzrlib/tests/__init__.py'
25--- bzrlib/tests/__init__.py 2009-06-29 15:25:08 +0000
26+++ bzrlib/tests/__init__.py 2009-07-01 22:35:49 +0000
27@@ -3986,3 +3986,20 @@
28 return result
29 except ImportError:
30 pass
31+
32+
33+class _PyrexFeature(Feature):
34+
35+ def _probe(self):
36+ try:
37+ import Pyrex
38+ return True
39+ except ImportError:
40+ return False
41+
42+ def feature_name(self):
43+ return 'pyrex'
44+
45+PyrexFeature = _PyrexFeature()
46+
47+
48
49=== modified file 'bzrlib/tests/test_setup.py'
50--- bzrlib/tests/test_setup.py 2009-03-23 14:59:43 +0000
51+++ bzrlib/tests/test_setup.py 2009-07-01 22:35:49 +0000
52@@ -20,55 +20,47 @@
53 import sys
54 import subprocess
55 import shutil
56-from tempfile import TemporaryFile
57+import tempfile
58
59 import bzrlib
60-from bzrlib.tests import TestCase, TestSkipped
61-import bzrlib.osutils as osutils
62+from bzrlib import (
63+ osutils,
64+ tests,
65+ )
66
67-# XXX: This clobbers the build directory in the real source tree; it'd be nice
68-# to avoid that.
69-#
70 # TODO: Run bzr from the installed copy to see if it works. Really we need to
71 # run something that exercises every module, just starting it may not detect
72 # some missing modules.
73 #
74+# NOTE: Doing the above for every commit seems overkill. I'm not against
75+# defining a separate test uite for precisely that purpose to be run at
76+# *release* time, but then, our actual test suite sounds adequate
77+# -- vila 20090623
78+#
79 # TODO: Check that the version numbers are in sync. (Or avoid this...)
80
81-class TestSetup(TestCase):
82-
83- def test_build_and_install(self):
84- """ test cmd `python setup.py build`
85-
86- This tests that the build process and man generator run correctly.
87- It also can catch new subdirectories that weren't added to setup.py.
88- """
89- if not os.path.isfile('setup.py'):
90- raise TestSkipped('There is no setup.py file in current directory')
91- try:
92- import distutils.sysconfig
93- makefile_path = distutils.sysconfig.get_makefile_filename()
94- if not os.path.exists(makefile_path):
95- raise TestSkipped('You must have the python Makefile installed to run this test.'
96- ' Usually this can be found by installing "python-dev"')
97- except ImportError:
98- raise TestSkipped('You must have distutils installed to run this test.'
99- ' Usually this can be found by installing "python-dev"')
100- self.log('test_build running in %s' % os.getcwd())
101- install_dir = osutils.mkdtemp()
102+class TestSetup(tests.TestCase):
103+
104+ def setUp(self):
105+ super(TestSetup, self).setUp()
106 # setup.py must be run from the root source directory, but the tests
107 # are not necessarily invoked from there
108 self.source_dir = os.path.dirname(os.path.dirname(bzrlib.__file__))
109- try:
110- self.run_setup(['clean'])
111- # build is implied by install
112- ## self.run_setup(['build'])
113- self.run_setup(['install', '--prefix', install_dir])
114- self.run_setup(['clean'])
115- finally:
116- osutils.rmtree(install_dir)
117+ self.build_dir = os.path.join(self.source_dir, 'build')
118+ # Do we already have a build dir in the source hierarchy ?
119+ if os.path.exists(self.build_dir):
120+ # Put it aside during the test
121+ tmp_dir = tempfile.mkdtemp(dir=self.source_dir)
122+ self.addCleanup(os.rmdir, tmp_dir)
123+ tmp_build_dir = os.path.join(tmp_dir, 'build')
124+ os.rename(self.build_dir, tmp_build_dir)
125+ # And restore the original after the test...
126+ self.addCleanup(os.rename, tmp_build_dir, self.build_dir)
127+ # Create a pristine build dir for the test
128+ os.mkdir(self.build_dir)
129+ self.addCleanup(self.rmtree, self.build_dir)
130
131- def run_setup(self, args):
132+ def run_setup(self, args, retcode=0):
133 args = [sys.executable, './setup.py', ] + args
134 self.log('source base directory: %s', self.source_dir)
135 self.log('args: %r', args)
136@@ -78,5 +70,59 @@
137 stderr=self._log_file,
138 )
139 s = p.communicate()
140- self.assertEqual(0, p.returncode,
141+ self.assertEqual(retcode, p.returncode,
142 'invocation of %r failed' % args)
143+
144+ def rmtree(self, dir):
145+ """Wrap osutils.rmtre
146+
147+ osutils.rmtree is lazy loaded and can't be used directly in cleanp hooks
148+ """
149+ osutils.rmtree(dir)
150+
151+
152+class TestSetupInstall(TestSetup):
153+
154+ def test_build_and_install(self):
155+ """ test cmd `python setup.py build`
156+
157+ This tests that the build process and man generator run correctly.
158+ It also can catch new subdirectories that weren't added to setup.py.
159+ """
160+ self.requireFeature(tests.PyrexFeature)
161+ if not os.path.isfile('setup.py'):
162+ raise TestSkipped('There is no setup.py file in current directory')
163+ try:
164+ import distutils.sysconfig
165+ makefile_path = distutils.sysconfig.get_makefile_filename()
166+ if not os.path.exists(makefile_path):
167+ raise TestSkipped('You must have the python Makefile installed'
168+ ' to run this test. Usually this can be found'
169+ ' by installing "python-dev"')
170+ except ImportError:
171+ raise TestSkipped('You must have distutils installed to run this'
172+ ' test. Usually this can be found by installing'
173+ ' "python-dev"')
174+ self.log('test_build_and_install running in %s' % os.getcwd())
175+ install_dir = osutils.mkdtemp()
176+ self.addCleanup(self.rmtree, install_dir)
177+
178+ self.run_setup(['clean'])
179+ # build is implied by install
180+ ## self.run_setup(['build'])
181+ self.run_setup(['install', '--prefix', install_dir])
182+ self.run_setup(['clean'])
183+
184+
185+class TestSetupBuild(TestSetup):
186+
187+ def test_with_pyrex(self):
188+ self.requireFeature(tests.PyrexFeature)
189+ self.run_setup(['build_ext'])
190+
191+ def test_without_pyrex(self):
192+ if tests.PyrexFeature.available():
193+ raise tests.TestSkipped('pyrex *is* installed')
194+ self.run_setup(['build_ext'], retcode=1)
195+
196+
197
198=== modified file 'setup.py'
199--- setup.py 2009-06-23 07:10:03 +0000
200+++ setup.py 2009-07-01 22:35:49 +0000
201@@ -161,22 +161,27 @@
202 from distutils import log
203 from distutils.errors import CCompilerError, DistutilsPlatformError
204 from distutils.extension import Extension
205+
206 ext_modules = []
207+
208 try:
209 from Pyrex.Distutils import build_ext
210 except ImportError:
211 have_pyrex = False
212- # try to build the extension from the prior generated source.
213+ # We will try to build the extension from the generated source.
214+ from distutils.command.build_ext import build_ext
215+else:
216+ have_pyrex = True
217+ from Pyrex.Compiler.Version import version as pyrex_version
218+
219+
220+if not have_pyrex:
221 print
222 print ("The python package 'Pyrex' is not available."
223 " If the .c files are available,")
224 print ("they will be built,"
225 " but modifying the .pyx files will not rebuild them.")
226 print
227- from distutils.command.build_ext import build_ext
228-else:
229- have_pyrex = True
230- from Pyrex.Compiler.Version import version as pyrex_version
231
232
233 class build_ext_if_possible(build_ext):
234@@ -221,15 +226,16 @@
235
236 # Override the build_ext if we have Pyrex available
237 command_classes['build_ext'] = build_ext_if_possible
238-unavailable_files = []
239+missing_pyrex_generated_files = []
240
241
242 def add_pyrex_extension(module_name, libraries=None, extra_source=[]):
243 """Add a pyrex module to build.
244
245 This will use Pyrex to auto-generate the .c file if it is available.
246- Otherwise it will fall back on the .c file. If the .c file is not
247- available, it will warn, and not add anything.
248+ Otherwise it will fall back on using the existing .c file. If the .c file
249+ is not available, it will not add the extension and setup will abort
250+ (whatever command is used).
251
252 You can pass any extra options to Extension through kwargs. One example is
253 'libraries = []'.
254@@ -250,13 +256,14 @@
255 source = [pyrex_name]
256 else:
257 if not os.path.isfile(c_name):
258- unavailable_files.append(c_name)
259+ missing_pyrex_generated_files.append(c_name)
260 return
261 else:
262 source = [c_name]
263 source.extend(extra_source)
264 ext_modules.append(Extension(module_name, source,
265- define_macros=define_macros, libraries=libraries))
266+ define_macros=define_macros,
267+ libraries=libraries))
268
269
270 add_pyrex_extension('bzrlib._bencode_pyx')
271@@ -292,12 +299,11 @@
272 ['bzrlib/_patiencediff_c.c']))
273
274
275-if unavailable_files:
276+if missing_pyrex_generated_files:
277 print 'C extension(s) not found:'
278- print ' %s' % ('\n '.join(unavailable_files),)
279- print 'The python versions will be used instead.'
280+ print ' %s' % ('\n '.join(missing_pyrex_generated_files),)
281 print
282-
283+ exit(1)
284
285 def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
286 gui_targets, data_files):