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
=== modified file '.bzrignore'
--- .bzrignore 2009-06-22 12:52:39 +0000
+++ .bzrignore 2009-07-01 22:35:49 +0000
@@ -38,16 +38,8 @@
38./api38./api
39doc/**/*.html39doc/**/*.html
40doc/developers/performance.png40doc/developers/performance.png
41bzrlib/_bencode_pyx.c41# Pyrex generated C files
42bzrlib/_btree_serializer_pyx.c42bzrlib/_*_pyx.c
43bzrlib/_chk_map_pyx.c
44bzrlib/_chunks_to_lines_pyx.c
45bzrlib/_dirstate_helpers_pyx.c
46bzrlib/_groupcompress_pyx.c
47bzrlib/_knit_load_data_pyx.c
48bzrlib/_known_graph_pyx.c
49bzrlib/_readdir_pyx.c
50bzrlib/_rio_pyx.c
51bzrlib/_walkdirs_win32.c43bzrlib/_walkdirs_win32.c
52doc/en/release-notes/NEWS.txt44doc/en/release-notes/NEWS.txt
53doc/en/developer-guide/HACKING.txt45doc/en/developer-guide/HACKING.txt
5446
=== modified file 'bzrlib/tests/__init__.py'
--- bzrlib/tests/__init__.py 2009-06-29 15:25:08 +0000
+++ bzrlib/tests/__init__.py 2009-07-01 22:35:49 +0000
@@ -3986,3 +3986,20 @@
3986 return result3986 return result
3987except ImportError:3987except ImportError:
3988 pass3988 pass
3989
3990
3991class _PyrexFeature(Feature):
3992
3993 def _probe(self):
3994 try:
3995 import Pyrex
3996 return True
3997 except ImportError:
3998 return False
3999
4000 def feature_name(self):
4001 return 'pyrex'
4002
4003PyrexFeature = _PyrexFeature()
4004
4005
39894006
=== modified file 'bzrlib/tests/test_setup.py'
--- bzrlib/tests/test_setup.py 2009-03-23 14:59:43 +0000
+++ bzrlib/tests/test_setup.py 2009-07-01 22:35:49 +0000
@@ -20,55 +20,47 @@
20import sys20import sys
21import subprocess21import subprocess
22import shutil22import shutil
23from tempfile import TemporaryFile23import tempfile
2424
25import bzrlib25import bzrlib
26from bzrlib.tests import TestCase, TestSkipped26from bzrlib import (
27import bzrlib.osutils as osutils27 osutils,
28 tests,
29 )
2830
29# XXX: This clobbers the build directory in the real source tree; it'd be nice
30# to avoid that.
31#
32# TODO: Run bzr from the installed copy to see if it works. Really we need to31# TODO: Run bzr from the installed copy to see if it works. Really we need to
33# run something that exercises every module, just starting it may not detect32# run something that exercises every module, just starting it may not detect
34# some missing modules.33# some missing modules.
35#34#
35# NOTE: Doing the above for every commit seems overkill. I'm not against
36# defining a separate test uite for precisely that purpose to be run at
37# *release* time, but then, our actual test suite sounds adequate
38# -- vila 20090623
39#
36# TODO: Check that the version numbers are in sync. (Or avoid this...)40# TODO: Check that the version numbers are in sync. (Or avoid this...)
3741
38class TestSetup(TestCase):42class TestSetup(tests.TestCase):
3943
40 def test_build_and_install(self):44 def setUp(self):
41 """ test cmd `python setup.py build`45 super(TestSetup, self).setUp()
42
43 This tests that the build process and man generator run correctly.
44 It also can catch new subdirectories that weren't added to setup.py.
45 """
46 if not os.path.isfile('setup.py'):
47 raise TestSkipped('There is no setup.py file in current directory')
48 try:
49 import distutils.sysconfig
50 makefile_path = distutils.sysconfig.get_makefile_filename()
51 if not os.path.exists(makefile_path):
52 raise TestSkipped('You must have the python Makefile installed to run this test.'
53 ' Usually this can be found by installing "python-dev"')
54 except ImportError:
55 raise TestSkipped('You must have distutils installed to run this test.'
56 ' Usually this can be found by installing "python-dev"')
57 self.log('test_build running in %s' % os.getcwd())
58 install_dir = osutils.mkdtemp()
59 # setup.py must be run from the root source directory, but the tests46 # setup.py must be run from the root source directory, but the tests
60 # are not necessarily invoked from there47 # are not necessarily invoked from there
61 self.source_dir = os.path.dirname(os.path.dirname(bzrlib.__file__))48 self.source_dir = os.path.dirname(os.path.dirname(bzrlib.__file__))
62 try:49 self.build_dir = os.path.join(self.source_dir, 'build')
63 self.run_setup(['clean'])50 # Do we already have a build dir in the source hierarchy ?
64 # build is implied by install51 if os.path.exists(self.build_dir):
65 ## self.run_setup(['build'])52 # Put it aside during the test
66 self.run_setup(['install', '--prefix', install_dir])53 tmp_dir = tempfile.mkdtemp(dir=self.source_dir)
67 self.run_setup(['clean'])54 self.addCleanup(os.rmdir, tmp_dir)
68 finally:55 tmp_build_dir = os.path.join(tmp_dir, 'build')
69 osutils.rmtree(install_dir)56 os.rename(self.build_dir, tmp_build_dir)
57 # And restore the original after the test...
58 self.addCleanup(os.rename, tmp_build_dir, self.build_dir)
59 # Create a pristine build dir for the test
60 os.mkdir(self.build_dir)
61 self.addCleanup(self.rmtree, self.build_dir)
7062
71 def run_setup(self, args):63 def run_setup(self, args, retcode=0):
72 args = [sys.executable, './setup.py', ] + args64 args = [sys.executable, './setup.py', ] + args
73 self.log('source base directory: %s', self.source_dir)65 self.log('source base directory: %s', self.source_dir)
74 self.log('args: %r', args)66 self.log('args: %r', args)
@@ -78,5 +70,59 @@
78 stderr=self._log_file,70 stderr=self._log_file,
79 )71 )
80 s = p.communicate()72 s = p.communicate()
81 self.assertEqual(0, p.returncode,73 self.assertEqual(retcode, p.returncode,
82 'invocation of %r failed' % args)74 'invocation of %r failed' % args)
75
76 def rmtree(self, dir):
77 """Wrap osutils.rmtre
78
79 osutils.rmtree is lazy loaded and can't be used directly in cleanp hooks
80 """
81 osutils.rmtree(dir)
82
83
84class TestSetupInstall(TestSetup):
85
86 def test_build_and_install(self):
87 """ test cmd `python setup.py build`
88
89 This tests that the build process and man generator run correctly.
90 It also can catch new subdirectories that weren't added to setup.py.
91 """
92 self.requireFeature(tests.PyrexFeature)
93 if not os.path.isfile('setup.py'):
94 raise TestSkipped('There is no setup.py file in current directory')
95 try:
96 import distutils.sysconfig
97 makefile_path = distutils.sysconfig.get_makefile_filename()
98 if not os.path.exists(makefile_path):
99 raise TestSkipped('You must have the python Makefile installed'
100 ' to run this test. Usually this can be found'
101 ' by installing "python-dev"')
102 except ImportError:
103 raise TestSkipped('You must have distutils installed to run this'
104 ' test. Usually this can be found by installing'
105 ' "python-dev"')
106 self.log('test_build_and_install running in %s' % os.getcwd())
107 install_dir = osutils.mkdtemp()
108 self.addCleanup(self.rmtree, install_dir)
109
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
116
117class TestSetupBuild(TestSetup):
118
119 def test_with_pyrex(self):
120 self.requireFeature(tests.PyrexFeature)
121 self.run_setup(['build_ext'])
122
123 def test_without_pyrex(self):
124 if tests.PyrexFeature.available():
125 raise tests.TestSkipped('pyrex *is* installed')
126 self.run_setup(['build_ext'], retcode=1)
127
128
83129
=== modified file 'setup.py'
--- setup.py 2009-06-23 07:10:03 +0000
+++ setup.py 2009-07-01 22:35:49 +0000
@@ -161,22 +161,27 @@
161from distutils import log161from distutils import log
162from distutils.errors import CCompilerError, DistutilsPlatformError162from distutils.errors import CCompilerError, DistutilsPlatformError
163from distutils.extension import Extension163from distutils.extension import Extension
164
164ext_modules = []165ext_modules = []
166
165try:167try:
166 from Pyrex.Distutils import build_ext168 from Pyrex.Distutils import build_ext
167except ImportError:169except ImportError:
168 have_pyrex = False170 have_pyrex = False
169 # try to build the extension from the prior generated source.171 # We will try to build the extension from the generated source.
172 from distutils.command.build_ext import build_ext
173else:
174 have_pyrex = True
175 from Pyrex.Compiler.Version import version as pyrex_version
176
177
178if not have_pyrex:
170 print179 print
171 print ("The python package 'Pyrex' is not available."180 print ("The python package 'Pyrex' is not available."
172 " If the .c files are available,")181 " If the .c files are available,")
173 print ("they will be built,"182 print ("they will be built,"
174 " but modifying the .pyx files will not rebuild them.")183 " but modifying the .pyx files will not rebuild them.")
175 print184 print
176 from distutils.command.build_ext import build_ext
177else:
178 have_pyrex = True
179 from Pyrex.Compiler.Version import version as pyrex_version
180185
181186
182class build_ext_if_possible(build_ext):187class build_ext_if_possible(build_ext):
@@ -221,15 +226,16 @@
221226
222# Override the build_ext if we have Pyrex available227# Override the build_ext if we have Pyrex available
223command_classes['build_ext'] = build_ext_if_possible228command_classes['build_ext'] = build_ext_if_possible
224unavailable_files = []229missing_pyrex_generated_files = []
225230
226231
227def add_pyrex_extension(module_name, libraries=None, extra_source=[]):232def add_pyrex_extension(module_name, libraries=None, extra_source=[]):
228 """Add a pyrex module to build.233 """Add a pyrex module to build.
229234
230 This will use Pyrex to auto-generate the .c file if it is available.235 This will use Pyrex to auto-generate the .c file if it is available.
231 Otherwise it will fall back on the .c file. If the .c file is not236 Otherwise it will fall back on using the existing .c file. If the .c file
232 available, it will warn, and not add anything.237 is not available, it will not add the extension and setup will abort
238 (whatever command is used).
233239
234 You can pass any extra options to Extension through kwargs. One example is240 You can pass any extra options to Extension through kwargs. One example is
235 'libraries = []'.241 'libraries = []'.
@@ -250,13 +256,14 @@
250 source = [pyrex_name]256 source = [pyrex_name]
251 else:257 else:
252 if not os.path.isfile(c_name):258 if not os.path.isfile(c_name):
253 unavailable_files.append(c_name)259 missing_pyrex_generated_files.append(c_name)
254 return260 return
255 else:261 else:
256 source = [c_name]262 source = [c_name]
257 source.extend(extra_source)263 source.extend(extra_source)
258 ext_modules.append(Extension(module_name, source,264 ext_modules.append(Extension(module_name, source,
259 define_macros=define_macros, libraries=libraries))265 define_macros=define_macros,
266 libraries=libraries))
260267
261268
262add_pyrex_extension('bzrlib._bencode_pyx')269add_pyrex_extension('bzrlib._bencode_pyx')
@@ -292,12 +299,11 @@
292 ['bzrlib/_patiencediff_c.c']))299 ['bzrlib/_patiencediff_c.c']))
293300
294301
295if unavailable_files:302if missing_pyrex_generated_files:
296 print 'C extension(s) not found:'303 print 'C extension(s) not found:'
297 print ' %s' % ('\n '.join(unavailable_files),)304 print ' %s' % ('\n '.join(missing_pyrex_generated_files),)
298 print 'The python versions will be used instead.'
299 print305 print
300306 exit(1)
301307
302def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,308def get_tbzr_py2exe_info(includes, excludes, packages, console_targets,
303 gui_targets, data_files):309 gui_targets, data_files):