This commit is contained in:
Michael Bebenita 2015-01-29 14:35:13 -08:00
Родитель 85188342bc a8e4f17f32
Коммит d3d7dc68cf
15 изменённых файлов: 1371 добавлений и 16 удалений

6
.gitignore поставляемый
Просмотреть файл

@ -7,6 +7,12 @@ certs/j2se_test.ks
tests/Testlets.java
output/
build/
.checksum
# These are generated by the python preprocessor. They're generated from
# the *.in files.
config.ts
main.html
config/build.js
# These are generated by the Java pre-processor. They're generated from

Просмотреть файл

@ -1,15 +1,35 @@
.PHONY: all test tests j2me java certs app clean jasmin aot shumway config-build relooper
BASIC_SRCS=$(shell find . -maxdepth 2 -name "*.ts" -not -path "./build/*")
.PHONY: all test tests j2me java certs app clean jasmin aot shumway config-build
BASIC_SRCS=$(shell find . -maxdepth 2 -name "*.ts" -not -path "./build/*") config.ts
JIT_SRCS=$(shell find jit -name "*.ts" -not -path "./build/*")
SHUMWAY_SRCS=$(shell find shumway -name "*.ts")
RELEASE ?= 0
VERSION ?=$(shell date +%s)
PROFILE ?= 0
# Create a checksum file to monitor the changes of the Makefile configuration.
# If the configuration has changed, we update the checksum file to let the files
# which depend on it to regenerate.
CHECKSUM := "$(RELEASE)$(PROFILE)"
OLD_CHECKSUM := "$(shell [ -f .checksum ] && cat .checksum)"
$(shell [ $(CHECKSUM) != $(OLD_CHECKSUM) ] && echo $(CHECKSUM) > .checksum)
toBool = $(if $(findstring 1,$(1)),true,false)
PREPROCESS = python tools/preprocess-1.1.0/lib/preprocess.py -s \
-D RELEASE=$(call toBool,$(RELEASE)) \
-D PROFILE=$(call toBool,$(PROFILE)) \
-D VERSION=$(VERSION)
PREPROCESS_SRCS = $(shell find . -name "*.in" -not -path config/build.js.in)
PREPROCESS_DESTS = $(PREPROCESS_SRCS:.in=)
all: config-build java jasmin tests j2me shumway aot
test: all
tests/runtests.py
$(PREPROCESS_DESTS): $(PREPROCESS_SRCS) .checksum
$(foreach file,$(PREPROCESS_SRCS),$(PREPROCESS) -o $(file:.in=) $(file);)
jasmin:
make -C tools/jasmin-2.4
@ -51,10 +71,10 @@ shumway: build/shumway.js
build/shumway.js: $(SHUMWAY_SRCS)
node tools/tsc.js --sourcemap --target ES5 shumway/references.ts --out build/shumway.js
config-build:
echo "// generated, build-specific configuration" > config/build.js
echo "config.release = ${RELEASE};" >> config/build.js
echo "config.version = \"${VERSION}\";" >> config/build.js
# We should update config/build.js everytime to generate the new VERSION number
# based on current time.
config-build: config/build.js.in
$(PREPROCESS) -o config/build.js config/build.js.in
tests/tests.jar: tests
tests:

18
config.ts.in Normal file
Просмотреть файл

@ -0,0 +1,18 @@
/*
* Copyright 2014 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/** @const */ var release: boolean = @RELEASE@;
/** @const */ var profile: boolean = @PROFILE@;

4
config/build.js.in Normal file
Просмотреть файл

@ -0,0 +1,4 @@
// generated, build-specific configuration
config.release = @RELEASE@;
config.version = "@VERSION@";

Просмотреть файл

@ -11,7 +11,7 @@ classes.jar: $(SRCS) $(JPP_DESTS)
mkdir build build-src
cp -a cldc1.1.1/. vm/. midp/. jsr-075/. build-src/
cp -a custom/. build-src/
javac -cp build-src -source 1.3 -target 1.3 -bootclasspath "" -extdirs "" -d ./build `find ./build-src -name *.java` > /dev/null
javac -cp build-src -g:none -source 1.3 -target 1.3 -bootclasspath "" -extdirs "" -d ./build `find ./build-src -name *.java` > /dev/null
cd build && jar cvf0 ../classes.jar *
jar uvf0 classes.jar $(EXTRA)
rm -rf build build-src

Просмотреть файл

@ -3,6 +3,9 @@ package com.nokia.mid.s40.bg;
import com.sun.midp.main.MIDletSuiteUtils;
class WaitUserInteractionThread extends Thread {
public WaitUserInteractionThread() {
setPriority(Thread.MAX_PRIORITY);
}
public void run() {
BGUtils.waitUserInteraction();
BGUtils.startMIDlet();

Просмотреть файл

@ -14,8 +14,9 @@
<script type="text/javascript" src="config/midlet.js" defer></script>
<script type="text/javascript" src="config/build.js" defer></script>
<script type="text/javascript" src="config/urlparams.js" defer></script>
<!-- Uncomment the line below to enable the profiler. -->
<!--<script type="text/javascript" src="build/shumway.js" defer></script>-->
<!-- #if PROFILE == "true" -->
<script type="text/javascript" src="build/shumway.js" defer></script>
<!-- #endif -->
<!--<script type="text/javascript" src="libs/terminal.js" defer></script>-->
<script type="text/javascript" src="libs/console.js" defer></script>
<script type="text/javascript" src="libs/promise-6.0.0.js" defer></script>

Просмотреть файл

@ -1,4 +1,5 @@
// Basics
///<reference path='config.ts' />
///<reference path='utilities.ts' />
///<reference path='classfile/classfile.ts' />

Просмотреть файл

@ -1,5 +1,6 @@
// Basics
///<reference path='config.ts' />
///<reference path='utilities.ts' />
///<reference path='classfile/classfile.ts' />

Просмотреть файл

@ -1170,16 +1170,31 @@ module J2ME {
return null;
}
function reportError(method, key) {
return function() {
try {
return method.apply(this, arguments);
} catch (e) {
// Filter JAVA exception and only report the native js exception, which
// cannnot be handled properly by the JAVA code.
if (!e.klass) {
stderrWriter.errorLn("Native " + key + " throws: " + e);
}
throw e;
}
};
}
function findNativeMethodImplementation(methodInfo: MethodInfo) {
var implKey = methodInfo.implKey;
// Look in bindings first.
var binding = findNativeMethodBinding(methodInfo);
if (binding) {
return binding;
return release ? binding : reportError(binding, implKey);
}
var implKey = methodInfo.implKey;
if (methodInfo.isNative) {
if (implKey in Native) {
return Native[implKey];
return release ? Native[implKey] : reportError(Native[implKey], implKey);
} else {
// Some Native MethodInfos are constructed but never called;
// that's fine, unless we actually try to call them.
@ -1188,7 +1203,7 @@ module J2ME {
}
}
} else if (implKey in Override) {
return Override[implKey];
return release ? Override[implKey] : reportError(Override[implKey], implKey);
}
return null;
}

Просмотреть файл

@ -12,6 +12,7 @@ mkdir $PACKAGE_DIR/build
# setup the root
cp *.js *.html *.webapp $PACKAGE_DIR/.
cp build/j2me.js $PACKAGE_DIR/build/.
cp build/classes.jar.js $PACKAGE_DIR/build/.
# copy over jars/jads that are used for the webapp
# NB: we could be smart about this and parse the manifest, patches welcome!

Просмотреть файл

@ -0,0 +1,21 @@
Copyright (c) 2002-2005 ActiveState Corp.
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Просмотреть файл

@ -0,0 +1,420 @@
preprocess.py -- a portable multi-language file preprocessor
============================================================
Download the latest preprocess.py packages from here:
(archive) http://preprocess.googlecode.com/files/preprocess-1.1.0.zip
Home : http://trentm.com/projects/preprocess/
License : MIT (see LICENSE.txt)
Platforms : Windows, Linux, Mac OS X, Unix
Current Version : 1.1
Dev Status : Fairly mature, has been used in the Komodo build system for
over 7 years.
Requirements : Python >= 2.3 (http://www.activestate.com/ActivePython/)
What's new?
-----------
Support has been added for preprocessing TeX, Fortran, C#, Java, Shell
script and PHP files. See the [Change Log](#changelog) below for more.
Why preprocess.py?
------------------
There are millions of templating systems out there (most of them
developed for the web). This isn't one of those, though it does share
some basics: a markup syntax for templates that are processed to give
resultant text output. The main difference with `preprocess.py` is
that its syntax is hidden in comments (whatever the syntax for comments
maybe in the target filetype) so that the file can still have valid
syntax. A comparison with the C preprocessor is more apt.
`preprocess.py` is targetted at build systems that deal with many
types of files. Languages for which it works include: C++, Python,
Perl, Tcl, XML, JavaScript, CSS, IDL, TeX, Fortran, PHP, Java, Shell
scripts (Bash, CSH, etc.) and C#. Preprocess is usable both as a
command line app and as a Python module.
Here is how is works: All preprocessor statements are on their own
line. A preprocessor statement is a comment (as appropriate for the
language of the file being preprocessed). This way the preprocessor
statements do not make an unpreprocessed file syntactically incorrect.
For example:
preprocess -D FEATURES=macros,scc myapp.py
will yield this transformation:
... ...
# #if "macros" in FEATURES
def do_work_with_macros(): def do_work_with_macros():
pass pass
# #else
def do_work_without_macros():
pass
# #endif
... ...
or, with a JavaScript file:
... ...
// #if "macros" in FEATURES
function do_work_with_macros() { function do_work_with_macros() {
} }
// #else
function do_work_without_macros() {
}
// #endif
... ...
Despite these contrived examples preprocess has proved useful for
build-time code differentiation in the
[Komodo](http://www.activestate.com/Komodo) build system -- which
includes source code in Python, JavaScript, XML, CSS, Perl, and C/C++.
The #if expression (`"macros" in FEATURES` in the example) is Python
code, so has Python's full comparison richness. A number of
preprocessor statements are implemented:
#define VAR [VALUE]
#undef VAR
#ifdef VAR
#ifndef VAR
#if EXPRESSION
#elif EXPRESSION
#else
#endif
#error ERROR_STRING
#include "FILE"
@varName@
As well, preprocess will do in-line substitution of defined variables.
Although this is currently off by default because substitution will occur
in program strings, which is not ideal. When a future version of
preprocess can lex languages being preprocessed it will NOT substitute
into program strings and substitution will be turned ON by default.
A workaround is made to resolve this issue. Variables embraced by @ will be
substitute by its value in the emitted lines. For example if we define
NAME="Yuan Xulei", the input file:
<p>Hello I'm @NAME@!</p>
will output:
<p>Hello I'm Yuan Xulei!</p>
Please send any feedback to [Trent Mick](mailto:trentm@google's mail
thing.com).
Install Notes
-------------
Download the latest `preprocess` source package, unzip it, and run `python
setup.py install`:
unzip preprocess-1.1.0.zip
cd preprocess-1.1.0
python setup.py install
If your install fails then please visit [the Troubleshooting
FAQ](http://trentm.com/faq.html#troubleshooting-python-package-installation).
This will install `preprocess.py` into your Python `site-packages` and
also into your Python bin directory. If you can now run `preprocess`
and get a response then you are good to go, otherwise read on.
The *problem* is that the Python bin directory is not always on your
PATH on some operating systems -- notably Mac OS X. To finish the
install on OS X either manually move 'preprocess' to somewhere on your
PATH:
cp preprocess.py /usr/local/bin/preprocess
or create a symlink to it (try one of these depending on your Python
version):
ln -s /System/Library/Frameworks/Python.framework/Versions/2.3/bin/preprocess /usr/local/bin/preprocess
ln -s /Library/Frameworks/Python.framework/Versions/2.4/bin/preprocess /usr/local/bin/preprocess
(Note: You'll probably need to prefix those commands with `sudo` and
the exact paths may differ on your system.)
Getting Started
---------------
Once you have it install, run `preprocess --help` for full usage
information:
$ preprocess --help
Preprocess a file.
Command Line Usage:
preprocess [<options>...] <infile>
Options:
-h, --help Print this help and exit.
-V, --version Print the version info and exit.
-v, --verbose Give verbose output for errors.
-o <outfile> Write output to the given file instead of to stdout.
-f, --force Overwrite given output file. (Otherwise an IOError
will be raised if <outfile> already exists.
-D <define> Define a variable for preprocessing. <define>
can simply be a variable name (in which case it
will be true) or it can be of the form
<var>=<val>. An attempt will be made to convert
<val> to an integer so "-D FOO=0" will create a
false value.
-I <dir> Add an directory to the include path for
#include directives.
-k, --keep-lines Emit empty lines for preprocessor statement
lines and skipped output lines. This allows line
numbers to stay constant.
-s, --substitute Substitute defines into emitted lines. By
default substitution is NOT done because it
currently will substitute into program strings.
Module Usage:
from preprocess import preprocess
preprocess(infile, outfile=sys.stdout, defines={}, force=0,
keepLines=0, includePath=[], substitute=0)
The <infile> can be marked up with special preprocessor statement lines
of the form:
<comment-prefix> <preprocessor-statement> <comment-suffix>
where the <comment-prefix/suffix> are the native comment delimiters for
that file type.
Examples
--------
HTML (*.htm, *.html) or XML (*.xml, *.kpf, *.xul) files:
<!-- #if FOO -->
...
<!-- #endif -->
Python (*.py), Perl (*.pl), Tcl (*.tcl), Ruby (*.rb), Bash (*.sh),
or make ([Mm]akefile*) files:
# #if defined('FAV_COLOR') and FAV_COLOR == "blue"
...
# #elif FAV_COLOR == "red"
...
# #else
...
# #endif
C (*.c, *.h), C++ (*.cpp, *.cxx, *.cc, *.h, *.hpp, *.hxx, *.hh),
Java (*.java), PHP (*.php) or C# (*.cs) files:
// #define FAV_COLOR 'blue'
...
/* #ifndef FAV_COLOR */
...
// #endif
Fortran 77 (*.f) or 90/95 (*.f90) files:
C #if COEFF == 'var'
...
C #endif
Preprocessor Syntax
-------------------
- Valid statements:
#define <var> [<value>]
#undef <var>
#ifdef <var>
#ifndef <var>
#if <expr>
#elif <expr>
#else
#endif
#error <error string>
#include "<file>"
where <expr> is any valid Python expression.
- The expression after #if/elif may be a Python statement. It is an
error to refer to a variable that has not been defined by a -D
option or by an in-content #define.
- Special built-in methods for expressions:
defined(varName) Return true if given variable is defined.
Tips
----
A suggested file naming convention is to let input files to
preprocess be of the form <basename>.p.<ext> and direct the output
of preprocess to <basename>.<ext>, e.g.:
preprocess -o foo.py foo.p.py
The advantage is that other tools (esp. editors) will still
recognize the unpreprocessed file as the original language.
And, for module usage, read the preprocess.preprocess() docstring:
pydoc preprocess.preprocess
Change Log
----------
### v1.1.0
- Move to code.google.com/p/preprocess for code hosting.
- Re-org directory structure to assist with deployment to pypi and
better installation with setup.py.
- Pulled the "content.types" file that assists with filetype
determination into "preprocess.py". This makes "preprocess.py" fully
independent and also makes the "setup.py" simpler. The
"-c|--content-types-path" option can be used to specify
addition content types information.
### v1.0.9
- Fix the 'contentType' optional arg for #include's.
- Add cheap XML content sniffing.
### v1.0.8
- Allow for JS and CSS-style comment delims in XML/HTML. Ideally this
would deal with full lexing but that isn't going to happen soon.
### v1.0.7
- Allow explicit specification of content type.
### v1.0.6
- Add ability to include a filename mentioned in a define: '#include
VAR'.
### v1.0.5
- Make sure to use the *longest* define names first when doing
substitutions. This ensure that substitution in this line:
FOO and BAR are FOOBAR
will do the right thing if there are "FOO" and "FOOBAR" defines.
### v1.0.4
- Add WiX XML file extensions.
- Add XSLT file extensions.
### v1.0.3
- TeX support (from Hans Petter Langtangen)
### v1.0.2
- Fix a bug with -k|--keep-lines and preprocessor some directives in
ignored if blocks (undef, define, error, include): those lines were
not kept. (bug noted by Eric Promislow)
### v1.0.1
- Fix documentation error for '#define' statement. The correct syntax
is '#define VAR [VALUE]' while the docs used to say
'#define VAR[=VALUE]'. (from Hans Petter Langtangen)
- Correct '! ...' comment-style for Fortran -- the '!' can be on any
column in Fortran 90. (from Hans Petter Langtangen)
- Return a non-zero exit code on error.
### v1.0.0
- Update the test suite (it had been broken for quite a while) and add
a Fortran test case.
- Improve Fortran support to support any char in the first column to
indicate a comment. (Idea from Hans Petter Langtangen)
- Recognize '.f90' files as Fortran. (from Hans Petter Langtangen)
- Add Java, C#, Shell script and PHP support. (from Hans Petter
Langtangen)
### v0.9.2
- Add Fortran support (from Hans Petter Langtangen)
- Ensure content.types gets written to "bindir" next to preprocess.py
there so it can be picked up (from Hans Petter Langtangen).
### v0.9.1
- Fully read in the input file before processing. This allows
preprocessing of a file onto itself.
### v0.9.0
- Change version attributes and semantics. Before: had a _version_
tuple. After: __version__ is a string, __version_info__ is a tuple.
### v0.8.1
- Mentioned #ifdef and #ifndef in documentation (these have been there
for a while).
- Add preprocess.exe to source package (should fix installation on
Windows).
- Incorporate Komodo changes:
- change 171050: add Ruby support
- change 160914: Only attempt to convert define strings from the
command-line to *int* instead of eval'ing as any Python
expression: which could surprise with strings that work as
floats.
- change 67962: Fix '#include' directives in preprocessed files.
### v0.8.0
- Move hosting to trentm.com. Improve the starter docs a little bit.
### 0.7.0:
- Fix bug 1: Nested #if-blocks will not be properly handled.
- Add 'Text' type for .txt files and default (with a warn) unknown
filetypes to 'Text'. Text files are defined to use to '#...'-style
comments to allow if/else/.../endif directives as in
Perl/Python/Tcl files.
### 0.6.1:
- Fix a bug where preprocessor statements were not ignored when not
emitting. For example the following should _not_ cause an error:
# #if 0
# #error womba womba womba
# #endif
- Fix a bug where multiple uses of preprocess.preprocess() in the
same interpreter would erroneously re-use the same list of
__preprocessedFiles. This could cause false detection of recursive
#include's.
- Fix #include, broken in 0.6.0.
### 0.6.0:
- substitution: Variables can now replaced with their defined value
in preprocessed file content. This is turned OFF by default
because, IMO, substitution should not be done in program strings.
I need to add lexing for all supported languages before I can do
*that* properly. Substitution can be turned on with the
--substitute command-line option or the subst=1 module interface
option.
- Add support for preprocessing HTML files.
### 0.5.0:
- Add #error, #define, #undef, #ifdef and #ifndef statements.
- #include statement, -I command line option and 'includePath'
module interface option to specify an include path
- Add __FILE__ and __LINE__ default defines.
- More strict and more helpful error messages:
- Lines of the form "#else <expr>" and "#endif <expr>" no longer
match.
- error messages for illegal #if-block constructs
- error messages for use of defined(BAR) instead of
defined('BAR') in expressions
- New "keep lines" option to output blank lines for skipped content
lines and preprocessor statement lines (to preserve line numbers
in the processed file).
### 0.4.0:
- Add #elif preprocessor statement.
- Add defined() built-in, e.g. #if defined('FOO')
### 0.3.2:
- Make #if expressions Python code.
- Change "defines" attribute of preprocess.preprocess().
- Add -f|--force option to overwrite given output file.
### 0.2.0:
- Add content types for C/C++.
- Better module documentation.
- You can define *false* vars on the command line now.
- 'python setup.py install' works.
### 0.1.0:
- First release.

Просмотреть файл

@ -0,0 +1,847 @@
#!/usr/bin/env python
# Copyright (c) 2002-2008 ActiveState Software Inc.
# License: MIT License (http://www.opensource.org/licenses/mit-license.php)
"""
Preprocess a file.
Command Line Usage:
preprocess [<options>...] <infile>
Options:
-h, --help Print this help and exit.
-V, --version Print the version info and exit.
-v, --verbose Give verbose output for errors.
-o <outfile> Write output to the given file instead of to stdout.
-f, --force Overwrite given output file. (Otherwise an IOError
will be raised if <outfile> already exists.
-D <define> Define a variable for preprocessing. <define>
can simply be a variable name (in which case it
will be true) or it can be of the form
<var>=<val>. An attempt will be made to convert
<val> to an integer so "-D FOO=0" will create a
false value.
-I <dir> Add an directory to the include path for
#include directives.
-k, --keep-lines Emit empty lines for preprocessor statement
lines and skipped output lines. This allows line
numbers to stay constant.
-s, --substitute Substitute defines into emitted lines. By
default substitution is NOT done because it
currently will substitute into program strings.
-c, --content-types-path <path>
Specify a path to a content.types file to assist
with filetype determination. See the
`_gDefaultContentTypes` string in this file for
details on its format.
Module Usage:
from preprocess import preprocess
preprocess(infile, outfile=sys.stdout, defines={}, force=0,
keepLines=0, includePath=[], substitute=0,
contentType=None)
The <infile> can be marked up with special preprocessor statement lines
of the form:
<comment-prefix> <preprocessor-statement> <comment-suffix>
where the <comment-prefix/suffix> are the native comment delimiters for
that file type.
Examples
--------
HTML (*.htm, *.html) or XML (*.xml, *.kpf, *.xul) files:
<!-- #if FOO -->
...
<!-- #endif -->
Python (*.py), Perl (*.pl), Tcl (*.tcl), Ruby (*.rb), Bash (*.sh),
or make ([Mm]akefile*) files:
# #if defined('FAV_COLOR') and FAV_COLOR == "blue"
...
# #elif FAV_COLOR == "red"
...
# #else
...
# #endif
C (*.c, *.h), C++ (*.cpp, *.cxx, *.cc, *.h, *.hpp, *.hxx, *.hh),
Java (*.java), PHP (*.php) or C# (*.cs) files:
// #define FAV_COLOR 'blue'
...
/* #ifndef FAV_COLOR */
...
// #endif
Fortran 77 (*.f) or 90/95 (*.f90) files:
C #if COEFF == 'var'
...
C #endif
And other languages.
Variables embraced by @ will be substitute by its value in the emitted
lines. For example if we define NAME="Yuan Xulei", the input file:
<p>Hello I'm @NAME@!</p>
will output:
<p>Hello I'm Yuan Xulei!</p>
Preprocessor Syntax
-------------------
- Valid statements:
#define <var> [<value>]
#undef <var>
#ifdef <var>
#ifndef <var>
#if <expr>
#elif <expr>
#else
#endif
#error <error string>
#include "<file>"
#include <var>
@varName@
where <expr> is any valid Python expression.
- The expression after #if/elif may be a Python statement. It is an
error to refer to a variable that has not been defined by a -D
option or by an in-content #define.
- Special built-in methods for expressions:
defined(varName) Return true if given variable is defined.
Tips
----
A suggested file naming convention is to let input files to
preprocess be of the form <basename>.p.<ext> and direct the output
of preprocess to <basename>.<ext>, e.g.:
preprocess -o foo.py foo.p.py
The advantage is that other tools (esp. editors) will still
recognize the unpreprocessed file as the original language.
"""
__version_info__ = (1, 1, 0)
__version__ = '.'.join(map(str, __version_info__))
import os
import sys
import getopt
import types
import re
import pprint
#---- exceptions
class PreprocessError(Exception):
def __init__(self, errmsg, file=None, lineno=None, line=None):
self.errmsg = str(errmsg)
self.file = file
self.lineno = lineno
self.line = line
Exception.__init__(self, errmsg, file, lineno, line)
def __str__(self):
s = ""
if self.file is not None:
s += self.file + ":"
if self.lineno is not None:
s += str(self.lineno) + ":"
if self.file is not None or self.lineno is not None:
s += " "
s += self.errmsg
#if self.line is not None:
# s += ": " + self.line
return s
#---- global data
# Comment delimiter info.
# A mapping of content type to a list of 2-tuples defining the line
# prefix and suffix for a comment. Each prefix or suffix can either
# be a string (in which case it is transformed into a pattern allowing
# whitespace on either side) or a compiled regex.
_commentGroups = {
"Python": [ ('#', '') ],
"Perl": [ ('#', '') ],
"PHP": [ ('/*', '*/'), ('//', ''), ('#', '') ],
"Ruby": [ ('#', '') ],
"Tcl": [ ('#', '') ],
"Shell": [ ('#', '') ],
# Allowing for CSS and JavaScript comments in XML/HTML.
"XML": [ ('<!--', '-->'), ('/*', '*/'), ('//', '') ],
"HTML": [ ('<!--', '-->'), ('/*', '*/'), ('//', '') ],
"Makefile": [ ('#', '') ],
"JavaScript": [ ('/*', '*/'), ('//', '') ],
"CSS": [ ('/*', '*/') ],
"C": [ ('/*', '*/') ],
"C++": [ ('/*', '*/'), ('//', '') ],
"Java": [ ('/*', '*/'), ('//', '') ],
"C#": [ ('/*', '*/'), ('//', '') ],
"IDL": [ ('/*', '*/'), ('//', '') ],
"Text": [ ('#', '') ],
"Fortran": [ (re.compile(r'^[a-zA-Z*$]\s*'), ''), ('!', '') ],
"TeX": [ ('%', '') ],
}
#---- internal logging facility
class _Logger:
DEBUG, INFO, WARN, ERROR, CRITICAL = range(5)
def __init__(self, name, level=None, streamOrFileName=sys.stderr):
self._name = name
if level is None:
self.level = self.WARN
else:
self.level = level
if type(streamOrFileName) == types.StringType:
self.stream = open(streamOrFileName, 'w')
self._opennedStream = 1
else:
self.stream = streamOrFileName
self._opennedStream = 0
def __del__(self):
if self._opennedStream:
self.stream.close()
def getLevel(self):
return self.level
def setLevel(self, level):
self.level = level
def _getLevelName(self, level):
levelNameMap = {
self.DEBUG: "DEBUG",
self.INFO: "INFO",
self.WARN: "WARN",
self.ERROR: "ERROR",
self.CRITICAL: "CRITICAL",
}
return levelNameMap[level]
def isEnabled(self, level):
return level >= self.level
def isDebugEnabled(self): return self.isEnabled(self.DEBUG)
def isInfoEnabled(self): return self.isEnabled(self.INFO)
def isWarnEnabled(self): return self.isEnabled(self.WARN)
def isErrorEnabled(self): return self.isEnabled(self.ERROR)
def isFatalEnabled(self): return self.isEnabled(self.FATAL)
def log(self, level, msg, *args):
if level < self.level:
return
message = "%s: %s: " % (self._name, self._getLevelName(level).lower())
message = message + (msg % args) + "\n"
self.stream.write(message)
self.stream.flush()
def debug(self, msg, *args):
self.log(self.DEBUG, msg, *args)
def info(self, msg, *args):
self.log(self.INFO, msg, *args)
def warn(self, msg, *args):
self.log(self.WARN, msg, *args)
def error(self, msg, *args):
self.log(self.ERROR, msg, *args)
def fatal(self, msg, *args):
self.log(self.CRITICAL, msg, *args)
log = _Logger("preprocess", _Logger.WARN)
#---- internal support stuff
def _evaluate(expr, defines):
"""Evaluate the given expression string with the given context.
WARNING: This runs eval() on a user string. This is unsafe.
"""
#interpolated = _interpolate(s, defines)
try:
rv = eval(expr, {'defined':lambda v: v in defines}, defines)
except Exception, ex:
msg = str(ex)
if msg.startswith("name '") and msg.endswith("' is not defined"):
# A common error (at least this is presumed:) is to have
# defined(FOO) instead of defined('FOO')
# We should give a little as to what might be wrong.
# msg == "name 'FOO' is not defined" --> varName == "FOO"
varName = msg[len("name '"):-len("' is not defined")]
if expr.find("defined(%s)" % varName) != -1:
# "defined(FOO)" in expr instead of "defined('FOO')"
msg += " (perhaps you want \"defined('%s')\" instead of "\
"\"defined(%s)\")" % (varName, varName)
elif msg.startswith("invalid syntax"):
msg = "invalid syntax: '%s'" % expr
raise PreprocessError(msg, defines['__FILE__'], defines['__LINE__'])
log.debug("evaluate %r -> %s (defines=%r)", expr, rv, defines)
return rv
#---- module API
def preprocess(infile, outfile=sys.stdout, defines={},
force=0, keepLines=0, includePath=[], substitute=0,
contentType=None, contentTypesRegistry=None,
__preprocessedFiles=None):
"""Preprocess the given file.
"infile" is the input path.
"outfile" is the output path or stream (default is sys.stdout).
"defines" is a dictionary of defined variables that will be
understood in preprocessor statements. Keys must be strings and,
currently, only the truth value of any key's value matters.
"force" will overwrite the given outfile if it already exists. Otherwise
an IOError will be raise if the outfile already exists.
"keepLines" will cause blank lines to be emitted for preprocessor lines
and content lines that would otherwise be skipped.
"includePath" is a list of directories to search for given #include
directives. The directory of the file being processed is presumed.
"substitute", if true, will allow substitution of defines into emitted
lines. (NOTE: This substitution will happen within program strings
as well. This may not be what you expect.)
"contentType" can be used to specify the content type of the input
file. It not given, it will be guessed.
"contentTypesRegistry" is an instance of ContentTypesRegistry. If not specified
a default registry will be created.
"__preprocessedFiles" (for internal use only) is used to ensure files
are not recusively preprocessed.
Returns the modified dictionary of defines or raises PreprocessError if
there was some problem.
"""
if __preprocessedFiles is None:
__preprocessedFiles = []
log.info("preprocess(infile=%r, outfile=%r, defines=%r, force=%r, "\
"keepLines=%r, includePath=%r, contentType=%r, "\
"__preprocessedFiles=%r)", infile, outfile, defines, force,
keepLines, includePath, contentType, __preprocessedFiles)
absInfile = os.path.normpath(os.path.abspath(infile))
if absInfile in __preprocessedFiles:
raise PreprocessError("detected recursive #include of '%s'"\
% infile)
__preprocessedFiles.append(os.path.abspath(infile))
# Determine the content type and comment info for the input file.
if contentType is None:
registry = contentTypesRegistry or getDefaultContentTypesRegistry()
contentType = registry.getContentType(infile)
if contentType is None:
contentType = "Text"
log.warn("defaulting content type for '%s' to '%s'",
infile, contentType)
try:
cgs = _commentGroups[contentType]
except KeyError:
raise PreprocessError("don't know comment delimiters for content "\
"type '%s' (file '%s')"\
% (contentType, infile))
# Generate statement parsing regexes. Basic format:
# <comment-prefix> <preprocessor-stmt> <comment-suffix>
# Examples:
# <!-- #if foo -->
# ...
# <!-- #endif -->
#
# # #if BAR
# ...
# # #else
# ...
# # #endif
stmts = ['#\s*(?P<op>if|elif|ifdef|ifndef)\s+(?P<expr>.*?)',
'#\s*(?P<op>else|endif)',
'#\s*(?P<op>error)\s+(?P<error>.*?)',
'#\s*(?P<op>define)\s+(?P<var>[^\s]*?)(\s+(?P<val>.+?))?',
'#\s*(?P<op>undef)\s+(?P<var>[^\s]*?)',
'#\s*(?P<op>include)\s+"(?P<fname>.*?)"',
r'#\s*(?P<op>include)\s+(?P<var>[^\s]+?)',
]
patterns = []
for stmt in stmts:
# The comment group prefix and suffix can either be just a
# string or a compiled regex.
for cprefix, csuffix in cgs:
if hasattr(cprefix, "pattern"):
pattern = cprefix.pattern
else:
pattern = r"^\s*%s\s*" % re.escape(cprefix)
pattern += stmt
if hasattr(csuffix, "pattern"):
pattern += csuffix.pattern
else:
pattern += r"\s*%s\s*$" % re.escape(csuffix)
patterns.append(pattern)
stmtRes = [re.compile(p) for p in patterns]
# Process the input file.
# (Would be helpful if I knew anything about lexing and parsing
# simple grammars.)
fin = open(infile, 'r')
lines = fin.readlines()
fin.close()
if type(outfile) in types.StringTypes:
if force and os.path.exists(outfile):
os.chmod(outfile, 0777)
os.remove(outfile)
fout = open(outfile, 'w')
else:
fout = outfile
defines['__FILE__'] = infile
SKIP, EMIT = range(2) # states
states = [(EMIT, # a state is (<emit-or-skip-lines-in-this-section>,
0, # <have-emitted-in-this-if-block>,
0)] # <have-seen-'else'-in-this-if-block>)
lineNum = 0
for line in lines:
lineNum += 1
log.debug("line %d: %r", lineNum, line)
defines['__LINE__'] = lineNum
# Is this line a preprocessor stmt line?
#XXX Could probably speed this up by optimizing common case of
# line NOT being a preprocessor stmt line.
for stmtRe in stmtRes:
match = stmtRe.match(line)
if match:
break
else:
match = None
if match:
op = match.group("op")
log.debug("%r stmt (states: %r)", op, states)
if op == "define":
if not (states and states[-1][0] == SKIP):
var, val = match.group("var", "val")
if val is None:
val = None
else:
try:
val = eval(val, {}, {})
except:
pass
defines[var] = val
elif op == "undef":
if not (states and states[-1][0] == SKIP):
var = match.group("var")
try:
del defines[var]
except KeyError:
pass
elif op == "include":
if not (states and states[-1][0] == SKIP):
if "var" in match.groupdict():
# This is the second include form: #include VAR
var = match.group("var")
f = defines[var]
else:
# This is the first include form: #include "path"
f = match.group("fname")
for d in [os.path.dirname(infile)] + includePath:
fname = os.path.normpath(os.path.join(d, f))
if os.path.exists(fname):
break
else:
raise PreprocessError("could not find #include'd file "\
"\"%s\" on include path: %r"\
% (f, includePath))
defines = preprocess(fname, fout, defines, force,
keepLines, includePath, substitute,
contentTypesRegistry=contentTypesRegistry,
__preprocessedFiles=__preprocessedFiles)
elif op in ("if", "ifdef", "ifndef"):
if op == "if":
expr = match.group("expr")
elif op == "ifdef":
expr = "defined('%s')" % match.group("expr")
elif op == "ifndef":
expr = "not defined('%s')" % match.group("expr")
try:
if states and states[-1][0] == SKIP:
# Were are nested in a SKIP-portion of an if-block.
states.append((SKIP, 0, 0))
elif _evaluate(expr, defines):
states.append((EMIT, 1, 0))
else:
states.append((SKIP, 0, 0))
except KeyError:
raise PreprocessError("use of undefined variable in "\
"#%s stmt" % op, defines['__FILE__'],
defines['__LINE__'], line)
elif op == "elif":
expr = match.group("expr")
try:
if states[-1][2]: # already had #else in this if-block
raise PreprocessError("illegal #elif after #else in "\
"same #if block", defines['__FILE__'],
defines['__LINE__'], line)
elif states[-1][1]: # if have emitted in this if-block
states[-1] = (SKIP, 1, 0)
elif states[:-1] and states[-2][0] == SKIP:
# Were are nested in a SKIP-portion of an if-block.
states[-1] = (SKIP, 0, 0)
elif _evaluate(expr, defines):
states[-1] = (EMIT, 1, 0)
else:
states[-1] = (SKIP, 0, 0)
except IndexError:
raise PreprocessError("#elif stmt without leading #if "\
"stmt", defines['__FILE__'],
defines['__LINE__'], line)
elif op == "else":
try:
if states[-1][2]: # already had #else in this if-block
raise PreprocessError("illegal #else after #else in "\
"same #if block", defines['__FILE__'],
defines['__LINE__'], line)
elif states[-1][1]: # if have emitted in this if-block
states[-1] = (SKIP, 1, 1)
elif states[:-1] and states[-2][0] == SKIP:
# Were are nested in a SKIP-portion of an if-block.
states[-1] = (SKIP, 0, 1)
else:
states[-1] = (EMIT, 1, 1)
except IndexError:
raise PreprocessError("#else stmt without leading #if "\
"stmt", defines['__FILE__'],
defines['__LINE__'], line)
elif op == "endif":
try:
states.pop()
except IndexError:
raise PreprocessError("#endif stmt without leading #if"\
"stmt", defines['__FILE__'],
defines['__LINE__'], line)
elif op == "error":
if not (states and states[-1][0] == SKIP):
error = match.group("error")
raise PreprocessError("#error: "+error, defines['__FILE__'],
defines['__LINE__'], line)
log.debug("states: %r", states)
if keepLines:
fout.write("\n")
else:
try:
if states[-1][0] == EMIT:
log.debug("emit line (%s)" % states[-1][1])
# Substitute all defines into line.
# XXX Should avoid recursive substitutions. But that
# would be a pain right now.
sline = line
if substitute:
for name in reversed(sorted(defines, key=len)):
value = defines[name]
sline = sline.replace("@" + name + "@", str(value))
fout.write(sline)
elif keepLines:
log.debug("keep blank line (%s)" % states[-1][1])
fout.write("\n")
else:
log.debug("skip line (%s)" % states[-1][1])
except IndexError:
raise PreprocessError("superfluous #endif before this line",
defines['__FILE__'],
defines['__LINE__'])
if len(states) > 1:
raise PreprocessError("unterminated #if block", defines['__FILE__'],
defines['__LINE__'])
elif len(states) < 1:
raise PreprocessError("superfluous #endif on or before this line",
defines['__FILE__'], defines['__LINE__'])
if fout != outfile:
fout.close()
return defines
#---- content-type handling
_gDefaultContentTypes = """
# Default file types understood by "preprocess.py".
#
# Format is an extension of 'mime.types' file syntax.
# - '#' indicates a comment to the end of the line.
# - a line is:
# <filetype> [<pattern>...]
# where,
# <filetype>'s are equivalent in spirit to the names used in the Windows
# registry in HKCR, but some of those names suck or are inconsistent;
# and
# <pattern> is a suffix (pattern starts with a '.'), a regular expression
# (pattern is enclosed in '/' characters), a full filename (anything
# else).
#
# Notes on case-sensitivity:
#
# A suffix pattern is case-insensitive on Windows and case-sensitive
# elsewhere. A filename pattern is case-sensitive everywhere. A regex
# pattern's case-sensitivity is defined by the regex. This means it is by
# default case-sensitive, but this can be changed using Python's inline
# regex option syntax. E.g.:
# Makefile /^(?i)makefile.*$/ # case-INsensitive regex
Python .py
Python .pyw
Perl .pl
Ruby .rb
Tcl .tcl
XML .xml
XML .kpf
XML .xul
XML .rdf
XML .xslt
XML .xsl
XML .wxs
XML .wxi
HTML .htm
HTML .html
XML .xhtml
Makefile /^[Mm]akefile.*$/
PHP .php
JavaScript .js
JavaScript .ts
CSS .css
C++ .c # C++ because then we can use //-style comments
C++ .cpp
C++ .cxx
C++ .cc
C++ .h
C++ .hpp
C++ .hxx
C++ .hh
IDL .idl
Text .txt
Fortran .f
Fortran .f90
Shell .sh
Shell .csh
Shell .ksh
Shell .zsh
Java .java
C# .cs
TeX .tex
# Some Komodo-specific file extensions
Python .ksf # Fonts & Colors scheme files
Text .kkf # Keybinding schemes files
"""
class ContentTypesRegistry:
"""A class that handles determining the filetype of a given path.
Usage:
>>> registry = ContentTypesRegistry()
>>> registry.getContentType("foo.py")
"Python"
"""
def __init__(self, contentTypesPaths=None):
self.contentTypesPaths = contentTypesPaths
self._load()
def _load(self):
from os.path import dirname, join, exists
self.suffixMap = {}
self.regexMap = {}
self.filenameMap = {}
self._loadContentType(_gDefaultContentTypes)
localContentTypesPath = join(dirname(__file__), "content.types")
if exists(localContentTypesPath):
log.debug("load content types file: `%r'" % localContentTypesPath)
self._loadContentType(open(localContentTypesPath, 'r').read())
for path in (self.contentTypesPaths or []):
log.debug("load content types file: `%r'" % path)
self._loadContentType(open(path, 'r').read())
def _loadContentType(self, content, path=None):
"""Return the registry for the given content.types file.
The registry is three mappings:
<suffix> -> <content type>
<regex> -> <content type>
<filename> -> <content type>
"""
for line in content.splitlines(0):
words = line.strip().split()
for i in range(len(words)):
if words[i][0] == '#':
del words[i:]
break
if not words: continue
contentType, patterns = words[0], words[1:]
if not patterns:
if line[-1] == '\n': line = line[:-1]
raise PreprocessError("bogus content.types line, there must "\
"be one or more patterns: '%s'" % line)
for pattern in patterns:
if pattern.startswith('.'):
if sys.platform.startswith("win"):
# Suffix patterns are case-insensitive on Windows.
pattern = pattern.lower()
self.suffixMap[pattern] = contentType
elif pattern.startswith('/') and pattern.endswith('/'):
self.regexMap[re.compile(pattern[1:-1])] = contentType
else:
self.filenameMap[pattern] = contentType
def getContentType(self, path):
"""Return a content type for the given path.
@param path {str} The path of file for which to guess the
content type.
@returns {str|None} Returns None if could not determine the
content type.
"""
basename = os.path.basename(path).rstrip(".in")
contentType = None
# Try to determine from the path.
if not contentType and self.filenameMap.has_key(basename):
contentType = self.filenameMap[basename]
log.debug("Content type of '%s' is '%s' (determined from full "\
"path).", path, contentType)
# Try to determine from the suffix.
if not contentType and '.' in basename:
suffix = "." + basename.split(".")[-1]
if sys.platform.startswith("win"):
# Suffix patterns are case-insensitive on Windows.
suffix = suffix.lower()
if self.suffixMap.has_key(suffix):
contentType = self.suffixMap[suffix]
log.debug("Content type of '%s' is '%s' (determined from "\
"suffix '%s').", path, contentType, suffix)
# Try to determine from the registered set of regex patterns.
if not contentType:
for regex, ctype in self.regexMap.items():
if regex.search(basename):
contentType = ctype
log.debug("Content type of '%s' is '%s' (matches regex '%s')",
path, contentType, regex.pattern)
break
# Try to determine from the file contents.
content = open(path, 'rb').read()
if content.startswith("<?xml"): # cheap XML sniffing
contentType = "XML"
return contentType
_gDefaultContentTypesRegistry = None
def getDefaultContentTypesRegistry():
global _gDefaultContentTypesRegistry
if _gDefaultContentTypesRegistry is None:
_gDefaultContentTypesRegistry = ContentTypesRegistry()
return _gDefaultContentTypesRegistry
#---- internal support stuff
#TODO: move other internal stuff down to this section
try:
reversed
except NameError:
# 'reversed' added in Python 2.4 (http://www.python.org/doc/2.4/whatsnew/node7.html)
def reversed(seq):
rseq = list(seq)
rseq.reverse()
for item in rseq:
yield item
try:
sorted
except NameError:
# 'sorted' added in Python 2.4. Note that I'm only implementing enough
# of sorted as is used in this module.
def sorted(seq, key=None):
identity = lambda x: x
key_func = (key or identity)
sseq = list(seq)
sseq.sort(lambda self, other: cmp(key_func(self), key_func(other)))
for item in sseq:
yield item
#---- mainline
def main(argv):
try:
optlist, args = getopt.getopt(argv[1:], 'hVvo:D:fkI:sc:',
['help', 'version', 'verbose', 'force', 'keep-lines',
'substitute', 'content-types-path='])
except getopt.GetoptError, msg:
sys.stderr.write("preprocess: error: %s. Your invocation was: %s\n"\
% (msg, argv))
sys.stderr.write("See 'preprocess --help'.\n")
return 1
outfile = sys.stdout
defines = {}
force = 0
keepLines = 0
substitute = 0
includePath = []
contentTypesPaths = []
for opt, optarg in optlist:
if opt in ('-h', '--help'):
sys.stdout.write(__doc__)
return 0
elif opt in ('-V', '--version'):
sys.stdout.write("preprocess %s\n" % __version__)
return 0
elif opt in ('-v', '--verbose'):
log.setLevel(log.DEBUG)
elif opt == '-o':
outfile = optarg
if opt in ('-f', '--force'):
force = 1
elif opt == '-D':
if optarg.find('=') != -1:
var, val = optarg.split('=', 1)
try:
val = int(val)
except ValueError:
pass
else:
var, val = optarg, None
defines[var] = val
elif opt in ('-k', '--keep-lines'):
keepLines = 1
elif opt == '-I':
includePath.append(optarg)
elif opt in ('-s', '--substitute'):
substitute = 1
elif opt in ('-c', '--content-types-path'):
contentTypesPaths.append(optarg)
if len(args) != 1:
sys.stderr.write("preprocess: error: incorrect number of "\
"arguments: argv=%r\n" % argv)
return 1
else:
infile = args[0]
try:
contentTypesRegistry = ContentTypesRegistry(contentTypesPaths)
preprocess(infile, outfile, defines, force, keepLines, includePath,
substitute, contentTypesRegistry=contentTypesRegistry)
except PreprocessError, ex:
if log.isDebugEnabled():
import traceback
traceback.print_exc(file=sys.stderr)
else:
sys.stderr.write("preprocess: error: %s\n" % str(ex))
return 1
if __name__ == "__main__":
__file__ = sys.argv[0]
sys.exit( main(sys.argv) )

Просмотреть файл

@ -20,9 +20,6 @@ var inBrowser = typeof console !== "undefined" && console.info;
declare var putstr;
declare var printErr;
/** @const */ var release: boolean = true;
/** @const */ var profile: boolean = true;
declare var dateNow: () => number;
declare var dump: (message: string) => void;