Skip to content

Commit

Permalink
Merge pull request #317 from conda/noarch
Browse files Browse the repository at this point in the history
Noarch
  • Loading branch information
ilanschnell committed Feb 2, 2015
2 parents 2d0d102 + 9fa1e24 commit 8677e15
Show file tree
Hide file tree
Showing 9 changed files with 281 additions and 100 deletions.
100 changes: 100 additions & 0 deletions conda_build/_link.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import os
import sys
import shutil
from os.path import dirname, exists, isdir, join, normpath


THIS_DIR = dirname(__file__)
PREFIX = normpath(sys.prefix)
if sys.platform == 'win32':
BIN_DIR = join(PREFIX, 'Scripts')
SITE_PACKAGES = 'Lib/site-packages'
else:
BIN_DIR = join(PREFIX, 'bin')
SITE_PACKAGES = 'lib/python%s/site-packages' % sys.version[:3]

# the list of these files is going to be store in info/_files
FILES = []


def _link(src, dst):
try:
os.link(src, dst)
# on Windows os.link raises AttributeError
except (OSError, AttributeError):
shutil.copy2(src, dst)


def _unlink(path):
try:
os.unlink(path)
except OSError:
pass


def pyc_f(f):
if sys.version_info[0] == 2:
return f + 'c'
dn, fn = f.rsplit('/', 1)
return '%s/__pycache__/%s.cpython-%d%d.pyc' % (
dn, fn[:-3], sys.version_info[0], sys.version_info[1])


def link_files(src_root, dst_root, files):
for f in files:
src = join(THIS_DIR, src_root, f)
dst = join(PREFIX, dst_root, f)
dst_dir = dirname(dst)
if not isdir(dst_dir):
os.makedirs(dst_dir)
if exists(dst):
_unlink(dst)
_link(src, dst)
f = '%s/%s' % (dst_root, f)
FILES.append(f)
if f.endswith('.py'):
FILES.append(pyc_f(f))


def create_script(fn):
src = join(THIS_DIR, 'python-scripts', fn)
dst = join(BIN_DIR, fn)
if sys.platform == 'win32':
shutil.copyfile(src, dst + '-script.py')
FILES.append('Scripts/%s-script.py' % fn)
shutil.copyfile(join(THIS_DIR,
'cli-%d.exe' % (8 * tuple.__itemsize__)),
dst + '.exe')
FILES.append('Scripts/%s.exe' % fn)
else:
with open(src) as fi:
data = fi.read()
with open(dst, 'w') as fo:
fo.write('#!%s\n' % normpath(sys.executable))
fo.write(data)
os.chmod(dst, 0o755)
FILES.append('bin/%s' % fn)


def create_scripts(files):
if not files:
return
if not isdir(BIN_DIR):
os.mkdir(BIN_DIR)
for fn in files:
create_script(fn)


def main():
create_scripts(DATA['python-scripts'])
link_files('site-packages', SITE_PACKAGES, DATA['site-packages'])
link_files('Examples', 'Examples', DATA['Examples'])

with open(join(PREFIX, 'conda-meta',
'%s.files' % DATA['dist']), 'w') as fo:
for f in FILES:
fo.write('%s\n' % f)


if __name__ == '__main__':
main()
10 changes: 5 additions & 5 deletions conda_build/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ def create_info_files(m, files, include_recipe=True):
files = [f.replace('\\', '/') for f in files]

with open(join(config.info_dir, 'files'), 'w') as fo:
if m.get_value('build/noarch') and 'py_' in m.dist():
if m.get_value('build/noarch_python'):
fo.write('\n')
else:
for f in files:
Expand All @@ -189,7 +189,7 @@ def create_info_files(m, files, include_recipe=True):
files_with_prefix = sorted(have_prefix_files(files))
binary_has_prefix_files = m.binary_has_prefix_files()
text_has_prefix_files = m.has_prefix_files()
if files_with_prefix:
if files_with_prefix and not m.get_value('build/noarch_python'):
auto_detect = m.get_value('build/detect_binary_files_with_prefix')
if sys.platform == 'win32':
# Paths on Windows can contain spaces, so we need to quote the
Expand Down Expand Up @@ -399,9 +399,9 @@ def build(m, get_src=True, verbose=True, post=None):
post_build(m, sorted(files2 - files1))
create_info_files(m, sorted(files2 - files1),
include_recipe=bool(m.path))
if m.get_value('build/noarch'):
import conda_build.noarch as noarch
noarch.transform(m, sorted(files2 - files1))
if m.get_value('build/noarch_python'):
import conda_build.noarch_python as noarch_python
noarch_python.transform(m, sorted(files2 - files1))

files3 = prefix_files()
fix_permissions(files3 - files1)
Expand Down
9 changes: 6 additions & 3 deletions conda_build/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ def _git_clean(source_meta):
'patches'],
'build': ['number', 'string', 'entry_points', 'osx_is_app',
'features', 'track_features', 'preserve_egg_dir',
'no_link', 'binary_relocation', 'script', 'noarch',
'no_link', 'binary_relocation', 'script', 'noarch_python',
'has_prefix_files', 'binary_has_prefix_files',
'detect_binary_files_with_prefix', 'rpaths',
'always_include_files', ],
Expand Down Expand Up @@ -322,7 +322,8 @@ def ms_depends(self, typ='run'):
raise RuntimeError("Invalid package specification: %r" % spec)
for name, ver in name_ver_list:
if ms.name == name:
if ms.strictness != 1 or self.get_value('build/noarch'):
if (ms.strictness != 1 or
self.get_value('build/noarch_python')):
continue
str_ver = text_type(ver)
if '.' not in str_ver:
Expand Down Expand Up @@ -395,14 +396,16 @@ def info_index(self):
license = self.get_value('about/license'),
platform = cc.platform,
arch = cc.arch_name,
subdir = cc.subdir,
depends = sorted(ms.spec for ms in self.ms_depends())
)
if self.get_value('build/features'):
d['features'] = ' '.join(self.get_value('build/features'))
if self.get_value('build/track_features'):
d['track_features'] = ' '.join(self.get_value('build/track_features'))
if self.get_value('build/noarch'):
if self.get_value('build/noarch_python'):
d['platform'] = d['arch'] = None
d['subdir'] = 'noarch'
if self.is_app():
d.update(self.app_meta())
return d
Expand Down
91 changes: 0 additions & 91 deletions conda_build/noarch.py

This file was deleted.

108 changes: 108 additions & 0 deletions conda_build/noarch_python.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import os
import io
import sys
import json
import shutil
import locale
from os.path import basename, dirname, isdir, join

from conda_build.config import config
from conda_build.post import SHEBANG_PAT


def rewrite_script(fn):
src = join(config.build_prefix, 'bin', fn)
with io.open(src, encoding=locale.getpreferredencoding()) as fi:
try:
data = fi.read()
except UnicodeDecodeError: # file is binary
raise Exception("Binary: %s" % fn)
os.unlink(src)

m = SHEBANG_PAT.match(data)
if not (m and 'python' in m.group()):
raise Exception("No python shebang in: %s" % fn)
new_data = data[data.find('\n') + 1:]

dst_dir = join(config.build_prefix, 'python-scripts')
if not isdir(dst_dir):
os.makedirs(dst_dir)
with open(join(dst_dir, fn), 'w') as fo:
fo.write(new_data)


def handle_file(f, d):
path = join(config.build_prefix, f)
if f.endswith(('.egg-info', '.pyc')):
os.unlink(path)

elif f.endswith('.so'):
sys.exit("[noarch_python] Error: Shared object file found: %s" % f)

elif 'site-packages' in f:
nsp = join(config.build_prefix, 'site-packages')
if not isdir(nsp):
os.mkdir(nsp)
g = f[f.find('site-packages'):]
dst = join(config.build_prefix, g)
dst_dir = dirname(dst)
if not isdir(dst_dir):
os.makedirs(dst_dir)
os.rename(path, dst)
d['site-packages'].append(g[14:])

elif f.startswith('bin/'):
fn = basename(path)
rewrite_script(fn)
d['python-scripts'].append(fn)

elif f.startswith('Examples/'):
d['Examples'].append(f[9:])

else:
sys.exit("[noarch_python] Error: Don't know how to handle file: %s" % f)


def transform(m, files):
assert 'py_' in m.dist()
if sys.platform == 'win32':
sys.exit("[noarch_python] Error: Python noarch packages can currently "
"not be created on Windows systems.")

prefix = config.build_prefix
name = m.name()
with open(join(prefix, 'bin/.%s-pre-link.sh' % name), 'w') as fo:
fo.write('''\
#!/bin/bash
$PREFIX/bin/python $SOURCE_DIR/link.py
''')

scripts_dir = join(prefix, 'Scripts')
if not isdir(scripts_dir):
os.mkdir(scripts_dir)

with open(join(scripts_dir, '.%s-pre-link.bat' % name), 'w') as fo:
fo.write('''\
@echo off
"%PREFIX%\\python.exe" "%SOURCE_DIR%\\link.py"
''')

d = {'dist': m.dist(),
'site-packages': [],
'python-scripts': [],
'Examples': []}
for f in files:
handle_file(f, d)

this_dir = dirname(__file__)
if d['python-scripts']:
for fn in 'cli-32.exe', 'cli-64.exe':
shutil.copyfile(join(this_dir, fn), join(prefix, fn))

with open(join(this_dir, '_link.py')) as fi:
link_code = fi.read()
with open(join(prefix, 'link.py'), 'w') as fo:
fo.write('DATA = ')
json.dump(d, fo, indent=2, sort_keys=True)
fo.write('\n## END DATA\n\n')
fo.write(link_code)
2 changes: 1 addition & 1 deletion conda_build/tarcheck.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def __init__(self, path):
self.name, self.version, self.build = self.dist.rsplit('-', 2)

def info_files(self):
if self.build.startswith('py_'):
if 'py_' in self.build:
return
lista = [p.strip().decode('utf-8') for p in
self.t.extractfile('info/files').readlines()]
Expand Down
7 changes: 7 additions & 0 deletions example_packages/noarch_python/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#!/bin/bash

$PYTHON setup.py install

EXAMPLES=$PREFIX/Examples
mkdir $EXAMPLES
mv examples $EXAMPLES/bokeh

0 comments on commit 8677e15

Please sign in to comment.