Browse Source

Add Comm Build System

tags/28.9.0_Release
Matt A. Tobin 4 years ago
parent
commit
75dd33577e
40 changed files with 7105 additions and 0 deletions
  1. +15
    -0
      .ycm_extra_conf.py
  2. +27
    -0
      aclocal.m4
  3. +1454
    -0
      build/autoconf/config.guess
  4. +76
    -0
      build/autoconf/mozconfig-find
  5. +76
    -0
      build/autoconf/mozconfig2client-mk
  6. +0
    -0
      build/dumbmake-dependencies
  7. +14
    -0
      build/mach_bootstrap.py
  8. +40
    -0
      build/pymake/make.py
  9. +3
    -0
      build/pypng/check-sync-exceptions
  10. +128
    -0
      build/pypng/exnumpy.py
  11. +537
    -0
      build/pypng/iccp.py
  12. +45
    -0
      build/pypng/mkiccp.py
  13. +99
    -0
      build/pypng/pdsimgtopng
  14. +73
    -0
      build/pypng/pipasgrey
  15. +44
    -0
      build/pypng/pipcat
  16. +56
    -0
      build/pypng/pipcolours
  17. +121
    -0
      build/pypng/pipcomposite
  18. +181
    -0
      build/pypng/pipdither
  19. +36
    -0
      build/pypng/piprgb
  20. +53
    -0
      build/pypng/pipscalez
  21. +127
    -0
      build/pypng/pipstack
  22. +67
    -0
      build/pypng/pipwindow
  23. +293
    -0
      build/pypng/plan9topng.py
  24. +172
    -0
      build/pypng/pngchunk
  25. +79
    -0
      build/pypng/pnghist
  26. +31
    -0
      build/pypng/pnglsch
  27. +151
    -0
      build/pypng/texttopng
  28. +463
    -0
      client.mk
  29. +16
    -0
      config/baseconfig.mk
  30. +7
    -0
      config/config.mk
  31. +2279
    -0
      config/configobj.py
  32. +94
    -0
      config/makefiles/autotargets.mk
  33. +117
    -0
      config/makefiles/makeutils.mk
  34. +25
    -0
      config/printconfigsetting.py
  35. +9
    -0
      config/recurse.mk
  36. +13
    -0
      config/rules.mk
  37. +38
    -0
      configure.in
  38. +32
    -0
      configure.py
  39. +7
    -0
      moz.build
  40. +7
    -0
      moz.configure

+ 15
- 0
.ycm_extra_conf.py View File

@@ -0,0 +1,15 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import imp, os, sys

old_bytecode = sys.dont_write_bytecode
sys.dont_write_bytecode = True

ycm_module = imp.load_source("_ycm_extra_conf", os.path.join("mozilla", ".ycm_extra_conf.py"))

sys.dont_write_bytecode = old_bytecode

# Expose the FlagsForFile function from mozilla/.ycm_extra_conf.py
FlagsForFile = ycm_module.FlagsForFile

+ 27
- 0
aclocal.m4 View File

@@ -0,0 +1,27 @@
dnl
dnl Local autoconf macros used with UXP
dnl The contents of this file are under the Public Domain.
dnl

builtin(include, platform/build/autoconf/toolchain.m4)dnl
builtin(include, platform/build/autoconf/config.status.m4)dnl
builtin(include, platform/build/autoconf/nspr.m4)dnl
builtin(include, platform/build/autoconf/nss.m4)dnl
builtin(include, platform/build/autoconf/pkg.m4)dnl
builtin(include, platform/build/autoconf/codeset.m4)dnl
builtin(include, platform/build/autoconf/altoptions.m4)dnl
builtin(include, platform/build/autoconf/mozprog.m4)dnl
builtin(include, platform/build/autoconf/acwinpaths.m4)dnl
builtin(include, platform/build/autoconf/lto.m4)dnl
builtin(include, platform/build/autoconf/frameptr.m4)dnl
builtin(include, platform/build/autoconf/compiler-opts.m4)dnl
builtin(include, platform/build/autoconf/zlib.m4)dnl
builtin(include, platform/build/autoconf/expandlibs.m4)dnl

MOZ_PROG_CHECKMSYS()

# Read the user's .mozconfig script. We can't do this in
# configure.in: autoconf puts the argument parsing code above anything
# expanded from configure.in, and we need to get the configure options
# from .mozconfig in place before that argument parsing code.
dnl MOZ_READ_MOZCONFIG(platform)

+ 1454
- 0
build/autoconf/config.guess
File diff suppressed because it is too large
View File


+ 76
- 0
build/autoconf/mozconfig-find View File

@@ -0,0 +1,76 @@
#! /bin/sh
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# mozconfigfind - Loads options from .mozconfig onto configure's
# command-line. The .mozconfig file is searched for in the
# order:
# If $MOZCONFIG is set, use that.
# If one of $TOPSRCDIR/.mozconfig or $TOPSRCDIR/mozconfig exists, use it.
# If both exist, or if various legacy locations contain a mozconfig, error.
# Otherwise, use the default build options.
#
topsrcdir=$1

abspath() {
if uname -s | grep -q MINGW; then
# We have no way to figure out whether we're in gmake or pymake right
# now. gmake gives us Unix-style paths while pymake gives us Windows-style
# paths, so attempt to handle both.
regexes='^\([A-Za-z]:\|\\\\\|\/\) ^\/'
else
regexes='^\/'
fi

for regex in $regexes; do
if echo $1 | grep -q $regex; then
echo $1
return
fi
done

# If we're at this point, we have a relative path
echo `pwd`/$1
}

if [ -n "$MOZCONFIG" ] && ! [ -f "$MOZCONFIG" ]; then
echo "Specified MOZCONFIG \"$MOZCONFIG\" does not exist!" 1>&2
exit 1
fi

if [ -n "$MOZ_MYCONFIG" ]; then
echo "Your environment currently has the MOZ_MYCONFIG variable set to \"$MOZ_MYCONFIG\". MOZ_MYCONFIG is no longer supported. Please use MOZCONFIG instead." 1>&2
exit 1
fi

if [ -z "$MOZCONFIG" ] && [ -f "$topsrcdir/.mozconfig" ] && [ -f "$topsrcdir/mozconfig" ]; then
echo "Both \$topsrcdir/.mozconfig and \$topsrcdir/mozconfig are supported, but you must choose only one. Please remove the other." 1>&2
exit 1
fi

for _config in "$MOZCONFIG" \
"$topsrcdir/.mozconfig" \
"$topsrcdir/mozconfig"
do
if test -f "$_config"; then
abspath $_config
exit 0
fi
done

# We used to support a number of other implicit .mozconfig locations. We now
# detect if we were about to use any of these locations and issue an error if we
# find any.
for _config in "$topsrcdir/mozconfig.sh" \
"$topsrcdir/myconfig.sh" \
"$HOME/.mozconfig" \
"$HOME/.mozconfig.sh" \
"$HOME/.mozmyconfig.sh"
do
if test -f "$_config"; then
echo "You currently have a mozconfig at \"$_config\". This implicit location is no longer supported. Please move it to $topsrcdir/.mozconfig or specify it explicitly via \$MOZCONFIG." 1>&2
exit 1
fi
done

+ 76
- 0
build/autoconf/mozconfig2client-mk View File

@@ -0,0 +1,76 @@
#! /bin/sh
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# mozconfig2client-mk - Translates .mozconfig into options for client.mk.
# Prints defines to stdout.
#
# See mozconfig2configure for more details

print_header() {
cat <<EOF
# gmake
# This file is automatically generated for client.mk.
# Do not edit. Edit $FOUND_MOZCONFIG instead.

EOF
}

ac_add_options() {
for _opt
do
case "$_opt" in
--target=*)
echo $_opt | sed s/--target/CONFIG_GUESS/
;;
*)
echo "# $_opt is used by configure (not client.mk)"
;;
esac
done
}

ac_add_app_options() {
echo "# $* is used by configure (not client.mk)"
}

mk_add_options() {
for _opt
do
# Escape shell characters, space, tab, dollar, quote, backslash,
# and substitute '@<word>@' with '$(<word>)'.
_opt=`echo "$_opt" | sed -e 's/\([\"\\]\)/\\\\\1/g; s/@\([^@]*\)@/\$(\1)/g;'`
echo $_opt;
done
}

# Main
#--------------------------------------------------

scriptdir=`dirname $0`
topsrcdir=$1

# If the path changes, configure should be rerun
echo "# PATH=$PATH"

# If FOUND_MOZCONFIG isn't set, look for it and make sure the script doesn't error out
isfoundset=${FOUND_MOZCONFIG+yes}
if [ -z $isfoundset ]; then
FOUND_MOZCONFIG=`$scriptdir/mozconfig-find $topsrcdir`
if [ $? -ne 0 ]; then
echo '$(error Fix above errors before continuing.)'
else
isfoundset=yes
fi
fi

if [ -n $isfoundset ]; then
if [ "$FOUND_MOZCONFIG" ]
then
print_header
. "$FOUND_MOZCONFIG"
echo "FOUND_MOZCONFIG := $FOUND_MOZCONFIG"
fi
fi

+ 0
- 0
build/dumbmake-dependencies View File


+ 14
- 0
build/mach_bootstrap.py View File

@@ -0,0 +1,14 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from __future__ import unicode_literals

import os, sys

def bootstrap(topsrcdir, mozilla_dir=None):
if mozilla_dir is None:
mozilla_dir = os.path.join(topsrcdir, 'platform')
sys.path[0:0] = [mozilla_dir]
import build.mach_bootstrap
return build.mach_bootstrap.bootstrap(topsrcdir, mozilla_dir)

+ 40
- 0
build/pymake/make.py View File

@@ -0,0 +1,40 @@
#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# This is a wrapper around mozilla-central's pymake. If that isn't found then
# this uses client.py to pull it in.

import os
import sys
import subprocess
import shlex

def getpath(relpath):
thisdir = os.path.dirname(__file__)
return os.path.abspath(os.path.join(thisdir, *relpath))

PYMAKE = getpath(["..", "..", "platform", "build", "pymake", "make.py"])

def main(args):
if 'TINDERBOX_OUTPUT' in os.environ:
# When building on mozilla build slaves, execute mozmake instead. Until bug
# 978211, this is the easiest, albeit hackish, way to do this.
mozmake = os.path.join(os.path.dirname(__file__), '..', '..',
'mozmake.exe')
if os.path.exists(mozmake):
cmd = [mozmake]
cmd.extend(sys.argv[1:])
shell = os.environ.get('SHELL')
if shell and not shell.lower().endswith('.exe'):
cmd += ['SHELL=%s.exe' % shell]
sys.exit(subprocess.call(cmd))

if not os.path.exists(PYMAKE):
raise Exception("Pymake not found")

subprocess.check_call([sys.executable, PYMAKE] + args)

if __name__ == "__main__":
main(sys.argv[1:])

+ 3
- 0
build/pypng/check-sync-exceptions View File

@@ -0,0 +1,3 @@
# Nothing in this directory needs to be in sync with mozilla
# The contents are used only in c-c
*

+ 128
- 0
build/pypng/exnumpy.py View File

@@ -0,0 +1,128 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/exnumpy.py $
# $Rev: 126 $

# Numpy example.
# Original code created by Mel Raab, modified by David Jones.

'''
Example code integrating RGB PNG files, PyPNG and NumPy
(abstracted from Mel Raab's functioning code)
'''

# http://www.python.org/doc/2.4.4/lib/module-itertools.html
import itertools

import numpy
import png


''' If you have a PNG file for an RGB image,
and want to create a numpy array of data from it.
'''
# Read the file "picture.png" from the current directory. The `Reader`
# class can take a filename, a file-like object, or the byte data
# directly; this suggests alternatives such as using urllib to read
# an image from the internet:
# png.Reader(file=urllib.urlopen('http://www.libpng.org/pub/png/PngSuite/basn2c16.png'))
pngReader=png.Reader(filename='picture.png')
# Tuple unpacking, using multiple assignment, is very useful for the
# result of asDirect (and other methods).
# See
# http://docs.python.org/tutorial/introduction.html#first-steps-towards-programming
row_count, column_count, pngdata, meta = pngReader.asDirect()
bitdepth=meta['bitdepth']
plane_count=meta['planes']

# Make sure we're dealing with RGB files
assert plane_count == 3

''' Boxed row flat pixel:
list([R,G,B, R,G,B, R,G,B],
[R,G,B, R,G,B, R,G,B])
Array dimensions for this example: (2,9)

Create `image_2d` as a two-dimensional NumPy array by stacking a
sequence of 1-dimensional arrays (rows).
The NumPy array mimics PyPNG's (boxed row flat pixel) representation;
it will have dimensions ``(row_count,column_count*plane_count)``.
'''
# The use of ``numpy.uint16``, below, is to convert each row to a NumPy
# array with data type ``numpy.uint16``. This is a feature of NumPy,
# discussed further in
# http://docs.scipy.org/doc/numpy/user/basics.types.html .
# You can use avoid the explicit conversion with
# ``numpy.vstack(pngdata)``, but then NumPy will pick the array's data
# type; in practice it seems to pick ``numpy.int32``, which is large enough
# to hold any pixel value for any PNG image but uses 4 bytes per value when
# 1 or 2 would be enough.
# --- extract 001 start
image_2d = numpy.vstack(itertools.imap(numpy.uint16, pngdata))
# --- extract 001 end
# Do not be tempted to use ``numpy.asarray``; when passed an iterator
# (`pngdata` is often an iterator) it will attempt to create a size 1
# array with the iterator as its only element.
# An alternative to the above is to create the target array of the right
# shape, then populate it row by row:
if 0:
image_2d = numpy.zeros((row_count,plane_count*column_count),
dtype=numpy.uint16)
for row_index, one_boxed_row_flat_pixels in enumerate(pngdata):
image_2d[row_index,:]=one_boxed_row_flat_pixels

del pngReader
del pngdata


''' Reconfigure for easier referencing, similar to
Boxed row boxed pixel:
list([ (R,G,B), (R,G,B), (R,G,B) ],
[ (R,G,B), (R,G,B), (R,G,B) ])
Array dimensions for this example: (2,3,3)

``image_3d`` will contain the image as a three-dimensional numpy
array, having dimensions ``(row_count,column_count,plane_count)``.
'''
# --- extract 002 start
image_3d = numpy.reshape(image_2d,
(row_count,column_count,plane_count))
# --- extract 002 end


''' ============= '''

''' Convert NumPy image_3d array to PNG image file.

If the data is three-dimensional, as it is above, the best thing
to do is reshape it into a two-dimensional array with a shape of
``(row_count, column_count*plane_count)``. Because a
two-dimensional numpy array is an iterator, it can be passed
directly to the ``png.Writer.write`` method.
'''

row_count, column_count, plane_count = image_3d.shape
assert plane_count==3

pngfile = open('picture_out.png', 'wb')
try:
# This example assumes that you have 16-bit pixel values in the data
# array (that's what the ``bitdepth=16`` argument is for).
# If you don't, then the resulting PNG file will likely be
# very dark. Hey, it's only an example.
pngWriter = png.Writer(column_count, row_count,
greyscale=False,
alpha=False,
bitdepth=16)
# As of 2009-04-13 passing a numpy array that has an element type
# that is a numpy integer type (for example, the `image_3d` array has an
# element type of ``numpy.uint16``) generates a deprecation warning.
# This is probably a bug in numpy; it may go away in the future.
# The code still works despite the warning.
# See http://code.google.com/p/pypng/issues/detail?id=44
# --- extract 003 start
pngWriter.write(pngfile,
numpy.reshape(image_3d, (-1, column_count*plane_count)))
# --- extract 003 end
finally:
pngfile.close()


+ 537
- 0
build/pypng/iccp.py View File

@@ -0,0 +1,537 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/iccp.py $
# $Rev: 182 $

# iccp
#
# International Color Consortium Profile
#
# Tools for manipulating ICC profiles.
#
# An ICC profile can be extracted from a PNG image (iCCP chunk).
#
#
# Non-standard ICCP tags.
#
# Apple use some (widespread but) non-standard tags. These can be
# displayed in Apple's ColorSync Utility.
# - 'vcgt' (Video Card Gamma Tag). Table to load into video
# card LUT to apply gamma.
# - 'ndin' Apple display native information.
# - 'dscm' Apple multi-localized description strings.
# - 'mmod' Apple display make and model information.
#

# References
#
# [ICC 2001] ICC Specification ICC.1:2001-04 (Profile version 2.4.0)
# [ICC 2004] ICC Specification ICC.1:2004-10 (Profile version 4.2.0.0)

import struct

import png

class FormatError(Exception):
pass

class Profile:
"""An International Color Consortium Profile (ICC Profile)."""

def __init__(self):
self.rawtagtable = None
self.rawtagdict = {}
self.d = dict()

def fromFile(self, inp, name='<unknown>'):

# See [ICC 2004]
profile = inp.read(128)
if len(profile) < 128:
raise FormatError("ICC Profile is too short.")
size, = struct.unpack('>L', profile[:4])
profile += inp.read(d['size'] - len(profile))
return self.fromString(profile, name)

def fromString(self, profile, name='<unknown>'):
self.d = dict()
d = self.d
if len(profile) < 128:
raise FormatError("ICC Profile is too short.")
d.update(
zip(['size', 'preferredCMM', 'version',
'profileclass', 'colourspace', 'pcs'],
struct.unpack('>L4sL4s4s4s', profile[:24])))
if len(profile) < d['size']:
warnings.warn(
'Profile size declared to be %d, but only got %d bytes' %
(d['size'], len(profile)))
d['version'] = '%08x' % d['version']
d['created'] = readICCdatetime(profile[24:36])
d.update(
zip(['acsp', 'platform', 'flag', 'manufacturer', 'model'],
struct.unpack('>4s4s3L', profile[36:56])))
if d['acsp'] != 'acsp':
warnings.warn('acsp field not present (not an ICC Profile?).')
d['deviceattributes'] = profile[56:64]
d['intent'], = struct.unpack('>L', profile[64:68])
d['pcsilluminant'] = readICCXYZNumber(profile[68:80])
d['creator'] = profile[80:84]
d['id'] = profile[84:100]
ntags, = struct.unpack('>L', profile[128:132])
d['ntags'] = ntags
fmt = '4s2L' * ntags
# tag table
tt = struct.unpack('>' + fmt, profile[132:132+12*ntags])
tt = group(tt, 3)

# Could (should) detect 2 or more tags having the same sig. But
# we don't. Two or more tags with the same sig is illegal per
# the ICC spec.
# Convert (sig,offset,size) triples into (sig,value) pairs.
rawtag = map(lambda x: (x[0], profile[x[1]:x[1]+x[2]]), tt)
self.rawtagtable = rawtag
self.rawtagdict = dict(rawtag)
tag = dict()
# Interpret the tags whose types we know about
for sig, v in rawtag:
if sig in tag:
warnings.warn("Duplicate tag %r found. Ignoring." % sig)
continue
v = ICCdecode(v)
if v is not None:
tag[sig] = v
self.tag = tag
return self

def greyInput(self):
"""Adjust ``self.d`` dictionary for greyscale input device.
``profileclass`` is 'scnr', ``colourspace`` is 'GRAY', ``pcs``
is 'XYZ '.
"""

self.d.update(dict(profileclass='scnr',
colourspace='GRAY', pcs='XYZ '))
return self

def maybeAddDefaults(self):
if self.rawtagdict:
return
self._addTags(
cprt='Copyright unknown.',
desc='created by $URL: http://pypng.googlecode.com/svn/trunk/code/iccp.py $ $Rev: 182 $',
wtpt=D50(),
)

def addTags(self, **k):
self.maybeAddDefaults()
self._addTags(**k)

def _addTags(self, **k):
"""Helper for :meth:`addTags`."""

for tag, thing in k.items():
if not isinstance(thing, (tuple, list)):
thing = (thing,)
typetag = defaulttagtype[tag]
self.rawtagdict[tag] = encode(typetag, *thing)
return self

def write(self, out):
"""Write ICC Profile to the file."""

if not self.rawtagtable:
self.rawtagtable = self.rawtagdict.items()
tags = tagblock(self.rawtagtable)
self.writeHeader(out, 128 + len(tags))
out.write(tags)
out.flush()

return self

def writeHeader(self, out, size=999):
"""Add default values to the instance's `d` dictionary, then
write a header out onto the file stream. The size of the
profile must be specified using the `size` argument.
"""

def defaultkey(d, key, value):
"""Add ``[key]==value`` to the dictionary `d`, but only if
it does not have that key already.
"""

if key in d:
return
d[key] = value

z = '\x00' * 4
defaults = dict(preferredCMM=z,
version='02000000',
profileclass=z,
colourspace=z,
pcs='XYZ ',
created=writeICCdatetime(),
acsp='acsp',
platform=z,
flag=0,
manufacturer=z,
model=0,
deviceattributes=0,
intent=0,
pcsilluminant=encodefuns()['XYZ'](*D50()),
creator=z,
)
for k,v in defaults.items():
defaultkey(self.d, k, v)

hl = map(self.d.__getitem__,
['preferredCMM', 'version', 'profileclass', 'colourspace',
'pcs', 'created', 'acsp', 'platform', 'flag',
'manufacturer', 'model', 'deviceattributes', 'intent',
'pcsilluminant', 'creator'])
# Convert to struct.pack input
hl[1] = int(hl[1], 16)

out.write(struct.pack('>L4sL4s4s4s12s4s4sL4sLQL12s4s', size, *hl))
out.write('\x00' * 44)
return self

def encodefuns():
"""Returns a dictionary mapping ICC type signature sig to encoding
function. Each function returns a string comprising the content of
the encoded value. To form the full value, the type sig and the 4
zero bytes should be prefixed (8 bytes).
"""

def desc(ascii):
"""Return textDescription type [ICC 2001] 6.5.17. The ASCII part is
filled in with the string `ascii`, the Unicode and ScriptCode parts
are empty."""

ascii += '\x00'
l = len(ascii)

return struct.pack('>L%ds2LHB67s' % l,
l, ascii, 0, 0, 0, 0, '')

def text(ascii):
"""Return textType [ICC 2001] 6.5.18."""

return ascii + '\x00'

def curv(f=None, n=256):
"""Return a curveType, [ICC 2001] 6.5.3. If no arguments are
supplied then a TRC for a linear response is generated (no entries).
If an argument is supplied and it is a number (for *f* to be a
number it means that ``float(f)==f``) then a TRC for that
gamma value is generated.
Otherwise `f` is assumed to be a function that maps [0.0, 1.0] to
[0.0, 1.0]; an `n` element table is generated for it.
"""

if f is None:
return struct.pack('>L', 0)
try:
if float(f) == f:
return struct.pack('>LH', 1, int(round(f*2**8)))
except (TypeError, ValueError):
pass
assert n >= 2
table = []
M = float(n-1)
for i in range(n):
x = i/M
table.append(int(round(f(x) * 65535)))
return struct.pack('>L%dH' % n, n, *table)

def XYZ(*l):
return struct.pack('>3l', *map(fs15f16, l))

return locals()

# Tag type defaults.
# Most tags can only have one or a few tag types.
# When encoding, we associate a default tag type with each tag so that
# the encoding is implicit.
defaulttagtype=dict(
A2B0='mft1',
A2B1='mft1',
A2B2='mft1',
bXYZ='XYZ',
bTRC='curv',
B2A0='mft1',
B2A1='mft1',
B2A2='mft1',
calt='dtim',
targ='text',
chad='sf32',
chrm='chrm',
cprt='desc',
crdi='crdi',
dmnd='desc',
dmdd='desc',
devs='',
gamt='mft1',
kTRC='curv',
gXYZ='XYZ',
gTRC='curv',
lumi='XYZ',
meas='',
bkpt='XYZ',
wtpt='XYZ',
ncol='',
ncl2='',
resp='',
pre0='mft1',
pre1='mft1',
pre2='mft1',
desc='desc',
pseq='',
psd0='data',
psd1='data',
psd2='data',
psd3='data',
ps2s='data',
ps2i='data',
rXYZ='XYZ',
rTRC='curv',
scrd='desc',
scrn='',
tech='sig',
bfd='',
vued='desc',
view='view',
)

def encode(tsig, *l):
"""Encode a Python value as an ICC type. `tsig` is the type
signature to (the first 4 bytes of the encoded value, see [ICC 2004]
section 10.
"""

fun = encodefuns()
if tsig not in fun:
raise "No encoder for type %r." % tsig
v = fun[tsig](*l)
# Padd tsig out with spaces.
tsig = (tsig + ' ')[:4]
return tsig + '\x00'*4 + v

def tagblock(tag):
"""`tag` should be a list of (*signature*, *element*) pairs, where
*signature* (the key) is a length 4 string, and *element* is the
content of the tag element (another string).
The entire tag block (consisting of first a table and then the
element data) is constructed and returned as a string.
"""

n = len(tag)
tablelen = 12*n

# Build the tag table in two parts. A list of 12-byte tags, and a
# string of element data. Offset is the offset from the start of
# the profile to the start of the element data (so the offset for
# the next element is this offset plus the length of the element
# string so far).
offset = 128 + tablelen + 4
# The table. As a string.
table = ''
# The element data
element = ''
for k,v in tag:
table += struct.pack('>4s2L', k, offset + len(element), len(v))
element += v
return struct.pack('>L', n) + table + element

def iccp(out, inp):
profile = Profile().fromString(*profileFromPNG(inp))
print >>out, profile.d
print >>out, map(lambda x: x[0], profile.rawtagtable)
print >>out, profile.tag

def profileFromPNG(inp):
"""Extract profile from PNG file. Return (*profile*, *name*)
pair."""
r = png.Reader(file=inp)
_,chunk = r.chunk('iCCP')
i = chunk.index('\x00')
name = chunk[:i]
compression = chunk[i+1]
assert compression == chr(0)
profile = chunk[i+2:].decode('zlib')
return profile, name

def iccpout(out, inp):
"""Extract ICC Profile from PNG file `inp` and write it to
the file `out`."""

out.write(profileFromPNG(inp)[0])

def fs15f16(x):
"""Convert float to ICC s15Fixed16Number (as a Python ``int``)."""

return int(round(x * 2**16))

def D50():
"""Return D50 illuminant as an (X,Y,Z) triple."""

# See [ICC 2001] A.1
return (0.9642, 1.0000, 0.8249)


def writeICCdatetime(t=None):
"""`t` should be a gmtime tuple (as returned from
``time.gmtime()``). If not supplied, the current time will be used.
Return an ICC dateTimeNumber in a 12 byte string.
"""

import time
if t is None:
t = time.gmtime()
return struct.pack('>6H', *t[:6])

def readICCdatetime(s):
"""Convert from 12 byte ICC representation of dateTimeNumber to
ISO8601 string. See [ICC 2004] 5.1.1"""

return '%04d-%02d-%02dT%02d:%02d:%02dZ' % struct.unpack('>6H', s)

def readICCXYZNumber(s):
"""Convert from 12 byte ICC representation of XYZNumber to (x,y,z)
triple of floats. See [ICC 2004] 5.1.11"""

return s15f16l(s)

def s15f16l(s):
"""Convert sequence of ICC s15Fixed16 to list of float."""
# Note: As long as float has at least 32 bits of mantissa, all
# values are preserved.
n = len(s)//4
t = struct.unpack('>%dl' % n, s)
return map((2**-16).__mul__, t)

# Several types and their byte encodings are defined by [ICC 2004]
# section 10. When encoded, a value begins with a 4 byte type
# signature. We use the same 4 byte type signature in the names of the
# Python functions that decode the type into a Pythonic representation.

def ICCdecode(s):
"""Take an ICC encoded tag, and dispatch on its type signature
(first 4 bytes) to decode it into a Python value. Pair (*sig*,
*value*) is returned, where *sig* is a 4 byte string, and *value* is
some Python value determined by the content and type.
"""

sig = s[0:4].strip()
f=dict(text=RDtext,
XYZ=RDXYZ,
curv=RDcurv,
vcgt=RDvcgt,
sf32=RDsf32,
)
if sig not in f:
return None
return (sig, f[sig](s))

def RDXYZ(s):
"""Convert ICC XYZType to rank 1 array of trimulus values."""

# See [ICC 2001] 6.5.26
assert s[0:4] == 'XYZ '
return readICCXYZNumber(s[8:])

def RDsf32(s):
"""Convert ICC s15Fixed16ArrayType to list of float."""
# See [ICC 2004] 10.18
assert s[0:4] == 'sf32'
return s15f16l(s[8:])

def RDmluc(s):
"""Convert ICC multiLocalizedUnicodeType. This types encodes
several strings together with a language/country code for each
string. A list of (*lc*, *string*) pairs is returned where *lc* is
the 4 byte language/country code, and *string* is the string
corresponding to that code. It seems unlikely that the same
language/country code will appear more than once with different
strings, but the ICC standard does not prohibit it."""
# See [ICC 2004] 10.13
assert s[0:4] == 'mluc'
n,sz = struct.unpack('>2L', s[8:16])
assert sz == 12
record = []
for i in range(n):
lc,l,o = struct.unpack('4s2L', s[16+12*n:28+12*n])
record.append(lc, s[o:o+l])
# How are strings encoded?
return record

def RDtext(s):
"""Convert ICC textType to Python string."""
# Note: type not specified or used in [ICC 2004], only in older
# [ICC 2001].
# See [ICC 2001] 6.5.18
assert s[0:4] == 'text'
return s[8:-1]

def RDcurv(s):
"""Convert ICC curveType."""
# See [ICC 2001] 6.5.3
assert s[0:4] == 'curv'
count, = struct.unpack('>L', s[8:12])
if count == 0:
return dict(gamma=1)
table = struct.unpack('>%dH' % count, s[12:])
if count == 1:
return dict(gamma=table[0]*2**-8)
return table

def RDvcgt(s):
"""Convert Apple CMVideoCardGammaType."""
# See
# http://developer.apple.com/documentation/GraphicsImaging/Reference/ColorSync_Manager/Reference/reference.html#//apple_ref/c/tdef/CMVideoCardGammaType
assert s[0:4] == 'vcgt'
tagtype, = struct.unpack('>L', s[8:12])
if tagtype != 0:
return s[8:]
if tagtype == 0:
# Table.
channels,count,size = struct.unpack('>3H', s[12:18])
if size == 1:
fmt = 'B'
elif size == 2:
fmt = 'H'
else:
return s[8:]
l = len(s[18:])//size
t = struct.unpack('>%d%s' % (l, fmt), s[18:])
t = group(t, count)
return size, t
return s[8:]


def group(s, n):
# See
# http://www.python.org/doc/2.6/library/functions.html#zip
return zip(*[iter(s)]*n)


def main(argv=None):
import sys
from getopt import getopt
if argv is None:
argv = sys.argv
argv = argv[1:]
opt,arg = getopt(argv, 'o:')
if len(arg) > 0:
inp = open(arg[0], 'rb')
else:
inp = sys.stdin
for o,v in opt:
if o == '-o':
f = open(v, 'wb')
return iccpout(f, inp)
return iccp(sys.stdout, inp)

if __name__ == '__main__':
main()

+ 45
- 0
build/pypng/mkiccp.py View File

@@ -0,0 +1,45 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/mkiccp.py $
# $Rev: 182 $
# Make ICC Profile

# References
#
# [ICC 2001] ICC Specification ICC.1:2001-04 (Profile version 2.4.0)
# [ICC 2004] ICC Specification ICC.1:2004-10 (Profile version 4.2.0.0)

import struct

# Local module.
import iccp

def black(m):
"""Return a function that maps all values from [0.0,m] to 0, and maps
the range [m,1.0] into [0.0, 1.0] linearly.
"""

m = float(m)

def f(x):
if x <= m:
return 0.0
return (x-m)/(1.0-m)
return f

# For monochrome input the required tags are (See [ICC 2001] 6.3.1.1):
# profileDescription [ICC 2001] 6.4.32
# grayTRC [ICC 2001] 6.4.19
# mediaWhitePoint [ICC 2001] 6.4.25
# copyright [ICC 2001] 6.4.13

def agreyprofile(out):
it = iccp.Profile().greyInput()
it.addTags(kTRC=black(0.07))
it.write(out)

def main():
import sys
agreyprofile(sys.stdout)

if __name__ == '__main__':
main()

+ 99
- 0
build/pypng/pdsimgtopng View File

@@ -0,0 +1,99 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pdsimgtopng $
# $Rev: 154 $
# PDS Image to PNG

import re
import struct

import png

class FormatError(Exception):
pass

def pdskey(s, k):
"""Lookup key `k` in string `s`. Returns value (as a string), or
raises exception if not found.
"""

assert re.match(r' *\^?[:\w]+$', k)
safere = '^' + re.escape(k) +r' *= *(\w+)'
m = re.search(safere, s, re.MULTILINE)
if not m:
raise FormatError("Can't find %s." % k)
return m.group(1)

def img(inp):
"""Open the PDS IMG file `inp` and return (*pixels*, *info*).
*pixels* is an iterator over the rows, *info* is the information
dictionary.
"""

err = __import__('sys').stderr

consumed = 1024

s = inp.read(consumed)
record_type = pdskey(s, 'RECORD_TYPE')
if record_type != 'FIXED_LENGTH':
raise FormatError(
"Can only deal with FIXED_LENGTH record type (found %s)" %
record_type)
record_bytes = int(pdskey(s,'RECORD_BYTES'))
file_records = int(pdskey(s, 'FILE_RECORDS'))
label_records = int(pdskey(s, 'LABEL_RECORDS'))
remaining = label_records * record_bytes - consumed
s += inp.read(remaining)
consumed += remaining

image_pointer = int(pdskey(s, '^IMAGE'))
# "^IMAGE" locates a record. Records are numbered starting from 1.
image_index = image_pointer - 1
image_offset = image_index * record_bytes
gap = image_offset - consumed
assert gap >= 0
if gap:
inp.read(gap)
# This assumes there is only one OBJECT in the file, and it is the
# IMAGE.
height = int(pdskey(s, ' LINES'))
width = int(pdskey(s, ' LINE_SAMPLES'))
sample_type = pdskey(s, ' SAMPLE_TYPE')
sample_bits = int(pdskey(s, ' SAMPLE_BITS'))
# For Messenger MDIS, SAMPLE_BITS is reported as 16, but only values
# from 0 ot 4095 are used.
bitdepth = 12
if sample_type == 'MSB_UNSIGNED_INTEGER':
fmt = '>H'
else:
raise 'Unknown sample type: %s.' % sample_type
sample_bytes = (1,2)[bitdepth > 8]
row_bytes = sample_bytes * width
fmt = fmt[:1] + str(width) + fmt[1:]
def rowiter():
for y in range(height):
yield struct.unpack(fmt, inp.read(row_bytes))
info = dict(greyscale=True, alpha=False, bitdepth=bitdepth,
size=(width,height), gamma=1.0)
return rowiter(), info


def main(argv=None):
import sys

if argv is None:
argv = sys.argv
argv = argv[1:]
arg = argv
if len(arg) >= 1:
f = open(arg[0], 'rb')
else:
f = sys.stdin
pixels,info = img(f)
w = png.Writer(**info)
w.write(sys.stdout, pixels)

if __name__ == '__main__':
main()



+ 73
- 0
build/pypng/pipasgrey View File

@@ -0,0 +1,73 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipasgrey $
# $Rev: 187 $

# pipasgrey

# Convert image to grey (L, or LA), but only if that involves no colour
# change.

def asgrey(out, inp, quiet=False):
"""Convert image to greyscale, but only when no colour change. This
works by using the input G channel (green) as the output L channel
(luminance) and checking that every pixel is grey as we go. A non-grey
pixel will raise an error, but if `quiet` is true then the grey pixel
check is suppressed.
"""

from array import array

import png

r = png.Reader(file=inp)
_,_,pixels,info = r.asDirect()
if info['greyscale']:
w = png.Writer(**info)
return w.write(out, pixels)
planes = info['planes']
targetplanes = planes - 2
alpha = info['alpha']
width = info['size'][0]
typecode = 'BH'[info['bitdepth'] > 8]
# Values per target row
vpr = width * (targetplanes)
def iterasgrey():
for i,row in enumerate(pixels):
row = array(typecode, row)
targetrow = array(typecode, [0]*vpr)
# Copy G (and possibly A) channel.
green = row[0::planes]
if alpha:
targetrow[0::2] = green
targetrow[1::2] = row[3::4]
else:
targetrow = green
# Check R and B channel match.
if not quiet and (
green != row[0::planes] or green != row[2::planes]):
raise ValueError('Row %i contains non-grey pixel.' % i)
yield targetrow
info['greyscale'] = True
del info['planes']
w = png.Writer(**info)
w.write(out, iterasgrey())

def main(argv=None):
from getopt import getopt
import sys
if argv is None:
argv = sys.argv
argv = argv[1:]
opt,argv = getopt(argv, 'q')
quiet = False
for o,v in opt:
if o == '-q':
quiet = True
if len(argv) > 0:
f = open(argv[0], 'rb')
else:
f = sys.stdin
return asgrey(sys.stdout, f, quiet)

if __name__ == '__main__':
main()

+ 44
- 0
build/pypng/pipcat View File

@@ -0,0 +1,44 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipcat $
# $Rev: 77 $

# http://www.python.org/doc/2.4.4/lib/module-itertools.html
import itertools
import sys

import png

def cat(out, l):
"""Concatenate the list of images. All input images must be same
height and have the same number of channels. They are concatenated
left-to-right. `out` is the (open file) destination for the
output image. `l` should be a list of open files (the input
image files).
"""

l = map(lambda f: png.Reader(file=f), l)
# Ewgh, side effects.
map(lambda r: r.preamble(), l)
# The reference height; from the first image.
height = l[0].height
# The total target width
width = 0
for i,r in enumerate(l):
if r.height != height:
raise Error('Image %d, height %d, does not match %d.' %
(i, r.height, height))
width += r.width
pixel,info = zip(*map(lambda r: r.asDirect()[2:4], l))
tinfo = dict(info[0])
del tinfo['size']
w = png.Writer(width, height, **tinfo)
def itercat():
for row in itertools.izip(*pixel):
yield itertools.chain(*row)
w.write(out, itercat())

def main(argv):
return cat(sys.stdout, map(lambda n: open(n, 'rb'), argv[1:]))

if __name__ == '__main__':
main(sys.argv)

+ 56
- 0
build/pypng/pipcolours View File

@@ -0,0 +1,56 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipcolours $
# $Rev: 96 $

# pipcolours - extract all colours present in source image.

def colours(out, inp):
import itertools
import png

r = png.Reader(file=inp)
_,_,pixels,info = r.asDirect()
planes = info['planes']
col = set()
for row in pixels:
# Ewgh, side effects on col
map(col.add, png.group(row, planes))
col,planes = channel_reduce(col, planes)
col = list(col)
col.sort()
col = list(itertools.chain(*col))
width = len(col)//planes
greyscale = planes in (1,2)
alpha = planes in (2,4)
bitdepth = info['bitdepth']
w = png.Writer(width, 1,
bitdepth=bitdepth, greyscale=greyscale, alpha=alpha)
w.write(out, [col])

def channel_reduce(col, planes):
"""Attempt to reduce the number of channels in the set of
colours."""
if planes >= 3:
def isgrey(c):
return c[0] == c[1] == c[2]
if min(map(isgrey, col)) == True:
# Every colour is grey.
col = set(map(lambda x: x[0::3], col))
planes -= 2
return col,planes

def main(argv=None):
import sys

if argv is None:
argv = sys.argv

argv = argv[1:]
if len(argv) > 0:
f = open(argv[0], 'rb')
else:
f = sys.stdin
return colours(sys.stdout, f)

if __name__ == '__main__':
main()

+ 121
- 0
build/pypng/pipcomposite View File

@@ -0,0 +1,121 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipcomposite $
# $Rev: 208 $
# pipcomposite
# Image alpha compositing.

"""
pipcomposite [--background #rrggbb] file.png

Composite an image onto a background and output the result. The
background colour is specified with an HTML-style triple (3, 6, or 12
hex digits), and defaults to black (#000).

The output PNG has no alpha channel.

It is valid for the input to have no alpha channel, but it doesn't
make much sense: the output will equal the input.
"""

import sys

def composite(out, inp, background):
import png

p = png.Reader(file=inp)
w,h,pixel,info = p.asRGBA()

outinfo = dict(info)
outinfo['alpha'] = False
outinfo['planes'] -= 1
outinfo['interlace'] = 0

# Convert to tuple and normalise to same range as source.
background = rgbhex(background)
maxval = float(2**info['bitdepth'] - 1)
background = map(lambda x: int(0.5 + x*maxval/65535.0),
background)
# Repeat background so that it's a whole row of sample values.
background *= w

def iterrow():
for row in pixel:
# Remove alpha from row, then create a list with one alpha
# entry _per channel value_.
# Squirrel the alpha channel away (and normalise it).
t = map(lambda x: x/maxval, row[3::4])
row = list(row)
del row[3::4]
alpha = row[:]
for i in range(3):
alpha[i::3] = t
assert len(alpha) == len(row) == len(background)
yield map(lambda a,v,b: int(0.5 + a*v + (1.0-a)*b),
alpha, row, background)

w = png.Writer(**outinfo)
w.write(out, iterrow())

def rgbhex(s):
"""Take an HTML style string of the form "#rrggbb" and return a
colour (R,G,B) triple. Following the initial '#' there can be 3, 6,
or 12 digits (for 4-, 8- or 16- bits per channel). In all cases the
values are expanded to a full 16-bit range, so the returned values
are all in range(65536).
"""

assert s[0] == '#'
s = s[1:]
assert len(s) in (3,6,12)

# Create a target list of length 12, and expand the string s to make
# it length 12.
l = ['z']*12
if len(s) == 3:
for i in range(4):
l[i::4] = s
if len(s) == 6:
for i in range(2):
l[i::4] = s[i::2]
l[i+2::4] = s[i::2]
if len(s) == 12:
l[:] = s
s = ''.join(l)
return map(lambda x: int(x, 16), (s[:4], s[4:8], s[8:]))

class Usage(Exception):
pass

def main(argv=None):
import getopt
import sys

if argv is None:
argv = sys.argv

argv = argv[1:]

try:
try:
opt,arg = getopt.getopt(argv, '',
['background='])
except getopt.error, msg:
raise Usage(msg)
background = '#000'
for o,v in opt:
if o in ['--background']:
background = v
except Usage, err:
print >>sys.stderr, __doc__
print >>sys.stderr, str(err)
return 2

if len(arg) > 0:
f = open(arg[0], 'rb')
else:
f = sys.stdin
return composite(sys.stdout, f, background)


if __name__ == '__main__':
main()

+ 181
- 0
build/pypng/pipdither View File

@@ -0,0 +1,181 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipdither $
# $Rev: 150 $

# pipdither
# Error Diffusing image dithering.
# Now with serpentine scanning.

# See http://www.efg2.com/Lab/Library/ImageProcessing/DHALF.TXT

# http://www.python.org/doc/2.4.4/lib/module-bisect.html
from bisect import bisect_left

import png

def dither(out, inp,
bitdepth=1, linear=False, defaultgamma=1.0, targetgamma=None,
cutoff=0.75):
"""Dither the input PNG `inp` into an image with a smaller bit depth
and write the result image onto `out`. `bitdepth` specifies the bit
depth of the new image.
Normally the source image gamma is honoured (the image is
converted into a linear light space before being dithered), but
if the `linear` argument is true then the image is treated as
being linear already: no gamma conversion is done (this is
quicker, and if you don't care much about accuracy, it won't
matter much).
Images with no gamma indication (no ``gAMA`` chunk) are normally
treated as linear (gamma = 1.0), but often it can be better
to assume a different gamma value: For example continuous tone
photographs intended for presentation on the web often carry
an implicit assumption of being encoded with a gamma of about
0.45 (because that's what you get if you just "blat the pixels"
onto a PC framebuffer), so ``defaultgamma=0.45`` might be a
good idea. `defaultgamma` does not override a gamma value
specified in the file itself: It is only used when the file
does not specify a gamma.

If you (pointlessly) specify both `linear` and `defaultgamma`,
`linear` wins.

The gamma of the output image is, by default, the same as the input
image. The `targetgamma` argument can be used to specify a
different gamma for the output image. This effectively recodes the
image to a different gamma, dithering as we go. The gamma specified
is the exponent used to encode the output file (and appears in the
output PNG's ``gAMA`` chunk); it is usually less than 1.

"""

# Encoding is what happened when the PNG was made (and also what
# happens when we output the PNG). Decoding is what we do to the
# source PNG in order to process it.

# The dithering algorithm is not completely general; it
# can only do bit depth reduction, not arbitrary palette changes.
import operator
maxval = 2**bitdepth - 1
r = png.Reader(file=inp)
# If image gamma is 1 or gamma is not present and we are assuming a
# value of 1, then it is faster to pass a maxval parameter to
# asFloat (the multiplications get combined). With gamma, we have
# to have the pixel values from 0.0 to 1.0 (as long as we are doing
# gamma correction here).
# Slightly annoyingly, we don't know the image gamma until we've
# called asFloat().
_,_,pixels,info = r.asDirect()
planes = info['planes']
assert planes == 1
width = info['size'][0]
sourcemaxval = 2**info['bitdepth'] - 1
if linear:
gamma = 1
else:
gamma = info.get('gamma') or defaultgamma
# Convert gamma from encoding gamma to the required power for
# decoding.
decode = 1.0/gamma
# Build a lookup table for decoding; convert from pixel values to linear
# space:
sourcef = 1.0/sourcemaxval
incode = map(sourcef.__mul__, range(sourcemaxval+1))
if decode != 1.0:
incode = map(decode.__rpow__, incode)
# Could be different, later on. targetdecode is the assumed gamma
# that is going to be used to decoding the target PNG. It is the
# reciprocal of the exponent that we use to encode the target PNG.
# This is the value that we need to build our table that we use for
# converting from linear to target colour space.
if targetgamma is None:
targetdecode = decode
else:
targetdecode = 1.0/targetgamma
# The table we use for encoding (creating the target PNG), still
# maps from pixel value to linear space, but we use it inverted, by
# searching through it with bisect.
targetf = 1.0/maxval
outcode = map(targetf.__mul__, range(maxval+1))
if targetdecode != 1.0:
outcode = map(targetdecode.__rpow__, outcode)
# The table used for choosing output codes. These values represent
# the cutoff points between two adjacent output codes.
choosecode = zip(outcode[1:], outcode)
p = cutoff
choosecode = map(lambda x: x[0]*p+x[1]*(1.0-p), choosecode)
def iterdither():
# Errors diffused downwards (into next row)
ed = [0.0]*width
flipped = False
for row in pixels:
row = map(incode.__getitem__, row)
row = map(operator.add, ed, row)
if flipped:
row = row[::-1]
targetrow = [0] * width
for i,v in enumerate(row):
# Clamp. Necessary because previously added errors may take
# v out of range.
v = max(0.0, min(v, 1.0))
# `it` will be the index of the chosen target colour;
it = bisect_left(choosecode, v)
t = outcode[it]
targetrow[i] = it
# err is the error that needs distributing.
err = v - t
# Sierra "Filter Lite" distributes * 2
# as per this diagram. 1 1
ef = err/2.0
# :todo: consider making rows one wider at each end and
# removing "if"s
if i+1 < width:
row[i+1] += ef
ef /= 2.0
ed[i] = ef
if i:
ed[i-1] += ef
if flipped:
ed = ed[::-1]
targetrow = targetrow[::-1]
yield targetrow
flipped = not flipped
info['bitdepth'] = bitdepth
info['gamma'] = 1.0/targetdecode
w = png.Writer(**info)
w.write(out, iterdither())


def main(argv=None):
# http://www.python.org/doc/2.4.4/lib/module-getopt.html
from getopt import getopt
import sys
if argv is None:
argv = sys.argv
opt,argv = getopt(argv[1:], 'b:c:g:lo:')
k = {}
for o,v in opt:
if o == '-b':
k['bitdepth'] = int(v)
if o == '-c':
k['cutoff'] = float(v)
if o == '-g':
k['defaultgamma'] = float(v)
if o == '-l':
k['linear'] = True
if o == '-o':
k['targetgamma'] = float(v)
if o == '-?':
print >>sys.stderr, "pipdither [-b bits] [-c cutoff] [-g assumed-gamma] [-l] [in.png]"

if len(argv) > 0:
f = open(argv[0], 'rb')
else:
f = sys.stdin

return dither(sys.stdout, f, **k)


if __name__ == '__main__':
main()

+ 36
- 0
build/pypng/piprgb View File

@@ -0,0 +1,36 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/piprgb $
# $Rev: 131 $
# piprgb
#
# Convert input image to RGB or RGBA format. Output will be colour type
# 2 or 6, and will not have a tRNS chunk.

import png

def rgb(out, inp):
"""Convert to RGB/RGBA."""

r = png.Reader(file=inp)
r.preamble()
if r.alpha or r.trns:
get = r.asRGBA
else:
get = r.asRGB
pixels,info = get()[2:4]
w = png.Writer(**info)
w.write(out, pixels)

def main(argv=None):
import sys

if argv is None:
argv = sys.argv
if len(argv) > 1:
f = open(argv[1], 'rb')
else:
f = sys.stdin
return rgb(sys.stdout, f)

if __name__ == '__main__':
main()

+ 53
- 0
build/pypng/pipscalez View File

@@ -0,0 +1,53 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipscalez $
# $Rev: 131 $

# pipscalez
# Enlarge an image by an integer factor horizontally and vertically.

def rescale(inp, out, xf, yf):
from array import array
import png

r = png.Reader(file=inp)
_,_,pixels,meta = r.asDirect()
typecode = 'BH'[meta['bitdepth'] > 8]
planes = meta['planes']
# We are going to use meta in the call to Writer, so expand the
# size.
x,y = meta['size']
x *= xf
y *= yf
meta['size'] = (x,y)
del x
del y
# Values per row, target row.
vpr = meta['size'][0] * planes
def iterscale():
for row in pixels:
bigrow = array(typecode, [0]*vpr)
row = array(typecode, row)
for c in range(planes):
channel = row[c::planes]
for i in range(xf):
bigrow[i*planes+c::xf*planes] = channel
for _ in range(yf):
yield bigrow
w = png.Writer(**meta)
w.write(out, iterscale())


def main(argv=None):
import sys

if argv is None:
argv = sys.argv
xf = int(argv[1])
if len(argv) > 2:
yf = int(argv[2])
else:
yf = xf
return rescale(sys.stdin, sys.stdout, xf, yf)

if __name__ == '__main__':
main()

+ 127
- 0
build/pypng/pipstack View File

@@ -0,0 +1,127 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipstack $
# $Rev: 190 $

# pipstack
# Combine input PNG files into a multi-channel output PNG.

"""
pipstack file1.png [file2.png ...]

pipstack can be used to combine 3 greyscale PNG files into a colour, RGB,
PNG file. In fact it is slightly more general than that. The number of
channels in the output PNG is equal to the sum of the numbers of
channels in the input images. It is an error if this sum exceeds 4 (the
maximum number of channels in a PNG image is 4, for an RGBA image). The
output colour model corresponds to the number of channels: 1 -
greyscale; 2 - greyscale+alpha; 3 - RGB; 4 - RGB+alpha.

In this way it is possible to combine 3 greyscale PNG files into an RGB
PNG (a common expected use) as well as more esoteric options: rgb.png +
grey.png = rgba.png; grey.png + grey.png = greyalpha.png.

Color Profile, Gamma, and so on.

[This is not implemented yet]

If an input has an ICC Profile (``iCCP`` chunk) then the output will
have an ICC Profile, but only if it is possible to combine all the input
ICC Profiles. It is possible to combine all the input ICC Profiles
only when: they all use the same Profile Connection Space; the PCS white
point is the same (specified in the header; should always be D50);
possibly some other things I haven't thought of yet.

If some of the inputs have a ``gAMA`` chunk (specifying gamma) and
an output ICC Profile is being generated, then the gamma information
will be incorporated into the ICC Profile.

When the output is an RGB colour type and the output ICC Profile is
synthesized, it is necessary to supply colorant tags (``rXYZ`` and so
on). These are taken from ``sRGB``.

If the input images have ``gAMA`` chunks and no input image has an ICC
Profile then the output image will have a ``gAMA`` chunk, but only if
all the ``gAMA`` chunks specify the same value. Otherwise a warning
will be emitted and no ``gAMA`` chunk. It is possible to add or replace
a ``gAMA`` chunk using the ``pipchunk`` tool.

gAMA, pHYs, iCCP, sRGB, tIME, any other chunks.
"""

class Error(Exception):
pass

def stack(out, inp):
"""Stack the input PNG files into a single output PNG."""

from array import array
import itertools
# Local module
import png

if len(inp) < 1:
raise Error("Required input is missing.")

l = map(png.Reader, inp)
# Let data be a list of (pixel,info) pairs.
data = map(lambda p: p.asDirect()[2:], l)
totalchannels = sum(map(lambda x: x[1]['planes'], data))

if not (0 < totalchannels <= 4):
raise Error("Too many channels in input.")
alpha = totalchannels in (2,4)
greyscale = totalchannels in (1,2)
bitdepth = []
for b in map(lambda x: x[1]['bitdepth'], data):
try:
if b == int(b):
bitdepth.append(b)
continue
except (TypeError, ValueError):
pass
# Assume a tuple.
bitdepth += b
# Currently, fail unless all bitdepths equal.
if len(set(bitdepth)) > 1:
raise NotImplemented("Cannot cope when bitdepths differ - sorry!")
bitdepth = bitdepth[0]
arraytype = 'BH'[bitdepth > 8]
size = map(lambda x: x[1]['size'], data)
# Currently, fail unless all images same size.
if len(set(size)) > 1:
raise NotImplemented("Cannot cope when sizes differ - sorry!")
size = size[0]
# Values per row
vpr = totalchannels * size[0]
def iterstack():
# the izip call creates an iterator that yields the next row
# from all the input images combined into a tuple.
for irow in itertools.izip(*map(lambda x: x[0], data)):
row = array(arraytype, [0]*vpr)
# output channel
och = 0
for i,arow in enumerate(irow):
# ensure incoming row is an array
arow = array(arraytype, arow)
n = data[i][1]['planes']
for j in range(n):
row[och::totalchannels] = arow[j::n]
och += 1
yield row
w = png.Writer(size[0], size[1],
greyscale=greyscale, alpha=alpha, bitdepth=bitdepth)
w.write(out, iterstack())


def main(argv=None):
import sys

if argv is None:
argv = sys.argv
argv = argv[1:]
arg = argv[:]
return stack(sys.stdout, arg)


if __name__ == '__main__':
main()

+ 67
- 0
build/pypng/pipwindow View File

@@ -0,0 +1,67 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pipwindow $
# $Rev: 173 $

# pipwindow
# Tool to crop/expand an image to a rectangular window. Come the
# revolution this tool will allow the image and the window to be placed
# arbitrarily (in particular the window can be bigger than the picture
# and/or overlap it only partially) and the image can be OpenGL style
# border/repeat effects (repeat, mirrored repeat, clamp, fixed
# background colour, background colour from source file). For now it
# only acts as crop. The window must be no greater than the image in
# both x and y.

def window(tl, br, inp, out):
"""Place a window onto the image and cut-out the resulting
rectangle. The window is an axis aligned rectangle opposite corners
at *tl* and *br* (each being an (x,y) pair). *inp* specifies the
input file which should be a PNG image.
"""

import png

r = png.Reader(file=inp)
x,y,pixels,meta = r.asDirect()
if not (0 <= tl[0] < br[0] <= x):
raise NotImplementedError()
if not (0 <= tl[1] < br[1] <= y):
raise NotImplementedError()
# Compute left and right bounds for each row
l = tl[0] * meta['planes']
r = br[0] * meta['planes']
def itercrop():
"""An iterator to perform the crop."""

for i,row in enumerate(pixels):
if i < tl[1]:
continue
if i >= br[1]:
# Same as "raise StopIteration"
return
yield row[l:r]
meta['size'] = (br[0]-tl[0], br[1]-tl[1])
w = png.Writer(**meta)
w.write(out, itercrop())

def main(argv=None):
import sys

if argv is None:
argv = sys.argv
argv = argv[1:]

tl = (0,0)
br = tuple(map(int, argv[:2]))
if len(argv) >= 4:
tl = br
br = tuple(map(int, argv[2:4]))
if len(argv) in (2, 4):
f = sys.stdin
else:
f = open(argv[-1], 'rb')

return window(tl, br, f, sys.stdout)

if __name__ == '__main__':
main()

+ 293
- 0
build/pypng/plan9topng.py View File

@@ -0,0 +1,293 @@
#!/usr/bin/env python
# $Rev: 184 $
# $URL: http://pypng.googlecode.com/svn/trunk/code/plan9topng.py $

# Imported from //depot/prj/plan9topam/master/code/plan9topam.py#4 on
# 2009-06-15.

"""Command line tool to convert from Plan 9 image format to PNG format.

Plan 9 image format description:
http://plan9.bell-labs.com/magic/man2html/6/image
"""

# http://www.python.org/doc/2.3.5/lib/module-itertools.html
import itertools
# http://www.python.org/doc/2.3.5/lib/module-re.html
import re
# http://www.python.org/doc/2.3.5/lib/module-sys.html
import sys

def block(s, n):
# See http://www.python.org/doc/2.6.2/library/functions.html#zip
return zip(*[iter(s)]*n)

def convert(f, output=sys.stdout) :
"""Convert Plan 9 file to PNG format. Works with either uncompressed
or compressed files.
"""

r = f.read(11)
if r == 'compressed\n' :
png(output, *decompress(f))
else :
png(output, *glue(f, r))


def glue(f, r) :
"""Return (metadata, stream) pair where `r` is the initial portion of
the metadata that has already been read from the stream `f`.
"""

r = r + f.read(60-len(r))
return (r, f)

def meta(r) :
"""Convert 60 character string `r`, the metadata from an image file.
Returns a 5-tuple (*chan*,*minx*,*miny*,*limx*,*limy*). 5-tuples may
settle into lists in transit.
As per http://plan9.bell-labs.com/magic/man2html/6/image the metadata
comprises 5 words separated by blanks. As it happens each word starts
at an index that is a multiple of 12, but this routine does not care
about that."""

r = r.split()
# :todo: raise FormatError
assert len(r) == 5
r = [r[0]] + map(int, r[1:])
return r

def bitdepthof(pixel) :
"""Return the bitdepth for a Plan9 pixel format string."""

maxd = 0
for c in re.findall(r'[a-z]\d*', pixel) :
if c[0] != 'x':
maxd = max(maxd, int(c[1:]))
return maxd

def maxvalof(pixel):
"""Return the netpbm MAXVAL for a Plan9 pixel format string."""

bitdepth = bitdepthof(pixel)
return (2**bitdepth)-1

def pixmeta(metadata, f) :
"""Convert (uncompressed) Plan 9 image file to pair of (*metadata*,
*pixels*). This is intended to be used by PyPNG format. *metadata*
is the metadata returned in a dictionary, *pixels* is an iterator that
yields each row in boxed row flat pixel format.

`f`, the input file, should be cued up to the start of the image data.
"""

chan,minx,miny,limx,limy = metadata
rows = limy - miny
width = limx - minx
nchans = len(re.findall('[a-wyz]', chan))
alpha = 'a' in chan
# Iverson's convention for the win!
ncolour = nchans - alpha
greyscale = ncolour == 1
bitdepth = bitdepthof(chan)
maxval = 2**bitdepth - 1
# PNG style metadata
meta=dict(size=(width,rows), bitdepth=bitdepthof(chan),
greyscale=greyscale, alpha=alpha, planes=nchans)

return itertools.imap(lambda x: itertools.chain(*x),
block(unpack(f, rows, width, chan, maxval), width)), meta

def png(out, metadata, f):
"""Convert to PNG format. `metadata` should be a Plan9 5-tuple; `f`
the input file (see :meth:`pixmeta`).
"""

import png

pixels,meta = pixmeta(metadata, f)
p = png.Writer(**meta)
p.write(out, pixels)

def spam():
"""Not really spam, but old PAM code, which is in limbo."""

if nchans == 3 or nchans == 1 :
# PGM (P5) or PPM (P6) format.
output.write('P%d\n%d %d %d\n' % (5+(nchans==3), width, rows, maxval))
else :
# PAM format.
output.write("""P7
WIDTH %d
HEIGHT %d
DEPTH %d
MAXVAL %d
""" % (width, rows, nchans, maxval))

def unpack(f, rows, width, pixel, maxval) :
"""Unpack `f` into pixels. Assumes the pixel format is such that the depth
is either a multiple or a divisor of 8.
`f` is assumed to be an iterator that returns blocks of input such
that each block contains a whole number of pixels. An iterator is
returned that yields each pixel as an n-tuple. `pixel` describes the
pixel format using the Plan9 syntax ("k8", "r8g8b8", and so on).
"""

def mask(w) :
"""An integer, to be used as a mask, with bottom `w` bits set to 1."""

return (1 << w)-1

def deblock(f, depth, width) :
"""A "packer" used to convert multiple bytes into single pixels.
`depth` is the pixel depth in bits (>= 8), `width` is the row width in
pixels.
"""

w = depth // 8
i = 0
for block in f :
for i in range(len(block)//w) :
p = block[w*i:w*(i+1)]
i += w
# Convert p to little-endian integer, x
x = 0
s = 1 # scale
for j in p :
x += s * ord(j)
s <<= 8
yield x

def bitfunge(f, depth, width) :
"""A "packer" used to convert single bytes into multiple pixels.
Depth is the pixel depth (< 8), width is the row width in pixels.
"""

for block in f :
col = 0
for i in block :
x = ord(i)
for j in range(8/depth) :
yield x >> (8 - depth)
col += 1
if col == width :
# A row-end forces a new byte even if we haven't consumed
# all of the current byte. Effectively rows are bit-padded
# to make a whole number of bytes.
col = 0
break
x <<= depth

# number of bits in each channel
chan = map(int, re.findall(r'\d+', pixel))
# type of each channel
type = re.findall('[a-z]', pixel)

depth = sum(chan)

# According to the value of depth pick a "packer" that either gathers
# multiple bytes into a single pixel (for depth >= 8) or split bytes
# into several pixels (for depth < 8)
if depth >= 8 :
#
assert depth % 8 == 0
packer = deblock
else :
assert 8 % depth == 0
packer = bitfunge

for x in packer(f, depth, width) :
# x is the pixel as an unsigned integer
o = []
# This is a bit yucky. Extract each channel from the _most_
# significant part of x.
for j in range(len(chan)) :
v = (x >> (depth - chan[j])) & mask(chan[j])
x <<= chan[j]
if type[j] != 'x' :
# scale to maxval
v = v * float(maxval) / mask(chan[j])
v = int(v+0.5)
o.append(v)
yield o


def decompress(f) :
"""Decompress a Plan 9 image file. Assumes f is already cued past the
initial 'compressed\n' string.
"""

r = meta(f.read(60))
return r, decomprest(f, r[4])


def decomprest(f, rows) :
"""Iterator that decompresses the rest of a file once the metadata
have been consumed."""

row = 0
while row < rows :
row,o = deblock(f)
yield o


def deblock(f) :
"""Decompress a single block from a compressed Plan 9 image file.
Each block starts with 2 decimal strings of 12 bytes each. Yields a
sequence of (row, data) pairs where row is the total number of rows
processed according to the file format and data is the decompressed
data for a set of rows."""

row = int(f.read(12))
size = int(f.read(12))
if not (0 <= size <= 6000) :
raise 'block has invalid size; not a Plan 9 image file?'

# Since each block is at most 6000 bytes we may as well read it all in
# one go.
d = f.read(size)
i = 0
o = []

while i < size :
x = ord(d[i])
i += 1
if x & 0x80 :
x = (x & 0x7f) + 1
lit = d[i:i+x]
i += x
o.extend(lit)
continue
# x's high-order bit is 0
l = (x >> 2) + 3
# Offset is made from bottom 2 bits of x and all 8 bits of next
# byte. http://plan9.bell-labs.com/magic/man2html/6/image doesn't
# say whether x's 2 bits are most signiificant or least significant.
# But it is clear from inspecting a random file,
# http://plan9.bell-labs.com/sources/plan9/sys/games/lib/sokoban/images/cargo.bit
# that x's 2 bit are most significant.
#
offset = (x & 3) << 8
offset |= ord(d[i])
i += 1
# Note: complement operator neatly maps (0 to 1023) to (-1 to
# -1024). Adding len(o) gives a (non-negative) offset into o from
# which to start indexing.
offset = ~offset + len(o)
if offset < 0 :
raise 'byte offset indexes off the begininning of the output buffer; not a Plan 9 image file?'
for j in range(l) :
o.append(o[offset+j])
return row,''.join(o)

def main(argv=None) :
if argv is None :
argv = sys.argv
if len(sys.argv) <= 1 :
return convert(sys.stdin)
else :
return convert(open(argv[1], 'rb'))

if __name__ == '__main__' :
sys.exit(main())

+ 172
- 0
build/pypng/pngchunk View File

@@ -0,0 +1,172 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pngchunk $
# $Rev: 156 $
# pngchunk
# Chunk editing/extraction tool.

import struct
import warnings

# Local module.
import png

"""
pngchunk [--gamma g] [--iccprofile file] [--sigbit b] [-c cHNK!] [-c cHNK:foo] [-c cHNK<file]

The ``-c`` option is used to add or remove chunks. A chunk is specified
by its 4 byte chunk type. If this is followed by a ``!`` then that
chunk is removed from the PNG file. If the chunk type is followed by a
``:`` then the chunk is replaced with the contents of the rest of the
argument (this is probably only useful if the content is mostly ASCII,
otherwise it's a pain to quote the contents, otherwise see...). A ``<``
can be used to take the contents of the chunk from the named file.
"""


def chunk(out, inp, l):
"""Process the input PNG file to the output, chunk by chunk. Chunks
can be inserted, removed, replaced, or sometimes edited. Generally,
chunks are not inspected, so pixel data (in the ``IDAT`` chunks)
cannot be modified. `l` should be a list of (*chunktype*,
*content*) pairs. *chunktype* is usually the type of the PNG chunk,
specified as a 4-byte Python string, and *content* is the chunk's
content, also as a string; if *content* is ``None`` then *all*
chunks of that type will be removed.

This function *knows* about certain chunk types and will
automatically convert from Python friendly representations to
string-of-bytes.

chunktype
'gamma' 'gAMA' float
'sigbit' 'sBIT' int, or tuple of length 1,2 or 3

Note that the length of the strings used to identify *friendly*
chunk types is greater than 4, hence they cannot be confused with
canonical chunk types.

Chunk types, if specified using the 4-byte syntax, need not be
official PNG chunks at all. Non-standard chunks can be created.
"""

def canonical(p):
"""Take a pair (*chunktype*, *content*), and return canonical
representation (*chunktype*, *content*) where `chunktype` is the
4-byte PNG chunk type and `content` is a string.
"""

t,v = p
if len(t) == 4:
return t,v
if t == 'gamma':
t = 'gAMA'
v = int(round(1e5*v))
v = struct.pack('>I', v)
elif t == 'sigbit':
t = 'sBIT'
try:
v[0]
except TypeError:
v = (v,)
v = struct.pack('%dB' % len(v), *v)
elif t == 'iccprofile':
t = 'iCCP'
# http://www.w3.org/TR/PNG/#11iCCP
v = 'a color profile\x00\x00' + v.encode('zip')
else:
warnings.warn('Unknown chunk type %r' % t)
return t[:4],v

l = map(canonical, l)
# Some chunks automagically replace ones that are present in the
# source PNG. There can only be one of each of these chunk types.
# Create a 'replace' dictionary to record these chunks.
add = []
delete = set()
replacing = set(['gAMA', 'sBIT', 'PLTE', 'tRNS', 'sPLT', 'IHDR'])
replace = dict()
for t,v in l:
if v is None:
delete.add(t)
elif t in replacing:
replace[t] = v
else:
add.append((t,v))
del l
r = png.Reader(file=inp)
chunks = r.chunks()
def iterchunks():
for t,v in chunks:
if t in delete:
continue
if t in replace:
yield t,replace[t]
del replace[t]
continue
if t == 'IDAT' and replace:
# Insert into the output any chunks that are on the
# replace list. We haven't output them yet, because we
# didn't see an original chunk of the same type to
# replace. Thus the "replace" is actually an "insert".
for u,w in replace.items():
yield u,w
del replace[u]
if t == 'IDAT' and add:
for item in add:
yield item
del add[:]
yield t,v
return png.write_chunks(out, iterchunks())

class Usage(Exception):
pass

def main(argv=None):
import getopt
import re
import sys

if argv is None:
argv = sys.argv

argv = argv[1:]

try:
try:
opt,arg = getopt.getopt(argv, 'c:',
['gamma=', 'iccprofile=', 'sigbit='])
except getopt.error, msg:
raise Usage(msg)
k = []
for o,v in opt:
if o in ['--gamma']:
k.append(('gamma', float(v)))
if o in ['--sigbit']:
k.append(('sigbit', int(v)))
if o in ['--iccprofile']:
k.append(('iccprofile', open(v, 'rb').read()))
if o in ['-c']:
type = v[:4]
if not re.match('[a-zA-Z]{4}', type):
raise Usage('Chunk type must consist of 4 letters.')
if v[4] == '!':
k.append((type, None))
if v[4] == ':':
k.append((type, v[5:]))
if v[4] == '<':
k.append((type, open(v[5:], 'rb').read()))
except Usage, err:
print >>sys.stderr, (
"usage: pngchunk [--gamma d.dd] [--sigbit b] [-c cHNK! | -c cHNK:text-string]")
print >>sys.stderr, err.message
return 2

if len(arg) > 0:
f = open(arg[0], 'rb')
else:
f = sys.stdin
return chunk(sys.stdout, f, k)


if __name__ == '__main__':
main()

+ 79
- 0
build/pypng/pnghist View File

@@ -0,0 +1,79 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pnghist $
# $Rev: 153 $
# PNG Histogram
# Only really works on grayscale images.

from array import array
import getopt

import png

def decidemax(level):
"""Given an array of levels, decide the maximum value to use for the
histogram. This is normally chosen to be a bit bigger than the 99th
percentile, but if the 100th percentile is not much more (within a
factor of 2) then the 100th percentile is chosen.
"""

truemax = max(level)
sl = level[:]
sl.sort(reverse=True)
i99 = int(round(len(level)*0.01))
if truemax <= 2*sl[i99]:
return truemax
return 1.05*sl[i99]

def hist(out, inp, verbose=None):
"""Open the PNG file `inp` and generate a histogram."""

r = png.Reader(file=inp)
x,y,pixels,info = r.asDirect()
bitdepth = info['bitdepth']
level = [0]*2**bitdepth
for row in pixels:
for v in row:
level[v] += 1
maxlevel = decidemax(level)

h = 100
outbitdepth = 8
outmaxval = 2**outbitdepth - 1
def genrow():
for y in range(h):
y = h-y-1
# :todo: vary typecode according to outbitdepth
row = array('B', [0]*len(level))
fl = y*maxlevel/float(h)
ce = (y+1)*maxlevel/float(h)
for x in range(len(row)):
if level[x] <= fl:
# Relies on row being initialised to all 0
continue
if level[x] >= ce:
row[x] = outmaxval
continue
frac = (level[x] - fl)/(ce - fl)
row[x] = int(round(outmaxval*frac))
yield row
w = png.Writer(len(level), h, gamma=1.0,
greyscale=True, alpha=False, bitdepth=outbitdepth)
w.write(out, genrow())
if verbose: print >>verbose, level

def main(argv=None):
import sys

if argv is None:
argv = sys.argv
argv = argv[1:]
opt,arg = getopt.getopt(argv, '')

if len(arg) < 1:
f = sys.stdin
else:
f = open(arg[0])
hist(sys.stdout, f)

if __name__ == '__main__':
main()

+ 31
- 0
build/pypng/pnglsch View File

@@ -0,0 +1,31 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/pnglsch $
# $Rev: 107 $
# pnglsch
# PNG List Chunks

import png

def list(out, inp):
r = png.Reader(file=inp)
for t,v in r.chunks():
add = ''
if len(v) <= 28:
add = ' ' + v.encode('hex')
print >>out, "%s %10d%s" % (t, len(v), add)

def main(argv=None):
import sys

if argv is None:
argv = sys.argv
arg = argv[1:]

if len(arg) > 0:
f = open(arg[0], 'rb')
else:
f = sys.stdin
return list(sys.stdout, f)

if __name__ == '__main__':
main()

+ 151
- 0
build/pypng/texttopng View File

@@ -0,0 +1,151 @@
#!/usr/bin/env python
# $URL: http://pypng.googlecode.com/svn/trunk/code/texttopng $
# $Rev: 132 $
# Script to renders text as a PNG image.

from array import array
import itertools

font = {
32: '0000000000000000',
33: '0010101010001000',
34: '0028280000000000',
35: '0000287c287c2800',
36: '00103c5038147810',
37: '0000644810244c00',
38: '0020502054483400',
39: '0010100000000000',
40: '0008101010101008',
41: '0020101010101020',
42: '0010543838541000',
43: '000010107c101000',
44: '0000000000301020',
45: '000000007c000000',
46: '0000000000303000',
47: '0000040810204000',
48: '0038445454443800',
49: '0008180808080800',
50: '0038043840407c00',
51: '003c041804043800',
52: '00081828487c0800',
53: '0078407804047800',
54: '0038407844443800',
55: '007c040810101000',
56: '0038443844443800',
57: '0038443c04040400',
58: '0000303000303000',
59: '0000303000301020',
60: '0004081020100804',
61: '0000007c007c0000',
62: '0040201008102040',
63: '0038440810001000',
64: '00384c545c403800',
65: '0038447c44444400',
66: '0078447844447800',
67: '0038444040443800',
68: '0070484444487000',
69: '007c407840407c00',
70: '007c407840404000',
71: '003844405c443c00',
72: '0044447c44444400',
73: '0038101010103800',
74: '003c040404443800',
75: '0044487048444400',
76: '0040404040407c00',
77: '006c545444444400',
78: '004464544c444400',
79: '0038444444443800',
80: '0078447840404000',
81: '0038444444443c02',
82: '0078447844444400',
83: '0038403804047800',
84: '007c101010101000',
85: '0044444444443c00',
86: '0044444444281000',
87: '0044445454543800',
88: '0042241818244200',
89: '0044443810101000',
90: '007c081020407c00',
91: '0038202020202038',
92: '0000402010080400',
93: '0038080808080838',
94: '0010284400000000',
95: '000000000000fe00',
96: '0040200000000000',
97: '000038043c443c00',
98: '0040784444447800',
99: '0000384040403800',
100: '00043c4444443c00',
101: '000038447c403c00',
102: '0018203820202000',
103: '00003c44443c0438',
104: '0040784444444400',
105: '0010003010101000',
106: '0010003010101020',
107: '0040404870484400',
108: '0030101010101000',
109: '0000385454444400',
110: '0000784444444400',
111: '0000384444443800',
112: '0000784444784040',
113: '00003c44443c0406',
114: '00001c2020202000',
115: '00003c4038047800',
116: '0020203820201800',
117: '0000444444443c00',
118: '0000444444281000',
119: '0000444454543800',
120: '0000442810284400',
121: '00004444443c0438',
122: '00007c0810207c00',
123: '0018202060202018',
124: '0010101000101010',
125: '003008080c080830',
126: '0020540800000000',
}

def char(i):
"""Get image data for the character `i` (a one character string).
Returned as a list of rows. Each row is a tuple containing the
packed pixels.
"""

i = ord(i)
if i not in font:
return [(0,)]*8
return map(lambda row: (ord(row),), font[i].decode('hex'))

def texttoraster(m):
"""Convert string *m* to a raster image (by rendering it using the
font in *font*). A triple of (*width*, *height*, *pixels*) is
returned; *pixels* is in boxed row packed pixel format.
"""

# Assumes monospaced font.
x = 8*len(m)
y = 8
return x,y,itertools.imap(lambda row: itertools.chain(*row),
zip(*map(char, m)))


def render(message, out):
import png

x,y,pixels = texttoraster(message)
w = png.Writer(x, y, greyscale=True, bitdepth=1)
w.write_packed(out, pixels)
out.flush()

def main(argv=None):
import sys

if argv is None:
argv = sys.argv
if len(argv) > 1:
message = argv[1]
else:
message = sys.stdin.read()
render(message, sys.stdout)

if __name__ == '__main__':
main()

+ 463
- 0
client.mk View File

@@ -0,0 +1,463 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Build a comm application (Mozilla calendar, mail or suite).
#
# To build a tree,
# 1. hg clone http://hg.mozilla.org/comm-central comm
# 2. cd comm
# 3. python client.py checkout
# 4. create your .mozconfig file with
# ac_add_options --enable-application=suite
# (or mail, or calendar)
# 5. gmake -f client.mk
#
# Other targets (gmake -f client.mk [targets...]),
# build
# clean
# distclean
#
# See http://developer.mozilla.org/en/Build_Documentation for
# more information.
#
# Options:
# MOZ_BUILD_PROJECTS - Build multiple projects in subdirectories
# of MOZ_OBJDIR
# MOZ_OBJDIR - Destination object directory
# MOZ_MAKE_FLAGS - Flags to pass to $(MAKE)
# MOZ_PREFLIGHT_ALL } - Makefiles to run before any project in
# MOZ_PREFLIGHT } MOZ_BUILD_PROJECTS, before each project, after
# MOZ_POSTFLIGHT } each project, and after all projects; these
# MOZ_POSTFLIGHT_ALL } variables contain space-separated lists
# MOZ_UNIFY_BDATE - Set to use the same bdate for each project in
# MOZ_BUILD_PROJECTS
#
#######################################################################
# Defines

comma := ,

CWD := $(CURDIR)
ifneq (1,$(words $(CWD)))
$(error The platform directory cannot be located in a path with spaces.)
endif

ifeq "$(CWD)" "/"
CWD := /.
endif

ifndef TOPSRCDIR
ifeq (,$(wildcard client.mk))
TOPSRCDIR := $(patsubst %/,%,$(dir $(MAKEFILE_LIST)))
else
TOPSRCDIR := $(CWD)
endif
endif

SH := /bin/sh
PERL ?= perl
PYTHON ?= $(shell which python2.7 > /dev/null 2>&1 && echo python2.7 || echo python)

CONFIG_GUESS_SCRIPT := $(wildcard $(TOPSRCDIR)/build/autoconf/config.guess)
ifdef CONFIG_GUESS_SCRIPT
CONFIG_GUESS := $(shell $(CONFIG_GUESS_SCRIPT))
endif


####################################
# Sanity checks

# Windows checks.
ifneq (,$(findstring mingw,$(CONFIG_GUESS)))

# check for CRLF line endings
ifneq (0,$(shell $(PERL) -e 'binmode(STDIN); while (<STDIN>) { if (/\r/) { print "1"; exit } } print "0"' < $(TOPSRCDIR)/client.mk))
$(error This source tree appears to have Windows-style line endings. To \
convert it to Unix-style line endings, check \
"https://developer.mozilla.org/en-US/docs/Developer_Guide/Mozilla_build_FAQ\#Win32-specific_questions" \
for a workaround of this issue.)
endif
endif

####################################
# Load mozconfig Options

# See build pages, http://www.mozilla.org/build/ for how to set up mozconfig.

MOZCONFIG_LOADER := build/autoconf/mozconfig2client-mk

define CR


endef

# As $(shell) doesn't preserve newlines, use sed to replace them with an
# unlikely sequence (||), which is then replaced back to newlines by make
# before evaluation. $(shell) replacing newlines with spaces, || is always
# followed by a space (since sed doesn't remove newlines), except on the
# last line, so replace both '|| ' and '||'.
# Also, make MOZ_PGO available to mozconfig when passed on make command line.
MOZCONFIG_CONTENT := $(subst ||,$(CR),$(subst || ,$(CR),$(shell MOZ_PGO=$(MOZ_PGO) $(TOPSRCDIR)/$(MOZCONFIG_LOADER) $(TOPSRCDIR) | sed 's/$$/||/')))
$(eval $(MOZCONFIG_CONTENT))

export FOUND_MOZCONFIG

# As '||' was used as a newline separator, it means it's not occurring in
# lines themselves. It can thus safely be used to replaces normal spaces,
# to then replace newlines with normal spaces. This allows to get a list
# of mozconfig output lines.
MOZCONFIG_OUT_LINES := $(subst $(CR), ,$(subst $(NULL) $(NULL),||,$(MOZCONFIG_CONTENT)))
# Filter-out comments from those lines.
START_COMMENT = \#
MOZCONFIG_OUT_FILTERED := $(filter-out $(START_COMMENT)%,$(MOZCONFIG_OUT_LINES))

ifdef MOZ_PGO
export MOZ_PGO
endif

# Automatically add -jN to make flags if not defined. N defaults to number of cores.
ifeq (,$(findstring -j,$(MOZ_MAKE_FLAGS)))
cores=$(shell $(PYTHON) -c 'import multiprocessing; print(multiprocessing.cpu_count())')
MOZ_MAKE_FLAGS += -j$(cores)
endif


ifndef MOZ_OBJDIR
MOZ_OBJDIR = obj-$(CONFIG_GUESS)
else
# On Windows Pymake builds check MOZ_OBJDIR doesn't start with "/"
ifneq (,$(findstring mingw,$(CONFIG_GUESS)))
ifeq (1_a,$(.PYMAKE)_$(firstword a$(subst /, ,$(MOZ_OBJDIR))))
$(error For Windows Pymake builds, MOZ_OBJDIR must be a Windows [and not MSYS] style path.)
endif
endif
endif

ifdef MOZ_BUILD_PROJECTS

ifdef MOZ_CURRENT_PROJECT
OBJDIR = $(MOZ_OBJDIR)/$(MOZ_CURRENT_PROJECT)
MOZ_MAKE = $(MAKE) $(MOZ_MAKE_FLAGS) -C $(OBJDIR)
BUILD_PROJECT_ARG = MOZ_BUILD_APP=$(MOZ_CURRENT_PROJECT)
else
OBJDIR = $(error Cannot find the OBJDIR when MOZ_CURRENT_PROJECT is not set.)
MOZ_MAKE = $(error Cannot build in the OBJDIR when MOZ_CURRENT_PROJECT is not set.)
endif

else # MOZ_BUILD_PROJECTS

OBJDIR = $(MOZ_OBJDIR)
MOZ_MAKE = $(MAKE) $(MOZ_MAKE_FLAGS) -C $(OBJDIR)

endif # MOZ_BUILD_PROJECTS

# If we have a MOZ_OBJDIR that's set from the environment, ensure that it is an
# absolute path.
ifdef MOZ_OBJDIR
MOZ_OBJDIR := $(shell $(PYTHON) -c "import os.path; print(os.path.join(\"$(TOPSRCDIR)\", \"$(MOZ_OBJDIR)\").replace('\\\\','/'))")
endif

# 'configure' scripts generated by autoconf.
CONFIGURES := $(TOPSRCDIR)/configure
CONFIGURES += $(TOPSRCDIR)/platform/configure
CONFIGURES += $(TOPSRCDIR)/platform/js/src/configure

# Make targets that are going to be passed to the real build system
OBJDIR_TARGETS = install export libs clean realclean distclean maybe_clobber_profiledbuild upload sdk installer package package-compare stage-package source-package l10n-check automation/build

#######################################################################
# Rules

# The default rule is build
build::

# Define mkdir
include $(TOPSRCDIR)/config/makefiles/makeutils.mk
include $(TOPSRCDIR)/config/makefiles/autotargets.mk

# Create a makefile containing the mk_add_options values from mozconfig,
# but only do so when OBJDIR is defined (see further above).
ifdef MOZ_BUILD_PROJECTS
ifdef MOZ_CURRENT_PROJECT
WANT_MOZCONFIG_MK = 1
else
WANT_MOZCONFIG_MK =
endif
else
WANT_MOZCONFIG_MK = 1
endif

ifdef WANT_MOZCONFIG_MK
# For now, only output "export" lines from mozconfig2client-mk output.
MOZCONFIG_MK_LINES := $(filter export||%,$(MOZCONFIG_OUT_LINES))
$(OBJDIR)/.mozconfig.mk: $(FOUND_MOZCONFIG) $(call mkdir_deps,$(OBJDIR))
$(if $(MOZCONFIG_MK_LINES),( $(foreach line,$(MOZCONFIG_MK_LINES), echo "$(subst ||, ,$(line))";) )) > $@
ifdef MOZ_CURRENT_PROJECT
echo export MOZ_CURRENT_PROJECT=$(MOZ_CURRENT_PROJECT) >> $@
endif

# Include that makefile so that it is created. This should not actually change
# the environment since MOZCONFIG_CONTENT, which MOZCONFIG_OUT_LINES derives
# from, has already been eval'ed.
include $(OBJDIR)/.mozconfig.mk
endif

# UPLOAD_EXTRA_FILES is appended to and exported from mozconfig, which makes
# submakes as well as configure add even more to that, so just unexport it
# for submakes to pick it from .mozconfig.mk and for configure to pick it
# from mach environment.
unexport UPLOAD_EXTRA_FILES

# These targets are candidates for auto-running client.py

ifeq (01,$(MAKELEVEL)$(if $(ALWAYS_RUN_CLIENT_PY),1,))

build profiledbuild configure:: run_client_py
$(MAKE) -f $(TOPSRCDIR)/client.mk $@
else


# Print out any options loaded from mozconfig.
all build clean distclean export libs install realclean::
ifneq (,$(strip $(MOZCONFIG_OUT_FILTERED)))
$(info Adding client.mk options from $(FOUND_MOZCONFIG):)
$(foreach line,$(MOZCONFIG_OUT_FILTERED),$(info $(NULL) $(NULL) $(NULL) $(NULL) $(subst ||, ,$(line))))
endif

# Windows equivalents
build_all: build
clobber clobber_all: clean

# Do everything from scratch
everything: clean build

####################################
# Profile-Guided Optimization
# To use this, you should set the following variables in your mozconfig
# mk_add_options PROFILE_GEN_SCRIPT=/path/to/profile-script
#
# The profile script should exercise the functionality to be included
# in the profile feedback.
#
# This is up here, outside of the MOZ_CURRENT_PROJECT logic so that this
# is usable in multi-pass builds, where you might not have a runnable
# application until all the build passes and postflight scripts have run.
ifdef MOZ_OBJDIR
PGO_OBJDIR = $(MOZ_OBJDIR)
else
PGO_OBJDIR := $(TOPSRCDIR)
endif

profiledbuild::
$(MAKE) -f $(TOPSRCDIR)/client.mk build MOZ_PROFILE_GENERATE=1
$(MAKE) -C $(PGO_OBJDIR) stage-package
OBJDIR=${PGO_OBJDIR} $(PROFILE_GEN_SCRIPT)
$(MAKE) -f $(TOPSRCDIR)/client.mk maybe_clobber_profiledbuild
$(MAKE) -f $(TOPSRCDIR)/client.mk build MOZ_PROFILE_USE=1

#####################################################
# Build date unification

ifdef MOZ_UNIFY_BDATE
ifndef MOZ_BUILD_DATE
ifdef MOZ_BUILD_PROJECTS
MOZ_BUILD_DATE = $(shell $(PYTHON) $(TOPSRCDIR)/platform/build/variables.py buildid_header | awk '{print $$3}')
export MOZ_BUILD_DATE
endif
endif
endif

#####################################################
# Preflight, before building any project

build preflight_all::
ifeq (,$(MOZ_CURRENT_PROJECT)$(if $(MOZ_PREFLIGHT_ALL),,1))
# Don't run preflight_all for individual projects in multi-project builds
# (when MOZ_CURRENT_PROJECT is set.)
ifndef MOZ_BUILD_PROJECTS
# Building a single project, OBJDIR is usable.
set -e; \
for mkfile in $(MOZ_PREFLIGHT_ALL); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
done
else
# OBJDIR refers to the project-specific OBJDIR, which is not available at
# this point when building multiple projects. Only MOZ_OBJDIR is available.
set -e; \
for mkfile in $(MOZ_PREFLIGHT_ALL); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight_all TOPSRCDIR=$(TOPSRCDIR) MOZ_OBJDIR=$(MOZ_OBJDIR) MOZ_BUILD_PROJECTS='$(MOZ_BUILD_PROJECTS)'; \
done
endif
endif

# If we're building multiple projects, but haven't specified which project,
# loop through them.

ifeq (,$(MOZ_CURRENT_PROJECT)$(if $(MOZ_BUILD_PROJECTS),,1))
configure build preflight postflight $(OBJDIR_TARGETS)::
set -e; \
for app in $(MOZ_BUILD_PROJECTS); do \
$(MAKE) -f $(TOPSRCDIR)/client.mk $@ MOZ_CURRENT_PROJECT=$$app; \
done

else

# MOZ_CURRENT_PROJECT: either doing a single-project build, or building an
# individual project in a multi-project build.

####################################
# Configure

MAKEFILE = $(wildcard $(OBJDIR)/Makefile)
CONFIG_STATUS = $(wildcard $(OBJDIR)/config.status)
CONFIG_CACHE = $(wildcard $(OBJDIR)/config.cache)

EXTRA_CONFIG_DEPS := \
$(TOPSRCDIR)/aclocal.m4 \
$(TOPSRCDIR)/platform/aclocal.m4 \
$(TOPSRCDIR)/platform/old-configure.in \
$(wildcard $(TOPSRCDIR)/platform/build/autoconf/*.m4) \
$(TOPSRCDIR)/platform/js/src/aclocal.m4 \
$(TOPSRCDIR)/platform/js/src/old-configure.in \
$(NULL)

$(CONFIGURES): %: %.in $(EXTRA_CONFIG_DEPS)
@echo Generating $@
sed '1,/^divert/d' $< > $@
chmod +x $@

CONFIG_STATUS_DEPS := \
$(wildcard $(CONFIGURES)) \
$(wildcard $(TOPSRCDIR)/platform/nsprpub/configure) \
$(wildcard $(TOPSRCDIR)/platform/config/milestone.txt) \
$(wildcard $(TOPSRCDIR)/platform/ldap/sdks/c-sdk/configure) \
$(wildcard $(addsuffix confvars.sh,$(wildcard $(TOPSRCDIR)/*/))) \
$(NULL)

CONFIGURE_ENV_ARGS += \
MAKE='$(MAKE)' \
$(NULL)

# configure uses the program name to determine @srcdir@. Calling it without
# $(TOPSRCDIR) will set @srcdir@ to '.'; otherwise, it is set to the full
# path of $(TOPSRCDIR).
ifeq ($(TOPSRCDIR),$(OBJDIR))
CONFIGURE = ./configure
else
CONFIGURE = $(TOPSRCDIR)/configure
endif

configure-files: $(CONFIGURES)

configure-preqs = \
configure-files \
$(call mkdir_deps,$(OBJDIR)) \
$(if $(MOZ_BUILD_PROJECTS),$(call mkdir_deps,$(MOZ_OBJDIR))) \
$(NULL)

configure:: $(configure-preqs)
@echo cd $(OBJDIR);
@echo $(CONFIGURE) $(CONFIGURE_ARGS)
@cd $(OBJDIR) && $(BUILD_PROJECT_ARG) $(CONFIGURE_ENV_ARGS) $(CONFIGURE) $(CONFIGURE_ARGS) \
|| ( echo '*** Fix above errors and then restart with\
"$(MAKE) -f client.mk build"' && exit 1 )
@touch $(OBJDIR)/Makefile

ifneq (,$(MAKEFILE))
$(OBJDIR)/Makefile: $(OBJDIR)/config.status

$(OBJDIR)/config.status: $(CONFIG_STATUS_DEPS)
else
$(OBJDIR)/Makefile: $(CONFIG_STATUS_DEPS)
endif
@$(MAKE) -f $(TOPSRCDIR)/client.mk configure

ifneq (,$(CONFIG_STATUS))
$(OBJDIR)/config/autoconf.mk: $(TOPSRCDIR)/config/autoconf.mk.in
$(PYTHON) $(OBJDIR)/config.status -n --file=$(OBJDIR)/config/autoconf.mk
endif


####################################
# Preflight

build preflight::
ifdef MOZ_PREFLIGHT
set -e; \
for mkfile in $(MOZ_PREFLIGHT); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile preflight TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
done
endif

####################################
# Build it

build:: $(OBJDIR)/Makefile $(OBJDIR)/config.status
+$(MOZ_MAKE)

####################################
# Other targets

# Pass these target onto the real build system
$(OBJDIR_TARGETS):: $(OBJDIR)/Makefile $(OBJDIR)/config.status
+$(MOZ_MAKE) $@

####################################
# Postflight

build postflight::
ifdef MOZ_POSTFLIGHT
set -e; \
for mkfile in $(MOZ_POSTFLIGHT); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
done
endif

endif # MOZ_CURRENT_PROJECT
endif # RAN_CLIENT_PY

####################################
# Postflight, after building all projects

build postflight_all::
ifeq (,$(MOZ_CURRENT_PROJECT)$(if $(MOZ_POSTFLIGHT_ALL),,1))
# Don't run postflight_all for individual projects in multi-project builds
# (when MOZ_CURRENT_PROJECT is set.)
ifndef MOZ_BUILD_PROJECTS
# Building a single project, OBJDIR is usable.
set -e; \
for mkfile in $(MOZ_POSTFLIGHT_ALL); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight_all TOPSRCDIR=$(TOPSRCDIR) OBJDIR=$(OBJDIR) MOZ_OBJDIR=$(MOZ_OBJDIR); \
done
else
# OBJDIR refers to the project-specific OBJDIR, which is not available at
# this point when building multiple projects. Only MOZ_OBJDIR is available.
set -e; \
for mkfile in $(MOZ_POSTFLIGHT_ALL); do \
$(MAKE) -f $(TOPSRCDIR)/$$mkfile postflight_all TOPSRCDIR=$(TOPSRCDIR) MOZ_OBJDIR=$(MOZ_OBJDIR) MOZ_BUILD_PROJECTS='$(MOZ_BUILD_PROJECTS)'; \
done
endif
endif

cleansrcdir:
@cd $(TOPSRCDIR); \
if [ -f Makefile ]; then \
$(MAKE) distclean ; \
else \
echo 'Removing object files from srcdir...'; \
rm -fr `find . -type d \( -name .deps -print -o -name CVS \
-o -exec test ! -d {}/CVS \; \) -prune \
-o \( -name '*.[ao]' -o -name '*.so' \) -type f -print`; \
build/autoconf/clean-config.sh; \
fi;

echo-variable-%:
@echo $($*)

# This makefile doesn't support parallel execution. It does pass
# MOZ_MAKE_FLAGS to sub-make processes, so they will correctly execute
# in parallel.
.NOTPARALLEL:

.PHONY: checkout co real_checkout build profiledbuild cleansrcdir pull_all build_all clobber clobber_all pull_and_build_all everything configure preflight_all preflight postflight postflight_all $(OBJDIR_TARGETS)

+ 16
- 0
config/baseconfig.mk View File

@@ -0,0 +1,16 @@
# This file is normally included by autoconf.mk, but it is also used
# directly in python/mozbuild/mozbuild/base.py for gmake validation.
# We thus use INCLUDED_AUTOCONF_MK to enable/disable some parts depending
# whether a normal build is happening or whether the check is running.

# When mach wants to know if we're to use mozmake, it runs:
# make -f topsrcdir/config/baseconfig.mk
# The first word of MAKEFILE_LIST is the main file we're running. Grabbing the
# parent of that directory therefore gets us the topsrcdir of comm-central,
# whence we get the mozilla directory to run the "real" baseconfig.mk logic.
ifndef INCLUDED_AUTOCONF_MK
topsrcdir := $(dir $(firstword $(MAKEFILE_LIST)))..
endif

MOZILLA_SRCDIR = $(topsrcdir)/platform
include $(MOZILLA_SRCDIR)/config/baseconfig.mk

+ 7
- 0
config/config.mk View File

@@ -0,0 +1,7 @@
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# Just use mozilla-central's copy of config.mk now.
include $(MOZILLA_DIR)/config/config.mk

+ 2279
- 0
config/configobj.py
File diff suppressed because it is too large
View File


+ 94
- 0
config/makefiles/autotargets.mk View File

@@ -0,0 +1,94 @@
# -*- makefile -*-
# vim:set ts=8 sw=8 sts=8 noet:
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#

ifndef INCLUDED_AUTOTARGETS_MK #{

# Conditional does not wrap the entire file so multiple
# includes will be able to accumulate dependencies.

###########################################################################
# AUTO_DEPS - A list of deps/targets drived from other macros.
###########################################################################

MKDIR ?= mkdir -p
TOUCH ?= touch

# declare for local use, rules.mk may not have been loaded
space = $(NULL) $(NULL)

# Deps will be considered intermediate when used as a pre-requisite for
# wildcard targets. Inhibit their removal, mkdir -p is a standalone op.
.PRECIOUS: %/.mkdir.done

#########################
##---] FUNCTIONS [---##
#########################

# Squeeze can be overzealous, restore root for abspath
getPathPrefix =$(if $(filter /%,$(1)),/)

# Squeeze '//' from the path, easily created by string functions
_slashSqueeze =$(foreach val,$(getargv),$(call getPathPrefix,$(val))$(subst $(space),/,$(strip $(subst /,$(space),$(val)))))

# Squeeze extraneous directory slashes from the path
# o protect embedded spaces within the path
# o replace //+ sequences with /
slash_strip = \
$(strip \
$(subst <--[**]-->,$(space),\
$(call _slashSqueeze,\
$(subst $(space),<--[**]-->,$(1))\
)))

# Extract directory path from a dependency file.
mkdir_stem =$(foreach val,$(getargv),$(subst /.mkdir.done,$(NULL),$(val)))

## Generate timestamp file for threadsafe directory creation
mkdir_deps =$(foreach dir,$(getargv),$(call slash_strip,$(dir)/.mkdir.done))

#######################
##---] TARGETS [---##
#######################

%/.mkdir.done: # mkdir -p -p => mkdir -p
$(subst $(space)-p,$(null),$(MKDIR)) -p '$(dir $@)'
# Make the timestamp old enough for not being a problem with symbolic links
# targets depending on it. Use Jan 3, 1980 to accomodate any timezone where
# 198001010000 would translate to something older than FAT epoch.
@$(TOUCH) -t 198001030000 '$@'

# A handful of makefiles are attempting "mkdir dot".
# tbpl/valgrind builds are using this target
# https://bugzilla.mozilla.org/show_bug.cgi?id=837754
.mkdir.done:
@echo 'WARNING: $(MKDIR) -dot- requested by $(MAKE) -C $(CURDIR) $(MAKECMDGOALS)'
@$(TOUCH) -t 198001030000 '$@'

INCLUDED_AUTOTARGETS_MK = 1
endif #}


## Accumulate deps and cleanup
ifneq (,$(GENERATED_DIRS))
GENERATED_DIRS := $(strip $(sort $(GENERATED_DIRS)))
tmpauto :=$(call mkdir_deps,GENERATED_DIRS)
GENERATED_DIRS_DEPS +=$(tmpauto)
GARBAGE_DIRS +=$(GENERATED_DIRS)
endif

#################################################################
# One ring/dep to rule them all:
# config/rules.mk::all target is available by default
# Add $(AUTO_DEPS) as an explicit target dependency when needed.
#################################################################

AUTO_DEPS +=$(GENERATED_DIRS_DEPS)
AUTO_DEPS := $(strip $(sort $(AUTO_DEPS)))

# Complain loudly if deps have not loaded so getargv != $(NULL)
$(call requiredfunction,getargv)

+ 117
- 0
config/makefiles/makeutils.mk View File

@@ -0,0 +1,117 @@
# -*- makefile -*-
# vim:set ts=8 sw=8 sts=8 noet:
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

## Identify function argument types
istype =$(if $(value ${1}),list,scalar)
isval =$(if $(filter-out list,$(call istype,${1})),true)
isvar =$(if $(filter-out scalar,$(call istype,${1})),true)

# Access up to 9 arguments passed, option needed to emulate $*
# Inline for function expansion, do not use $(call )
argv =$(strip
argv +=$(if $(1), $(1))$(if $(2), $(2))$(if $(3), $(3))$(if $(4), $(4))
argv +=$(if $(5), $(5))$(if $(6), $(6))$(if $(7), $(7))$(if $(8), $(8))
argv +=$(if $(9), $(9))
argv +=$(if $(10), $(error makeutils.mk::argv can only handle 9 arguments))
argv +=)

###########################################################################
## Access function args as a simple list, inline within user functions.
## Usage: $(info ** $(call banner,$(getargv)))
## $(call banner,scalar)
## $(call banner,list0 list1 list2)
## $(call banner,ref) ; ref=foo bar tans
## getarglist() would be a more accurate name but is longer to type
getargv = $(if $(call isvar,$(1)),$($(1)),$(argv))

###########################################################################
# Strip [n] leading options from an argument list. This will allow passing
# extra args to user functions that will not propogate to sub-$(call )'s
# Usage: $(call subargv,2)
subargv =$(wordlist $(1),$(words $(getargv)),$(getargv))

###########################################################################
# Intent: Display a distinct banner heading in the output stream
# Usage: $(call banner,BUILDING: foo bar tans)
# Debug:
# target-preqs = \
# $(call banner,target-preqs-BEGIN) \
# foo bar tans \
# $(call banner,target-preqs-END) \
# $(NULL)
# target: $(target-preqs)

banner = \
$(info ) \
$(info ***************************************************************************) \
$(info ** $(getargv)) \
$(info ***************************************************************************) \
$(NULL)

#####################################################################
# Intent: Determine if a string or pattern is contained in a list
# Usage: strcmp - $(call if_XinY,clean,$(MAKECMDGOALS))
# : pattern - $(call if_XinY,clean%,$(MAKECMDGOALS))
is_XinY =$(filter $(1),$(call subargv,3,$(getargv)))

#####################################################################
# Provide an alternate var to support testing
ifdef MAKEUTILS_UNIT_TEST
mcg_goals=TEST_MAKECMDGOALS
else
mcg_goals=MAKECMDGOALS
endif

# Intent: Conditionals for detecting common/tier target use
isTargetStem = $(sort \
$(foreach var,$(getargv),\
$(foreach pat,$(var)% %$(var),\
$(call is_XinY,$(pat),${$(mcg_goals)})\
)))
isTargetStemClean = $(call isTargetStem,clean)
isTargetStemExport = $(call isTargetStem,export)
isTargetStemLibs = $(call isTargetStem,libs)
isTargetStemTools = $(call isTargetStem,tools)

##################################################
# Intent: Validation functions / unit test helpers

errorifneq =$(if $(subst $(strip $(1)),$(NULL),$(strip $(2))),$(error expected [$(1)] but found [$(2)]))

# Intent: verify function declaration exists
requiredfunction =$(foreach func,$(1) $(2) $(3) $(4) $(5) $(6) $(7) $(8) $(9),$(if $(value $(func)),$(NULL),$(error required function [$(func)] is unavailable)))



## http://www.gnu.org/software/make/manual/make.html#Call-Function
## Usage: o = $(call map,origin,o map $(MAKE))
map = $(foreach val,$(2),$(call $(1),$(val)))


## Disable checking for clean targets
ifeq (,$(filter %clean clean%,$(MAKECMDGOALS))) #{

# Usage: $(call checkIfEmpty,[error|warning] foo NULL bar)
checkIfEmpty =$(foreach var,$(wordlist 2,100,$(argv)),$(if $(strip $($(var))),$(NOP),$(call $(1),Variable $(var) does not contain a value)))

# Usage: $(call errorIfEmpty,foo NULL bar)
errorIfEmpty =$(call checkIfEmpty,error $(argv))
warnIfEmpty =$(call checkIfEmpty,warning $(argv))

endif #}

###########################################################################
## Common makefile library loader
###########################################################################
topORerr =$(if $(topsrcdir),$(topsrcdir),$(error topsrcdir is not defined))

ifdef USE_AUTOTARGETS_MK # mkdir_deps
include $(topORerr)/config/makefiles/autotargets.mk
endif

## copy(src, dst): recursive copy
copy_dir = (cd $(1)/. && $(TAR) $(TAR_CREATE_FLAGS) - .) | (cd $(2)/. && tar -xf -)

+ 25
- 0
config/printconfigsetting.py View File

@@ -0,0 +1,25 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

import configobj, sys

try:
(file, section, key) = sys.argv[1:]
except ValueError:
print "Usage: printconfigsetting.py <file> <section> <setting>"
sys.exit(1)

c = configobj.ConfigObj(file)

try:
s = c[section]
except KeyError:
print >>sys.stderr, "Section [%s] not found." % section
sys.exit(1)

try:
print s[key]
except KeyError:
print >>sys.stderr, "Key %s not found." % key
sys.exit(1)

+ 9
- 0
config/recurse.mk View File

@@ -0,0 +1,9 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

ifndef INCLUDED_RULES_MK
include $(topsrcdir)/config/rules.mk
endif

include $(MOZILLA_DIR)/config/recurse.mk

+ 13
- 0
config/rules.mk View File

@@ -0,0 +1,13 @@
# vim:set ts=8 sw=8 sts=8 noet:
#
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.
#

ifndef topsrcdir
$(error topsrcdir was not set))
endif

# Use mozilla-central's copy of rules.mk.
include $(topsrcdir)/platform/config/rules.mk

+ 38
- 0
configure.in View File

@@ -0,0 +1,38 @@
dnl -*- Mode: Autoconf; tab-width: 4; indent-tabs-mode: nil; -*-
dnl vi: set tabstop=4 shiftwidth=4 expandtab:
dnl This Source Code Form is subject to the terms of the Mozilla Public
dnl License, v. 2.0. If a copy of the MPL was not distributed with this
dnl file, You can obtain one at http://mozilla.org/MPL/2.0/.
dnl
dnl This is a not-really-autoconf script (which is to say, it's a shell script
dnl that is piped through m4 first) that executes the mozilla-central python
dnl configure, first doing a little bit of processing to handle mozconfig and
dnl the --with-external-source-dir rules.
dnl ========================================================
divert(0)dnl
#!/bin/sh
SRCDIR=$(dirname $0)
TOPSRCDIR="$SRCDIR"
MOZILLA_SRCDIR="${SRCDIR}/platform"
export OLD_CONFIGURE="${MOZILLA_SRCDIR}"/old-configure

# Ensure the comm-* values are used.
export MOZ_SOURCE_CHANGESET=$(hg -R "$TOPSRCDIR" parent --template="{node}" 2>/dev/null)
export MOZ_SOURCE_REPO=$(hg -R "$TOPSRCDIR" showconfig paths.default 2>/dev/null | sed -e "s/^ssh:/https:/")

# If MOZCONFIG isn't set, use the .mozconfig from the current directory. This
# overrides the lookup in mozilla-central's configure, which looks in the wrong
# directory for this file.
if test -z "$MOZCONFIG" -a -f "$SRCDIR"/.mozconfig; then
export MOZCONFIG="$SRCDIR"/.mozconfig
elif test -z "$MOZCONFIG" -a -f "$SRCDIR"/mozconfig; then
export MOZCONFIG="$SRCDIR"/mozconfig
fi

# Execute the mozilla configure script in the current directory, adding the
# parameter we need to run comm-central. Since the configure script is really
# just a wrapper around invoking a python variant, execute the underlying python
# directly. We use a copy of the underlying configure script to get paths
# correct.
set -- "$@" --with-external-source-dir="$TOPSRCDIR"
which python2.7 > /dev/null && exec python2.7 "$TOPSRCDIR/configure.py" "$@" || exec python "$TOPSRCDIR/configure.py" "$@"

+ 32
- 0
configure.py View File

@@ -0,0 +1,32 @@
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

from __future__ import print_function, unicode_literals

import imp
import os
import sys


base_dir = os.path.abspath(os.path.dirname(__file__))
sys.path.append(os.path.join(base_dir, 'platform', 'python', 'mozbuild'))
from mozbuild.configure import ConfigureSandbox

# We can't just import config_status since configure is shadowed by this file!
f, pathname, desc = imp.find_module('configure',
[os.path.join(base_dir, 'platform')])
config_status = imp.load_module('configure', f, pathname, desc).config_status

def main(argv):
config = {}
sandbox = ConfigureSandbox(config, os.environ, argv)
sandbox.run(os.path.join(os.path.dirname(__file__), 'moz.configure'))

if sandbox._help:
return 0

return config_status(config)

if __name__ == '__main__':
sys.exit(main(sys.argv))

+ 7
- 0
moz.build View File

@@ -0,0 +1,7 @@
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

# This file needs to stay here even if empty so that mach will work,
# specifically commands like mach file-info.

+ 7
- 0
moz.configure View File

@@ -0,0 +1,7 @@
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
# vim: set filetype=python:
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

include('platform/moz.configure')

Loading…
Cancel
Save