diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /build | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip |
Add m-esr52 at 52.6.0
Diffstat (limited to 'build')
495 files changed, 54866 insertions, 0 deletions
diff --git a/build/.gdbinit_python.in b/build/.gdbinit_python.in new file mode 100644 index 000000000..54c5c2db8 --- /dev/null +++ b/build/.gdbinit_python.in @@ -0,0 +1,6 @@ +#filter substitution +python +import sys +sys.path.append('@topsrcdir@/python/gdbpp') +import gdbpp +end diff --git a/build/.lldbinit.in b/build/.lldbinit.in new file mode 100644 index 000000000..425ca0f09 --- /dev/null +++ b/build/.lldbinit.in @@ -0,0 +1,2 @@ +#filter substitution +script topsrcdir = "@topsrcdir@"; lldb.debugger.HandleCommand("command source -s true '%s'" % os.path.join(topsrcdir, ".lldbinit")) diff --git a/build/Makefile.in b/build/Makefile.in new file mode 100644 index 000000000..2f284c253 --- /dev/null +++ b/build/Makefile.in @@ -0,0 +1,20 @@ +# +# 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/. + +ifdef MOZTTDIR +# Install the Firefox OS fonts. +include $(MOZTTDIR)/fonts.mk +MOZTT_DEST = $(FINAL_TARGET)/fonts +MOZTT_FILES = $(patsubst external/moztt/%,$(MOZTTDIR)/%,$(filter external/moztt/%,$(subst :, ,$(PRODUCT_COPY_FILES)))) +INSTALL_TARGETS += MOZTT +endif + +include $(topsrcdir)/config/rules.mk + +TARGET_DEPTH = .. +include $(srcdir)/automation-build.mk + +libs:: automation.py + diff --git a/build/__init__.py b/build/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/__init__.py diff --git a/build/annotationProcessors/AnnotationInfo.java b/build/annotationProcessors/AnnotationInfo.java new file mode 100644 index 000000000..a8dbc53ce --- /dev/null +++ b/build/annotationProcessors/AnnotationInfo.java @@ -0,0 +1,54 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors; + +/** + * Object holding annotation data. Used by GeneratableElementIterator. + */ +public class AnnotationInfo { + public enum ExceptionMode { + ABORT, + NSRESULT, + IGNORE; + + String nativeValue() { + return "mozilla::jni::ExceptionMode::" + name(); + } + } + + public enum CallingThread { + GECKO, + UI, + ANY; + + String nativeValue() { + return "mozilla::jni::CallingThread::" + name(); + } + } + + public enum DispatchTarget { + GECKO, + PROXY, + CURRENT; + + String nativeValue() { + return "mozilla::jni::DispatchTarget::" + name(); + } + } + + public final String wrapperName; + public final ExceptionMode exceptionMode; + public final CallingThread callingThread; + public final DispatchTarget dispatchTarget; + + public AnnotationInfo(String wrapperName, ExceptionMode exceptionMode, + CallingThread callingThread, DispatchTarget dispatchTarget) { + + this.wrapperName = wrapperName; + this.exceptionMode = exceptionMode; + this.callingThread = callingThread; + this.dispatchTarget = dispatchTarget; + } +} diff --git a/build/annotationProcessors/AnnotationProcessor.java b/build/annotationProcessors/AnnotationProcessor.java new file mode 100644 index 000000000..4f53317cd --- /dev/null +++ b/build/annotationProcessors/AnnotationProcessor.java @@ -0,0 +1,175 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors; + +import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; +import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions; +import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader; +import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator; + +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.Iterator; + +public class AnnotationProcessor { + public static final String GENERATED_COMMENT = + "// GENERATED CODE\n" + + "// Generated by the Java program at /build/annotationProcessors at compile time\n" + + "// from annotations on Java methods. To update, change the annotations on the\n" + + "// corresponding Java methods and rerun the build. Manually updating this file\n" + + "// will cause your build to fail.\n" + + "\n"; + + private static final StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT); + private static final StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT); + private static final StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT); + + public static void main(String[] args) { + // We expect a list of jars on the commandline. If missing, whinge about it. + if (args.length <= 2) { + System.err.println("Usage: java AnnotationProcessor outprefix jarfiles ..."); + System.exit(1); + } + + final String OUTPUT_PREFIX = args[0]; + final String SOURCE_FILE = OUTPUT_PREFIX + "JNIWrappers.cpp"; + final String HEADER_FILE = OUTPUT_PREFIX + "JNIWrappers.h"; + final String NATIVES_FILE = OUTPUT_PREFIX + "JNINatives.h"; + + System.out.println("Processing annotations..."); + + // We want to produce the same output as last time as often as possible. Ordering of + // generated statements, therefore, needs to be consistent. + final String[] jars = Arrays.copyOfRange(args, 1, args.length); + Arrays.sort(jars); + + // Start the clock! + long s = System.currentTimeMillis(); + + // Get an iterator over the classes in the jar files given... + Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(jars); + + headerFile.append( + "#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" + + "#define " + getHeaderGuardName(HEADER_FILE) + "\n" + + "\n" + + "#include \"mozilla/jni/Refs.h\"\n" + + "\n" + + "namespace mozilla {\n" + + "namespace java {\n" + + "\n"); + + implementationFile.append( + "#include \"" + HEADER_FILE + "\"\n" + + "#include \"mozilla/jni/Accessors.h\"\n" + + "\n" + + "namespace mozilla {\n" + + "namespace java {\n" + + "\n"); + + nativesFile.append( + "#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" + + "#define " + getHeaderGuardName(NATIVES_FILE) + "\n" + + "\n" + + "#include \"" + HEADER_FILE + "\"\n" + + "#include \"mozilla/jni/Natives.h\"\n" + + "\n" + + "namespace mozilla {\n" + + "namespace java {\n" + + "\n"); + + while (jarClassIterator.hasNext()) { + generateClass(jarClassIterator.next()); + } + + implementationFile.append( + "} /* java */\n" + + "} /* mozilla */\n"); + + headerFile.append( + "} /* java */\n" + + "} /* mozilla */\n" + + "#endif // " + getHeaderGuardName(HEADER_FILE) + "\n"); + + nativesFile.append( + "} /* java */\n" + + "} /* mozilla */\n" + + "#endif // " + getHeaderGuardName(NATIVES_FILE) + "\n"); + + writeOutputFile(SOURCE_FILE, implementationFile); + writeOutputFile(HEADER_FILE, headerFile); + writeOutputFile(NATIVES_FILE, nativesFile); + + long e = System.currentTimeMillis(); + System.out.println("Annotation processing complete in " + (e - s) + "ms"); + } + + private static void generateClass(final ClassWithOptions annotatedClass) { + // Get an iterator over the appropriately generated methods of this class + final GeneratableElementIterator methodIterator + = new GeneratableElementIterator(annotatedClass); + final ClassWithOptions[] innerClasses = methodIterator.getInnerClasses(); + + if (!methodIterator.hasNext() && innerClasses.length == 0) { + return; + } + + final CodeGenerator generatorInstance = new CodeGenerator(annotatedClass); + generatorInstance.generateClasses(innerClasses); + + // Iterate all annotated members in this class.. + while (methodIterator.hasNext()) { + AnnotatableEntity aElementTuple = methodIterator.next(); + switch (aElementTuple.mEntityType) { + case METHOD: + generatorInstance.generateMethod(aElementTuple); + break; + case NATIVE: + generatorInstance.generateNative(aElementTuple); + break; + case FIELD: + generatorInstance.generateField(aElementTuple); + break; + case CONSTRUCTOR: + generatorInstance.generateConstructor(aElementTuple); + break; + } + } + + headerFile.append(generatorInstance.getHeaderFileContents()); + implementationFile.append(generatorInstance.getWrapperFileContents()); + nativesFile.append(generatorInstance.getNativesFileContents()); + + for (ClassWithOptions innerClass : innerClasses) { + generateClass(innerClass); + } + } + + private static String getHeaderGuardName(final String name) { + return name.replaceAll("\\W", "_"); + } + + private static void writeOutputFile(final String name, + final StringBuilder content) { + FileOutputStream outStream = null; + try { + outStream = new FileOutputStream(name); + outStream.write(content.toString().getBytes()); + } catch (IOException e) { + System.err.println("Unable to write " + name + ". Perhaps a permissions issue?"); + e.printStackTrace(System.err); + } finally { + if (outStream != null) { + try { + outStream.close(); + } catch (IOException e) { + System.err.println("Unable to close outStream due to "+e); + e.printStackTrace(System.err); + } + } + } + } +} diff --git a/build/annotationProcessors/CodeGenerator.java b/build/annotationProcessors/CodeGenerator.java new file mode 100644 index 000000000..56d257c03 --- /dev/null +++ b/build/annotationProcessors/CodeGenerator.java @@ -0,0 +1,627 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors; + +import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; +import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions; +import org.mozilla.gecko.annotationProcessors.utils.Utils; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashSet; + +public class CodeGenerator { + private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0]; + + // Buffers holding the strings to ultimately be written to the output files. + private final StringBuilder cpp = new StringBuilder(); + private final StringBuilder header = new StringBuilder(); + private final StringBuilder natives = new StringBuilder(); + private final StringBuilder nativesInits = new StringBuilder(); + + private final Class<?> cls; + private final String clsName; + private AnnotationInfo.CallingThread callingThread = null; + private int numNativesInits; + + private final HashSet<String> takenMethodNames = new HashSet<String>(); + + public CodeGenerator(ClassWithOptions annotatedClass) { + this.cls = annotatedClass.wrappedClass; + this.clsName = annotatedClass.generatedName; + + final String unqualifiedName = Utils.getUnqualifiedName(clsName); + header.append( + "class " + clsName + " : public mozilla::jni::ObjectBase<" + + unqualifiedName + ">\n" + + "{\n" + + "public:\n" + + " static const char name[];\n" + + "\n" + + " explicit " + unqualifiedName + "(const Context& ctx) : ObjectBase<" + + unqualifiedName + ">(ctx) {}\n" + + "\n"); + + cpp.append( + "const char " + clsName + "::name[] =\n" + + " \"" + cls.getName().replace('.', '/') + "\";\n" + + "\n"); + + natives.append( + "template<class Impl>\n" + + "class " + clsName + "::Natives : " + + "public mozilla::jni::NativeImpl<" + unqualifiedName + ", Impl>\n" + + "{\n" + + "public:\n"); + } + + private String getTraitsName(String uniqueName, boolean includeScope) { + return (includeScope ? clsName + "::" : "") + uniqueName + "_t"; + } + + /** + * Return the C++ type name for this class or any class within the chain + * of declaring classes, if the target class matches the given type. + * + * Return null if the given type does not match any class searched. + */ + private String getMatchingClassType(final Class<?> type) { + Class<?> cls = this.cls; + String clsName = this.clsName; + + while (cls != null) { + if (type.equals(cls)) { + return clsName; + } + cls = cls.getDeclaringClass(); + clsName = clsName.substring(0, Math.max(0, clsName.lastIndexOf("::"))); + } + return null; + } + + private String getNativeParameterType(Class<?> type, AnnotationInfo info) { + final String clsName = getMatchingClassType(type); + if (clsName != null) { + return Utils.getUnqualifiedName(clsName) + "::Param"; + } + return Utils.getNativeParameterType(type, info); + } + + private String getNativeReturnType(Class<?> type, AnnotationInfo info) { + final String clsName = getMatchingClassType(type); + if (clsName != null) { + return Utils.getUnqualifiedName(clsName) + "::LocalRef"; + } + return Utils.getNativeReturnType(type, info); + } + + private void generateMember(AnnotationInfo info, Member member, + String uniqueName, Class<?> type, Class<?>[] argTypes) { + final StringBuilder args = new StringBuilder(); + for (Class<?> argType : argTypes) { + args.append("\n " + getNativeParameterType(argType, info) + ","); + } + if (args.length() > 0) { + args.setLength(args.length() - 1); + } + + header.append( + " struct " + getTraitsName(uniqueName, /* includeScope */ false) + " {\n" + + " typedef " + Utils.getUnqualifiedName(clsName) + " Owner;\n" + + " typedef " + getNativeReturnType(type, info) + " ReturnType;\n" + + " typedef " + getNativeParameterType(type, info) + " SetterType;\n" + + " typedef mozilla::jni::Args<" + args + "> Args;\n" + + " static constexpr char name[] = \"" + + Utils.getMemberName(member) + "\";\n" + + " static constexpr char signature[] =\n" + + " \"" + Utils.getSignature(member) + "\";\n" + + " static const bool isStatic = " + Utils.isStatic(member) + ";\n" + + " static const mozilla::jni::ExceptionMode exceptionMode =\n" + + " " + info.exceptionMode.nativeValue() + ";\n" + + " static const mozilla::jni::CallingThread callingThread =\n" + + " " + info.callingThread.nativeValue() + ";\n" + + " static const mozilla::jni::DispatchTarget dispatchTarget =\n" + + " " + info.dispatchTarget.nativeValue() + ";\n" + + " };\n" + + "\n"); + + cpp.append( + "constexpr char " + getTraitsName(uniqueName, /* includeScope */ true) + + "::name[];\n" + + "constexpr char " + getTraitsName(uniqueName, /* includeScope */ true) + + "::signature[];\n" + + "\n"); + + if (this.callingThread == null) { + this.callingThread = info.callingThread; + } else if (this.callingThread != info.callingThread) { + // We have a mix of calling threads, so specify "any" for the whole class. + this.callingThread = AnnotationInfo.CallingThread.ANY; + } + } + + private String getUniqueMethodName(String basename) { + String newName = basename; + int index = 1; + + while (takenMethodNames.contains(newName)) { + newName = basename + (++index); + } + + takenMethodNames.add(newName); + return newName; + } + + /** + * Generate a method prototype that includes return and argument types, + * without specifiers (static, const, etc.). + */ + private String generatePrototype(String name, Class<?>[] argTypes, + Class<?> returnType, AnnotationInfo info, + boolean includeScope, boolean includeArgName, + boolean isConst) { + + final StringBuilder proto = new StringBuilder(); + int argIndex = 0; + + proto.append("auto "); + + if (includeScope) { + proto.append(clsName).append("::"); + } + + proto.append(name).append('('); + + for (Class<?> argType : argTypes) { + proto.append(getNativeParameterType(argType, info)); + if (includeArgName) { + proto.append(" a").append(argIndex++); + } + proto.append(", "); + } + + if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT && + !returnType.equals(void.class)) { + proto.append(getNativeReturnType(returnType, info)).append('*'); + if (includeArgName) { + proto.append(" a").append(argIndex++); + } + proto.append(", "); + } + + if (proto.substring(proto.length() - 2).equals(", ")) { + proto.setLength(proto.length() - 2); + } + + proto.append(')'); + + if (isConst) { + proto.append(" const"); + } + + if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) { + proto.append(" -> nsresult"); + } else { + proto.append(" -> ").append(getNativeReturnType(returnType, info)); + } + return proto.toString(); + } + + /** + * Generate a method declaration that includes the prototype with specifiers, + * but without the method body. + */ + private String generateDeclaration(String name, Class<?>[] argTypes, + Class<?> returnType, AnnotationInfo info, + boolean isStatic) { + + return (isStatic ? "static " : "") + + generatePrototype(name, argTypes, returnType, info, + /* includeScope */ false, /* includeArgName */ false, + /* isConst */ !isStatic) + ';'; + } + + /** + * Generate a method definition that includes the prototype with specifiers, + * and with the method body. + */ + private String generateDefinition(String accessorName, String name, Class<?>[] argTypes, + Class<?> returnType, AnnotationInfo info, boolean isStatic) { + + final StringBuilder def = new StringBuilder( + generatePrototype(name, argTypes, returnType, info, + /* includeScope */ true, /* includeArgName */ true, + /* isConst */ !isStatic)); + def.append("\n{\n"); + + + // Generate code to handle the return value, if needed. + // We initialize rv to NS_OK instead of NS_ERROR_* because loading NS_OK (0) uses + // fewer instructions. We are guaranteed to set rv to the correct value later. + + if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT && + returnType.equals(void.class)) { + def.append( + " nsresult rv = NS_OK;\n" + + " "); + + } else if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) { + // Non-void return type + final String resultArg = "a" + argTypes.length; + def.append( + " MOZ_ASSERT(" + resultArg + ");\n" + + " nsresult rv = NS_OK;\n" + + " *" + resultArg + " = "); + + } else { + def.append( + " return "); + } + + + // Generate a call, e.g., Method<Traits>::Call(a0, a1, a2); + + def.append(accessorName).append("(") + .append(Utils.getUnqualifiedName(clsName) + + (isStatic ? "::Context()" : "::mCtx")); + + if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) { + def.append(", &rv"); + } else { + def.append(", nullptr"); + } + + // Generate the call argument list. + for (int argIndex = 0; argIndex < argTypes.length; argIndex++) { + def.append(", a").append(argIndex); + } + + def.append(");\n"); + + + if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) { + def.append(" return rv;\n"); + } + + return def.append("}").toString(); + } + + /** + * Append the appropriate generated code to the buffers for the method provided. + * + * @param annotatedMethod The Java method, plus annotation data. + */ + public void generateMethod(AnnotatableEntity annotatedMethod) { + // Unpack the tuple and extract some useful fields from the Method.. + final Method method = annotatedMethod.getMethod(); + final AnnotationInfo info = annotatedMethod.mAnnotationInfo; + final String uniqueName = getUniqueMethodName(info.wrapperName); + final Class<?>[] argTypes = method.getParameterTypes(); + final Class<?> returnType = method.getReturnType(); + + if (method.isSynthetic()) { + return; + } + + // Sanity check + if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) { + throw new IllegalStateException("Invalid dispatch target \"" + + info.dispatchTarget.name().toLowerCase() + + "\" for non-native method " + clsName + "::" + uniqueName); + } + + generateMember(info, method, uniqueName, returnType, argTypes); + + final boolean isStatic = Utils.isStatic(method); + + header.append( + " " + generateDeclaration(info.wrapperName, argTypes, + returnType, info, isStatic) + "\n" + + "\n"); + + cpp.append( + generateDefinition( + "mozilla::jni::Method<" + + getTraitsName(uniqueName, /* includeScope */ false) + ">::Call", + info.wrapperName, argTypes, returnType, info, isStatic) + "\n" + + "\n"); + } + + /** + * Append the appropriate generated code to the buffers for the native method provided. + * + * @param annotatedMethod The Java native method, plus annotation data. + */ + public void generateNative(AnnotatableEntity annotatedMethod) { + // Unpack the tuple and extract some useful fields from the Method.. + final Method method = annotatedMethod.getMethod(); + final AnnotationInfo info = annotatedMethod.mAnnotationInfo; + final String uniqueName = getUniqueMethodName(info.wrapperName); + final Class<?>[] argTypes = method.getParameterTypes(); + final Class<?> returnType = method.getReturnType(); + + // Sanity check + if (info.exceptionMode != AnnotationInfo.ExceptionMode.ABORT && + info.exceptionMode != AnnotationInfo.ExceptionMode.IGNORE) { + throw new IllegalStateException("Invalid exception mode \"" + + info.exceptionMode.name().toLowerCase() + + "\" for native method " + clsName + "::" + uniqueName); + } + if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT && + returnType != void.class) { + throw new IllegalStateException( + "Must return void when not dispatching to current thread for native method " + + clsName + "::" + uniqueName); + } + + generateMember(info, method, uniqueName, returnType, argTypes); + + final String traits = getTraitsName(uniqueName, /* includeScope */ true); + + if (nativesInits.length() > 0) { + nativesInits.append(','); + } + + nativesInits.append( + "\n" + + "\n" + + " mozilla::jni::MakeNativeMethod<" + traits + ">(\n" + + " mozilla::jni::NativeStub<" + traits + ", Impl>\n" + + " ::template Wrap<&Impl::" + info.wrapperName + ">)"); + numNativesInits++; + } + + private String getLiteral(Object val, AnnotationInfo info) { + final Class<?> type = val.getClass(); + + if (type.equals(char.class) || type.equals(Character.class)) { + final char c = (char) val; + if (c >= 0x20 && c < 0x7F) { + return "'" + c + '\''; + } + return "u'\\u" + Integer.toHexString(0x10000 | (int) c).substring(1) + '\''; + + } else if (type.equals(CharSequence.class) || type.equals(String.class)) { + final CharSequence str = (CharSequence) val; + final StringBuilder out = new StringBuilder("u\""); + for (int i = 0; i < str.length(); i++) { + final char c = str.charAt(i); + if (c >= 0x20 && c < 0x7F) { + out.append(c); + } else { + out.append("\\u").append(Integer.toHexString(0x10000 | (int) c).substring(1)); + } + } + return out.append('"').toString(); + } + + return String.valueOf(val); + } + + public void generateField(AnnotatableEntity annotatedField) { + final Field field = annotatedField.getField(); + final AnnotationInfo info = annotatedField.mAnnotationInfo; + final String uniqueName = info.wrapperName; + final Class<?> type = field.getType(); + + // Handles a peculiar case when dealing with enum types. We don't care about this field. + // It just gets in the way and stops our code from compiling. + if (field.isSynthetic() || field.getName().equals("$VALUES")) { + return; + } + + // Sanity check + if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) { + throw new IllegalStateException("Invalid dispatch target \"" + + info.dispatchTarget.name().toLowerCase() + + "\" for field " + clsName + "::" + uniqueName); + } + + final boolean isStatic = Utils.isStatic(field); + final boolean isFinal = Utils.isFinal(field); + + if (isStatic && isFinal && (type.isPrimitive() || type.equals(String.class))) { + Object val = null; + try { + field.setAccessible(true); + val = field.get(null); + } catch (final IllegalAccessException e) { + } + + if (val != null && type.isPrimitive()) { + // For static final primitive fields, we can use a "static const" declaration. + header.append( + " static const " + Utils.getNativeReturnType(type, info) + + ' ' + info.wrapperName + " = " + getLiteral(val, info) + ";\n" + + "\n"); + return; + + } else if (val != null && type.equals(String.class)) { + final String nativeType = "char16_t"; + + header.append( + " static const " + nativeType + ' ' + info.wrapperName + "[];\n" + + "\n"); + + cpp.append( + "const " + nativeType + ' ' + clsName + "::" + info.wrapperName + + "[] = " + getLiteral(val, info) + ";\n" + + "\n"); + return; + } + + // Fall back to using accessors if we encounter an exception. + } + + generateMember(info, field, uniqueName, type, EMPTY_CLASS_ARRAY); + + final Class<?>[] getterArgs = EMPTY_CLASS_ARRAY; + + header.append( + " " + generateDeclaration(info.wrapperName, getterArgs, + type, info, isStatic) + "\n" + + "\n"); + + cpp.append( + generateDefinition( + "mozilla::jni::Field<" + + getTraitsName(uniqueName, /* includeScope */ false) + ">::Get", + info.wrapperName, getterArgs, type, info, isStatic) + "\n" + + "\n"); + + if (isFinal) { + return; + } + + final Class<?>[] setterArgs = new Class<?>[] { type }; + + header.append( + " " + generateDeclaration(info.wrapperName, setterArgs, + void.class, info, isStatic) + "\n" + + "\n"); + + cpp.append( + generateDefinition( + "mozilla::jni::Field<" + + getTraitsName(uniqueName, /* includeScope */ false) + ">::Set", + info.wrapperName, setterArgs, void.class, info, isStatic) + "\n" + + "\n"); + } + + public void generateConstructor(AnnotatableEntity annotatedConstructor) { + // Unpack the tuple and extract some useful fields from the Method.. + final Constructor<?> method = annotatedConstructor.getConstructor(); + final AnnotationInfo info = annotatedConstructor.mAnnotationInfo; + final String wrapperName = "New"; + final String uniqueName = getUniqueMethodName(wrapperName); + final Class<?>[] argTypes = method.getParameterTypes(); + final Class<?> returnType = cls; + + if (method.isSynthetic()) { + return; + } + + // Sanity check + if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) { + throw new IllegalStateException("Invalid dispatch target \"" + + info.dispatchTarget.name().toLowerCase() + + "\" for constructor " + clsName + "::" + uniqueName); + } + + generateMember(info, method, uniqueName, returnType, argTypes); + + header.append( + " " + generateDeclaration(wrapperName, argTypes, + returnType, info, /* isStatic */ true) + "\n" + + "\n"); + + cpp.append( + generateDefinition( + "mozilla::jni::Constructor<" + + getTraitsName(uniqueName, /* includeScope */ false) + ">::Call", + wrapperName, argTypes, returnType, info, /* isStatic */ true) + "\n" + + "\n"); + } + + public void generateMembers(Member[] members) { + for (Member m : members) { + if (!Modifier.isPublic(m.getModifiers())) { + continue; + } + + String name = Utils.getMemberName(m); + name = name.substring(0, 1).toUpperCase() + name.substring(1); + + // Default for SDK bindings. + final AnnotationInfo info = new AnnotationInfo(name, + AnnotationInfo.ExceptionMode.NSRESULT, + AnnotationInfo.CallingThread.ANY, + AnnotationInfo.DispatchTarget.CURRENT); + final AnnotatableEntity entity = new AnnotatableEntity(m, info); + + if (m instanceof Constructor) { + generateConstructor(entity); + } else if (m instanceof Method) { + generateMethod(entity); + } else if (m instanceof Field) { + generateField(entity); + } else { + throw new IllegalArgumentException( + "expected member to be Constructor, Method, or Field"); + } + } + } + + public void generateClasses(final ClassWithOptions[] classes) { + if (classes.length == 0) { + return; + } + + for (final ClassWithOptions cls : classes) { + // Extract "Inner" from "Outer::Inner". + header.append( + " class " + Utils.getUnqualifiedName(cls.generatedName) + ";\n"); + } + header.append('\n'); + } + + /** + * Get the finalised bytes to go into the generated wrappers file. + * + * @return The bytes to be written to the wrappers file. + */ + public String getWrapperFileContents() { + return cpp.toString(); + } + + /** + * Get the finalised bytes to go into the generated header file. + * + * @return The bytes to be written to the header file. + */ + public String getHeaderFileContents() { + if (this.callingThread == null) { + this.callingThread = AnnotationInfo.CallingThread.ANY; + } + + header.append( + " static const mozilla::jni::CallingThread callingThread =\n" + + " " + this.callingThread.nativeValue() + ";\n" + + "\n"); + + if (nativesInits.length() > 0) { + header.append( + " template<class Impl> class Natives;\n"); + } + header.append( + "};\n" + + "\n"); + return header.toString(); + } + + /** + * Get the finalised bytes to go into the generated natives header file. + * + * @return The bytes to be written to the header file. + */ + public String getNativesFileContents() { + if (nativesInits.length() == 0) { + return ""; + } + natives.append( + " static const JNINativeMethod methods[" + numNativesInits + "];\n" + + "};\n" + + "\n" + + "template<class Impl>\n" + + "const JNINativeMethod " + clsName + "::Natives<Impl>::methods[] = {" + nativesInits + '\n' + + "};\n" + + "\n"); + return natives.toString(); + } +} diff --git a/build/annotationProcessors/Makefile.in b/build/annotationProcessors/Makefile.in new file mode 100644 index 000000000..55b455e0a --- /dev/null +++ b/build/annotationProcessors/Makefile.in @@ -0,0 +1,10 @@ +# 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 $(topsrcdir)/config/rules.mk + +# Include Android specific java flags, instead of what's in rules.mk. +include $(topsrcdir)/config/android-common.mk + +export:: annotationProcessors.jar diff --git a/build/annotationProcessors/SDKProcessor.java b/build/annotationProcessors/SDKProcessor.java new file mode 100644 index 000000000..7928978c0 --- /dev/null +++ b/build/annotationProcessors/SDKProcessor.java @@ -0,0 +1,258 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors; + +import com.android.tools.lint.checks.ApiLookup; +import com.android.tools.lint.LintCliClient; + +import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; +import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions; +import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader; +import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator; +import org.mozilla.gecko.annotationProcessors.utils.Utils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Iterator; +import java.util.Properties; +import java.util.Scanner; +import java.util.Vector; +import java.net.URL; +import java.net.URLClassLoader; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +public class SDKProcessor { + public static final String GENERATED_COMMENT = + "// GENERATED CODE\n" + + "// Generated by the Java program at /build/annotationProcessors at compile time\n" + + "// from annotations on Java methods. To update, change the annotations on the\n" + + "// corresponding Javamethods and rerun the build. Manually updating this file\n" + + "// will cause your build to fail.\n" + + "\n"; + + private static ApiLookup sApiLookup; + private static int sMaxSdkVersion; + + public static void main(String[] args) throws Exception { + // We expect a list of jars on the commandline. If missing, whinge about it. + if (args.length < 5) { + System.err.println("Usage: java SDKProcessor sdkjar classlistfile outdir fileprefix max-sdk-version"); + System.exit(1); + } + + System.out.println("Processing platform bindings..."); + + String sdkJar = args[0]; + Vector classes = getClassList(args[1]); + String outdir = args[2]; + String generatedFilePrefix = args[3]; + sMaxSdkVersion = Integer.parseInt(args[4]); + + LintCliClient lintClient = new LintCliClient(); + sApiLookup = ApiLookup.get(lintClient); + + // Start the clock! + long s = System.currentTimeMillis(); + + // Get an iterator over the classes in the jar files given... + // Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args); + + StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT); + headerFile.append( + "#ifndef " + generatedFilePrefix + "_h__\n" + + "#define " + generatedFilePrefix + "_h__\n" + + "\n" + + "#include \"mozilla/jni/Refs.h\"\n" + + "\n" + + "namespace mozilla {\n" + + "namespace java {\n" + + "namespace sdk {\n" + + "\n"); + + StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT); + implementationFile.append( + "#include \"" + generatedFilePrefix + ".h\"\n" + + "#include \"mozilla/jni/Accessors.h\"\n" + + "\n" + + "namespace mozilla {\n" + + "namespace java {\n" + + "namespace sdk {\n" + + "\n"); + + // Used to track the calls to the various class-specific initialisation functions. + ClassLoader loader = null; + try { + loader = URLClassLoader.newInstance(new URL[] { new URL("file://" + sdkJar) }, + SDKProcessor.class.getClassLoader()); + } catch (Exception e) { + throw new RuntimeException(e.toString()); + } + + for (Iterator<String> i = classes.iterator(); i.hasNext(); ) { + String className = i.next(); + System.out.println("Looking up: " + className); + + generateClass(Class.forName(className, true, loader), + implementationFile, + headerFile); + } + + implementationFile.append( + "} /* sdk */\n" + + "} /* java */\n" + + "} /* mozilla */\n"); + + headerFile.append( + "} /* sdk */\n" + + "} /* java */\n" + + "} /* mozilla */\n" + + "#endif\n"); + + writeOutputFiles(outdir, generatedFilePrefix, headerFile, implementationFile); + long e = System.currentTimeMillis(); + System.out.println("SDK processing complete in " + (e - s) + "ms"); + } + + private static int getAPIVersion(Class<?> cls, Member m) { + if (m instanceof Method || m instanceof Constructor) { + return sApiLookup.getCallVersion( + cls.getName().replace('.', '/'), + Utils.getMemberName(m), + Utils.getSignature(m)); + } else if (m instanceof Field) { + return sApiLookup.getFieldVersion( + Utils.getClassDescriptor(m.getDeclaringClass()), m.getName()); + } else { + throw new IllegalArgumentException("expected member to be Method, Constructor, or Field"); + } + } + + private static Member[] sortAndFilterMembers(Class<?> cls, Member[] members) { + Arrays.sort(members, new Comparator<Member>() { + @Override + public int compare(Member a, Member b) { + return a.getName().compareTo(b.getName()); + } + }); + + ArrayList<Member> list = new ArrayList<>(); + for (Member m : members) { + // Sometimes (e.g. Bundle) has methods that moved to/from a superclass in a later SDK + // version, so we check for both classes and see if we can find a minimum SDK version. + int version = getAPIVersion(cls, m); + final int version2 = getAPIVersion(m.getDeclaringClass(), m); + if (version2 > 0 && version2 < version) { + version = version2; + } + if (version > sMaxSdkVersion) { + System.out.println("Skipping " + m.getDeclaringClass().getName() + "." + m.getName() + + ", version " + version + " > " + sMaxSdkVersion); + continue; + } + + // Sometimes (e.g. KeyEvent) a field can appear in both a class and a superclass. In + // that case we want to filter out the version that appears in the superclass, or + // we'll have bindings with duplicate names. + try { + if (m instanceof Field && !m.equals(cls.getField(m.getName()))) { + // m is a field in a superclass that has been hidden by + // a field with the same name in a subclass. + System.out.println("Skipping " + m.getName() + + " from " + m.getDeclaringClass()); + continue; + } + } catch (final NoSuchFieldException e) { + } + + list.add(m); + } + + return list.toArray(new Member[list.size()]); + } + + private static void generateClass(Class<?> clazz, + StringBuilder implementationFile, + StringBuilder headerFile) { + String generatedName = clazz.getSimpleName(); + + CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName)); + + generator.generateMembers(sortAndFilterMembers(clazz, clazz.getConstructors())); + generator.generateMembers(sortAndFilterMembers(clazz, clazz.getMethods())); + generator.generateMembers(sortAndFilterMembers(clazz, clazz.getFields())); + + headerFile.append(generator.getHeaderFileContents()); + implementationFile.append(generator.getWrapperFileContents()); + } + + private static Vector<String> getClassList(String path) { + Scanner scanner = null; + try { + scanner = new Scanner(new FileInputStream(path)); + + Vector lines = new Vector(); + while (scanner.hasNextLine()) { + lines.add(scanner.nextLine()); + } + return lines; + } catch (Exception e) { + System.out.println(e.toString()); + return null; + } finally { + if (scanner != null) { + scanner.close(); + } + } + } + + private static void writeOutputFiles(String aOutputDir, String aPrefix, StringBuilder aHeaderFile, + StringBuilder aImplementationFile) { + FileOutputStream implStream = null; + try { + implStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".cpp")); + implStream.write(aImplementationFile.toString().getBytes()); + } catch (IOException e) { + System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?"); + e.printStackTrace(System.err); + } finally { + if (implStream != null) { + try { + implStream.close(); + } catch (IOException e) { + System.err.println("Unable to close implStream due to "+e); + e.printStackTrace(System.err); + } + } + } + + FileOutputStream headerStream = null; + try { + headerStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".h")); + headerStream.write(aHeaderFile.toString().getBytes()); + } catch (IOException e) { + System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?"); + e.printStackTrace(System.err); + } finally { + if (headerStream != null) { + try { + headerStream.close(); + } catch (IOException e) { + System.err.println("Unable to close headerStream due to "+e); + e.printStackTrace(System.err); + } + } + } + } +} diff --git a/build/annotationProcessors/classloader/AnnotatableEntity.java b/build/annotationProcessors/classloader/AnnotatableEntity.java new file mode 100644 index 000000000..b11a6c49a --- /dev/null +++ b/build/annotationProcessors/classloader/AnnotatableEntity.java @@ -0,0 +1,62 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors.classloader; + +import org.mozilla.gecko.annotationProcessors.AnnotationInfo; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; + +/** + * Union type to hold either a method, field, or ctor. Allows us to iterate "The generatable stuff", despite + * the fact that such things can be of either flavour. + */ +public class AnnotatableEntity { + public enum ENTITY_TYPE {METHOD, NATIVE, FIELD, CONSTRUCTOR} + + private final Member mMember; + public final ENTITY_TYPE mEntityType; + + public final AnnotationInfo mAnnotationInfo; + + public AnnotatableEntity(Member aObject, AnnotationInfo aAnnotationInfo) { + mMember = aObject; + mAnnotationInfo = aAnnotationInfo; + + if (aObject instanceof Method) { + if (Modifier.isNative(aObject.getModifiers())) { + mEntityType = ENTITY_TYPE.NATIVE; + } else { + mEntityType = ENTITY_TYPE.METHOD; + } + } else if (aObject instanceof Field) { + mEntityType = ENTITY_TYPE.FIELD; + } else { + mEntityType = ENTITY_TYPE.CONSTRUCTOR; + } + } + + public Method getMethod() { + if (mEntityType != ENTITY_TYPE.METHOD && mEntityType != ENTITY_TYPE.NATIVE) { + throw new UnsupportedOperationException("Attempt to cast to unsupported member type."); + } + return (Method) mMember; + } + public Field getField() { + if (mEntityType != ENTITY_TYPE.FIELD) { + throw new UnsupportedOperationException("Attempt to cast to unsupported member type."); + } + return (Field) mMember; + } + public Constructor<?> getConstructor() { + if (mEntityType != ENTITY_TYPE.CONSTRUCTOR) { + throw new UnsupportedOperationException("Attempt to cast to unsupported member type."); + } + return (Constructor<?>) mMember; + } +} diff --git a/build/annotationProcessors/classloader/ClassWithOptions.java b/build/annotationProcessors/classloader/ClassWithOptions.java new file mode 100644 index 000000000..070cff8b6 --- /dev/null +++ b/build/annotationProcessors/classloader/ClassWithOptions.java @@ -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/. */ + +package org.mozilla.gecko.annotationProcessors.classloader; + +public class ClassWithOptions { + public final Class<?> wrappedClass; + public final String generatedName; + + public ClassWithOptions(Class<?> someClass, String name) { + wrappedClass = someClass; + generatedName = name; + } +} diff --git a/build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java b/build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java new file mode 100644 index 000000000..7e74399ca --- /dev/null +++ b/build/annotationProcessors/classloader/IterableJarLoadingURLClassLoader.java @@ -0,0 +1,75 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors.classloader; + +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.jar.JarEntry; +import java.util.jar.JarFile; + +/** + * A classloader which can be initialised with a list of jar files and which can provide an iterator + * over the top level classes in the jar files it was initialised with. + * classNames is kept sorted to ensure iteration order is consistent across program invocations. + * Otherwise, we'd forever be reporting the outdatedness of the generated code as we permute its + * contents. + */ +public class IterableJarLoadingURLClassLoader extends URLClassLoader { + LinkedList<String> classNames = new LinkedList<String>(); + + /** + * Create an instance and return its iterator. Provides an iterator over the classes in the jar + * files provided as arguments. + * Inner classes are not supported. + * + * @param args A list of jar file names an iterator over the classes of which is desired. + * @return An iterator over the top level classes in the jar files provided, in arbitrary order. + */ + public static Iterator<ClassWithOptions> getIteratorOverJars(String[] args) { + URL[] urlArray = new URL[args.length]; + LinkedList<String> aClassNames = new LinkedList<String>(); + + for (int i = 0; i < args.length; i++) { + try { + urlArray[i] = (new File(args[i])).toURI().toURL(); + + Enumeration<JarEntry> entries = new JarFile(args[i]).entries(); + while (entries.hasMoreElements()) { + JarEntry e = entries.nextElement(); + String fName = e.getName(); + if (!fName.endsWith(".class")) { + continue; + } + final String className = fName.substring(0, fName.length() - 6).replace('/', '.'); + + aClassNames.add(className); + } + } catch (IOException e) { + System.err.println("Error loading jar file \"" + args[i] + '"'); + e.printStackTrace(System.err); + } + } + Collections.sort(aClassNames); + return new JarClassIterator(new IterableJarLoadingURLClassLoader(urlArray, aClassNames)); + } + + /** + * Constructs a classloader capable of loading all classes given as URLs in urls. Used by static + * method above. + * + * @param urls URLs for all classes the new instance shall be capable of loading. + * @param aClassNames A list of names of the classes this instance shall be capable of loading. + */ + protected IterableJarLoadingURLClassLoader(URL[] urls, LinkedList<String> aClassNames) {// Array to populate with URLs for each class in the given jars. + super(urls); + classNames = aClassNames; + } +} diff --git a/build/annotationProcessors/classloader/JarClassIterator.java b/build/annotationProcessors/classloader/JarClassIterator.java new file mode 100644 index 000000000..452de8131 --- /dev/null +++ b/build/annotationProcessors/classloader/JarClassIterator.java @@ -0,0 +1,84 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors.classloader; + +import java.util.Iterator; + +/** + * Class for iterating over an IterableJarLoadingURLClassLoader's classes. + * + * This class is not thread safe: use it only from a single thread. + */ +public class JarClassIterator implements Iterator<ClassWithOptions> { + private IterableJarLoadingURLClassLoader mTarget; + private Iterator<String> mTargetClassListIterator; + + private ClassWithOptions lookAhead; + + public JarClassIterator(IterableJarLoadingURLClassLoader aTarget) { + mTarget = aTarget; + mTargetClassListIterator = aTarget.classNames.iterator(); + } + + @Override + public boolean hasNext() { + return fillLookAheadIfPossible(); + } + + @Override + public ClassWithOptions next() { + if (!fillLookAheadIfPossible()) { + throw new IllegalStateException("Failed to look ahead in next()!"); + } + ClassWithOptions next = lookAhead; + lookAhead = null; + return next; + } + + private boolean fillLookAheadIfPossible() { + if (lookAhead != null) { + return true; + } + + if (!mTargetClassListIterator.hasNext()) { + return false; + } + + String className = mTargetClassListIterator.next(); + try { + Class<?> ret = mTarget.loadClass(className); + + // Incremental builds can leave stale classfiles in the jar. Such classfiles will cause + // an exception at this point. We can safely ignore these classes - they cannot possibly + // ever be loaded as they conflict with their parent class and will be killed by + // Proguard later on anyway. + final Class<?> enclosingClass; + try { + enclosingClass = ret.getEnclosingClass(); + } catch (IncompatibleClassChangeError e) { + return fillLookAheadIfPossible(); + } + + if (enclosingClass != null) { + // Anonymous inner class - unsupported. + // Or named inner class, which will be processed when we process the outer class. + return fillLookAheadIfPossible(); + } + + lookAhead = new ClassWithOptions(ret, ret.getSimpleName()); + return true; + } catch (ClassNotFoundException e) { + System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?"); + e.printStackTrace(); + System.exit(2); + } + return false; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Removal of classes from iterator not supported."); + } +} diff --git a/build/annotationProcessors/moz.build b/build/annotationProcessors/moz.build new file mode 100644 index 000000000..ec570a9f1 --- /dev/null +++ b/build/annotationProcessors/moz.build @@ -0,0 +1,24 @@ +# -*- Mode: python; 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/. + +jar = add_java_jar('annotationProcessors') +jar.sources += [ + 'AnnotationInfo.java', + 'AnnotationProcessor.java', + 'classloader/AnnotatableEntity.java', + 'classloader/ClassWithOptions.java', + 'classloader/IterableJarLoadingURLClassLoader.java', + 'classloader/JarClassIterator.java', + 'CodeGenerator.java', + 'SDKProcessor.java', + 'utils/AlphabeticAnnotatableEntityComparator.java', + 'utils/GeneratableElementIterator.java', + 'utils/Utils.java', +] +jar.extra_jars += [ + CONFIG['ANDROID_TOOLS'] + '/lib/lint.jar', + CONFIG['ANDROID_TOOLS'] + '/lib/lint-checks.jar', +] diff --git a/build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java b/build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java new file mode 100644 index 000000000..2ee2ae56a --- /dev/null +++ b/build/annotationProcessors/utils/AlphabeticAnnotatableEntityComparator.java @@ -0,0 +1,81 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors.utils; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.Comparator; + +public class AlphabeticAnnotatableEntityComparator<T extends Member> implements Comparator<T> { + @Override + public int compare(T aLhs, T aRhs) { + // Constructors, Methods, Fields. + boolean lIsConstructor = aLhs instanceof Constructor; + boolean rIsConstructor = aRhs instanceof Constructor; + boolean lIsMethod = aLhs instanceof Method; + boolean rIsField = aRhs instanceof Field; + + if (lIsConstructor) { + if (!rIsConstructor) { + return -1; + } + } else if (lIsMethod) { + if (rIsConstructor) { + return 1; + } else if (rIsField) { + return -1; + } + } else { + if (!rIsField) { + return 1; + } + } + + // Verify these objects are the same type and cast them. + if (aLhs instanceof Method) { + return compare((Method) aLhs, (Method) aRhs); + } else if (aLhs instanceof Field) { + return compare((Field) aLhs, (Field) aRhs); + } else { + return compare((Constructor) aLhs, (Constructor) aRhs); + } + } + + // Alas, the type system fails us. + private static int compare(Method aLhs, Method aRhs) { + // Initially, attempt to differentiate the methods be name alone.. + String lName = aLhs.getName(); + String rName = aRhs.getName(); + + int ret = lName.compareTo(rName); + if (ret != 0) { + return ret; + } + + // The names were the same, so we need to compare signatures to find their uniqueness.. + lName = Utils.getSignature(aLhs); + rName = Utils.getSignature(aRhs); + + return lName.compareTo(rName); + } + + private static int compare(Constructor<?> aLhs, Constructor<?> aRhs) { + // The names will be the same, so we need to compare signatures to find their uniqueness.. + String lName = Utils.getSignature(aLhs); + String rName = Utils.getSignature(aRhs); + + return lName.compareTo(rName); + } + + private static int compare(Field aLhs, Field aRhs) { + // Compare field names.. + String lName = aLhs.getName(); + String rName = aRhs.getName(); + + return lName.compareTo(rName); + } +} diff --git a/build/annotationProcessors/utils/GeneratableElementIterator.java b/build/annotationProcessors/utils/GeneratableElementIterator.java new file mode 100644 index 000000000..8f94c8afa --- /dev/null +++ b/build/annotationProcessors/utils/GeneratableElementIterator.java @@ -0,0 +1,267 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors.utils; + +import org.mozilla.gecko.annotationProcessors.AnnotationInfo; +import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity; +import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions; + +import java.lang.annotation.Annotation; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Iterator; + +/** + * Iterator over the methods in a given method list which have the WrappedJNIMethod + * annotation. Returns an object containing both the annotation (Which may contain interesting + * parameters) and the argument. + */ +public class GeneratableElementIterator implements Iterator<AnnotatableEntity> { + private final ClassWithOptions mClass; + private final Member[] mObjects; + private AnnotatableEntity mNextReturnValue; + private int mElementIndex; + private AnnotationInfo mClassInfo; + + private boolean mIterateEveryEntry; + private boolean mSkipCurrentEntry; + + public GeneratableElementIterator(ClassWithOptions annotatedClass) { + mClass = annotatedClass; + + final Class<?> aClass = annotatedClass.wrappedClass; + // Get all the elements of this class as AccessibleObjects. + Member[] aMethods = aClass.getDeclaredMethods(); + Member[] aFields = aClass.getDeclaredFields(); + Member[] aCtors = aClass.getDeclaredConstructors(); + + // Shove them all into one buffer. + Member[] objs = new Member[aMethods.length + aFields.length + aCtors.length]; + + int offset = 0; + System.arraycopy(aMethods, 0, objs, 0, aMethods.length); + offset += aMethods.length; + System.arraycopy(aFields, 0, objs, offset, aFields.length); + offset += aFields.length; + System.arraycopy(aCtors, 0, objs, offset, aCtors.length); + + // Sort the elements to ensure determinism. + Arrays.sort(objs, new AlphabeticAnnotatableEntityComparator<Member>()); + mObjects = objs; + + // Check for "Wrap ALL the things" flag. + for (Annotation annotation : aClass.getDeclaredAnnotations()) { + mClassInfo = buildAnnotationInfo(aClass, annotation); + if (mClassInfo != null) { + mIterateEveryEntry = true; + break; + } + } + + if (mSkipCurrentEntry) { + throw new IllegalArgumentException("Cannot skip entire class"); + } + + findNextValue(); + } + + private Class<?>[] getFilteredInnerClasses() { + // Go through all inner classes and see which ones we want to generate. + final Class<?>[] candidates = mClass.wrappedClass.getDeclaredClasses(); + int count = 0; + + for (int i = 0; i < candidates.length; ++i) { + final GeneratableElementIterator testIterator + = new GeneratableElementIterator(new ClassWithOptions(candidates[i], null)); + if (testIterator.hasNext() + || testIterator.getFilteredInnerClasses() != null) { + count++; + continue; + } + // Clear out ones that don't match. + candidates[i] = null; + } + return count > 0 ? candidates : null; + } + + public ClassWithOptions[] getInnerClasses() { + final Class<?>[] candidates = getFilteredInnerClasses(); + if (candidates == null) { + return new ClassWithOptions[0]; + } + + int count = 0; + for (Class<?> candidate : candidates) { + if (candidate != null) { + count++; + } + } + + final ClassWithOptions[] ret = new ClassWithOptions[count]; + count = 0; + for (Class<?> candidate : candidates) { + if (candidate != null) { + ret[count++] = new ClassWithOptions( + candidate, mClass.generatedName + "::" + candidate.getSimpleName()); + } + } + assert ret.length == count; + + Arrays.sort(ret, new Comparator<ClassWithOptions>() { + @Override public int compare(ClassWithOptions lhs, ClassWithOptions rhs) { + return lhs.generatedName.compareTo(rhs.generatedName); + } + }); + return ret; + } + + private static <T extends Enum<T>> T getEnumValue(Class<T> type, String name) + throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { + try { + return Enum.valueOf(type, name.toUpperCase()); + + } catch (IllegalArgumentException e) { + Object[] values = (Object[]) type.getDeclaredMethod("values").invoke(null); + StringBuilder names = new StringBuilder(); + + for (int i = 0; i < values.length; i++) { + if (i != 0) { + names.append(", "); + } + names.append(values[i].toString().toLowerCase()); + } + + System.err.println("***"); + System.err.println("*** Invalid value \"" + name + "\" for " + type.getSimpleName()); + System.err.println("*** Specify one of " + names.toString()); + System.err.println("***"); + e.printStackTrace(System.err); + System.exit(6); + return null; + } + } + + private AnnotationInfo buildAnnotationInfo(AnnotatedElement element, Annotation annotation) { + Class<? extends Annotation> annotationType = annotation.annotationType(); + final String annotationTypeName = annotationType.getName(); + if (!annotationTypeName.equals("org.mozilla.gecko.annotation.WrapForJNI")) { + return null; + } + + String stubName = null; + AnnotationInfo.ExceptionMode exceptionMode = null; + AnnotationInfo.CallingThread callingThread = null; + AnnotationInfo.DispatchTarget dispatchTarget = null; + + try { + final Method skipMethod = annotationType.getDeclaredMethod("skip"); + skipMethod.setAccessible(true); + if ((Boolean) skipMethod.invoke(annotation)) { + mSkipCurrentEntry = true; + return null; + } + + // Determine the explicitly-given name of the stub to generate, if any. + final Method stubNameMethod = annotationType.getDeclaredMethod("stubName"); + stubNameMethod.setAccessible(true); + stubName = (String) stubNameMethod.invoke(annotation); + + final Method exceptionModeMethod = annotationType.getDeclaredMethod("exceptionMode"); + exceptionModeMethod.setAccessible(true); + exceptionMode = getEnumValue( + AnnotationInfo.ExceptionMode.class, + (String) exceptionModeMethod.invoke(annotation)); + + final Method calledFromMethod = annotationType.getDeclaredMethod("calledFrom"); + calledFromMethod.setAccessible(true); + callingThread = getEnumValue( + AnnotationInfo.CallingThread.class, + (String) calledFromMethod.invoke(annotation)); + + final Method dispatchToMethod = annotationType.getDeclaredMethod("dispatchTo"); + dispatchToMethod.setAccessible(true); + dispatchTarget = getEnumValue( + AnnotationInfo.DispatchTarget.class, + (String) dispatchToMethod.invoke(annotation)); + + } catch (NoSuchMethodException e) { + System.err.println("Unable to find expected field on WrapForJNI annotation. Did the signature change?"); + e.printStackTrace(System.err); + System.exit(3); + } catch (IllegalAccessException e) { + System.err.println("IllegalAccessException reading fields on WrapForJNI annotation. Seems the semantics of Reflection have changed..."); + e.printStackTrace(System.err); + System.exit(4); + } catch (InvocationTargetException e) { + System.err.println("InvocationTargetException reading fields on WrapForJNI annotation. This really shouldn't happen."); + e.printStackTrace(System.err); + System.exit(5); + } + + // If the method name was not explicitly given in the annotation generate one... + if (stubName.isEmpty()) { + stubName = Utils.getNativeName(element); + } + + return new AnnotationInfo(stubName, exceptionMode, callingThread, dispatchTarget); + } + + /** + * Find and cache the next appropriately annotated method, plus the annotation parameter, if + * one exists. Otherwise cache null, so hasNext returns false. + */ + private void findNextValue() { + while (mElementIndex < mObjects.length) { + Member candidateElement = mObjects[mElementIndex]; + mElementIndex++; + for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) { + AnnotationInfo info = buildAnnotationInfo((AnnotatedElement)candidateElement, annotation); + if (info != null) { + mNextReturnValue = new AnnotatableEntity(candidateElement, info); + return; + } + } + + if (mSkipCurrentEntry) { + mSkipCurrentEntry = false; + continue; + } + + // If no annotation found, we might be expected to generate anyway + // using default arguments, thanks to the "Generate everything" annotation. + if (mIterateEveryEntry) { + AnnotationInfo annotationInfo = new AnnotationInfo( + Utils.getNativeName(candidateElement), + mClassInfo.exceptionMode, + mClassInfo.callingThread, + mClassInfo.dispatchTarget); + mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo); + return; + } + } + mNextReturnValue = null; + } + + @Override + public boolean hasNext() { + return mNextReturnValue != null; + } + + @Override + public AnnotatableEntity next() { + AnnotatableEntity ret = mNextReturnValue; + findNextValue(); + return ret; + } + + @Override + public void remove() { + throw new UnsupportedOperationException("Removal of methods from GeneratableElementIterator not supported."); + } +} diff --git a/build/annotationProcessors/utils/Utils.java b/build/annotationProcessors/utils/Utils.java new file mode 100644 index 000000000..aea88cfbf --- /dev/null +++ b/build/annotationProcessors/utils/Utils.java @@ -0,0 +1,288 @@ +/* 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/. */ + +package org.mozilla.gecko.annotationProcessors.utils; + +import org.mozilla.gecko.annotationProcessors.AnnotationInfo; + +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.nio.ByteBuffer; +import java.util.HashMap; + +/** + * A collection of utility methods used by CodeGenerator. Largely used for translating types. + */ +public class Utils { + + // A collection of lookup tables to simplify the functions to follow... + private static final HashMap<String, String> NATIVE_TYPES = new HashMap<String, String>(); + + static { + NATIVE_TYPES.put("void", "void"); + NATIVE_TYPES.put("boolean", "bool"); + NATIVE_TYPES.put("byte", "int8_t"); + NATIVE_TYPES.put("char", "char16_t"); + NATIVE_TYPES.put("short", "int16_t"); + NATIVE_TYPES.put("int", "int32_t"); + NATIVE_TYPES.put("long", "int64_t"); + NATIVE_TYPES.put("float", "float"); + NATIVE_TYPES.put("double", "double"); + } + + private static final HashMap<String, String> NATIVE_ARRAY_TYPES = new HashMap<String, String>(); + + static { + NATIVE_ARRAY_TYPES.put("boolean", "mozilla::jni::BooleanArray"); + NATIVE_ARRAY_TYPES.put("byte", "mozilla::jni::ByteArray"); + NATIVE_ARRAY_TYPES.put("char", "mozilla::jni::CharArray"); + NATIVE_ARRAY_TYPES.put("short", "mozilla::jni::ShortArray"); + NATIVE_ARRAY_TYPES.put("int", "mozilla::jni::IntArray"); + NATIVE_ARRAY_TYPES.put("long", "mozilla::jni::LongArray"); + NATIVE_ARRAY_TYPES.put("float", "mozilla::jni::FloatArray"); + NATIVE_ARRAY_TYPES.put("double", "mozilla::jni::DoubleArray"); + } + + private static final HashMap<String, String> CLASS_DESCRIPTORS = new HashMap<String, String>(); + + static { + CLASS_DESCRIPTORS.put("void", "V"); + CLASS_DESCRIPTORS.put("boolean", "Z"); + CLASS_DESCRIPTORS.put("byte", "B"); + CLASS_DESCRIPTORS.put("char", "C"); + CLASS_DESCRIPTORS.put("short", "S"); + CLASS_DESCRIPTORS.put("int", "I"); + CLASS_DESCRIPTORS.put("long", "J"); + CLASS_DESCRIPTORS.put("float", "F"); + CLASS_DESCRIPTORS.put("double", "D"); + } + + /** + * Get the C++ type corresponding to the provided type parameter. + * + * @param type Class to determine the corresponding JNI type for. + * @return C++ type as a String + */ + public static String getNativeParameterType(Class<?> type, AnnotationInfo info) { + final String name = type.getName().replace('.', '/'); + + if (NATIVE_TYPES.containsKey(name)) { + return NATIVE_TYPES.get(name); + } + + if (type.isArray()) { + final String compName = type.getComponentType().getName(); + if (NATIVE_ARRAY_TYPES.containsKey(compName)) { + return NATIVE_ARRAY_TYPES.get(compName) + "::Param"; + } + return "mozilla::jni::ObjectArray::Param"; + } + + if (type.equals(String.class) || type.equals(CharSequence.class)) { + return "mozilla::jni::String::Param"; + } + + if (type.equals(Class.class)) { + // You're doing reflection on Java objects from inside C, returning Class objects + // to C, generating the corresponding code using this Java program. Really?! + return "mozilla::jni::Class::Param"; + } + + if (type.equals(Throwable.class)) { + return "mozilla::jni::Throwable::Param"; + } + + if (type.equals(ByteBuffer.class)) { + return "mozilla::jni::ByteBuffer::Param"; + } + + return "mozilla::jni::Object::Param"; + } + + public static String getNativeReturnType(Class<?> type, AnnotationInfo info) { + final String name = type.getName().replace('.', '/'); + + if (NATIVE_TYPES.containsKey(name)) { + return NATIVE_TYPES.get(name); + } + + if (type.isArray()) { + final String compName = type.getComponentType().getName(); + if (NATIVE_ARRAY_TYPES.containsKey(compName)) { + return NATIVE_ARRAY_TYPES.get(compName) + "::LocalRef"; + } + return "mozilla::jni::ObjectArray::LocalRef"; + } + + if (type.equals(String.class)) { + return "mozilla::jni::String::LocalRef"; + } + + if (type.equals(Class.class)) { + // You're doing reflection on Java objects from inside C, returning Class objects + // to C, generating the corresponding code using this Java program. Really?! + return "mozilla::jni::Class::LocalRef"; + } + + if (type.equals(Throwable.class)) { + return "mozilla::jni::Throwable::LocalRef"; + } + + if (type.equals(ByteBuffer.class)) { + return "mozilla::jni::ByteBuffer::LocalRef"; + } + + return "mozilla::jni::Object::LocalRef"; + } + + /** + * Get the JNI class descriptor corresponding to the provided type parameter. + * + * @param type Class to determine the corresponding JNI descriptor for. + * @return Class descripor as a String + */ + public static String getClassDescriptor(Class<?> type) { + final String name = type.getName().replace('.', '/'); + + if (CLASS_DESCRIPTORS.containsKey(name)) { + return CLASS_DESCRIPTORS.get(name); + } + + if (type.isArray()) { + // Array names are already in class descriptor form. + return name; + } + + return "L" + name + ';'; + } + + /** + * Get the JNI signaure for a member. + * + * @param member Member to get the signature for. + * @return JNI signature as a string + */ + public static String getSignature(Member member) { + return member instanceof Field ? getSignature((Field) member) : + member instanceof Method ? getSignature((Method) member) : + getSignature((Constructor<?>) member); + } + + /** + * Get the JNI signaure for a field. + * + * @param member Field to get the signature for. + * @return JNI signature as a string + */ + public static String getSignature(Field member) { + return getClassDescriptor(member.getType()); + } + + private static String getSignature(Class<?>[] args, Class<?> ret) { + final StringBuilder sig = new StringBuilder("("); + for (int i = 0; i < args.length; i++) { + sig.append(getClassDescriptor(args[i])); + } + return sig.append(')').append(getClassDescriptor(ret)).toString(); + } + + /** + * Get the JNI signaure for a method. + * + * @param member Method to get the signature for. + * @return JNI signature as a string + */ + public static String getSignature(Method member) { + return getSignature(member.getParameterTypes(), member.getReturnType()); + } + + /** + * Get the JNI signaure for a constructor. + * + * @param member Constructor to get the signature for. + * @return JNI signature as a string + */ + public static String getSignature(Constructor<?> member) { + return getSignature(member.getParameterTypes(), void.class); + } + + /** + * Get the C++ name for a member. + * + * @param member Member to get the name for. + * @return JNI name as a string + */ + public static String getNativeName(Member member) { + final String name = getMemberName(member); + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + /** + * Get the C++ name for a member. + * + * @param member Member to get the name for. + * @return JNI name as a string + */ + public static String getNativeName(Class<?> clz) { + final String name = clz.getName(); + return name.substring(0, 1).toUpperCase() + name.substring(1); + } + + /** + * Get the C++ name for a member. + * + * @param member Member to get the name for. + * @return JNI name as a string + */ + public static String getNativeName(AnnotatedElement element) { + if (element instanceof Class<?>) { + return getNativeName((Class<?>)element); + } else if (element instanceof Member) { + return getNativeName((Member)element); + } else { + return null; + } + } + + /** + * Get the JNI name for a member. + * + * @param member Member to get the name for. + * @return JNI name as a string + */ + public static String getMemberName(Member member) { + if (member instanceof Constructor) { + return "<init>"; + } + return member.getName(); + } + + public static String getUnqualifiedName(String name) { + return name.substring(name.lastIndexOf(':') + 1); + } + + /** + * Determine if a member is declared static. + * + * @param member The Member to check. + * @return true if the member is declared static, false otherwise. + */ + public static boolean isStatic(final Member member) { + return Modifier.isStatic(member.getModifiers()); + } + + /** + * Determine if a member is declared final. + * + * @param member The Member to check. + * @return true if the member is declared final, false otherwise. + */ + public static boolean isFinal(final Member member) { + return Modifier.isFinal(member.getModifiers()); + } +} diff --git a/build/appini_header.py b/build/appini_header.py new file mode 100644 index 000000000..356a0692e --- /dev/null +++ b/build/appini_header.py @@ -0,0 +1,60 @@ +# 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/. + +'''Parses a given application.ini file and outputs the corresponding + XULAppData structure as a C++ header file''' + +import ConfigParser +import sys + +def main(output, file): + config = ConfigParser.RawConfigParser() + config.read(file) + flags = set() + try: + if config.getint('XRE', 'EnableProfileMigrator') == 1: + flags.add('NS_XRE_ENABLE_PROFILE_MIGRATOR') + except: pass + try: + if config.getint('Crash Reporter', 'Enabled') == 1: + flags.add('NS_XRE_ENABLE_CRASH_REPORTER') + except: pass + appdata = dict(("%s:%s" % (s, o), config.get(s, o)) for s in config.sections() for o in config.options(s)) + appdata['flags'] = ' | '.join(flags) if flags else '0' + appdata['App:profile'] = '"%s"' % appdata['App:profile'] if 'App:profile' in appdata else 'NULL' + expected = ('App:vendor', 'App:name', 'App:remotingname', 'App:version', 'App:buildid', + 'App:id', 'Gecko:minversion', 'Gecko:maxversion') + missing = [var for var in expected if var not in appdata] + if missing: + print >>sys.stderr, \ + "Missing values in %s: %s" % (file, ', '.join(missing)) + sys.exit(1) + + if not 'Crash Reporter:serverurl' in appdata: + appdata['Crash Reporter:serverurl'] = '' + + output.write('''#include "nsXREAppData.h" + static const nsXREAppData sAppData = { + sizeof(nsXREAppData), + NULL, // directory + "%(App:vendor)s", + "%(App:name)s", + "%(App:remotingname)s", + "%(App:version)s", + "%(App:buildid)s", + "%(App:id)s", + NULL, // copyright + %(flags)s, + NULL, // xreDirectory + "%(Gecko:minversion)s", + "%(Gecko:maxversion)s", + "%(Crash Reporter:serverurl)s", + %(App:profile)s + };''' % appdata) + +if __name__ == '__main__': + if len(sys.argv) != 1: + main(sys.stdout, sys.argv[1]) + else: + print >>sys.stderr, "Usage: %s /path/to/application.ini" % sys.argv[0] diff --git a/build/application.ini b/build/application.ini new file mode 100644 index 000000000..6d27b4097 --- /dev/null +++ b/build/application.ini @@ -0,0 +1,53 @@ +#if MOZ_APP_STATIC_INI +#ifdef MOZ_BUILD_APP_IS_BROWSER +; This file is not used. If you modify it and want the application to use +; your modifications, move it under the browser/ subdirectory and start with +; the "-app /path/to/browser/application.ini" argument. +#else +; This file is not used. If you modify it and want the application to use +; your modifications, start with the "-app /path/to/application.ini" +; argument. +#endif +#endif +#if 0 +; 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/. +#endif +#filter substitution +#include @TOPOBJDIR@/buildid.h +#include @TOPOBJDIR@/source-repo.h +[App] +Vendor=@MOZ_APP_VENDOR@ +Name=@MOZ_APP_BASENAME@ +RemotingName=@MOZ_APP_REMOTINGNAME@ +#ifdef MOZ_APP_DISPLAYNAME +CodeName=@MOZ_APP_DISPLAYNAME@ +#endif +Version=@MOZ_APP_VERSION@ +#ifdef MOZ_APP_PROFILE +Profile=@MOZ_APP_PROFILE@ +#endif +BuildID=@MOZ_BUILDID@ +#ifdef MOZ_SOURCE_REPO +SourceRepository=@MOZ_SOURCE_REPO@ +#endif +#ifdef MOZ_SOURCE_STAMP +SourceStamp=@MOZ_SOURCE_STAMP@ +#endif +ID=@MOZ_APP_ID@ + +[Gecko] +MinVersion=@GRE_MILESTONE@ +MaxVersion=@GRE_MILESTONE@ + +[XRE] +#ifdef MOZ_PROFILE_MIGRATOR +EnableProfileMigrator=1 +#endif + +#if MOZ_CRASHREPORTER +[Crash Reporter] +Enabled=1 +ServerURL=https://crash-reports.mozilla.com/submit?id=@MOZ_APP_ID@&version=@MOZ_APP_VERSION@&buildid=@MOZ_BUILDID@ +#endif diff --git a/build/autoconf/acwinpaths.m4 b/build/autoconf/acwinpaths.m4 new file mode 100644 index 000000000..ad9c754bb --- /dev/null +++ b/build/autoconf/acwinpaths.m4 @@ -0,0 +1,31 @@ +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/. + +define(GENERATE_SUB_ABS, [ +define([AC_OUTPUT_FILES_SUB1], [ +patsubst($@, [/\*)], [/* | ?:/*)]) +]) +]) +GENERATE_SUB_ABS(defn([AC_OUTPUT_FILES])) + +define(GENERATE_SUB_NOSPLIT, [ +define([AC_OUTPUT_FILES], [ +patsubst($@, [-e "s%:% \$ac_given_srcdir/%g"], []) +]) +]) +GENERATE_SUB_NOSPLIT(defn([AC_OUTPUT_FILES_SUB1])) + +define(GENERATE_HEADER_NOSPLIT, [ +define([AC_OUTPUT_HEADER], [ +patsubst($@, [-e "s%:% \$ac_given_srcdir/%g"], []) +]) +]) +GENERATE_HEADER_NOSPLIT(defn([AC_OUTPUT_HEADER])) + +define(GENERATE_SUBDIRS_ABS, [ +define([AC_OUTPUT_SUBDIRS], [ +patsubst($@, [/\*)], [/* | ?:/*)]) +]) +]) +GENERATE_SUBDIRS_ABS(defn([AC_OUTPUT_SUBDIRS])) diff --git a/build/autoconf/alloc.m4 b/build/autoconf/alloc.m4 new file mode 100644 index 000000000..6a0772f58 --- /dev/null +++ b/build/autoconf/alloc.m4 @@ -0,0 +1,51 @@ +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 Check for the existence of various allocation headers/functions +AC_DEFUN([MOZ_CHECK_ALLOCATOR],[ + +MALLOC_HEADERS="malloc.h malloc_np.h malloc/malloc.h sys/malloc.h" +MALLOC_H= + +for file in $MALLOC_HEADERS; do + MOZ_CHECK_HEADER($file, [MALLOC_H=$file]) + if test "$MALLOC_H" != ""; then + AC_DEFINE_UNQUOTED(MALLOC_H, <$MALLOC_H>) + break + fi +done + +AC_CHECK_FUNCS(strndup posix_memalign memalign) + +AC_CHECK_FUNCS(malloc_usable_size) +MALLOC_USABLE_SIZE_CONST_PTR=const +if test -n "$HAVE_MALLOC_H"; then + AC_MSG_CHECKING([whether malloc_usable_size definition can use const argument]) + AC_TRY_COMPILE([#include <malloc.h> + #include <stddef.h> + size_t malloc_usable_size(const void *ptr);], + [return malloc_usable_size(0);], + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no]) + MALLOC_USABLE_SIZE_CONST_PTR=) +fi +AC_DEFINE_UNQUOTED([MALLOC_USABLE_SIZE_CONST_PTR],[$MALLOC_USABLE_SIZE_CONST_PTR]) + + +dnl In newer bionic headers, valloc is built but not defined, +dnl so we check more carefully here. +AC_MSG_CHECKING([for valloc in malloc.h]) +AC_EGREP_HEADER(valloc, malloc.h, + AC_DEFINE(HAVE_VALLOC) + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no])) + +AC_MSG_CHECKING([for valloc in unistd.h]) +AC_EGREP_HEADER(valloc, unistd.h, + AC_DEFINE(HAVE_VALLOC) + AC_MSG_RESULT([yes]), + AC_MSG_RESULT([no])) + + +]) diff --git a/build/autoconf/altoptions.m4 b/build/autoconf/altoptions.m4 new file mode 100644 index 000000000..bf6c3e6cf --- /dev/null +++ b/build/autoconf/altoptions.m4 @@ -0,0 +1,95 @@ +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 altoptions.m4 - An alternative way of specifying command-line options. +dnl These macros are needed to support a menu-based configurator. +dnl This file also includes the macro, AM_READ_MYCONFIG, for reading +dnl the 'myconfig.m4' file. + +dnl Send comments, improvements, bugs to Steve Lamm (slamm@netscape.com). + + +dnl MOZ_ARG_ENABLE_BOOL( NAME, HELP, IF-YES [, IF-NO [, ELSE]]) +dnl MOZ_ARG_DISABLE_BOOL( NAME, HELP, IF-NO [, IF-YES [, ELSE]]) +dnl MOZ_ARG_ENABLE_STRING( NAME, HELP, IF-SET [, ELSE]) +dnl MOZ_ARG_ENABLE_BOOL_OR_STRING( NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]]) +dnl MOZ_ARG_WITH_BOOL( NAME, HELP, IF-YES [, IF-NO [, ELSE]) +dnl MOZ_ARG_WITHOUT_BOOL( NAME, HELP, IF-NO [, IF-YES [, ELSE]) +dnl MOZ_ARG_WITH_STRING( NAME, HELP, IF-SET [, ELSE]) +dnl MOZ_ARG_HEADER(Comment) +dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file + +define([MOZ_DIVERSION_ARGS], 12) + +AC_DEFUN([MOZ_ARG],[dnl +AC_DIVERT_PUSH(MOZ_DIVERSION_ARGS)dnl + '$1', +AC_DIVERT_POP()dnl +]) +AC_DEFUN([MOZ_AC_ARG_ENABLE],[MOZ_ARG([--enable-]translit([$1],[_],[-]))AC_ARG_ENABLE([$1], [$2], [$3], [$4])]) +AC_DEFUN([MOZ_AC_ARG_WITH],[MOZ_ARG([--with-]translit([$1],[_],[-]))AC_ARG_WITH([$1], [$2], [$3], [$4])]) + +dnl MOZ_TWO_STRING_TEST(NAME, VAL, STR1, IF-STR1, STR2, IF-STR2 [, ELSE]) +AC_DEFUN([MOZ_TWO_STRING_TEST], +[if test "[$2]" = "[$3]"; then + ifelse([$4], , :, [$4]) + elif test "[$2]" = "[$5]"; then + ifelse([$6], , :, [$6]) + else + ifelse([$7], , + [AC_MSG_ERROR([Option, [$1], does not take an argument ([$2]).])], + [$7]) + fi]) + +dnl MOZ_ARG_ENABLE_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE]]) +AC_DEFUN([MOZ_ARG_ENABLE_BOOL], +[MOZ_AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4])], + [$5])]) + +dnl MOZ_ARG_DISABLE_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE]]) +AC_DEFUN([MOZ_ARG_DISABLE_BOOL], +[MOZ_AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], no, [$3], yes, [$4])], + [$5])]) + +dnl MOZ_ARG_ENABLE_STRING(NAME, HELP, IF-SET [, ELSE]) +AC_DEFUN([MOZ_ARG_ENABLE_STRING], +[MOZ_AC_ARG_ENABLE([$1], [$2], [$3], [$4])]) + +dnl MOZ_ARG_ENABLE_BOOL_OR_STRING(NAME, HELP, IF-YES, IF-NO, IF-SET[, ELSE]]]) +AC_DEFUN([MOZ_ARG_ENABLE_BOOL_OR_STRING], +[ifelse([$5], , + [errprint([Option, $1, needs an "IF-SET" argument. +]) + m4exit(1)], + [MOZ_AC_ARG_ENABLE([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$enableval], yes, [$3], no, [$4], [$5])], + [$6])])]) + +dnl MOZ_ARG_WITH_BOOL(NAME, HELP, IF-YES [, IF-NO [, ELSE]) +AC_DEFUN([MOZ_ARG_WITH_BOOL], +[MOZ_AC_ARG_WITH([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$withval], yes, [$3], no, [$4])], + [$5])]) + +dnl MOZ_ARG_WITHOUT_BOOL(NAME, HELP, IF-NO [, IF-YES [, ELSE]) +AC_DEFUN([MOZ_ARG_WITHOUT_BOOL], +[MOZ_AC_ARG_WITH([$1], [$2], + [MOZ_TWO_STRING_TEST([$1], [$withval], no, [$3], yes, [$4])], + [$5])]) + +dnl MOZ_ARG_WITH_STRING(NAME, HELP, IF-SET [, ELSE]) +AC_DEFUN([MOZ_ARG_WITH_STRING], +[MOZ_AC_ARG_WITH([$1], [$2], [$3], [$4])]) + +dnl MOZ_ARG_HEADER(Comment) +dnl This is used by webconfig to group options +define(MOZ_ARG_HEADER, [# $1]) + +dnl MOZ_READ_MYCONFIG() - Read in 'myconfig.sh' file +AC_DEFUN([MOZ_READ_MOZCONFIG], +[AC_REQUIRE([AC_INIT_BINSH])dnl +. ./old-configure.vars +]) diff --git a/build/autoconf/android.m4 b/build/autoconf/android.m4 new file mode 100644 index 000000000..4ab6d5a82 --- /dev/null +++ b/build/autoconf/android.m4 @@ -0,0 +1,376 @@ +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/. + +AC_DEFUN([MOZ_ANDROID_NDK], +[ + +MOZ_ARG_WITH_STRING(android-cxx-stl, +[ --with-android-cxx-stl=VALUE + use the specified C++ STL (libstdc++, libc++)], + android_cxx_stl=$withval, + android_cxx_stl=libc++) + +case "$target" in +*-android*|*-linuxandroid*) + dnl $android_platform will be set for us by Python configure. + CPPFLAGS="-idirafter $android_platform/usr/include $CPPFLAGS" + CFLAGS="-fno-short-enums -fno-exceptions $CFLAGS" + CXXFLAGS="-fno-short-enums -fno-exceptions $CXXFLAGS" + ASFLAGS="-idirafter $android_platform/usr/include -DANDROID $ASFLAGS" + + dnl Add --allow-shlib-undefined, because libGLESv2 links to an + dnl undefined symbol (present on the hardware, just not in the + dnl NDK.) + LDFLAGS="-L$android_platform/usr/lib -Wl,-rpath-link=$android_platform/usr/lib --sysroot=$android_platform -Wl,--allow-shlib-undefined $LDFLAGS" + dnl Add -llog by default, since we use it all over the place. + LIBS="-llog $LIBS" + ANDROID_PLATFORM="${android_platform}" + + AC_DEFINE(ANDROID) + AC_SUBST(ANDROID_PLATFORM) + + ;; +esac + +]) + +AC_DEFUN([MOZ_ANDROID_CPU_ARCH], +[ + +if test "$OS_TARGET" = "Android"; then + case "${CPU_ARCH}-${MOZ_ARCH}" in + arm-armv7*) + ANDROID_CPU_ARCH=armeabi-v7a + ;; + arm-*) + ANDROID_CPU_ARCH=armeabi + ;; + x86-*) + ANDROID_CPU_ARCH=x86 + ;; + mips32-*) # When target_cpu is mipsel, CPU_ARCH is mips32 + ANDROID_CPU_ARCH=mips + ;; + aarch64-*) + ANDROID_CPU_ARCH=arm64-v8a + ;; + esac + + AC_SUBST(ANDROID_CPU_ARCH) +fi +]) + +AC_DEFUN([MOZ_ANDROID_STLPORT], +[ + +if test "$OS_TARGET" = "Android"; then + cpu_arch_dir="$ANDROID_CPU_ARCH" + # NDK r12 removed the arm/thumb library split and just made everything + # thumb by default. Attempt to compensate. + if test "$MOZ_THUMB2" = 1 -a -d "$cpu_arch_dir/thumb"; then + cpu_arch_dir="$cpu_arch_dir/thumb" + fi + + if test -z "$STLPORT_CPPFLAGS$STLPORT_LIBS"; then + case "$android_cxx_stl" in + libstdc++) + # android-ndk-r8b and later + ndk_base="$android_ndk/sources/cxx-stl/gnu-libstdc++/$android_gnu_compiler_version" + ndk_libs_include="$ndk_base/libs/$ANDROID_CPU_ARCH" + ndk_libs="$ndk_base/libs/$cpu_arch_dir" + ndk_include="$ndk_base/include" + + if ! test -e "$ndk_libs/libgnustl_static.a"; then + AC_MSG_ERROR([Couldn't find path to gnu-libstdc++ in the android ndk]) + fi + + STLPORT_LIBS="-L$ndk_libs -lgnustl_static" + STLPORT_CPPFLAGS="-I$ndk_include -I$ndk_include/backward -I$ndk_libs_include/include" + ;; + libc++) + # android-ndk-r8b and later + ndk_base="$android_ndk/sources/cxx-stl" + cxx_base="$ndk_base/llvm-libc++" + cxx_libs="$cxx_base/libs/$cpu_arch_dir" + cxx_include="$cxx_base/libcxx/include" + cxxabi_base="$ndk_base/llvm-libc++abi" + cxxabi_include="$cxxabi_base/libcxxabi/include" + + if ! test -e "$cxx_libs/libc++_static.a"; then + AC_MSG_ERROR([Couldn't find path to llvm-libc++ in the android ndk]) + fi + + STLPORT_LIBS="-L$cxx_libs -lc++_static" + # NDK r12 split the libc++ runtime libraries into pieces. + for lib in c++abi unwind android_support; do + if test -e "$cxx_libs/lib${lib}.a"; then + STLPORT_LIBS="$STLPORT_LIBS -l${lib}" + fi + done + # Add android/support/include/ for prototyping long double math + # functions, locale-specific C library functions, multibyte support, + # etc. + STLPORT_CPPFLAGS="-I$android_ndk/sources/android/support/include -I$cxx_include -I$cxxabi_include" + ;; + *) + AC_MSG_ERROR([Bad value for --enable-android-cxx-stl]) + ;; + esac + fi + CXXFLAGS="$CXXFLAGS $STLPORT_CPPFLAGS" +fi +MOZ_ANDROID_CXX_STL=$android_cxx_stl +AC_SUBST([MOZ_ANDROID_CXX_STL]) +AC_SUBST([STLPORT_LIBS]) + +]) + + +AC_DEFUN([concat],[$1$2$3$4]) + +dnl Find a component of an AAR. +dnl Arg 1: variable name to expose, like ANDROID_SUPPORT_V4_LIB. +dnl Arg 2: path to component. +dnl Arg 3: if non-empty, expect and require component. +AC_DEFUN([MOZ_ANDROID_AAR_COMPONENT], [ + ifelse([$3], , + [ + if test -e "$$1" ; then + AC_MSG_ERROR([Found unexpected exploded $1!]) + fi + ], + [ + AC_MSG_CHECKING([for $1]) + $1="$2" + if ! test -e "$$1" ; then + AC_MSG_ERROR([Could not find required exploded $1!]) + fi + AC_MSG_RESULT([$$1]) + AC_SUBST($1) + ]) +]) + +dnl Find an AAR and expose variables representing its exploded components. +dnl AC_SUBSTs ANDROID_NAME_{AAR,AAR_RES,AAR_LIB,AAR_INTERNAL_LIB}. +dnl Arg 1: name, like play-services-base +dnl Arg 2: version, like 7.8.0 +dnl Arg 3: extras subdirectory, either android or google +dnl Arg 4: package subdirectory, like com/google/android/gms +dnl Arg 5: if non-empty, expect and require internal_impl JAR. +dnl Arg 6: if non-empty, expect and require assets/ directory. +AC_DEFUN([MOZ_ANDROID_AAR],[ + define([local_aar_var_base], translit($1, [-a-z], [_A-Z])) + define([local_aar_var], concat(ANDROID_, local_aar_var_base, _AAR)) + local_aar_var="$ANDROID_SDK_ROOT/extras/$3/m2repository/$4/$1/$2/$1-$2.aar" + AC_MSG_CHECKING([for $1 AAR]) + if ! test -e "$local_aar_var" ; then + AC_MSG_ERROR([You must download the $1 AAR. Run the Android SDK tool and install the Android and Google Support Repositories under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (Looked for $local_aar_var)]) + fi + AC_SUBST(local_aar_var) + AC_MSG_RESULT([$local_aar_var]) + + if ! $PYTHON -m mozbuild.action.explode_aar --destdir=$MOZ_BUILD_ROOT/dist/exploded-aar $local_aar_var ; then + AC_MSG_ERROR([Could not explode $local_aar_var!]) + fi + + define([root], $MOZ_BUILD_ROOT/dist/exploded-aar/$1-$2/) + MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _LIB), concat(root, $1-$2-classes.jar), REQUIRED) + MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _RES), concat(root, res), REQUIRED) + MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _INTERNAL_LIB), concat(root, libs/$1-$2-internal_impl-$2.jar), $5) + MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _ASSETS), concat(root, assets), $6) +]) + +AC_DEFUN([MOZ_ANDROID_GOOGLE_PLAY_SERVICES], +[ + +if test -n "$MOZ_NATIVE_DEVICES" ; then + AC_SUBST(MOZ_NATIVE_DEVICES) + + MOZ_ANDROID_AAR(play-services-base, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-cast, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) + MOZ_ANDROID_AAR(mediarouter-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support, REQUIRED_INTERNAL_IMPL) +fi + +]) + +AC_DEFUN([MOZ_ANDROID_GOOGLE_CLOUD_MESSAGING], +[ + +if test -n "$MOZ_ANDROID_GCM" ; then + MOZ_ANDROID_AAR(play-services-base, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-gcm, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-measurement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) +fi + +]) + +AC_DEFUN([MOZ_ANDROID_INSTALL_TRACKING], +[ + +if test -n "$MOZ_INSTALL_TRACKING"; then + AC_SUBST(MOZ_INSTALL_TRACKING) + MOZ_ANDROID_AAR(play-services-ads, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) + MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms) +fi + +]) + +dnl Configure an Android SDK. +dnl Arg 1: target SDK version, like 23. +dnl Arg 2: list of build-tools versions, like "23.0.3 23.0.1". +AC_DEFUN([MOZ_ANDROID_SDK], +[ + +MOZ_ARG_WITH_STRING(android-sdk, +[ --with-android-sdk=DIR + location where the Android SDK can be found (like ~/.mozbuild/android-sdk-linux)], + android_sdk_root=$withval) + +android_sdk_root=${withval%/platforms/android-*} + +case "$target" in +*-android*|*-linuxandroid*) + if test -z "$android_sdk_root" ; then + AC_MSG_ERROR([You must specify --with-android-sdk=/path/to/sdk when targeting Android.]) + fi + + # We were given an old-style + # --with-android-sdk=/path/to/sdk/platforms/android-*. We could warn, but + # we'll get compliance by forcing the issue. + if test -e "$withval"/source.properties ; then + AC_MSG_ERROR([Including platforms/android-* in --with-android-sdk arguments is deprecated. Use --with-android-sdk=$android_sdk_root.]) + fi + + android_target_sdk=$1 + AC_MSG_CHECKING([for Android SDK platform version $android_target_sdk]) + android_sdk=$android_sdk_root/platforms/android-$android_target_sdk + if ! test -e "$android_sdk/source.properties" ; then + AC_MSG_ERROR([You must download Android SDK platform version $android_target_sdk. Try |mach bootstrap|. (Looked for $android_sdk)]) + fi + AC_MSG_RESULT([$android_sdk]) + + AC_MSG_CHECKING([for Android build-tools]) + android_build_tools_base="$android_sdk_root"/build-tools + android_build_tools_version="" + for version in $2; do + android_build_tools="$android_build_tools_base"/$version + if test -d "$android_build_tools" -a -f "$android_build_tools/aapt"; then + android_build_tools_version=$version + AC_MSG_RESULT([$android_build_tools]) + break + fi + done + if test "$android_build_tools_version" = ""; then + version=$(echo $2 | cut -d" " -f1) + AC_MSG_ERROR([You must install the Android build-tools version $version. Try |mach bootstrap|. (Looked for "$android_build_tools_base"/$version)]) + fi + + MOZ_PATH_PROG(ZIPALIGN, zipalign, :, [$android_build_tools]) + MOZ_PATH_PROG(DX, dx, :, [$android_build_tools]) + MOZ_PATH_PROG(AAPT, aapt, :, [$android_build_tools]) + MOZ_PATH_PROG(AIDL, aidl, :, [$android_build_tools]) + if test -z "$ZIPALIGN" -o "$ZIPALIGN" = ":"; then + AC_MSG_ERROR([The program zipalign was not found. Try |mach bootstrap|.]) + fi + if test -z "$DX" -o "$DX" = ":"; then + AC_MSG_ERROR([The program dx was not found. Try |mach bootstrap|.]) + fi + if test -z "$AAPT" -o "$AAPT" = ":"; then + AC_MSG_ERROR([The program aapt was not found. Try |mach bootstrap|.]) + fi + if test -z "$AIDL" -o "$AIDL" = ":"; then + AC_MSG_ERROR([The program aidl was not found. Try |mach bootstrap|.]) + fi + + android_platform_tools="$android_sdk_root"/platform-tools + AC_MSG_CHECKING([for Android platform-tools]) + if test -d "$android_platform_tools" -a -f "$android_platform_tools/adb"; then + AC_MSG_RESULT([$android_platform_tools]) + else + AC_MSG_ERROR([You must install the Android platform-tools. Try |mach bootstrap|. (Looked for $android_platform_tools)]) + fi + + MOZ_PATH_PROG(ADB, adb, :, [$android_platform_tools]) + if test -z "$ADB" -o "$ADB" = ":"; then + AC_MSG_ERROR([The program adb was not found. Try |mach bootstrap|.]) + fi + + android_tools="$android_sdk_root"/tools + AC_MSG_CHECKING([for Android tools]) + if test -d "$android_tools" -a -f "$android_tools/emulator"; then + AC_MSG_RESULT([$android_tools]) + else + AC_MSG_ERROR([You must install the Android tools. Try |mach bootstrap|. (Looked for $android_tools)]) + fi + + MOZ_PATH_PROG(EMULATOR, emulator, :, [$android_tools]) + if test -z "$EMULATOR" -o "$EMULATOR" = ":"; then + AC_MSG_ERROR([The program emulator was not found. Try |mach bootstrap|.]) + fi + + ANDROID_TARGET_SDK="${android_target_sdk}" + ANDROID_SDK="${android_sdk}" + ANDROID_SDK_ROOT="${android_sdk_root}" + ANDROID_TOOLS="${android_tools}" + ANDROID_BUILD_TOOLS_VERSION="$android_build_tools_version" + AC_DEFINE_UNQUOTED(ANDROID_TARGET_SDK,$ANDROID_TARGET_SDK) + AC_SUBST(ANDROID_TARGET_SDK) + AC_SUBST(ANDROID_SDK_ROOT) + AC_SUBST(ANDROID_SDK) + AC_SUBST(ANDROID_TOOLS) + AC_SUBST(ANDROID_BUILD_TOOLS_VERSION) + + MOZ_ANDROID_AAR(customtabs, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + MOZ_ANDROID_AAR(appcompat-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + MOZ_ANDROID_AAR(support-vector-drawable, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + MOZ_ANDROID_AAR(animated-vector-drawable, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + MOZ_ANDROID_AAR(cardview-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + MOZ_ANDROID_AAR(design, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + MOZ_ANDROID_AAR(recyclerview-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + MOZ_ANDROID_AAR(support-v4, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support, REQUIRED_INTERNAL_IMPL) + MOZ_ANDROID_AAR(palette-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support) + + ANDROID_SUPPORT_ANNOTATIONS_JAR="$ANDROID_SDK_ROOT/extras/android/m2repository/com/android/support/support-annotations/$ANDROID_SUPPORT_LIBRARY_VERSION/support-annotations-$ANDROID_SUPPORT_LIBRARY_VERSION.jar" + AC_MSG_CHECKING([for support-annotations JAR]) + if ! test -e $ANDROID_SUPPORT_ANNOTATIONS_JAR ; then + AC_MSG_ERROR([You must download the support-annotations lib. Run the Android SDK tool and install the Android Support Repository under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_SUPPORT_ANNOTATIONS_JAR)]) + fi + AC_MSG_RESULT([$ANDROID_SUPPORT_ANNOTATIONS_JAR]) + AC_SUBST(ANDROID_SUPPORT_ANNOTATIONS_JAR) + ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB=$ANDROID_SUPPORT_ANNOTATIONS_JAR + AC_SUBST(ANDROID_SUPPORT_ANNOTATIONS_JAR_LIB) + ;; +esac + +MOZ_ARG_WITH_STRING(android-min-sdk, +[ --with-android-min-sdk=[VER] Impose a minimum Firefox for Android SDK version], +[ MOZ_ANDROID_MIN_SDK_VERSION=$withval ]) + +MOZ_ARG_WITH_STRING(android-max-sdk, +[ --with-android-max-sdk=[VER] Impose a maximum Firefox for Android SDK version], +[ MOZ_ANDROID_MAX_SDK_VERSION=$withval ]) + +if test -n "$MOZ_ANDROID_MIN_SDK_VERSION"; then + if test -n "$MOZ_ANDROID_MAX_SDK_VERSION"; then + if test $MOZ_ANDROID_MAX_SDK_VERSION -lt $MOZ_ANDROID_MIN_SDK_VERSION ; then + AC_MSG_ERROR([--with-android-max-sdk must be at least the value of --with-android-min-sdk.]) + fi + fi + + if test $MOZ_ANDROID_MIN_SDK_VERSION -gt $ANDROID_TARGET_SDK ; then + AC_MSG_ERROR([--with-android-min-sdk is expected to be less than $ANDROID_TARGET_SDK]) + fi + + AC_DEFINE_UNQUOTED(MOZ_ANDROID_MIN_SDK_VERSION, $MOZ_ANDROID_MIN_SDK_VERSION) + AC_SUBST(MOZ_ANDROID_MIN_SDK_VERSION) +fi + +if test -n "$MOZ_ANDROID_MAX_SDK_VERSION"; then + AC_DEFINE_UNQUOTED(MOZ_ANDROID_MAX_SDK_VERSION, $MOZ_ANDROID_MAX_SDK_VERSION) + AC_SUBST(MOZ_ANDROID_MAX_SDK_VERSION) +fi + +]) diff --git a/build/autoconf/arch.m4 b/build/autoconf/arch.m4 new file mode 100644 index 000000000..20af6ad7d --- /dev/null +++ b/build/autoconf/arch.m4 @@ -0,0 +1,254 @@ +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/. + +AC_DEFUN([MOZ_ARCH_OPTS], +[ + +dnl ======================================================== +dnl = ARM toolchain tweaks +dnl ======================================================== + +MOZ_THUMB=toolchain-default +MOZ_THUMB_INTERWORK=toolchain-default +MOZ_FPU=toolchain-default +MOZ_FLOAT_ABI=toolchain-default +MOZ_SOFT_FLOAT=toolchain-default +MOZ_ALIGN=toolchain-default + +MOZ_ARG_WITH_STRING(arch, +[ --with-arch=[[type|toolchain-default]] + Use specific CPU features (-march=type). Resets + thumb, fpu, float-abi, etc. defaults when set], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-arch is not supported on non-GNU toolchains]) + fi + MOZ_ARCH=$withval) + +if test -z "$MOZ_ARCH"; then + dnl Defaults + case "${CPU_ARCH}-${OS_TARGET}" in + arm-Android) + MOZ_THUMB=yes + MOZ_ARCH=armv7-a + MOZ_FPU=vfp + MOZ_FLOAT_ABI=softfp + MOZ_ALIGN=no + ;; + arm-Darwin) + MOZ_ARCH=toolchain-default + ;; + esac +fi + +if test "$MOZ_ARCH" = "armv6" -a "$OS_TARGET" = "Android"; then + MOZ_FPU=vfp + MOZ_FLOAT_ABI=softfp +fi + +MOZ_ARG_WITH_STRING(thumb, +[ --with-thumb[[=yes|no|toolchain-default]]] +[ Use Thumb instruction set (-mthumb)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb is not supported on non-GNU toolchains]) + fi + MOZ_THUMB=$withval) + +MOZ_ARG_WITH_STRING(thumb-interwork, +[ --with-thumb-interwork[[=yes|no|toolchain-default]] + Use Thumb/ARM instuctions interwork (-mthumb-interwork)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-thumb-interwork is not supported on non-GNU toolchains]) + fi + MOZ_THUMB_INTERWORK=$withval) + +MOZ_ARG_WITH_STRING(fpu, +[ --with-fpu=[[type|toolchain-default]] + Use specific FPU type (-mfpu=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-fpu is not supported on non-GNU toolchains]) + fi + MOZ_FPU=$withval) + +MOZ_ARG_WITH_STRING(float-abi, +[ --with-float-abi=[[type|toolchain-default]] + Use specific arm float ABI (-mfloat-abi=type)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-float-abi is not supported on non-GNU toolchains]) + fi + MOZ_FLOAT_ABI=$withval) + +MOZ_ARG_WITH_STRING(soft-float, +[ --with-soft-float[[=yes|no|toolchain-default]] + Use soft float library (-msoft-float)], + if test -z "$GNU_CC"; then + AC_MSG_ERROR([--with-soft-float is not supported on non-GNU toolchains]) + fi + MOZ_SOFT_FLOAT=$withval) + +case "$MOZ_ARCH" in +toolchain-default|"") + arch_flag="" + ;; +*) + arch_flag="-march=$MOZ_ARCH" + ;; +esac + +case "$MOZ_THUMB" in +yes) + MOZ_THUMB2=1 + thumb_flag="-mthumb" + ;; +no) + MOZ_THUMB2= + thumb_flag="-marm" + ;; +*) + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$arch_flag" + AC_TRY_COMPILE([],[return sizeof(__thumb2__);], + MOZ_THUMB2=1, + MOZ_THUMB2=) + CFLAGS="$_SAVE_CFLAGS" + thumb_flag="" + ;; +esac + +if test "$MOZ_THUMB2" = 1; then + AC_DEFINE(MOZ_THUMB2) +fi + +case "$MOZ_THUMB_INTERWORK" in +yes) + thumb_interwork_flag="-mthumb-interwork" + ;; +no) + thumb_interwork_flag="-mno-thumb-interwork" + ;; +*) # toolchain-default + thumb_interwork_flag="" + ;; +esac + +case "$MOZ_FPU" in +toolchain-default|"") + fpu_flag="" + ;; +*) + fpu_flag="-mfpu=$MOZ_FPU" + ;; +esac + +case "$MOZ_FLOAT_ABI" in +toolchain-default|"") + float_abi_flag="" + ;; +*) + float_abi_flag="-mfloat-abi=$MOZ_FLOAT_ABI" + ;; +esac + +case "$MOZ_SOFT_FLOAT" in +yes) + soft_float_flag="-msoft-float" + ;; +no) + soft_float_flag="-mno-soft-float" + ;; +*) # toolchain-default + soft_float_flag="" + ;; +esac + +case "$MOZ_ALIGN" in +no) + align_flag="-mno-unaligned-access" + ;; +yes) + align_flag="-munaligned-access" + ;; +*) + align_flag="" + ;; +esac + +if test -n "$align_flag"; then + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$CFLAGS $align_flag" + AC_MSG_CHECKING(whether alignment flag ($align_flag) is supported) + AC_TRY_COMPILE([],[],,align_flag="") + CFLAGS="$_SAVE_CFLAGS" +fi + +dnl Use echo to avoid accumulating space characters +all_flags=`echo $arch_flag $thumb_flag $thumb_interwork_flag $fpu_flag $float_abi_flag $soft_float_flag $align_flag` +if test -n "$all_flags"; then + _SAVE_CFLAGS="$CFLAGS" + CFLAGS="$all_flags" + AC_MSG_CHECKING(whether the chosen combination of compiler flags ($all_flags) works) + AC_TRY_COMPILE([],[return 0;], + AC_MSG_RESULT([yes]), + AC_MSG_ERROR([no])) + + CFLAGS="$_SAVE_CFLAGS $all_flags" + CXXFLAGS="$CXXFLAGS $all_flags" + ASFLAGS="$ASFLAGS $all_flags" + if test -n "$thumb_flag"; then + LDFLAGS="$LDFLAGS $thumb_flag" + fi +fi + +AC_SUBST(MOZ_THUMB2) + +if test "$CPU_ARCH" = "arm"; then + NEON_FLAGS="-mfpu=neon" + AC_MSG_CHECKING(for ARM SIMD support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm("uqadd8 r1, r1, r2");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_SIMD) + HAVE_ARM_SIMD=1 + fi + + AC_MSG_CHECKING(ARM version support in compiler) + dnl Determine the target ARM architecture (5 for ARMv5, v5T, v5E, etc.; 6 for ARMv6, v6K, etc.) + ARM_ARCH=`${CC-cc} ${CFLAGS} -dM -E - < /dev/null | sed -n 's/.*__ARM_ARCH_\([[0-9]][[0-9]]*\).*/\1/p'` + AC_MSG_RESULT("$ARM_ARCH") + + AC_MSG_CHECKING(for ARM NEON support in compiler) + # We try to link so that this also fails when + # building with LTO. + AC_TRY_LINK([], + [asm(".fpu neon\n vadd.i8 d0, d0, d0");], + result="yes", result="no") + AC_MSG_RESULT("$result") + if test "$result" = "yes"; then + AC_DEFINE(HAVE_ARM_NEON) + HAVE_ARM_NEON=1 + + dnl We don't need to build NEON support if we're targetting a non-NEON device. + dnl This matches media/webrtc/trunk/webrtc/build/common.gypi. + if test -n "$ARM_ARCH"; then + if test "$ARM_ARCH" -lt 7; then + BUILD_ARM_NEON= + else + AC_DEFINE(BUILD_ARM_NEON) + BUILD_ARM_NEON=1 + fi + fi + fi + +fi # CPU_ARCH = arm + +AC_SUBST(HAVE_ARM_SIMD) +AC_SUBST(HAVE_ARM_NEON) +AC_SUBST(BUILD_ARM_NEON) +AC_SUBST(ARM_ARCH) +AC_SUBST_LIST(NEON_FLAGS) + +]) diff --git a/build/autoconf/clang-plugin.m4 b/build/autoconf/clang-plugin.m4 new file mode 100644 index 000000000..22a48976d --- /dev/null +++ b/build/autoconf/clang-plugin.m4 @@ -0,0 +1,162 @@ +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/. + +AC_DEFUN([MOZ_CONFIG_CLANG_PLUGIN], [ + +MOZ_ARG_ENABLE_BOOL(clang-plugin, +[ --enable-clang-plugin Enable building with the mozilla clang plugin ], + ENABLE_CLANG_PLUGIN=1, + ENABLE_CLANG_PLUGIN= ) +if test -n "$ENABLE_CLANG_PLUGIN"; then + if test -z "${CLANG_CC}${CLANG_CL}"; then + AC_MSG_ERROR([Can't use clang plugin without clang.]) + fi + + AC_MSG_CHECKING([for llvm-config]) + if test -z "$LLVMCONFIG"; then + if test -n "$CLANG_CL"; then + CXX_COMPILER="$(dirname "$CXX")/clang" + else + CXX_COMPILER="${CXX}" + fi + LLVMCONFIG=`$CXX_COMPILER -print-prog-name=llvm-config` + fi + + if test -z "$LLVMCONFIG"; then + LLVMCONFIG=`which llvm-config` + fi + + if test ! -x "$LLVMCONFIG"; then + AC_MSG_RESULT([not found]) + AC_MSG_ERROR([Cannot find an llvm-config binary for building a clang plugin]) + fi + + AC_MSG_RESULT([$LLVMCONFIG]) + + if test -z "$LLVMCONFIG"; then + AC_MSG_ERROR([Cannot find an llvm-config binary for building a clang plugin]) + fi + dnl For some reason the llvm-config downloaded from clang.llvm.org for clang3_8 + dnl produces a -isysroot flag for a sysroot which might not ship when passed + dnl --cxxflags. We use sed to remove this argument so that builds work on OSX + LLVM_CXXFLAGS=`$LLVMCONFIG --cxxflags | sed -e 's/-isysroot [[^ ]]*//'` + + dnl We are loaded into clang, so we don't need to link to very many things, + dnl we just need to link to clangASTMatchers because it is not used by clang + LLVM_LDFLAGS=`$LLVMCONFIG --ldflags | tr '\n' ' '` + + if test "${HOST_OS_ARCH}" = "Darwin"; then + dnl We need to make sure that we use the symbols coming from the clang + dnl binary. In order to do this, we need to pass -flat_namespace and + dnl -undefined suppress to the linker. This makes sure that we link the + dnl symbols into the flat namespace provided by clang, and thus get + dnl access to all of the symbols which are undefined in our dylib as we + dnl are building it right now, and also that we don't fail the build + dnl due to undefined symbols (which will be provided by clang). + CLANG_LDFLAGS="-Wl,-flat_namespace -Wl,-undefined,suppress -lclangASTMatchers" + elif test "${HOST_OS_ARCH}" = "WINNT"; then + CLANG_LDFLAGS="clangASTMatchers.lib" + else + CLANG_LDFLAGS="-lclangASTMatchers" + fi + + if test -n "$CLANG_CL"; then + dnl The llvm-config coming with clang-cl may give us arguments in the + dnl /ARG form, which in msys will be interpreted as a path name. So we + dnl need to split the args and convert the leading slashes that we find + dnl into a dash. + LLVM_REPLACE_CXXFLAGS='' + for arg in $LLVM_CXXFLAGS; do + dnl The following expression replaces a leading slash with a dash. + dnl Also replace any backslashes with forward slash. + arg=`echo "$arg"|sed -e 's/^\//-/' -e 's/\\\\/\//g'` + LLVM_REPLACE_CXXFLAGS="$LLVM_REPLACE_CXXFLAGS $arg" + done + LLVM_CXXFLAGS="$LLVM_REPLACE_CXXFLAGS" + + LLVM_REPLACE_LDFLAGS='' + for arg in $LLVM_LDFLAGS; do + dnl The following expression replaces a leading slash with a dash. + dnl Also replace any backslashes with forward slash. + arg=`echo "$arg"|sed -e 's/^\//-/' -e 's/\\\\/\//g'` + LLVM_REPLACE_LDFLAGS="$LLVM_REPLACE_LDFLAGS $arg" + done + LLVM_LDFLAGS="$LLVM_REPLACE_LDFLAGS" + + CLANG_REPLACE_LDFLAGS='' + for arg in $CLANG_LDFLAGS; do + dnl The following expression replaces a leading slash with a dash. + dnl Also replace any backslashes with forward slash. + arg=`echo "$arg"|sed -e 's/^\//-/' -e 's/\\\\/\//g'` + CLANG_REPLACE_LDFLAGS="$CLANG_REPLACE_LDFLAGS $arg" + done + CLANG_LDFLAGS="$CLANG_REPLACE_LDFLAGS" + fi + + dnl Check for the new ASTMatcher API names. Since this happened in the + dnl middle of the 3.8 cycle, our CLANG_VERSION_FULL is impossible to use + dnl correctly, so we have to detect this at configure time. + AC_CACHE_CHECK(for new ASTMatcher API, + ac_cv_have_new_ASTMatcher_api, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + _SAVE_CXXFLAGS="$CXXFLAGS" + _SAVE_CXX="$CXX" + _SAVE_MACOSX_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" + unset MACOSX_DEPLOYMENT_TARGET + CXXFLAGS="${LLVM_CXXFLAGS}" + CXX="${HOST_CXX}" + AC_TRY_COMPILE([#include "clang/ASTMatchers/ASTMatchers.h"], + [clang::ast_matchers::cxxConstructExpr();], + ac_cv_have_new_ASTMatcher_names="yes", + ac_cv_have_new_ASTMatcher_names="no") + CXX="$_SAVE_CXX" + CXXFLAGS="$_SAVE_CXXFLAGS" + export MACOSX_DEPLOYMENT_TARGET="$_SAVE_MACOSX_DEPLOYMENT_TARGET" + AC_LANG_RESTORE + ]) + if test "$ac_cv_have_new_ASTMatcher_names" = "yes"; then + LLVM_CXXFLAGS="$LLVM_CXXFLAGS -DHAVE_NEW_ASTMATCHER_NAMES" + fi + + dnl Check if we can compile has(ignoringParenImpCasts()) because + dnl before 3.9 that ignoringParenImpCasts was done internally by "has". + dnl See https://www.mail-archive.com/cfe-commits@lists.llvm.org/msg25234.html + AC_CACHE_CHECK(for has with ignoringParenImpCasts, + ac_cv_has_accepts_ignoringParenImpCasts, + [ + AC_LANG_SAVE + AC_LANG_CPLUSPLUS + _SAVE_CXXFLAGS="$CXXFLAGS" + _SAVE_CXX="$CXX" + _SAVE_MACOSX_DEPLOYMENT_TARGET="$MACOSX_DEPLOYMENT_TARGET" + unset MACOSX_DEPLOYMENT_TARGET + CXXFLAGS="${LLVM_CXXFLAGS}" + CXX="${HOST_CXX}" + AC_TRY_COMPILE([#include "clang/ASTMatchers/ASTMatchers.h"], + [using namespace clang::ast_matchers; + expr(has(ignoringParenImpCasts(declRefExpr()))); + ], + ac_cv_has_accepts_ignoringParenImpCasts="yes", + ac_cv_has_accepts_ignoringParenImpCasts="no") + CXX="$_SAVE_CXX" + CXXFLAGS="$_SAVE_CXXFLAGS" + export MACOSX_DEPLOYMENT_TARGET="$_SAVE_MACOSX_DEPLOYMENT_TARGET" + AC_LANG_RESTORE + ]) + if test "$ac_cv_has_accepts_ignoringParenImpCasts" = "yes"; then + LLVM_CXXFLAGS="$LLVM_CXXFLAGS -DHAS_ACCEPTS_IGNORINGPARENIMPCASTS" + fi + + AC_DEFINE(MOZ_CLANG_PLUGIN) +fi + +AC_SUBST(LLVM_CXXFLAGS) +AC_SUBST(LLVM_LDFLAGS) +AC_SUBST(CLANG_LDFLAGS) + +AC_SUBST(ENABLE_CLANG_PLUGIN) + +]) diff --git a/build/autoconf/codeset.m4 b/build/autoconf/codeset.m4 new file mode 100644 index 000000000..3a25c4296 --- /dev/null +++ b/build/autoconf/codeset.m4 @@ -0,0 +1,25 @@ +# codeset.m4 serial AM1 (gettext-0.10.40) +dnl Copyright (C) 2000-2002 Free Software Foundation, Inc. +dnl This file is free software, distributed under the terms of the GNU +dnl General Public License. As a special exception to the GNU General +dnl Public License, this file may be distributed as part of a program +dnl that contains a configuration script generated by Autoconf, under +dnl the same distribution terms as the rest of that program. + +dnl From Bruno Haible. + +AC_DEFUN([AM_LANGINFO_CODESET], +[ + AC_CACHE_CHECK([for nl_langinfo and CODESET], am_cv_langinfo_codeset, + [AC_TRY_LINK([#include <langinfo.h>], + [char* cs = nl_langinfo(CODESET);], + am_cv_langinfo_codeset=yes, + am_cv_langinfo_codeset=no) + ]) + if test $am_cv_langinfo_codeset = yes; then + AC_DEFINE(HAVE_LANGINFO_CODESET, 1, + [Define if you have <langinfo.h> and nl_langinfo(CODESET).]) + HAVE_LANGINFO_CODESET=1 + fi + AC_SUBST(HAVE_LANGINFO_CODESET) +]) diff --git a/build/autoconf/compiler-opts.m4 b/build/autoconf/compiler-opts.m4 new file mode 100644 index 000000000..5e495e107 --- /dev/null +++ b/build/autoconf/compiler-opts.m4 @@ -0,0 +1,316 @@ +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 Add compiler specific options + +AC_DEFUN([MOZ_DEFAULT_COMPILER], +[ +dnl set DEVELOPER_OPTIONS early; MOZ_DEFAULT_COMPILER is usually the first non-setup directive + if test -z "$MOZILLA_OFFICIAL"; then + DEVELOPER_OPTIONS=1 + fi + MOZ_ARG_ENABLE_BOOL(release, + [ --enable-release Build with more conservative, release engineering-oriented options. + This may slow down builds.], + DEVELOPER_OPTIONS=, + DEVELOPER_OPTIONS=1) + +dnl Default to MSVC for win32 and gcc-4.2 for darwin +dnl ============================================================== +if test -z "$CROSS_COMPILE"; then +case "$target" in +*-mingw*) + if test -z "$CPP"; then CPP="$CC -E -nologo"; fi + if test -z "$CXXCPP"; then CXXCPP="$CXX -TP -E -nologo"; ac_cv_prog_CXXCPP="$CXXCPP"; fi + if test -z "$AS"; then + case "${target_cpu}" in + i*86) + AS=ml; + ;; + x86_64) + AS=ml64; + ;; + esac + fi + if test -z "$MIDL"; then MIDL=midl; fi + + # need override this flag since we don't use $(LDFLAGS) for this. + if test -z "$HOST_LDFLAGS" ; then + HOST_LDFLAGS=" " + fi + ;; +esac +fi +]) + +dnl ============================================================================ +dnl C++ rtti +dnl We don't use it in the code, but it can be usefull for debugging, so give +dnl the user the option of enabling it. +dnl ============================================================================ +AC_DEFUN([MOZ_RTTI], +[ +MOZ_ARG_ENABLE_BOOL(cpp-rtti, +[ --enable-cpp-rtti Enable C++ RTTI ], +[ _MOZ_USE_RTTI=1 ], +[ _MOZ_USE_RTTI= ]) + +if test -z "$_MOZ_USE_RTTI"; then + if test "$GNU_CC"; then + CXXFLAGS="$CXXFLAGS -fno-rtti" + else + case "$target" in + *-mingw*) + CXXFLAGS="$CXXFLAGS -GR-" + esac + fi +fi +]) + +dnl ======================================================== +dnl = +dnl = Debugging Options +dnl = +dnl ======================================================== +AC_DEFUN([MOZ_DEBUGGING_OPTS], +[ + +if test -z "$MOZ_DEBUG" -o -n "$MOZ_ASAN"; then + MOZ_NO_DEBUG_RTL=1 +fi + +AC_SUBST(MOZ_NO_DEBUG_RTL) + +MOZ_DEBUG_ENABLE_DEFS="DEBUG TRACING" +MOZ_ARG_WITH_STRING(debug-label, +[ --with-debug-label=LABELS + Define DEBUG_<value> for each comma-separated + value given.], +[ for option in `echo $withval | sed 's/,/ /g'`; do + MOZ_DEBUG_ENABLE_DEFS="$MOZ_DEBUG_ENABLE_DEFS DEBUG_${option}" +done]) + +if test -n "$MOZ_DEBUG"; then + if test -n "$COMPILE_ENVIRONMENT"; then + AC_MSG_CHECKING([for valid debug flags]) + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $MOZ_DEBUG_FLAGS" + AC_TRY_COMPILE([#include <stdio.h>], + [printf("Hello World\n");], + _results=yes, + _results=no) + AC_MSG_RESULT([$_results]) + if test "$_results" = "no"; then + AC_MSG_ERROR([These compiler flags are invalid: $MOZ_DEBUG_FLAGS]) + fi + CFLAGS=$_SAVE_CFLAGS + fi + + MOZ_DEBUG_DEFINES="$MOZ_DEBUG_ENABLE_DEFS" +else + MOZ_DEBUG_DEFINES="NDEBUG TRIMMED" +fi + +AC_SUBST_LIST(MOZ_DEBUG_DEFINES) + +]) + +dnl A high level macro for selecting compiler options. +AC_DEFUN([MOZ_COMPILER_OPTS], +[ + MOZ_DEBUGGING_OPTS + MOZ_RTTI +if test "$CLANG_CXX"; then + ## We disable return-type-c-linkage because jsval is defined as a C++ type but is + ## returned by C functions. This is possible because we use knowledge about the ABI + ## to typedef it to a C type with the same layout when the headers are included + ## from C. + _WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wno-unknown-warning-option -Wno-return-type-c-linkage" +fi + +if test -n "$DEVELOPER_OPTIONS"; then + MOZ_FORCE_GOLD=1 +fi + +MOZ_ARG_ENABLE_BOOL(gold, +[ --enable-gold Enable GNU Gold Linker when it is not already the default], + MOZ_FORCE_GOLD=1, + MOZ_FORCE_GOLD= + ) + +if test "$GNU_CC" -a -n "$MOZ_FORCE_GOLD"; then + dnl if the default linker is BFD ld, check if gold is available and try to use it + dnl for local builds only. + if $CC -Wl,--version 2>&1 | grep -q "GNU ld"; then + GOLD=$($CC -print-prog-name=ld.gold) + case "$GOLD" in + /*) + ;; + *) + GOLD=$(which $GOLD) + ;; + esac + if test -n "$GOLD"; then + mkdir -p $_objdir/build/unix/gold + rm -f $_objdir/build/unix/gold/ld + ln -s "$GOLD" $_objdir/build/unix/gold/ld + if $CC -B $_objdir/build/unix/gold -Wl,--version 2>&1 | grep -q "GNU gold"; then + LDFLAGS="$LDFLAGS -B $_objdir/build/unix/gold" + else + rm -rf $_objdir/build/unix/gold + fi + fi + fi +fi +if test "$GNU_CC"; then + if $CC $LDFLAGS -Wl,--version 2>&1 | grep -q "GNU ld"; then + LD_IS_BFD=1 + fi +fi + +AC_SUBST([LD_IS_BFD]) + +if test "$GNU_CC"; then + if test -z "$DEVELOPER_OPTIONS"; then + CFLAGS="$CFLAGS -ffunction-sections -fdata-sections" + CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections" + fi + CFLAGS="$CFLAGS -fno-math-errno" + CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-math-errno" + + if test -z "$CLANG_CC"; then + case "$CC_VERSION" in + 4.*) + ;; + *) + # Lifetime Dead Store Elimination level 2 (default in GCC6+) breaks Gecko. + # Ideally, we'd use -flifetime-dse=1, but that means we'd forcefully + # enable it on optimization levels where it would otherwise not be enabled. + # So we disable it entirely. But since that would mean inconsistency with + # GCC5, which has level 1 depending on optimization level, disable it on + # GCC5 as well, because better safe than sorry. + # Add it first so that a mozconfig can override by setting CFLAGS/CXXFLAGS. + CFLAGS="-fno-lifetime-dse $CFLAGS" + CXXFLAGS="-fno-lifetime-dse $CXXFLAGS" + ;; + esac + fi +fi + +dnl ======================================================== +dnl = Identical Code Folding +dnl ======================================================== + +MOZ_ARG_DISABLE_BOOL(icf, +[ --disable-icf Disable Identical Code Folding], + MOZ_DISABLE_ICF=1, + MOZ_DISABLE_ICF= ) + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$MOZ_DISABLE_ICF" -a -z "$DEVELOPER_OPTIONS"; then + AC_CACHE_CHECK([whether the linker supports Identical Code Folding], + LD_SUPPORTS_ICF, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 42;}' \ + 'int main() {return foo() - bar();}' > conftest.${ac_ext} + # If the linker supports ICF, foo and bar symbols will have + # the same address + if AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS -Wl,--icf=safe -ffunction-sections conftest.${ac_ext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} && + objdump -t conftest${ac_exeext} | awk changequote(<<, >>)'{a[<<$>>6] = <<$>>1} END {if (a["foo"] && (a["foo"] != a["bar"])) { exit 1 }}'changequote([, ]); then + LD_SUPPORTS_ICF=yes + else + LD_SUPPORTS_ICF=no + fi + rm -rf conftest*]) + if test "$LD_SUPPORTS_ICF" = yes; then + _SAVE_LDFLAGS="$LDFLAGS -Wl,--icf=safe" + LDFLAGS="$LDFLAGS -Wl,--icf=safe -Wl,--print-icf-sections" + AC_TRY_LINK([], [], + [LD_PRINT_ICF_SECTIONS=-Wl,--print-icf-sections], + [LD_PRINT_ICF_SECTIONS=]) + AC_SUBST([LD_PRINT_ICF_SECTIONS]) + LDFLAGS="$_SAVE_LDFLAGS" + fi +fi + +dnl ======================================================== +dnl = Automatically remove dead symbols +dnl ======================================================== + +if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$DEVELOPER_OPTIONS"; then + if test -n "$MOZ_DEBUG_FLAGS"; then + dnl See bug 670659 + AC_CACHE_CHECK([whether removing dead symbols breaks debugging], + GC_SECTIONS_BREAKS_DEBUG_RANGES, + [echo 'int foo() {return 42;}' \ + 'int bar() {return 1;}' \ + 'int main() {return foo();}' > conftest.${ac_ext} + if AC_TRY_COMMAND([${CC-cc} -o conftest.${ac_objext} $CFLAGS $MOZ_DEBUG_FLAGS -c conftest.${ac_ext} 1>&2]) && + AC_TRY_COMMAND([${CC-cc} -o conftest${ac_exeext} $LDFLAGS $MOZ_DEBUG_FLAGS -Wl,--gc-sections conftest.${ac_objext} $LIBS 1>&2]) && + test -s conftest${ac_exeext} -a -s conftest.${ac_objext}; then + if test "`$PYTHON -m mozbuild.configure.check_debug_ranges conftest.${ac_objext} conftest.${ac_ext}`" = \ + "`$PYTHON -m mozbuild.configure.check_debug_ranges conftest${ac_exeext} conftest.${ac_ext}`"; then + GC_SECTIONS_BREAKS_DEBUG_RANGES=no + else + GC_SECTIONS_BREAKS_DEBUG_RANGES=yes + fi + else + dnl We really don't expect to get here, but just in case + GC_SECTIONS_BREAKS_DEBUG_RANGES="no, but it's broken in some other way" + fi + rm -rf conftest*]) + if test "$GC_SECTIONS_BREAKS_DEBUG_RANGES" = no; then + DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" + fi + else + DSO_LDOPTS="$DSO_LDOPTS -Wl,--gc-sections" + fi +fi + +# bionic in Android < 4.1 doesn't support PIE +# On OSX, the linker defaults to building PIE programs when targetting OSX 10.7+, +# but not when targetting OSX < 10.7. OSX < 10.7 doesn't support running PIE +# programs, so as long as support for OSX 10.6 is kept, we can't build PIE. +# Even after dropping 10.6 support, MOZ_PIE would not be useful since it's the +# default (and clang says the -pie option is not used). +# On other Unix systems, some file managers (Nautilus) can't start PIE programs +if test -n "$gonkdir" && test "$ANDROID_VERSION" -ge 16; then + MOZ_PIE=1 +else + MOZ_PIE= +fi + +MOZ_ARG_ENABLE_BOOL(pie, +[ --enable-pie Enable Position Independent Executables], + MOZ_PIE=1, + MOZ_PIE= ) + +if test "$GNU_CC" -a -n "$MOZ_PIE"; then + AC_MSG_CHECKING([for PIE support]) + _SAVE_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS $DSO_PIC_CFLAGS -pie" + AC_TRY_LINK(,,AC_MSG_RESULT([yes]) + [MOZ_PROGRAM_LDFLAGS="$MOZ_PROGRAM_LDFLAGS -pie"], + AC_MSG_RESULT([no]) + AC_MSG_ERROR([--enable-pie requires PIE support from the linker.])) + LDFLAGS=$_SAVE_LDFLAGS +fi + +AC_SUBST(MOZ_PROGRAM_LDFLAGS) + +dnl ASan assumes no symbols are being interposed, and when that happens, +dnl it's not happy with it. Unconveniently, since Firefox is exporting +dnl libffi symbols and Gtk+3 pulls system libffi via libwayland-client, +dnl system libffi interposes libffi symbols that ASan assumes are in +dnl libxul, so it barfs about buffer overflows. +dnl Using -Wl,-Bsymbolic ensures no exported symbol can be interposed. +if test -n "$GCC_USE_GNU_LD"; then + case "$LDFLAGS" in + *-fsanitize=address*) + LDFLAGS="$LDFLAGS -Wl,-Bsymbolic" + ;; + esac +fi + +]) diff --git a/build/autoconf/config.guess b/build/autoconf/config.guess new file mode 100755 index 000000000..d5d667d4a --- /dev/null +++ b/build/autoconf/config.guess @@ -0,0 +1,1454 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2016 Free Software Foundation, Inc. + +timestamp='2016-03-24' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +# +# Please send patches to <config-patches@gnu.org>. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system \`$me' is run on. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2016 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +trap 'exit 1' 1 2 15 + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, `CC_FOR_BUILD' used to be named `HOST_CC'. We still +# use `HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +set_cc_for_build=' +trap "exitcode=\$?; (rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null) && exit \$exitcode" 0 ; +trap "rm -f \$tmpfiles 2>/dev/null; rmdir \$tmp 2>/dev/null; exit 1" 1 2 13 15 ; +: ${TMPDIR=/tmp} ; + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir $tmp) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir $tmp) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } ; +dummy=$tmp/dummy ; +tmpfiles="$dummy.c $dummy.o $dummy.rel $dummy" ; +case $CC_FOR_BUILD,$HOST_CC,$CC in + ,,) echo "int x;" > $dummy.c ; + for c in cc gcc c89 c99 ; do + if ($c -c -o $dummy.o $dummy.c) >/dev/null 2>&1 ; then + CC_FOR_BUILD="$c"; break ; + fi ; + done ; + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found ; + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; +esac ; set_cc_for_build= ;' + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if (test -f /.attbin/uname) >/dev/null 2>&1 ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case "${UNAME_SYSTEM}" in +Linux|GNU|GNU/*) + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + LIBC=gnu + + eval $set_cc_for_build + cat <<-EOF > $dummy.c + #include <features.h> + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #else + LIBC=gnu + #endif + EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case "${UNAME_MACHINE}:${UNAME_SYSTEM}:${UNAME_RELEASE}:${UNAME_VERSION}" in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + sysctl="sysctl -n hw.machine_arch" + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/$sysctl 2>/dev/null || \ + /usr/sbin/$sysctl 2>/dev/null || \ + echo unknown)` + case "${UNAME_MACHINE_ARCH}" in + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo ${UNAME_MACHINE_ARCH} | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo ${UNAME_MACHINE_ARCH} | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=${UNAME_MACHINE_ARCH}-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently, or will in the future. + case "${UNAME_MACHINE_ARCH}" in + arm*|earm*|i386|m68k|ns32k|sh3*|sparc|vax) + eval $set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case "${UNAME_MACHINE_ARCH}" in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo ${UNAME_MACHINE_ARCH} | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case "${UNAME_VERSION}" in + Debian*) + release='-gnu' + ;; + *) + release=`echo ${UNAME_RELEASE} | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + echo "${machine}-${os}${release}${abi}" + exit ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-bitrig${UNAME_RELEASE} + exit ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-openbsd${UNAME_RELEASE} + exit ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + echo ${UNAME_MACHINE_ARCH}-unknown-libertybsd${UNAME_RELEASE} + exit ;; + *:ekkoBSD:*:*) + echo ${UNAME_MACHINE}-unknown-ekkobsd${UNAME_RELEASE} + exit ;; + *:SolidBSD:*:*) + echo ${UNAME_MACHINE}-unknown-solidbsd${UNAME_RELEASE} + exit ;; + macppc:MirBSD:*:*) + echo powerpc-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:MirBSD:*:*) + echo ${UNAME_MACHINE}-unknown-mirbsd${UNAME_RELEASE} + exit ;; + *:Sortix:*:*) + echo ${UNAME_MACHINE}-unknown-sortix + exit ;; + alpha:OSF1:*:*) + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case "$ALPHA_CPU_TYPE" in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + echo ${UNAME_MACHINE}-dec-osf`echo ${UNAME_RELEASE} | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + exitcode=$? + trap '' 0 + exit $exitcode ;; + Alpha\ *:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # Should we change UNAME_MACHINE based on the output of uname instead + # of the specific Alpha model? + echo alpha-pc-interix + exit ;; + 21064:Windows_NT:50:3) + echo alpha-dec-winnt3.5 + exit ;; + Amiga*:UNIX_System_V:4.0:*) + echo m68k-unknown-sysv4 + exit ;; + *:[Aa]miga[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-amigaos + exit ;; + *:[Mm]orph[Oo][Ss]:*:*) + echo ${UNAME_MACHINE}-unknown-morphos + exit ;; + *:OS/390:*:*) + echo i370-ibm-openedition + exit ;; + *:z/VM:*:*) + echo s390-ibm-zvmoe + exit ;; + *:OS400:*:*) + echo powerpc-ibm-os400 + exit ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + echo arm-acorn-riscix${UNAME_RELEASE} + exit ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + echo arm-unknown-riscos + exit ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + echo hppa1.1-hitachi-hiuxmpp + exit ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + if test "`(/bin/universe) 2>/dev/null`" = att ; then + echo pyramid-pyramid-sysv3 + else + echo pyramid-pyramid-bsd + fi + exit ;; + NILE*:*:*:dcosx) + echo pyramid-pyramid-svr4 + exit ;; + DRS?6000:unix:4.0:6*) + echo sparc-icl-nx6 + exit ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) echo sparc-icl-nx7; exit ;; + esac ;; + s390x:SunOS:*:*) + echo ${UNAME_MACHINE}-ibm-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4H:SunOS:5.*:*) + echo sparc-hal-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + echo sparc-sun-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + echo i386-pc-auroraux${UNAME_RELEASE} + exit ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + eval $set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + echo ${SUN_ARCH}-pc-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + echo sparc-sun-solaris3`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + sun4*:SunOS:*:*) + case "`/usr/bin/arch -k`" in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like `4.1.3-JL'. + echo sparc-sun-sunos`echo ${UNAME_RELEASE}|sed -e 's/-/_/'` + exit ;; + sun3*:SunOS:*:*) + echo m68k-sun-sunos${UNAME_RELEASE} + exit ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x${UNAME_RELEASE}" = x && UNAME_RELEASE=3 + case "`/bin/arch`" in + sun3) + echo m68k-sun-sunos${UNAME_RELEASE} + ;; + sun4) + echo sparc-sun-sunos${UNAME_RELEASE} + ;; + esac + exit ;; + aushp:SunOS:*:*) + echo sparc-auspex-sunos${UNAME_RELEASE} + exit ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + echo m68k-atari-mint${UNAME_RELEASE} + exit ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + echo m68k-milan-mint${UNAME_RELEASE} + exit ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + echo m68k-hades-mint${UNAME_RELEASE} + exit ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + echo m68k-unknown-mint${UNAME_RELEASE} + exit ;; + m68k:machten:*:*) + echo m68k-apple-machten${UNAME_RELEASE} + exit ;; + powerpc:machten:*:*) + echo powerpc-apple-machten${UNAME_RELEASE} + exit ;; + RISC*:Mach:*:*) + echo mips-dec-mach_bsd4.3 + exit ;; + RISC*:ULTRIX:*:*) + echo mips-dec-ultrix${UNAME_RELEASE} + exit ;; + VAX*:ULTRIX*:*:*) + echo vax-dec-ultrix${UNAME_RELEASE} + exit ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + echo clipper-intergraph-clix${UNAME_RELEASE} + exit ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c +#ifdef __cplusplus +#include <stdio.h> /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && + dummyarg=`echo "${UNAME_RELEASE}" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`$dummy $dummyarg` && + { echo "$SYSTEM_NAME"; exit; } + echo mips-mips-riscos${UNAME_RELEASE} + exit ;; + Motorola:PowerMAX_OS:*:*) + echo powerpc-motorola-powermax + exit ;; + Motorola:*:4.3:PL8-*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + echo powerpc-harris-powermax + exit ;; + Night_Hawk:Power_UNIX:*:*) + echo powerpc-harris-powerunix + exit ;; + m88k:CX/UX:7*:*) + echo m88k-harris-cxux7 + exit ;; + m88k:*:4*:R4*) + echo m88k-motorola-sysv4 + exit ;; + m88k:*:3*:R3*) + echo m88k-motorola-sysv3 + exit ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if [ $UNAME_PROCESSOR = mc88100 ] || [ $UNAME_PROCESSOR = mc88110 ] + then + if [ ${TARGET_BINARY_INTERFACE}x = m88kdguxelfx ] || \ + [ ${TARGET_BINARY_INTERFACE}x = x ] + then + echo m88k-dg-dgux${UNAME_RELEASE} + else + echo m88k-dg-dguxbcs${UNAME_RELEASE} + fi + else + echo i586-dg-dgux${UNAME_RELEASE} + fi + exit ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + echo m88k-dolphin-sysv3 + exit ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + echo m88k-motorola-sysv3 + exit ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + echo m88k-tektronix-sysv3 + exit ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + echo m68k-tektronix-bsd + exit ;; + *:IRIX*:*:*) + echo mips-sgi-irix`echo ${UNAME_RELEASE}|sed -e 's/-/_/g'` + exit ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + echo romp-ibm-aix # uname -m gives an 8 hex-code CPU id + exit ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + echo i386-ibm-aix + exit ;; + ia64:AIX:*:*) + if [ -x /usr/bin/oslevel ] ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${UNAME_MACHINE}-ibm-aix${IBM_REV} + exit ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <sys/systemcfg.h> + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` + then + echo "$SYSTEM_NAME" + else + echo rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + echo rs6000-ibm-aix3.2.4 + else + echo rs6000-ibm-aix3.2 + fi + exit ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El ${IBM_CPU_ID} | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if [ -x /usr/bin/lslpp ] ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=${UNAME_VERSION}.${UNAME_RELEASE} + fi + echo ${IBM_ARCH}-ibm-aix${IBM_REV} + exit ;; + *:AIX:*:*) + echo rs6000-ibm-aix + exit ;; + ibmrt:4.4BSD:*|romp-ibm:BSD:*) + echo romp-ibm-bsd4.4 + exit ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + echo romp-ibm-bsd${UNAME_RELEASE} # 4.3 with uname added to + exit ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + echo rs6000-bull-bosx + exit ;; + DPX/2?00:B.O.S.:*:*) + echo m68k-bull-sysv3 + exit ;; + 9000/[34]??:4.3bsd:1.*:*) + echo m68k-hp-bsd + exit ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + echo m68k-hp-bsd4.4 + exit ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + case "${UNAME_MACHINE}" in + 9000/31? ) HP_ARCH=m68000 ;; + 9000/[34]?? ) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if [ -x /usr/bin/getconf ]; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case "${sc_cpu_version}" in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case "${sc_kernel_bits}" in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if [ "${HP_ARCH}" = "" ]; then + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + + #define _HPUX_SOURCE + #include <stdlib.h> + #include <unistd.h> + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS= $CC_FOR_BUILD -o $dummy $dummy.c 2>/dev/null) && HP_ARCH=`$dummy` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if [ ${HP_ARCH} = hppa2.0w ] + then + eval $set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + echo ${HP_ARCH}-hp-hpux${HPUX_REV} + exit ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo ${UNAME_RELEASE}|sed -e 's/[^.]*.[0B]*//'` + echo ia64-hp-hpux${HPUX_REV} + exit ;; + 3050*:HI-UX:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #include <unistd.h> + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o $dummy $dummy.c && SYSTEM_NAME=`$dummy` && + { echo "$SYSTEM_NAME"; exit; } + echo unknown-hitachi-hiuxwe2 + exit ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:* ) + echo hppa1.1-hp-bsd + exit ;; + 9000/8??:4.3bsd:*:*) + echo hppa1.0-hp-bsd + exit ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + echo hppa1.0-hp-mpeix + exit ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:* ) + echo hppa1.1-hp-osf + exit ;; + hp8??:OSF1:*:*) + echo hppa1.0-hp-osf + exit ;; + i*86:OSF1:*:*) + if [ -x /usr/sbin/sysversion ] ; then + echo ${UNAME_MACHINE}-unknown-osf1mk + else + echo ${UNAME_MACHINE}-unknown-osf1 + fi + exit ;; + parisc*:Lites*:*:*) + echo hppa1.1-hp-lites + exit ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + echo c1-convex-bsd + exit ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + echo c34-convex-bsd + exit ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + echo c38-convex-bsd + exit ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + echo c4-convex-bsd + exit ;; + CRAY*Y-MP:*:*:*) + echo ymp-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*[A-Z]90:*:*:*) + echo ${UNAME_MACHINE}-cray-unicos${UNAME_RELEASE} \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + echo t90-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*T3E:*:*:*) + echo alphaev5-cray-unicosmk${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*SV1:*:*:*) + echo sv1-cray-unicos${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + *:UNICOS/mp:*:*) + echo craynv-cray-unicosmp${UNAME_RELEASE} | sed -e 's/\.[^.]*$/.X/' + exit ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | sed -e 's/ /_/'` + echo "${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo ${UNAME_RELEASE} | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + echo "sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}" + exit ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + echo ${UNAME_MACHINE}-pc-bsdi${UNAME_RELEASE} + exit ;; + sparc*:BSD/OS:*:*) + echo sparc-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:BSD/OS:*:*) + echo ${UNAME_MACHINE}-unknown-bsdi${UNAME_RELEASE} + exit ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`/usr/bin/uname -p` + case ${UNAME_PROCESSOR} in + amd64) + echo x86_64-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + *) + echo ${UNAME_PROCESSOR}-unknown-freebsd`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` ;; + esac + exit ;; + i*:CYGWIN*:*) + echo ${UNAME_MACHINE}-pc-cygwin + exit ;; + *:MINGW64*:*) + echo ${UNAME_MACHINE}-pc-mingw64 + exit ;; + *:MINGW*:*) + echo ${UNAME_MACHINE}-pc-mingw32 + exit ;; + *:MSYS*:*) + echo ${UNAME_MACHINE}-pc-msys + exit ;; + i*:windows32*:*) + # uname -m includes "-pc" on this system. + echo ${UNAME_MACHINE}-mingw32 + exit ;; + i*:PW*:*) + echo ${UNAME_MACHINE}-pc-pw32 + exit ;; + *:Interix*:*) + case ${UNAME_MACHINE} in + x86) + echo i586-pc-interix${UNAME_RELEASE} + exit ;; + authenticamd | genuineintel | EM64T) + echo x86_64-unknown-interix${UNAME_RELEASE} + exit ;; + IA64) + echo ia64-unknown-interix${UNAME_RELEASE} + exit ;; + esac ;; + [345]86:Windows_95:* | [345]86:Windows_98:* | [345]86:Windows_NT:*) + echo i${UNAME_MACHINE}-pc-mks + exit ;; + 8664:Windows_NT:*) + echo x86_64-pc-mks + exit ;; + i*:Windows_NT*:* | Pentium*:Windows_NT*:*) + # How do we know it's Interix rather than the generic POSIX subsystem? + # It also conflicts with pre-2.0 versions of AT&T UWIN. Should we + # UNAME_MACHINE based on the output of uname instead of i386? + echo i586-pc-interix + exit ;; + i*:UWIN*:*) + echo ${UNAME_MACHINE}-pc-uwin + exit ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + echo x86_64-unknown-cygwin + exit ;; + p*:CYGWIN*:*) + echo powerpcle-unknown-cygwin + exit ;; + prep*:SunOS:5.*:*) + echo powerpcle-unknown-solaris2`echo ${UNAME_RELEASE}|sed -e 's/[^.]*//'` + exit ;; + *:GNU:*:*) + # the GNU system + echo `echo ${UNAME_MACHINE}|sed -e 's,[-/].*$,,'`-unknown-${LIBC}`echo ${UNAME_RELEASE}|sed -e 's,/.*$,,'` + exit ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + echo ${UNAME_MACHINE}-unknown-`echo ${UNAME_SYSTEM} | sed 's,^[^/]*/,,' | tr '[A-Z]' '[a-z]'``echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'`-${LIBC} + exit ;; + i*86:Minix:*:*) + echo ${UNAME_MACHINE}-pc-minix + exit ;; + aarch64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' < /proc/cpuinfo` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arc:Linux:*:* | arceb:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + arm*:Linux:*:*) + eval $set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabi + else + echo ${UNAME_MACHINE}-unknown-linux-${LIBC}eabihf + fi + fi + exit ;; + avr32*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + cris:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + crisv32:Linux:*:*) + echo ${UNAME_MACHINE}-axis-linux-${LIBC} + exit ;; + e2k:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + frv:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + hexagon:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + ia64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + k1om:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m32r*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + m68*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + mips:Linux:*:* | mips64:Linux:*:*) + eval $set_cc_for_build + sed 's/^ //' << EOF >$dummy.c + #undef CPU + #undef ${UNAME_MACHINE} + #undef ${UNAME_MACHINE}el + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + CPU=${UNAME_MACHINE}el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + CPU=${UNAME_MACHINE} + #else + CPU= + #endif + #endif +EOF + eval `$CC_FOR_BUILD -E $dummy.c 2>/dev/null | grep '^CPU'` + test x"${CPU}" != x && { echo "${CPU}-unknown-linux-${LIBC}"; exit; } + ;; + openrisc*:Linux:*:*) + echo or1k-unknown-linux-${LIBC} + exit ;; + or32:Linux:*:* | or1k*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + padre:Linux:*:*) + echo sparc-unknown-linux-${LIBC} + exit ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + echo hppa64-unknown-linux-${LIBC} + exit ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) echo hppa1.1-unknown-linux-${LIBC} ;; + PA8*) echo hppa2.0-unknown-linux-${LIBC} ;; + *) echo hppa-unknown-linux-${LIBC} ;; + esac + exit ;; + ppc64:Linux:*:*) + echo powerpc64-unknown-linux-${LIBC} + exit ;; + ppc:Linux:*:*) + echo powerpc-unknown-linux-${LIBC} + exit ;; + ppc64le:Linux:*:*) + echo powerpc64le-unknown-linux-${LIBC} + exit ;; + ppcle:Linux:*:*) + echo powerpcle-unknown-linux-${LIBC} + exit ;; + s390:Linux:*:* | s390x:Linux:*:*) + echo ${UNAME_MACHINE}-ibm-linux-${LIBC} + exit ;; + sh64*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sh*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + tile*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + vax:Linux:*:*) + echo ${UNAME_MACHINE}-dec-linux-${LIBC} + exit ;; + x86_64:Linux:*:*) + echo ${UNAME_MACHINE}-pc-linux-${LIBC} + exit ;; + xtensa*:Linux:*:*) + echo ${UNAME_MACHINE}-unknown-linux-${LIBC} + exit ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + echo i386-sequent-sysv4 + exit ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + echo ${UNAME_MACHINE}-pc-sysv4.2uw${UNAME_VERSION} + exit ;; + i*86:OS/2:*:*) + # If we were able to find `uname', then EMX Unix compatibility + # is probably installed. + echo ${UNAME_MACHINE}-pc-os2-emx + exit ;; + i*86:XTS-300:*:STOP) + echo ${UNAME_MACHINE}-unknown-stop + exit ;; + i*86:atheos:*:*) + echo ${UNAME_MACHINE}-unknown-atheos + exit ;; + i*86:syllable:*:*) + echo ${UNAME_MACHINE}-pc-syllable + exit ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + echo i386-unknown-lynxos${UNAME_RELEASE} + exit ;; + i*86:*DOS:*:*) + echo ${UNAME_MACHINE}-pc-msdosdjgpp + exit ;; + i*86:*:4.*:* | i*86:SYSTEM_V:4.*:*) + UNAME_REL=`echo ${UNAME_RELEASE} | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + echo ${UNAME_MACHINE}-univel-sysv${UNAME_REL} + else + echo ${UNAME_MACHINE}-pc-sysv${UNAME_REL} + fi + exit ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + echo ${UNAME_MACHINE}-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + exit ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name` + echo ${UNAME_MACHINE}-pc-isc$UNAME_REL + elif /bin/uname -X 2>/dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + echo ${UNAME_MACHINE}-pc-sco$UNAME_REL + else + echo ${UNAME_MACHINE}-pc-sysv32 + fi + exit ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + echo i586-pc-msdosdjgpp + exit ;; + Intel:Mach:3*:*) + echo i386-pc-mach3 + exit ;; + paragon:*:*:*) + echo i860-intel-osf1 + exit ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + echo i860-stardent-sysv${UNAME_RELEASE} # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + echo i860-unknown-sysv${UNAME_RELEASE} # Unknown i860-SVR4 + fi + exit ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + echo m68010-convergent-sysv + exit ;; + mc68k:UNIX:SYSTEM5:3.51m) + echo m68k-convergent-sysv + exit ;; + M680?0:D-NIX:5.3:*) + echo m68k-diab-dnix + exit ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3${OS_REL}; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + echo m68k-unknown-lynxos${UNAME_RELEASE} + exit ;; + mc68030:UNIX_System_V:4.*:*) + echo m68k-atari-sysv4 + exit ;; + TSUNAMI:LynxOS:2.*:*) + echo sparc-unknown-lynxos${UNAME_RELEASE} + exit ;; + rs6000:LynxOS:2.*:*) + echo rs6000-unknown-lynxos${UNAME_RELEASE} + exit ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + echo powerpc-unknown-lynxos${UNAME_RELEASE} + exit ;; + SM[BE]S:UNIX_SV:*:*) + echo mips-dde-sysv${UNAME_RELEASE} + exit ;; + RM*:ReliantUNIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + RM*:SINIX-*:*:*) + echo mips-sni-sysv4 + exit ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + echo ${UNAME_MACHINE}-sni-sysv4 + else + echo ns32k-sni-sysv + fi + exit ;; + PENTIUM:*:4.0*:*) # Unisys `ClearPath HMP IX 4000' SVR4/MP effort + # says <Richard.M.Bartel@ccMail.Census.GOV> + echo i586-unisys-sysv4 + exit ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes <hewes@openmarket.com>. + # How about differentiating between stratus architectures? -djm + echo hppa1.1-stratus-sysv4 + exit ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + echo i860-stratus-sysv4 + exit ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + echo ${UNAME_MACHINE}-stratus-vos + exit ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + echo hppa1.1-stratus-vos + exit ;; + mc68*:A/UX:*:*) + echo m68k-apple-aux${UNAME_RELEASE} + exit ;; + news*:NEWS-OS:6*:*) + echo mips-sony-newsos6 + exit ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if [ -d /usr/nec ]; then + echo mips-nec-sysv${UNAME_RELEASE} + else + echo mips-unknown-sysv${UNAME_RELEASE} + fi + exit ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + echo powerpc-be-beos + exit ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + echo powerpc-apple-beos + exit ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + echo i586-pc-beos + exit ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + echo i586-pc-haiku + exit ;; + x86_64:Haiku:*:*) + echo x86_64-unknown-haiku + exit ;; + SX-4:SUPER-UX:*:*) + echo sx4-nec-superux${UNAME_RELEASE} + exit ;; + SX-5:SUPER-UX:*:*) + echo sx5-nec-superux${UNAME_RELEASE} + exit ;; + SX-6:SUPER-UX:*:*) + echo sx6-nec-superux${UNAME_RELEASE} + exit ;; + SX-7:SUPER-UX:*:*) + echo sx7-nec-superux${UNAME_RELEASE} + exit ;; + SX-8:SUPER-UX:*:*) + echo sx8-nec-superux${UNAME_RELEASE} + exit ;; + SX-8R:SUPER-UX:*:*) + echo sx8r-nec-superux${UNAME_RELEASE} + exit ;; + SX-ACE:SUPER-UX:*:*) + echo sxace-nec-superux${UNAME_RELEASE} + exit ;; + Power*:Rhapsody:*:*) + echo powerpc-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Rhapsody:*:*) + echo ${UNAME_MACHINE}-apple-rhapsody${UNAME_RELEASE} + exit ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` || UNAME_PROCESSOR=unknown + eval $set_cc_for_build + if test "$UNAME_PROCESSOR" = unknown ; then + UNAME_PROCESSOR=powerpc + fi + if test `echo "$UNAME_RELEASE" | sed -e 's/\..*//'` -le 10 ; then + if [ "$CC_FOR_BUILD" != no_compiler_found ]; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS= $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # Avoid executing cc on OS X 10.9, as it ships with a stub + # that puts up a graphical alert prompting to install + # developer tools. Any system running Mac OS X 10.7 or + # later (Darwin 11 and later) is required to have a 64-bit + # processor. This is not true of the ARM version of Darwin + # that Apple uses in portable devices. + UNAME_PROCESSOR=x86_64 + fi + echo ${UNAME_PROCESSOR}-apple-darwin${UNAME_RELEASE} + exit ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + echo ${UNAME_PROCESSOR}-${UNAME_MACHINE}-nto-qnx${UNAME_RELEASE} + exit ;; + *:QNX:*:4*) + echo i386-pc-qnx + exit ;; + NEO-?:NONSTOP_KERNEL:*:*) + echo neo-tandem-nsk${UNAME_RELEASE} + exit ;; + NSE-*:NONSTOP_KERNEL:*:*) + echo nse-tandem-nsk${UNAME_RELEASE} + exit ;; + NSR-?:NONSTOP_KERNEL:*:*) + echo nsr-tandem-nsk${UNAME_RELEASE} + exit ;; + *:NonStop-UX:*:*) + echo mips-compaq-nonstopux + exit ;; + BS2000:POSIX*:*:*) + echo bs2000-siemens-sysv + exit ;; + DS/*:UNIX_System_V:*:*) + echo ${UNAME_MACHINE}-${UNAME_SYSTEM}-${UNAME_RELEASE} + exit ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "$cputype" = 386; then + UNAME_MACHINE=i386 + else + UNAME_MACHINE="$cputype" + fi + echo ${UNAME_MACHINE}-unknown-plan9 + exit ;; + *:TOPS-10:*:*) + echo pdp10-unknown-tops10 + exit ;; + *:TENEX:*:*) + echo pdp10-unknown-tenex + exit ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + echo pdp10-dec-tops20 + exit ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + echo pdp10-xkl-tops20 + exit ;; + *:TOPS-20:*:*) + echo pdp10-unknown-tops20 + exit ;; + *:ITS:*:*) + echo pdp10-unknown-its + exit ;; + SEI:*:*:SEIUX) + echo mips-sei-seiux${UNAME_RELEASE} + exit ;; + *:DragonFly:*:*) + echo ${UNAME_MACHINE}-unknown-dragonfly`echo ${UNAME_RELEASE}|sed -e 's/[-(].*//'` + exit ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case "${UNAME_MACHINE}" in + A*) echo alpha-dec-vms ; exit ;; + I*) echo ia64-dec-vms ; exit ;; + V*) echo vax-dec-vms ; exit ;; + esac ;; + *:XENIX:*:SysV) + echo i386-pc-xenix + exit ;; + i*86:skyos:*:*) + echo ${UNAME_MACHINE}-pc-skyos`echo ${UNAME_RELEASE}` | sed -e 's/ .*$//' + exit ;; + i*86:rdos:*:*) + echo ${UNAME_MACHINE}-pc-rdos + exit ;; + i*86:AROS:*:*) + echo ${UNAME_MACHINE}-pc-aros + exit ;; + x86_64:VMkernel:*:*) + echo ${UNAME_MACHINE}-unknown-esx + exit ;; + amd64:Isilon\ OneFS:*:*) + echo x86_64-unknown-onefs + exit ;; +esac + +cat >&2 <<EOF +$0: unable to guess system type + +This script, last modified $timestamp, has failed to recognize +the operating system you are using. It is advised that you +download the most up to date version of the config scripts from + + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.guess +and + http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +If the version you run ($0) is already up to date, please +send the following data and any information you think might be +pertinent to <config-patches@gnu.org> in order to provide the needed +information to handle your system. + +config.guess timestamp = $timestamp + +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = ${UNAME_MACHINE} +UNAME_RELEASE = ${UNAME_RELEASE} +UNAME_SYSTEM = ${UNAME_SYSTEM} +UNAME_VERSION = ${UNAME_VERSION} +EOF + +exit 1 + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build/autoconf/config.status.m4 b/build/autoconf/config.status.m4 new file mode 100644 index 000000000..53e274a12 --- /dev/null +++ b/build/autoconf/config.status.m4 @@ -0,0 +1,182 @@ +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 For use in AC_SUBST replacement +define([MOZ_DIVERSION_SUBST], 11) + +dnl Replace AC_SUBST to store values in a format suitable for python. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_SUBST(),bar) +define([AC_SUBST], +[ifdef([AC_SUBST_SET_$1], [m4_fatal([Cannot use AC_SUBST and AC_SUBST_SET on the same variable ($1)])], +[ifdef([AC_SUBST_LIST_$1], [m4_fatal([Cannot use AC_SUBST and AC_SUBST_LIST on the same variable ($1)])], +[ifdef([AC_SUBST_$1], , +[define([AC_SUBST_$1], )dnl +AC_DIVERT_PUSH(MOZ_DIVERSION_SUBST)dnl + (''' $1 ''', r''' [$]$1 ''') +AC_DIVERT_POP()dnl +])])])]) + +dnl Like AC_SUBST, but makes the value available as a set in python, +dnl with values got from the value of the environment variable, split on +dnl whitespaces. +define([AC_SUBST_SET], +[ifdef([AC_SUBST_$1], [m4_fatal([Cannot use AC_SUBST and AC_SUBST_SET on the same variable ($1)])], +[ifdef([AC_SUBST_LIST$1], [m4_fatal([Cannot use AC_SUBST_LIST and AC_SUBST_SET on the same variable ($1)])], +[ifdef([AC_SUBST_SET_$1], , +[define([AC_SUBST_SET_$1], )dnl +AC_DIVERT_PUSH(MOZ_DIVERSION_SUBST)dnl + (''' $1 ''', unique_list(r''' [$]$1 '''.split())) +AC_DIVERT_POP()dnl +])])])]) + +dnl Like AC_SUBST, but makes the value available as a list in python, +dnl with values got from the value of the environment variable, split on +dnl whitespaces. +define([AC_SUBST_LIST], +[ifdef([AC_SUBST_$1], [m4_fatal([Cannot use AC_SUBST and AC_SUBST_LIST on the same variable ($1)])], +[ifdef([AC_SUBST_SET_$1], [m4_fatal([Cannot use AC_SUBST_SET and AC_SUBST_LIST on the same variable ($1)])], +[ifdef([AC_SUBST_LIST_$1], , +[define([AC_SUBST_LIST_$1], )dnl +AC_DIVERT_PUSH(MOZ_DIVERSION_SUBST)dnl + (''' $1 ''', list(r''' [$]$1 '''.split())) +AC_DIVERT_POP()dnl +])])])]) + +dnl Ignore AC_SUBSTs for variables we don't have use for but that autoconf +dnl itself exports. +define([AC_SUBST_CFLAGS], ) +define([AC_SUBST_CPPFLAGS], ) +define([AC_SUBST_CXXFLAGS], ) +define([AC_SUBST_FFLAGS], ) +define([AC_SUBST_DEFS], ) +define([AC_SUBST_LDFLAGS], ) +define([AC_SUBST_LIBS], ) + +dnl Wrap AC_DEFINE to store values in a format suitable for python. +dnl autoconf's AC_DEFINE still needs to be used to fill confdefs.h, +dnl which is #included during some compile checks. +dnl The necessary comma after the tuple can't be put here because it +dnl can mess around with things like: +dnl AC_SOMETHING(foo,AC_DEFINE(),bar) +define([_MOZ_AC_DEFINE], defn([AC_DEFINE])) +define([AC_DEFINE], +[cat >> confdefs.pytmp <<\EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +ifelse($#, 2, _MOZ_AC_DEFINE([$1], [$2]), $#, 3, _MOZ_AC_DEFINE([$1], [$2], [$3]),_MOZ_AC_DEFINE([$1]))dnl +]) + +dnl Wrap AC_DEFINE_UNQUOTED to store values in a format suitable for +dnl python. +define([_MOZ_AC_DEFINE_UNQUOTED], defn([AC_DEFINE_UNQUOTED])) +define([AC_DEFINE_UNQUOTED], +[cat >> confdefs.pytmp <<EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +ifelse($#, 2, _MOZ_AC_DEFINE_UNQUOTED($1, $2), $#, 3, _MOZ_AC_DEFINE_UNQUOTED($1, $2, $3),_MOZ_AC_DEFINE_UNQUOTED($1))dnl +]) + +dnl Replace AC_OUTPUT to create and call a python config.status +define([MOZ_CREATE_CONFIG_STATUS], +[dnl Top source directory in Windows format (as opposed to msys format). +WIN_TOP_SRC= +case "$host_os" in +mingw*) + WIN_TOP_SRC=`cd $srcdir; pwd -W` + ;; +esac +AC_SUBST(WIN_TOP_SRC) + +dnl Used in all Makefile.in files +top_srcdir=$srcdir +AC_SUBST(top_srcdir) + +dnl Picked from autoconf 2.13 +trap '' 1 2 15 +AC_CACHE_SAVE + +trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15 +: ${CONFIG_STATUS=./config.data} + +dnl We're going to need [ ] for python syntax. +changequote(<<<, >>>)dnl +echo creating $CONFIG_STATUS + +cat > $CONFIG_STATUS <<EOF +def unique_list(l): + result = [] + for i in l: + if l not in result: + result.append(i) + return result + +dnl All defines and substs are stored with an additional space at the beginning +dnl and at the end of the string, to avoid any problem with values starting or +dnl ending with quotes. +defines = [ +EOF + +dnl confdefs.pytmp contains AC_DEFINEs, in the expected format, but +dnl lacks the final comma (see above). +sed 's/$/,/' confdefs.pytmp >> $CONFIG_STATUS +rm confdefs.pytmp confdefs.h + +cat >> $CONFIG_STATUS <<\EOF +] + +substs = [ +EOF + +dnl The MOZ_DIVERSION_SUBST output diversion contains AC_SUBSTs, in the +dnl expected format, but lacks the final comma (see above). +sed 's/$/,/' >> $CONFIG_STATUS <<EOF +undivert(MOZ_DIVERSION_SUBST)dnl +EOF + +dnl Add in the output from the subconfigure script +for ac_subst_arg in $_subconfigure_ac_subst_args; do + variable='$'$ac_subst_arg + echo " (''' $ac_subst_arg ''', r''' `eval echo $variable` ''')," >> $CONFIG_STATUS +done + +cat >> $CONFIG_STATUS <<\EOF +] + +dnl List of AC_DEFINEs that aren't to be exposed in ALLDEFINES +non_global_defines = [ +EOF + +if test -n "$_NON_GLOBAL_ACDEFINES"; then + for var in $_NON_GLOBAL_ACDEFINES; do + echo " '$var'," >> $CONFIG_STATUS + done +fi + +cat >> $CONFIG_STATUS <<EOF +] + +flags = [ +undivert(MOZ_DIVERSION_ARGS)dnl +] +EOF + +changequote([, ]) +]) + +define([m4_fatal],[ +errprint([$1 +]) +m4exit(1) +]) + +define([AC_OUTPUT], [ifelse($#_$1, 1_, [MOZ_CREATE_CONFIG_STATUS() +MOZ_RUN_CONFIG_STATUS()], +[m4_fatal([Use CONFIGURE_SUBST_FILES in moz.build files to create substituted files.])] +)]) + +define([AC_CONFIG_HEADER], +[m4_fatal([Use CONFIGURE_DEFINE_FILES in moz.build files to produce header files.]) +]) diff --git a/build/autoconf/config.sub b/build/autoconf/config.sub new file mode 100755 index 000000000..8d39c4ba3 --- /dev/null +++ b/build/autoconf/config.sub @@ -0,0 +1,1815 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2016 Free Software Foundation, Inc. + +timestamp='2016-03-30' + +# This file is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, see <http://www.gnu.org/licenses/>. +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to <config-patches@gnu.org>. +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# http://git.savannah.gnu.org/gitweb/?p=config.git;a=blob_plain;f=config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Operation modes: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to <config-patches@gnu.org>." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2016 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try \`$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo $1 + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Separate what the user gave into CPU-COMPANY and OS or KERNEL-OS (if any). +# Here we must recognize all the valid KERNEL-OS combinations. +maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'` +case $maybe_os in + nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \ + linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \ + knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \ + kopensolaris*-gnu* | \ + storm-chaos* | os2-emx* | rtmk-nova*) + os=-$maybe_os + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'` + ;; + android-linux) + os=-linux-android + basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown + ;; + *) + basic_machine=`echo $1 | sed 's/-[^-]*$//'` + if [ $basic_machine != $1 ] + then os=`echo $1 | sed 's/.*-/-/'` + else os=; fi + ;; +esac + +### Let's recognize common machines as not being operating systems so +### that things like config.sub decstation-3100 work. We also +### recognize some manufacturers as not being operating systems, so we +### can provide default operating systems below. +case $os in + -sun*os*) + # Prevent following clause from handling this invalid input. + ;; + -dec* | -mips* | -sequent* | -encore* | -pc532* | -sgi* | -sony* | \ + -att* | -7300* | -3300* | -delta* | -motorola* | -sun[234]* | \ + -unicom* | -ibm* | -next | -hp | -isi* | -apollo | -altos* | \ + -convergent* | -ncr* | -news | -32* | -3600* | -3100* | -hitachi* |\ + -c[123]* | -convex* | -sun | -crds | -omron* | -dg | -ultra | -tti* | \ + -harris | -dolphin | -highlevel | -gould | -cbm | -ns | -masscomp | \ + -apple | -axis | -knuth | -cray | -microblaze*) + os= + basic_machine=$1 + ;; + -bluegene*) + os=-cnk + ;; + -sim | -cisco | -oki | -wec | -winbond) + os= + basic_machine=$1 + ;; + -scout) + ;; + -wrs) + os=-vxworks + basic_machine=$1 + ;; + -chorusos*) + os=-chorusos + basic_machine=$1 + ;; + -chorusrdb) + os=-chorusrdb + basic_machine=$1 + ;; + -hiux*) + os=-hiuxwe2 + ;; + -sco6) + os=-sco5v6 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5) + os=-sco3.2v5 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco4) + os=-sco3.2v4 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2.[4-9]*) + os=`echo $os | sed -e 's/sco3.2./sco3.2v/'` + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco3.2v[4-9]*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco5v6*) + # Don't forget version if it is 3.2v4 or newer. + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -sco*) + os=-sco3.2v2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -udk*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -isc) + os=-isc2.2 + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -clix*) + basic_machine=clipper-intergraph + ;; + -isc*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-pc/'` + ;; + -lynx*178) + os=-lynxos178 + ;; + -lynx*5) + os=-lynxos5 + ;; + -lynx*) + os=-lynxos + ;; + -ptx*) + basic_machine=`echo $1 | sed -e 's/86-.*/86-sequent/'` + ;; + -windowsnt*) + os=`echo $os | sed -e 's/windowsnt/winnt/'` + ;; + -psos*) + os=-psos + ;; + -mint | -mint[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; +esac + +# Decode aliases for certain CPU-COMPANY combinations. +case $basic_machine in + # Recognize the basic CPU types without company name. + # Some are omitted here because they have special meanings below. + 1750a | 580 \ + | a29k \ + | aarch64 | aarch64_be \ + | alpha | alphaev[4-8] | alphaev56 | alphaev6[78] | alphapca5[67] \ + | alpha64 | alpha64ev[4-8] | alpha64ev56 | alpha64ev6[78] | alpha64pca5[67] \ + | am33_2.0 \ + | arc | arceb \ + | arm | arm[bl]e | arme[lb] | armv[2-8] | armv[3-8][lb] | armv7[arm] \ + | avr | avr32 \ + | ba \ + | be32 | be64 \ + | bfin \ + | c4x | c8051 | clipper \ + | d10v | d30v | dlx | dsp16xx \ + | e2k | epiphany \ + | fido | fr30 | frv | ft32 \ + | h8300 | h8500 | hppa | hppa1.[01] | hppa2.0 | hppa2.0[nw] | hppa64 \ + | hexagon \ + | i370 | i860 | i960 | ia64 \ + | ip2k | iq2000 \ + | k1om \ + | le32 | le64 \ + | lm32 \ + | m32c | m32r | m32rle | m68000 | m68k | m88k \ + | maxq | mb | microblaze | microblazeel | mcore | mep | metag \ + | mips | mipsbe | mipseb | mipsel | mipsle \ + | mips16 \ + | mips64 | mips64el \ + | mips64octeon | mips64octeonel \ + | mips64orion | mips64orionel \ + | mips64r5900 | mips64r5900el \ + | mips64vr | mips64vrel \ + | mips64vr4100 | mips64vr4100el \ + | mips64vr4300 | mips64vr4300el \ + | mips64vr5000 | mips64vr5000el \ + | mips64vr5900 | mips64vr5900el \ + | mipsisa32 | mipsisa32el \ + | mipsisa32r2 | mipsisa32r2el \ + | mipsisa32r6 | mipsisa32r6el \ + | mipsisa64 | mipsisa64el \ + | mipsisa64r2 | mipsisa64r2el \ + | mipsisa64r6 | mipsisa64r6el \ + | mipsisa64sb1 | mipsisa64sb1el \ + | mipsisa64sr71k | mipsisa64sr71kel \ + | mipsr5900 | mipsr5900el \ + | mipstx39 | mipstx39el \ + | mn10200 | mn10300 \ + | moxie \ + | mt \ + | msp430 \ + | nds32 | nds32le | nds32be \ + | nios | nios2 | nios2eb | nios2el \ + | ns16k | ns32k \ + | open8 | or1k | or1knd | or32 \ + | pdp10 | pdp11 | pj | pjl \ + | powerpc | powerpc64 | powerpc64le | powerpcle \ + | pyramid \ + | riscv32 | riscv64 \ + | rl78 | rx \ + | score \ + | sh | sh[1234] | sh[24]a | sh[24]aeb | sh[23]e | sh[234]eb | sheb | shbe | shle | sh[1234]le | sh3ele \ + | sh64 | sh64le \ + | sparc | sparc64 | sparc64b | sparc64v | sparc86x | sparclet | sparclite \ + | sparcv8 | sparcv9 | sparcv9b | sparcv9v \ + | spu \ + | tahoe | tic4x | tic54x | tic55x | tic6x | tic80 | tron \ + | ubicom32 \ + | v850 | v850e | v850e1 | v850e2 | v850es | v850e2v3 \ + | visium \ + | we32k \ + | x86 | xc16x | xstormy16 | xtensa \ + | z8k | z80) + basic_machine=$basic_machine-unknown + ;; + c54x) + basic_machine=tic54x-unknown + ;; + c55x) + basic_machine=tic55x-unknown + ;; + c6x) + basic_machine=tic6x-unknown + ;; + leon|leon[3-9]) + basic_machine=sparc-$basic_machine + ;; + m6811 | m68hc11 | m6812 | m68hc12 | m68hcs12x | nvptx | picochip) + basic_machine=$basic_machine-unknown + os=-none + ;; + m88110 | m680[12346]0 | m683?2 | m68360 | m5200 | v70 | w65 | z8k) + ;; + ms1) + basic_machine=mt-unknown + ;; + + strongarm | thumb | xscale) + basic_machine=arm-unknown + ;; + xgate) + basic_machine=$basic_machine-unknown + os=-none + ;; + xscaleeb) + basic_machine=armeb-unknown + ;; + + xscaleel) + basic_machine=armel-unknown + ;; + + # We use `pc' rather than `unknown' + # because (1) that's what they normally are, and + # (2) the word "unknown" tends to confuse beginning users. + i*86 | x86_64) + basic_machine=$basic_machine-pc + ;; + # Object if more than one company name word. + *-*-*) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; + # Recognize the basic CPU types with company name. + 580-* \ + | a29k-* \ + | aarch64-* | aarch64_be-* \ + | alpha-* | alphaev[4-8]-* | alphaev56-* | alphaev6[78]-* \ + | alpha64-* | alpha64ev[4-8]-* | alpha64ev56-* | alpha64ev6[78]-* \ + | alphapca5[67]-* | alpha64pca5[67]-* | arc-* | arceb-* \ + | arm-* | armbe-* | armle-* | armeb-* | armv*-* \ + | avr-* | avr32-* \ + | ba-* \ + | be32-* | be64-* \ + | bfin-* | bs2000-* \ + | c[123]* | c30-* | [cjt]90-* | c4x-* \ + | c8051-* | clipper-* | craynv-* | cydra-* \ + | d10v-* | d30v-* | dlx-* \ + | e2k-* | elxsi-* \ + | f30[01]-* | f700-* | fido-* | fr30-* | frv-* | fx80-* \ + | h8300-* | h8500-* \ + | hppa-* | hppa1.[01]-* | hppa2.0-* | hppa2.0[nw]-* | hppa64-* \ + | hexagon-* \ + | i*86-* | i860-* | i960-* | ia64-* \ + | ip2k-* | iq2000-* \ + | k1om-* \ + | le32-* | le64-* \ + | lm32-* \ + | m32c-* | m32r-* | m32rle-* \ + | m68000-* | m680[012346]0-* | m68360-* | m683?2-* | m68k-* \ + | m88110-* | m88k-* | maxq-* | mcore-* | metag-* \ + | microblaze-* | microblazeel-* \ + | mips-* | mipsbe-* | mipseb-* | mipsel-* | mipsle-* \ + | mips16-* \ + | mips64-* | mips64el-* \ + | mips64octeon-* | mips64octeonel-* \ + | mips64orion-* | mips64orionel-* \ + | mips64r5900-* | mips64r5900el-* \ + | mips64vr-* | mips64vrel-* \ + | mips64vr4100-* | mips64vr4100el-* \ + | mips64vr4300-* | mips64vr4300el-* \ + | mips64vr5000-* | mips64vr5000el-* \ + | mips64vr5900-* | mips64vr5900el-* \ + | mipsisa32-* | mipsisa32el-* \ + | mipsisa32r2-* | mipsisa32r2el-* \ + | mipsisa32r6-* | mipsisa32r6el-* \ + | mipsisa64-* | mipsisa64el-* \ + | mipsisa64r2-* | mipsisa64r2el-* \ + | mipsisa64r6-* | mipsisa64r6el-* \ + | mipsisa64sb1-* | mipsisa64sb1el-* \ + | mipsisa64sr71k-* | mipsisa64sr71kel-* \ + | mipsr5900-* | mipsr5900el-* \ + | mipstx39-* | mipstx39el-* \ + | mmix-* \ + | mt-* \ + | msp430-* \ + | nds32-* | nds32le-* | nds32be-* \ + | nios-* | nios2-* | nios2eb-* | nios2el-* \ + | none-* | np1-* | ns16k-* | ns32k-* \ + | open8-* \ + | or1k*-* \ + | orion-* \ + | pdp10-* | pdp11-* | pj-* | pjl-* | pn-* | power-* \ + | powerpc-* | powerpc64-* | powerpc64le-* | powerpcle-* \ + | pyramid-* \ + | riscv32-* | riscv64-* \ + | rl78-* | romp-* | rs6000-* | rx-* \ + | sh-* | sh[1234]-* | sh[24]a-* | sh[24]aeb-* | sh[23]e-* | sh[34]eb-* | sheb-* | shbe-* \ + | shle-* | sh[1234]le-* | sh3ele-* | sh64-* | sh64le-* \ + | sparc-* | sparc64-* | sparc64b-* | sparc64v-* | sparc86x-* | sparclet-* \ + | sparclite-* \ + | sparcv8-* | sparcv9-* | sparcv9b-* | sparcv9v-* | sv1-* | sx*-* \ + | tahoe-* \ + | tic30-* | tic4x-* | tic54x-* | tic55x-* | tic6x-* | tic80-* \ + | tile*-* \ + | tron-* \ + | ubicom32-* \ + | v850-* | v850e-* | v850e1-* | v850es-* | v850e2-* | v850e2v3-* \ + | vax-* \ + | visium-* \ + | we32k-* \ + | x86-* | x86_64-* | xc16x-* | xps100-* \ + | xstormy16-* | xtensa*-* \ + | ymp-* \ + | z8k-* | z80-*) + ;; + # Recognize the basic CPU types without company name, with glob match. + xtensa*) + basic_machine=$basic_machine-unknown + ;; + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 386bsd) + basic_machine=i386-unknown + os=-bsd + ;; + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + basic_machine=m68000-att + ;; + 3b*) + basic_machine=we32k-att + ;; + a29khif) + basic_machine=a29k-amd + os=-udi + ;; + abacus) + basic_machine=abacus-unknown + ;; + adobe68k) + basic_machine=m68010-adobe + os=-scout + ;; + alliant | fx80) + basic_machine=fx80-alliant + ;; + altos | altos3068) + basic_machine=m68k-altos + ;; + am29k) + basic_machine=a29k-none + os=-bsd + ;; + amd64) + basic_machine=x86_64-pc + ;; + amd64-*) + basic_machine=x86_64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + amdahl) + basic_machine=580-amdahl + os=-sysv + ;; + amiga | amiga-*) + basic_machine=m68k-unknown + ;; + amigaos | amigados) + basic_machine=m68k-unknown + os=-amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + os=-sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + os=-sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + os=-bsd + ;; + aros) + basic_machine=i386-pc + os=-aros + ;; + asmjs) + basic_machine=asmjs-unknown + ;; + aux) + basic_machine=m68k-apple + os=-aux + ;; + balance) + basic_machine=ns32k-sequent + os=-dynix + ;; + blackfin) + basic_machine=bfin-unknown + os=-linux + ;; + blackfin-*) + basic_machine=bfin-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + bluegene*) + basic_machine=powerpc-ibm + os=-cnk + ;; + c54x-*) + basic_machine=tic54x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c55x-*) + basic_machine=tic55x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c6x-*) + basic_machine=tic6x-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + c90) + basic_machine=c90-cray + os=-unicos + ;; + cegcc) + basic_machine=arm-unknown + os=-cegcc + ;; + convex-c1) + basic_machine=c1-convex + os=-bsd + ;; + convex-c2) + basic_machine=c2-convex + os=-bsd + ;; + convex-c32) + basic_machine=c32-convex + os=-bsd + ;; + convex-c34) + basic_machine=c34-convex + os=-bsd + ;; + convex-c38) + basic_machine=c38-convex + os=-bsd + ;; + cray | j90) + basic_machine=j90-cray + os=-unicos + ;; + craynv) + basic_machine=craynv-cray + os=-unicosmp + ;; + cr16 | cr16-*) + basic_machine=cr16-unknown + os=-elf + ;; + crds | unos) + basic_machine=m68k-crds + ;; + crisv32 | crisv32-* | etraxfs*) + basic_machine=crisv32-axis + ;; + cris | cris-* | etrax*) + basic_machine=cris-axis + ;; + crx) + basic_machine=crx-unknown + os=-elf + ;; + da30 | da30-*) + basic_machine=m68k-da30 + ;; + decstation | decstation-3100 | pmax | pmax-* | pmin | dec3100 | decstatn) + basic_machine=mips-dec + ;; + decsystem10* | dec10*) + basic_machine=pdp10-dec + os=-tops10 + ;; + decsystem20* | dec20*) + basic_machine=pdp10-dec + os=-tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + basic_machine=m68k-motorola + ;; + delta88) + basic_machine=m88k-motorola + os=-sysv3 + ;; + dicos) + basic_machine=i686-pc + os=-dicos + ;; + djgpp) + basic_machine=i586-pc + os=-msdosdjgpp + ;; + dpx20 | dpx20-*) + basic_machine=rs6000-bull + os=-bosx + ;; + dpx2* | dpx2*-bull) + basic_machine=m68k-bull + os=-sysv3 + ;; + ebmon29k) + basic_machine=a29k-amd + os=-ebmon + ;; + elxsi) + basic_machine=elxsi-elxsi + os=-bsd + ;; + encore | umax | mmax) + basic_machine=ns32k-encore + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + os=-ose + ;; + fx2800) + basic_machine=i860-alliant + ;; + genix) + basic_machine=ns32k-ns + ;; + gmicro) + basic_machine=tron-gmicro + os=-sysv + ;; + go32) + basic_machine=i386-pc + os=-go32 + ;; + h3050r* | hiux*) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + h8300hms) + basic_machine=h8300-hitachi + os=-hms + ;; + h8300xray) + basic_machine=h8300-hitachi + os=-xray + ;; + h8500hms) + basic_machine=h8500-hitachi + os=-hms + ;; + harris) + basic_machine=m88k-harris + os=-sysv3 + ;; + hp300-*) + basic_machine=m68k-hp + ;; + hp300bsd) + basic_machine=m68k-hp + os=-bsd + ;; + hp300hpux) + basic_machine=m68k-hp + os=-hpux + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + basic_machine=m68000-hp + ;; + hp9k3[2-9][0-9]) + basic_machine=m68k-hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + basic_machine=hppa1.1-hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + basic_machine=hppa1.1-hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + basic_machine=hppa1.0-hp + ;; + hppa-next) + os=-nextstep3 + ;; + hppaosf) + basic_machine=hppa1.1-hp + os=-osf + ;; + hppro) + basic_machine=hppa1.1-hp + os=-proelf + ;; + i370-ibm* | ibm*) + basic_machine=i370-ibm + ;; + i*86v32) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv32 + ;; + i*86v4*) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv4 + ;; + i*86v) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-sysv + ;; + i*86sol2) + basic_machine=`echo $1 | sed -e 's/86.*/86-pc/'` + os=-solaris2 + ;; + i386mach) + basic_machine=i386-mach + os=-mach + ;; + i386-vsta | vsta) + basic_machine=i386-unknown + os=-vsta + ;; + iris | iris4d) + basic_machine=mips-sgi + case $os in + -irix*) + ;; + *) + os=-irix4 + ;; + esac + ;; + isi68 | isi) + basic_machine=m68k-isi + os=-sysv + ;; + leon-*|leon[3-9]-*) + basic_machine=sparc-`echo $basic_machine | sed 's/-.*//'` + ;; + m68knommu) + basic_machine=m68k-unknown + os=-linux + ;; + m68knommu-*) + basic_machine=m68k-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + m88k-omron*) + basic_machine=m88k-omron + ;; + magnum | m3230) + basic_machine=mips-mips + os=-sysv + ;; + merlin) + basic_machine=ns32k-utek + os=-sysv + ;; + microblaze*) + basic_machine=microblaze-xilinx + ;; + mingw64) + basic_machine=x86_64-pc + os=-mingw64 + ;; + mingw32) + basic_machine=i686-pc + os=-mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + os=-mingw32ce + ;; + miniframe) + basic_machine=m68000-convergent + ;; + *mint | -mint[0-9]* | *MiNT | *MiNT[0-9]*) + basic_machine=m68k-atari + os=-mint + ;; + mips3*-*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'` + ;; + mips3*) + basic_machine=`echo $basic_machine | sed -e 's/mips3/mips64/'`-unknown + ;; + monitor) + basic_machine=m68k-rom68k + os=-coff + ;; + morphos) + basic_machine=powerpc-unknown + os=-morphos + ;; + moxiebox) + basic_machine=moxie-unknown + os=-moxiebox + ;; + msdos) + basic_machine=i386-pc + os=-msdos + ;; + ms1-*) + basic_machine=`echo $basic_machine | sed -e 's/ms1-/mt-/'` + ;; + msys) + basic_machine=i686-pc + os=-msys + ;; + mvs) + basic_machine=i370-ibm + os=-mvs + ;; + nacl) + basic_machine=le32-unknown + os=-nacl + ;; + ncr3000) + basic_machine=i486-ncr + os=-sysv4 + ;; + netbsd386) + basic_machine=i386-unknown + os=-netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + os=-linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + os=-newsos + ;; + news1000) + basic_machine=m68030-sony + os=-newsos + ;; + news-3600 | risc-news) + basic_machine=mips-sony + os=-newsos + ;; + necv70) + basic_machine=v70-nec + os=-sysv + ;; + next | m*-next ) + basic_machine=m68k-next + case $os in + -nextstep* ) + ;; + -ns2*) + os=-nextstep2 + ;; + *) + os=-nextstep3 + ;; + esac + ;; + nh3000) + basic_machine=m68k-harris + os=-cxux + ;; + nh[45]000) + basic_machine=m88k-harris + os=-cxux + ;; + nindy960) + basic_machine=i960-intel + os=-nindy + ;; + mon960) + basic_machine=i960-intel + os=-mon960 + ;; + nonstopux) + basic_machine=mips-compaq + os=-nonstopux + ;; + np1) + basic_machine=np1-gould + ;; + neo-tandem) + basic_machine=neo-tandem + ;; + nse-tandem) + basic_machine=nse-tandem + ;; + nsr-tandem) + basic_machine=nsr-tandem + ;; + op50n-* | op60c-*) + basic_machine=hppa1.1-oki + os=-proelf + ;; + openrisc | openrisc-*) + basic_machine=or32-unknown + ;; + os400) + basic_machine=powerpc-ibm + os=-os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + os=-ose + ;; + os68k) + basic_machine=m68k-none + os=-os68k + ;; + pa-hitachi) + basic_machine=hppa1.1-hitachi + os=-hiuxwe2 + ;; + paragon) + basic_machine=i860-intel + os=-osf + ;; + parisc) + basic_machine=hppa-unknown + os=-linux + ;; + parisc-*) + basic_machine=hppa-`echo $basic_machine | sed 's/^[^-]*-//'` + os=-linux + ;; + pbd) + basic_machine=sparc-tti + ;; + pbb) + basic_machine=m68k-tti + ;; + pc532 | pc532-*) + basic_machine=ns32k-pc532 + ;; + pc98) + basic_machine=i386-pc + ;; + pc98-*) + basic_machine=i386-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium | p5 | k5 | k6 | nexgen | viac3) + basic_machine=i586-pc + ;; + pentiumpro | p6 | 6x86 | athlon | athlon_*) + basic_machine=i686-pc + ;; + pentiumii | pentium2 | pentiumiii | pentium3) + basic_machine=i686-pc + ;; + pentium4) + basic_machine=i786-pc + ;; + pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*) + basic_machine=i586-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumpro-* | p6-* | 6x86-* | athlon-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*) + basic_machine=i686-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pentium4-*) + basic_machine=i786-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + pn) + basic_machine=pn-gould + ;; + power) basic_machine=power-ibm + ;; + ppc | ppcbe) basic_machine=powerpc-unknown + ;; + ppc-* | ppcbe-*) + basic_machine=powerpc-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppcle | powerpclittle | ppc-le | powerpc-little) + basic_machine=powerpcle-unknown + ;; + ppcle-* | powerpclittle-*) + basic_machine=powerpcle-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64) basic_machine=powerpc64-unknown + ;; + ppc64-*) basic_machine=powerpc64-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ppc64le | powerpc64little | ppc64-le | powerpc64-little) + basic_machine=powerpc64le-unknown + ;; + ppc64le-* | powerpc64little-*) + basic_machine=powerpc64le-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + ps2) + basic_machine=i386-ibm + ;; + pw32) + basic_machine=i586-unknown + os=-pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + os=-rdos + ;; + rdos32) + basic_machine=i386-pc + os=-rdos + ;; + rom68k) + basic_machine=m68k-rom68k + os=-coff + ;; + rm[46]00) + basic_machine=mips-siemens + ;; + rtpc | rtpc-*) + basic_machine=romp-ibm + ;; + s390 | s390-*) + basic_machine=s390-ibm + ;; + s390x | s390x-*) + basic_machine=s390x-ibm + ;; + sa29200) + basic_machine=a29k-amd + os=-udi + ;; + sb1) + basic_machine=mipsisa64sb1-unknown + ;; + sb1el) + basic_machine=mipsisa64sb1el-unknown + ;; + sde) + basic_machine=mipsisa32-sde + os=-elf + ;; + sei) + basic_machine=mips-sei + os=-seiux + ;; + sequent) + basic_machine=i386-sequent + ;; + sh) + basic_machine=sh-hitachi + os=-hms + ;; + sh5el) + basic_machine=sh5le-unknown + ;; + sh64) + basic_machine=sh64-unknown + ;; + sparclite-wrs | simso-wrs) + basic_machine=sparclite-wrs + os=-vxworks + ;; + sps7) + basic_machine=m68k-bull + os=-sysv2 + ;; + spur) + basic_machine=spur-unknown + ;; + st2000) + basic_machine=m68k-tandem + ;; + stratus) + basic_machine=i860-stratus + os=-sysv4 + ;; + strongarm-* | thumb-*) + basic_machine=arm-`echo $basic_machine | sed 's/^[^-]*-//'` + ;; + sun2) + basic_machine=m68000-sun + ;; + sun2os3) + basic_machine=m68000-sun + os=-sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + os=-sunos4 + ;; + sun3os3) + basic_machine=m68k-sun + os=-sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + os=-sunos4 + ;; + sun4os3) + basic_machine=sparc-sun + os=-sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + os=-sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + os=-solaris2 + ;; + sun3 | sun3-*) + basic_machine=m68k-sun + ;; + sun4) + basic_machine=sparc-sun + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + ;; + sv1) + basic_machine=sv1-cray + os=-unicos + ;; + symmetry) + basic_machine=i386-sequent + os=-dynix + ;; + t3e) + basic_machine=alphaev5-cray + os=-unicos + ;; + t90) + basic_machine=t90-cray + os=-unicos + ;; + tile*) + basic_machine=$basic_machine-unknown + os=-linux-gnu + ;; + tx39) + basic_machine=mipstx39-unknown + ;; + tx39el) + basic_machine=mipstx39el-unknown + ;; + toad1) + basic_machine=pdp10-xkl + os=-tops20 + ;; + tower | tower-32) + basic_machine=m68k-ncr + ;; + tpf) + basic_machine=s390x-ibm + os=-tpf + ;; + udi29k) + basic_machine=a29k-amd + os=-udi + ;; + ultra3) + basic_machine=a29k-nyu + os=-sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + os=-none + ;; + vaxv) + basic_machine=vax-dec + os=-sysv + ;; + vms) + basic_machine=vax-dec + os=-vms + ;; + vpp*|vx|vx-*) + basic_machine=f301-fujitsu + ;; + vxworks960) + basic_machine=i960-wrs + os=-vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + os=-vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + os=-vxworks + ;; + w65*) + basic_machine=w65-wdc + os=-none + ;; + w89k-*) + basic_machine=hppa1.1-winbond + os=-proelf + ;; + xbox) + basic_machine=i686-pc + os=-mingw32 + ;; + xps | xps100) + basic_machine=xps100-honeywell + ;; + xscale-* | xscalee[bl]-*) + basic_machine=`echo $basic_machine | sed 's/^xscale/arm/'` + ;; + ymp) + basic_machine=ymp-cray + os=-unicos + ;; + z8k-*-coff) + basic_machine=z8k-unknown + os=-sim + ;; + z80-*-coff) + basic_machine=z80-unknown + os=-sim + ;; + none) + basic_machine=none-none + os=-none + ;; + +# Here we handle the default manufacturer of certain CPU types. It is in +# some cases the only manufacturer, in others, it is the most popular. + w89k) + basic_machine=hppa1.1-winbond + ;; + op50n) + basic_machine=hppa1.1-oki + ;; + op60c) + basic_machine=hppa1.1-oki + ;; + romp) + basic_machine=romp-ibm + ;; + mmix) + basic_machine=mmix-knuth + ;; + rs6000) + basic_machine=rs6000-ibm + ;; + vax) + basic_machine=vax-dec + ;; + pdp10) + # there are many clones, so DEC is not a safe bet + basic_machine=pdp10-unknown + ;; + pdp11) + basic_machine=pdp11-dec + ;; + we32k) + basic_machine=we32k-att + ;; + sh[1234] | sh[24]a | sh[24]aeb | sh[34]eb | sh[1234]le | sh[23]ele) + basic_machine=sh-unknown + ;; + sparc | sparcv8 | sparcv9 | sparcv9b | sparcv9v) + basic_machine=sparc-sun + ;; + cydra) + basic_machine=cydra-cydrome + ;; + orion) + basic_machine=orion-highlevel + ;; + orion105) + basic_machine=clipper-highlevel + ;; + mac | mpw | mac-mpw) + basic_machine=m68k-apple + ;; + pmac | pmac-mpw) + basic_machine=powerpc-apple + ;; + *-unknown) + # Make sure to match an already-canonicalized machine name. + ;; + *) + echo Invalid configuration \`$1\': machine \`$basic_machine\' not recognized 1>&2 + exit 1 + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $basic_machine in + *-digital*) + basic_machine=`echo $basic_machine | sed 's/digital.*/dec/'` + ;; + *-commodore*) + basic_machine=`echo $basic_machine | sed 's/commodore.*/cbm/'` + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if [ x"$os" != x"" ] +then +case $os in + # First match some system type aliases + # that might get confused with valid system types. + # -solaris* is a basic system type, with this one exception. + -auroraux) + os=-auroraux + ;; + -solaris1 | -solaris1.*) + os=`echo $os | sed -e 's|solaris1|sunos4|'` + ;; + -solaris) + os=-solaris2 + ;; + -svr4*) + os=-sysv4 + ;; + -unixware*) + os=-sysv4.2uw + ;; + -gnu/linux*) + os=`echo $os | sed -e 's|gnu/linux|linux-gnu|'` + ;; + # First accept the basic system types. + # The portable systems comes first. + # Each alternative MUST END IN A *, to match a version number. + # -sysv* is not here because it comes later, after sysvr4. + -gnu* | -bsd* | -mach* | -minix* | -genix* | -ultrix* | -irix* \ + | -*vms* | -sco* | -esix* | -isc* | -aix* | -cnk* | -sunos | -sunos[34]*\ + | -hpux* | -unos* | -osf* | -luna* | -dgux* | -auroraux* | -solaris* \ + | -sym* | -kopensolaris* | -plan9* \ + | -amigaos* | -amigados* | -msdos* | -newsos* | -unicos* | -aof* \ + | -aos* | -aros* | -cloudabi* | -sortix* \ + | -nindy* | -vxsim* | -vxworks* | -ebmon* | -hms* | -mvs* \ + | -clix* | -riscos* | -uniplus* | -iris* | -rtu* | -xenix* \ + | -hiux* | -386bsd* | -knetbsd* | -mirbsd* | -netbsd* \ + | -bitrig* | -openbsd* | -solidbsd* | -libertybsd* \ + | -ekkobsd* | -kfreebsd* | -freebsd* | -riscix* | -lynxos* \ + | -bosx* | -nextstep* | -cxux* | -aout* | -elf* | -oabi* \ + | -ptx* | -coff* | -ecoff* | -winnt* | -domain* | -vsta* \ + | -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \ + | -chorusos* | -chorusrdb* | -cegcc* \ + | -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \ + | -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \ + | -linux-newlib* | -linux-musl* | -linux-uclibc* \ + | -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \ + | -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \ + | -openstep* | -oskit* | -conix* | -pw32* | -nonstopux* \ + | -storm-chaos* | -tops10* | -tenex* | -tops20* | -its* \ + | -os2* | -vos* | -palmos* | -uclinux* | -nucleus* \ + | -morphos* | -superux* | -rtmk* | -rtmk-nova* | -windiss* \ + | -powermax* | -dnix* | -nx6 | -nx7 | -sei* | -dragonfly* \ + | -skyos* | -haiku* | -rdos* | -toppers* | -drops* | -es* \ + | -onefs* | -tirtos*) + # Remember, each alternative MUST END IN *, to match a version number. + ;; + -qnx*) + case $basic_machine in + x86-* | i*86-*) + ;; + *) + os=-nto$os + ;; + esac + ;; + -nto-qnx*) + ;; + -nto*) + os=`echo $os | sed -e 's|nto|nto-qnx|'` + ;; + -sim | -es1800* | -hms* | -xray | -os68k* | -none* | -v88r* \ + | -windows* | -osx | -abug | -netware* | -os9* | -beos* | -haiku* \ + | -macos* | -mpw* | -magic* | -mmixware* | -mon960* | -lnews*) + ;; + -mac*) + os=`echo $os | sed -e 's|mac|macos|'` + ;; + -linux-dietlibc) + os=-linux-dietlibc + ;; + -linux*) + os=`echo $os | sed -e 's|linux|linux-gnu|'` + ;; + -sunos5*) + os=`echo $os | sed -e 's|sunos5|solaris2|'` + ;; + -sunos6*) + os=`echo $os | sed -e 's|sunos6|solaris3|'` + ;; + -opened*) + os=-openedition + ;; + -os400*) + os=-os400 + ;; + -wince*) + os=-wince + ;; + -osfrose*) + os=-osfrose + ;; + -osf*) + os=-osf + ;; + -utek*) + os=-bsd + ;; + -dynix*) + os=-bsd + ;; + -acis*) + os=-aos + ;; + -atheos*) + os=-atheos + ;; + -syllable*) + os=-syllable + ;; + -386bsd) + os=-bsd + ;; + -ctix* | -uts*) + os=-sysv + ;; + -nova*) + os=-rtmk-nova + ;; + -ns2 ) + os=-nextstep2 + ;; + -nsk*) + os=-nsk + ;; + # Preserve the version number of sinix5. + -sinix5.*) + os=`echo $os | sed -e 's|sinix|sysv|'` + ;; + -sinix*) + os=-sysv4 + ;; + -tpf*) + os=-tpf + ;; + -triton*) + os=-sysv3 + ;; + -oss*) + os=-sysv3 + ;; + -svr4) + os=-sysv4 + ;; + -svr3) + os=-sysv3 + ;; + -sysvr4) + os=-sysv4 + ;; + # This must come after -sysvr4. + -sysv*) + ;; + -ose*) + os=-ose + ;; + -es1800*) + os=-ose + ;; + -xenix) + os=-xenix + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + os=-mint + ;; + -aros*) + os=-aros + ;; + -zvmoe) + os=-zvmoe + ;; + -dicos*) + os=-dicos + ;; + -nacl*) + ;; + -ios) + ;; + -none) + ;; + *) + # Get rid of the `-' at the beginning of $os. + os=`echo $os | sed 's/[^-]*-//'` + echo Invalid configuration \`$1\': system \`$os\' not recognized 1>&2 + exit 1 + ;; +esac +else + +# Here we handle the default operating systems that come with various machines. +# The value should be what the vendor currently ships out the door with their +# machine or put another way, the most popular os provided with the machine. + +# Note that if you're going to try to match "-MANUFACTURER" here (say, +# "-sun"), then you have to tell the case statement up towards the top +# that MANUFACTURER isn't an operating system. Otherwise, code above +# will signal an error saying that MANUFACTURER isn't an operating +# system, and we'll never get to this point. + +case $basic_machine in + score-*) + os=-elf + ;; + spu-*) + os=-elf + ;; + *-acorn) + os=-riscix1.2 + ;; + arm*-rebel) + os=-linux + ;; + arm*-semi) + os=-aout + ;; + c4x-* | tic4x-*) + os=-coff + ;; + c8051-*) + os=-elf + ;; + hexagon-*) + os=-elf + ;; + tic54x-*) + os=-coff + ;; + tic55x-*) + os=-coff + ;; + tic6x-*) + os=-coff + ;; + # This must come before the *-dec entry. + pdp10-*) + os=-tops20 + ;; + pdp11-*) + os=-none + ;; + *-dec | vax-*) + os=-ultrix4.2 + ;; + m68*-apollo) + os=-domain + ;; + i386-sun) + os=-sunos4.0.2 + ;; + m68000-sun) + os=-sunos3 + ;; + m68*-cisco) + os=-aout + ;; + mep-*) + os=-elf + ;; + mips*-cisco) + os=-elf + ;; + mips*-*) + os=-elf + ;; + or32-*) + os=-coff + ;; + *-tti) # must be before sparc entry or we get the wrong os. + os=-sysv3 + ;; + sparc-* | *-sun) + os=-sunos4.1.1 + ;; + *-be) + os=-beos + ;; + *-haiku) + os=-haiku + ;; + *-ibm) + os=-aix + ;; + *-knuth) + os=-mmixware + ;; + *-wec) + os=-proelf + ;; + *-winbond) + os=-proelf + ;; + *-oki) + os=-proelf + ;; + *-hp) + os=-hpux + ;; + *-hitachi) + os=-hiux + ;; + i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent) + os=-sysv + ;; + *-cbm) + os=-amigaos + ;; + *-dg) + os=-dgux + ;; + *-dolphin) + os=-sysv3 + ;; + m68k-ccur) + os=-rtu + ;; + m88k-omron*) + os=-luna + ;; + *-next ) + os=-nextstep + ;; + *-sequent) + os=-ptx + ;; + *-crds) + os=-unos + ;; + *-ns) + os=-genix + ;; + i370-*) + os=-mvs + ;; + *-next) + os=-nextstep3 + ;; + *-gould) + os=-sysv + ;; + *-highlevel) + os=-bsd + ;; + *-encore) + os=-bsd + ;; + *-sgi) + os=-irix + ;; + *-siemens) + os=-sysv4 + ;; + *-masscomp) + os=-rtu + ;; + f30[01]-fujitsu | f700-fujitsu) + os=-uxpv + ;; + *-rom68k) + os=-coff + ;; + *-*bug) + os=-coff + ;; + *-apple) + os=-macos + ;; + *-atari*) + os=-mint + ;; + *) + os=-none + ;; +esac +fi + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +vendor=unknown +case $basic_machine in + *-unknown) + case $os in + -riscix*) + vendor=acorn + ;; + -sunos*) + vendor=sun + ;; + -cnk*|-aix*) + vendor=ibm + ;; + -beos*) + vendor=be + ;; + -hpux*) + vendor=hp + ;; + -mpeix*) + vendor=hp + ;; + -hiux*) + vendor=hitachi + ;; + -unos*) + vendor=crds + ;; + -dgux*) + vendor=dg + ;; + -luna*) + vendor=omron + ;; + -genix*) + vendor=ns + ;; + -mvs* | -opened*) + vendor=ibm + ;; + -os400*) + vendor=ibm + ;; + -ptx*) + vendor=sequent + ;; + -tpf*) + vendor=ibm + ;; + -vxsim* | -vxworks* | -windiss*) + vendor=wrs + ;; + -aux*) + vendor=apple + ;; + -hms*) + vendor=hitachi + ;; + -mpw* | -macos*) + vendor=apple + ;; + -*mint | -mint[0-9]* | -*MiNT | -MiNT[0-9]*) + vendor=atari + ;; + -vos*) + vendor=stratus + ;; + esac + basic_machine=`echo $basic_machine | sed "s/unknown/$vendor/"` + ;; +esac + +echo $basic_machine$os +exit + +# Local variables: +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: diff --git a/build/autoconf/expandlibs.m4 b/build/autoconf/expandlibs.m4 new file mode 100644 index 000000000..898e5ce01 --- /dev/null +++ b/build/autoconf/expandlibs.m4 @@ -0,0 +1,66 @@ +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/. + +AC_DEFUN([MOZ_EXPAND_LIBS], +[ +dnl ======================================================== +dnl = +dnl = Check what kind of list files are supported by the +dnl = linker +dnl = +dnl ======================================================== + +AC_CACHE_CHECK(what kind of list files are supported by the linker, + EXPAND_LIBS_LIST_STYLE, + [echo "int main() {return 0;}" > conftest.${ac_ext} + if AC_TRY_COMMAND(${CC-cc} -o conftest.${OBJ_SUFFIX} -c $CFLAGS $CPPFLAGS conftest.${ac_ext} 1>&5) && test -s conftest.${OBJ_SUFFIX}; then + echo "INPUT(conftest.${OBJ_SUFFIX})" > conftest.list + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=linkerscript + else + echo "conftest.${OBJ_SUFFIX}" > conftest.list + dnl -filelist is for the OS X linker. We need to try -filelist + dnl first because clang understands @file, but may pass an + dnl oversized argument list to the linker depending on the + dnl contents of @file. + if AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS [-Wl,-filelist,conftest.list] $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=filelist + elif AC_TRY_COMMAND(${CC-cc} -o conftest${ac_exeext} $LDFLAGS @conftest.list $LIBS 1>&5) && test -s conftest${ac_exeext}; then + EXPAND_LIBS_LIST_STYLE=list + else + EXPAND_LIBS_LIST_STYLE=none + fi + fi + else + dnl We really don't expect to get here, but just in case + AC_ERROR([couldn't compile a simple C file]) + fi + rm -rf conftest*]) + +LIBS_DESC_SUFFIX=desc +AC_SUBST(LIBS_DESC_SUFFIX) +AC_SUBST(EXPAND_LIBS_LIST_STYLE) + +if test "$GCC_USE_GNU_LD"; then + AC_CACHE_CHECK(what kind of ordering can be done with the linker, + EXPAND_LIBS_ORDER_STYLE, + [> conftest.order + _SAVE_LDFLAGS="$LDFLAGS" + LDFLAGS="${LDFLAGS} -Wl,--section-ordering-file,conftest.order" + AC_TRY_LINK([], [], + EXPAND_LIBS_ORDER_STYLE=section-ordering-file, + EXPAND_LIBS_ORDER_STYLE=) + LDFLAGS="$_SAVE_LDFLAGS" + if test -z "$EXPAND_LIBS_ORDER_STYLE"; then + if AC_TRY_COMMAND(${CC-cc} ${DSO_LDOPTS} ${LDFLAGS} -o ${DLL_PREFIX}conftest${DLL_SUFFIX} -Wl,--verbose 2> /dev/null | sed -n '/^===/,/^===/p' | grep '\.text'); then + EXPAND_LIBS_ORDER_STYLE=linkerscript + else + EXPAND_LIBS_ORDER_STYLE=none + fi + rm -f ${DLL_PREFIX}conftest${DLL_SUFFIX} + fi]) +fi +AC_SUBST(EXPAND_LIBS_ORDER_STYLE) + +]) diff --git a/build/autoconf/frameptr.m4 b/build/autoconf/frameptr.m4 new file mode 100644 index 000000000..e7e50330e --- /dev/null +++ b/build/autoconf/frameptr.m4 @@ -0,0 +1,44 @@ +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 Set MOZ_FRAMEPTR_FLAGS to the flags that should be used for enabling or +dnl disabling frame pointers in this architecture based on the configure +dnl options + +AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [ + case "$target" in + *android*) + unwind_tables="-funwind-tables" + ;; + esac + if test "$GNU_CC"; then + MOZ_ENABLE_FRAME_PTR="-fno-omit-frame-pointer $unwind_tables" + MOZ_DISABLE_FRAME_PTR="-fomit-frame-pointer" + if test "$CPU_ARCH" = arm; then + # http://gcc.gnu.org/bugzilla/show_bug.cgi?id=54398 + MOZ_ENABLE_FRAME_PTR="$unwind_tables" + fi + else + case "$target" in + dnl Oy (Frame-Pointer Omission) is only support on x86 compilers + *-mingw32*) + MOZ_ENABLE_FRAME_PTR="-Oy-" + MOZ_DISABLE_FRAME_PTR="-Oy" + ;; + esac + fi + + # If we are debugging, profiling, using sanitizers, or on win32 we want a + # frame pointer. + if test -z "$MOZ_OPTIMIZE" -o \ + -n "$MOZ_PROFILING" -o \ + -n "$MOZ_DEBUG" -o \ + -n "$MOZ_MSAN" -o \ + -n "$MOZ_ASAN" -o \ + "$OS_ARCH:$CPU_ARCH" = "WINNT:x86"; then + MOZ_FRAMEPTR_FLAGS="$MOZ_ENABLE_FRAME_PTR" + else + MOZ_FRAMEPTR_FLAGS="$MOZ_DISABLE_FRAME_PTR" + fi +]) diff --git a/build/autoconf/hooks.m4 b/build/autoconf/hooks.m4 new file mode 100644 index 000000000..5c921b549 --- /dev/null +++ b/build/autoconf/hooks.m4 @@ -0,0 +1,82 @@ +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 Wrap AC_INIT_PREPARE to add the above trap. +define([_MOZ_AC_INIT_PREPARE], defn([AC_INIT_PREPARE])) +define([AC_INIT_PREPARE], +[_MOZ_AC_INIT_PREPARE($1) + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +> subconfigures +> skip_subconfigures +]) + +define([AC_OUTPUT_SUBDIRS], +[for moz_config_dir in $1; do + _CONFIG_SHELL=${CONFIG_SHELL-/bin/sh} + case "$moz_config_dir" in + *:*) + objdir=$(echo $moz_config_dir | awk -F: '{print [$]2}') + ;; + *) + objdir=$moz_config_dir + ;; + esac + dnl Because config.status, storing AC_SUBSTs, is written before any + dnl subconfigure runs, we need to use a file. Moreover, some subconfigures + dnl are started from a subshell, and variable modifications from a subshell + dnl wouldn't be preserved. + echo $objdir >> subconfigures + + dumpenv="true | " + case "$host" in + *-mingw*) + _CONFIG_SHELL=$(cd $(dirname $_CONFIG_SHELL); pwd -W)/$(basename $_CONFIG_SHELL) + if test ! -e "$_CONFIG_SHELL" -a -e "${_CONFIG_SHELL}.exe"; then + _CONFIG_SHELL="${_CONFIG_SHELL}.exe" + fi + dnl Yes, this is horrible. But since msys doesn't preserve environment + dnl variables and command line arguments as they are when transitioning + dnl from msys (this script) to python (below), we have to resort to hacks, + dnl storing the environment and command line arguments from a msys process + dnl (perl), and reading it from python. + dumpenv="$PERL $_topsrcdir/build/win32/dumpenv4python.pl $ac_configure_args | " + ;; + esac + + eval $dumpenv $PYTHON $_topsrcdir/build/subconfigure.py --prepare "$srcdir" "$moz_config_dir" "$_CONFIG_SHELL" $ac_configure_args ifelse($2,,,--cache-file="$2") + + dnl Actual subconfigure execution happens in MOZ_RUN_CONFIG_STATUS +done +]) + +define([AC_OUTPUT_SUBDIRS_NOW], +[ +for moz_config_dir_ in $1; do + AC_OUTPUT_SUBDIRS($moz_config_dir_,$2) + tail -1 subconfigures >> skip_subconfigures + MOZ_RUN_SUBCONFIGURES(`tail -1 skip_subconfigures`) +done +]) + +define([MOZ_RUN_SUBCONFIGURES], +[dnl Execute subconfigure, unless --no-recursion was passed to configure. +if test "$no_recursion" != yes; then + trap '' EXIT + if ! $PYTHON $_topsrcdir/build/subconfigure.py $1; then + exit 1 + fi +fi +]) + +define([MOZ_RUN_ALL_SUBCONFIGURES],[ +MOZ_RUN_SUBCONFIGURES([--list subconfigures --skip skip_subconfigures]) +]) + +dnl Print error messages in config.log as well as stderr +define([AC_MSG_ERROR], +[{ echo "configure: error: $1" 1>&2; echo "configure: error: $1" 1>&5; exit 1; }]) diff --git a/build/autoconf/hotfixes.m4 b/build/autoconf/hotfixes.m4 new file mode 100644 index 000000000..9c8362041 --- /dev/null +++ b/build/autoconf/hotfixes.m4 @@ -0,0 +1,23 @@ +dnl This Source Code Form is subject to the terms of the Mozilla Public +dnl dnl License, v. 2.0. If a copy of the MPL was not distributed with this +dnl dnl file, You can obtain one at http://mozilla.org/MPL/2.0/. + +dnl Set of hotfixes to address issues in autoconf 2.13 + +dnl Divert AC_CHECK_FUNC so that the #includes it uses can't interfere +dnl with the function it tests. +dnl So, when testing e.g. posix_memalign, any #include that AC_CHECK_FUNC +dnl prints is replaced with: +dnl #define posix_memalign innocuous_posix_memalign +dnl #include "theinclude" +dnl #undef posix_memalign +dnl This avoids double declaration of that function when the header normally +dnl declares it, while the test itself is just expecting the function not to be +dnl declared at all, and declares it differently (which doesn't matter for the +dnl test itself). +dnl More recent versions of autoconf are essentially doing this. +define([_AC_CHECK_FUNC],defn([AC_CHECK_FUNC]))dnl +define([AC_CHECK_FUNC], [dnl +patsubst(_AC_CHECK_FUNC($@), [#include.*$], [#define $1 innocuous_$1 +\& +#undef $1])])dnl diff --git a/build/autoconf/icu.m4 b/build/autoconf/icu.m4 new file mode 100644 index 000000000..794ddcdf4 --- /dev/null +++ b/build/autoconf/icu.m4 @@ -0,0 +1,111 @@ +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 Set the MOZ_ICU_VERSION variable to denote the current version of the +dnl ICU library, as well as a few other things. + +AC_DEFUN([MOZ_CONFIG_ICU], [ + +ICU_LIB_NAMES= +MOZ_SYSTEM_ICU= +MOZ_ARG_WITH_BOOL(system-icu, +[ --with-system-icu + Use system ICU (located with pkgconfig)], + MOZ_SYSTEM_ICU=1) + +if test -n "$MOZ_SYSTEM_ICU"; then + PKG_CHECK_MODULES(MOZ_ICU, icu-i18n >= 58.1) + CFLAGS="$CFLAGS $MOZ_ICU_CFLAGS" + CXXFLAGS="$CXXFLAGS $MOZ_ICU_CFLAGS" +fi + +AC_SUBST(MOZ_SYSTEM_ICU) + +MOZ_ARG_WITH_STRING(intl-api, +[ --with-intl-api, --with-intl-api=build, --without-intl-api + Determine the status of the ECMAScript Internationalization API. The first + (or lack of any of these) builds and exposes the API. The second builds it + but doesn't use ICU or expose the API to script. The third doesn't build + ICU at all.], + _INTL_API=$withval) + +ENABLE_INTL_API= +EXPOSE_INTL_API= +case "$_INTL_API" in +no) + ;; +build) + ENABLE_INTL_API=1 + ;; +yes) + ENABLE_INTL_API=1 + EXPOSE_INTL_API=1 + ;; +*) + AC_MSG_ERROR([Invalid value passed to --with-intl-api: $_INTL_API]) + ;; +esac + +if test -n "$ENABLE_INTL_API"; then + USE_ICU=1 +fi + +if test -n "$EXPOSE_INTL_API"; then + AC_DEFINE(EXPOSE_INTL_API) +fi + +if test -n "$ENABLE_INTL_API"; then + AC_DEFINE(ENABLE_INTL_API) +fi + +dnl Settings for the implementation of the ECMAScript Internationalization API +if test -n "$USE_ICU"; then + icudir="$_topsrcdir/intl/icu/source" + if test ! -d "$icudir"; then + icudir="$_topsrcdir/../../intl/icu/source" + if test ! -d "$icudir"; then + AC_MSG_ERROR([Cannot find the ICU directory]) + fi + fi + + version=`sed -n 's/^[[[:space:]]]*#[[:space:]]*define[[:space:]][[:space:]]*U_ICU_VERSION_MAJOR_NUM[[:space:]][[:space:]]*\([0-9][0-9]*\)[[:space:]]*$/\1/p' "$icudir/common/unicode/uvernum.h"` + if test x"$version" = x; then + AC_MSG_ERROR([cannot determine icu version number from uvernum.h header file $lineno]) + fi + MOZ_ICU_VERSION="$version" + + # TODO: the l is actually endian-dependent + # We could make this set as 'l' or 'b' for little or big, respectively, + # but we'd need to check in a big-endian version of the file. + ICU_DATA_FILE="icudt${version}l.dat" + + dnl We won't build ICU data as a separate file when building + dnl JS standalone so that embedders don't have to deal with it. + dnl We also don't do it on Windows because sometimes the file goes + dnl missing -- possibly due to overzealous antivirus software? -- + dnl which prevents the browser from starting up :( + if test -z "$JS_STANDALONE" -a -z "$MOZ_SYSTEM_ICU" -a "$OS_TARGET" != WINNT -a "$MOZ_WIDGET_TOOLKIT" != "android"; then + MOZ_ICU_DATA_ARCHIVE=1 + else + MOZ_ICU_DATA_ARCHIVE= + fi +fi + +AC_SUBST(MOZ_ICU_VERSION) +AC_SUBST(ENABLE_INTL_API) +AC_SUBST(USE_ICU) +AC_SUBST(ICU_DATA_FILE) +AC_SUBST(MOZ_ICU_DATA_ARCHIVE) + +if test -n "$USE_ICU" -a -z "$MOZ_SYSTEM_ICU"; then + if test -z "$YASM" -a -z "$GNU_AS" -a "$COMPILE_ENVIRONMENT"; then + AC_MSG_ERROR([Building ICU requires either yasm or a GNU assembler. If you do not have either of those available for this platform you must use --without-intl-api]) + fi + dnl We build ICU as a static library. + AC_DEFINE(U_STATIC_IMPLEMENTATION) + dnl Source files that use ICU should have control over which parts of the ICU + dnl namespace they want to use. + AC_DEFINE(U_USING_ICU_NAMESPACE,0) +fi +]) diff --git a/build/autoconf/install-sh b/build/autoconf/install-sh new file mode 100755 index 000000000..a4be13e59 --- /dev/null +++ b/build/autoconf/install-sh @@ -0,0 +1,123 @@ +#!/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/. + + +# +# install - install a program, script, or datafile +# This comes from X11R5; it is not part of GNU. +# +# $XConsortium: install.sh,v 1.2 89/12/18 14:47:22 jim Exp $ +# +# This script is compatible with the BSD install script, but was written +# from scratch. +# + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" + +instcmd="$mvprog" +chmodcmd="" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd="$cpprog" + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd="$stripprog" + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "install: no input file specified" + exit 1 +fi + +if [ x"$dst" = x ] +then + echo "install: no destination specified" + exit 1 +fi + + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + +if [ -d $dst ] +then + dst="$dst"/`basename $src` +fi + +# Make a temp file name in the proper directory. + +dstdir=`dirname $dst` +dsttmp=$dstdir/#inst.$$# + +# Move or copy the file name to the temp name + +$doit $instcmd $src $dsttmp + +# and set any options; do chmod last to preserve setuid bits + +if [ x"$chowncmd" != x ]; then $doit $chowncmd $dsttmp; fi +if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd $dsttmp; fi +if [ x"$stripcmd" != x ]; then $doit $stripcmd $dsttmp; fi +if [ x"$chmodcmd" != x ]; then $doit $chmodcmd $dsttmp; fi + +# Now rename the file to the real destination. + +$doit $rmcmd $dst +$doit $mvcmd $dsttmp $dst + + +exit 0 diff --git a/build/autoconf/ios.m4 b/build/autoconf/ios.m4 new file mode 100644 index 000000000..b341cda89 --- /dev/null +++ b/build/autoconf/ios.m4 @@ -0,0 +1,108 @@ +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/. + +AC_DEFUN([MOZ_IOS_PATH_PROG], +[ +changequote({,}) +_prog_name=ifelse($2, {}, `echo $1 | tr "[:upper:]" "[:lower:]"`, $2) +changequote([,]) +AC_CACHE_CHECK([for $_prog_name in iOS SDK], +ac_cv_ios_path_$1, +[ +_path=`xcrun --sdk $ios_sdk --find $_prog_name 2>/dev/null` +_res=$? +if test $_res -ne 0; then + AC_MSG_ERROR([Could not find '$_prog_name' in the iOS SDK]) +fi +ac_cv_ios_path_$1=$_path +]) +$1="${ac_cv_ios_path_$1}$3" +]) + +AC_DEFUN([MOZ_IOS_SDK], +[ + +MOZ_ARG_WITH_STRING(ios-sdk, +[ --with-ios-sdk=TYPE + Type of iOS SDK to use (iphonesimulator, iphoneos) + and optionally version (like iphoneos8.2)], + ios_sdk=$withval) + +MOZ_ARG_ENABLE_STRING(ios-target, + [ --enable-ios-target=VER (default=8.0) + Set the minimum iOS version needed at runtime], + [_IOS_TARGET=$enableval]) +_IOS_TARGET_DEFAULT=8.0 + +case "$target" in +arm*-apple-darwin*) + if test -z "$ios_sdk" -o "$ios_sdk" = "yes"; then + ios_sdk=iphoneos + fi + case "$ios_sdk" in + iphoneos*) + ios_target_arg="-miphoneos-version-min" + ;; + *) + AC_MSG_ERROR([Only 'iphoneos' SDKs are valid when targeting iOS device, don't know what to do with '$ios_sdk'.]) + ;; + esac + ;; +*-apple-darwin*) + ios_target_arg="-mios-simulator-version-min" + case "$ios_sdk" in + # Empty SDK is okay, this might be an OS X desktop build. + ""|iphonesimulator*) + ;; + # Default to iphonesimulator + yes) + ios_sdk=iphonesimulator + ;; + *) + AC_MSG_ERROR([Only 'iphonesimulator' SDKs are valid when targeting iOS simulator.]) + ;; + esac + ;; +esac + + +if test -n "$ios_sdk"; then + if test -z "$_IOS_TARGET"; then + _IOS_TARGET=$_IOS_TARGET_DEFAULT + ios_target_arg="${ios_target_arg}=${_IOS_TARGET}" + fi + # Ensure that xcrun knows where this SDK is. + ios_sdk_path=`xcrun --sdk $ios_sdk --show-sdk-path 2>/dev/null` + _ret=$? + if test $_ret -ne 0; then + AC_MSG_ERROR([iOS SDK '$ios_sdk' could not be found.]) + fi + MOZ_IOS=1 + export HOST_CC=clang + export HOST_CXX=clang++ + # Add isysroot, arch, and ios target arguments + case "$target_cpu" in + arm*) + ARGS="-arch armv7" + ;; + *) + # Unfortunately simulator builds need this. + export CROSS_COMPILE=1 + ;; + esac + ARGS=" $ARGS -isysroot $ios_sdk_path $ios_target_arg" + # Now find our tools + MOZ_IOS_PATH_PROG(CC, clang, $ARGS) + MOZ_IOS_PATH_PROG(CXX, clang++, $ARGS) + export CPP="$CC -E" + export LD="$CXX" + MOZ_IOS_PATH_PROG(AR) + MOZ_IOS_PATH_PROG(AS, as, $ARGS) + MOZ_IOS_PATH_PROG(OTOOL) + MOZ_IOS_PATH_PROG(STRIP) + export PKG_CONFIG_PATH=${ios_sdk_path}/usr/lib/pkgconfig/ +fi + +AC_SUBST(MOZ_IOS) +]) diff --git a/build/autoconf/jemalloc.m4 b/build/autoconf/jemalloc.m4 new file mode 100644 index 000000000..3b9c603f7 --- /dev/null +++ b/build/autoconf/jemalloc.m4 @@ -0,0 +1,114 @@ +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/. + +AC_DEFUN([MOZ_SUBCONFIGURE_JEMALLOC], [ + +if test "$MOZ_BUILD_APP" != js -o -n "$JS_STANDALONE"; then + + # Run jemalloc configure script + + if test -z "$MOZ_SYSTEM_JEMALLOC" -a "$MOZ_MEMORY" && test -n "$MOZ_JEMALLOC4" -o -n "$MOZ_REPLACE_MALLOC"; then + ac_configure_args="--build=$build --host=$target --enable-stats --with-jemalloc-prefix=je_ --disable-valgrind" + # We're using memalign for _aligned_malloc in memory/build/mozmemory_wrap.c + # on Windows, so just export memalign on all platforms. + ac_configure_args="$ac_configure_args ac_cv_func_memalign=yes" + if test -n "$MOZ_REPLACE_MALLOC"; then + # When using replace_malloc, we always want valloc exported from jemalloc. + ac_configure_args="$ac_configure_args ac_cv_func_valloc=yes" + if test "${OS_ARCH}" = Darwin; then + # We also need to enable pointer validation on Mac because jemalloc's + # zone allocator is not used. + ac_configure_args="$ac_configure_args --enable-ivsalloc" + fi + fi + if test -n "$MOZ_JEMALLOC4"; then + case "${OS_ARCH}" in + WINNT|Darwin) + # We want jemalloc functions to be kept hidden on both Mac and Windows + # See memory/build/mozmemory_wrap.h for details. + ac_configure_args="$ac_configure_args --without-export" + ;; + esac + if test "${OS_ARCH}" = WINNT; then + # Lazy lock initialization doesn't play well with lazy linking of + # mozglue.dll on Windows XP (leads to startup crash), so disable it. + ac_configure_args="$ac_configure_args --disable-lazy-lock" + + # 64-bit Windows builds require a minimum 16-byte alignment. + if test -n "$HAVE_64BIT_BUILD"; then + ac_configure_args="$ac_configure_args --with-lg-tiny-min=4" + fi + fi + elif test "${OS_ARCH}" = Darwin; then + # When building as a replace-malloc lib, disabling the zone allocator + # forces to use pthread_atfork. + ac_configure_args="$ac_configure_args --disable-zone-allocator" + fi + _MANGLE="malloc posix_memalign aligned_alloc calloc realloc free memalign valloc malloc_usable_size" + JEMALLOC_WRAPPER= + if test -z "$MOZ_REPLACE_MALLOC"; then + case "$OS_ARCH" in + Linux|DragonFly|FreeBSD|NetBSD|OpenBSD) + MANGLE=$_MANGLE + ;; + esac + elif test -z "$MOZ_JEMALLOC4"; then + MANGLE=$_MANGLE + JEMALLOC_WRAPPER=replace_ + fi + if test -n "$MANGLE"; then + MANGLED= + for mangle in ${MANGLE}; do + if test -n "$MANGLED"; then + MANGLED="$mangle:$JEMALLOC_WRAPPER$mangle,$MANGLED" + else + MANGLED="$mangle:$JEMALLOC_WRAPPER$mangle" + fi + done + ac_configure_args="$ac_configure_args --with-mangling=$MANGLED" + fi + unset CONFIG_FILES + if test -z "$MOZ_TLS"; then + ac_configure_args="$ac_configure_args --disable-tls" + fi + EXTRA_CFLAGS="$CFLAGS" + for var in AS CC CXX CPP LD AR RANLIB STRIP CPPFLAGS EXTRA_CFLAGS LDFLAGS; do + ac_configure_args="$ac_configure_args $var='`eval echo \\${${var}}`'" + done + + # jemalloc's configure assumes that if you have CFLAGS set at all, you set + # all the flags necessary to configure jemalloc, which is not likely to be + # the case on Windows if someone is building Firefox with flags set in + # their mozconfig. + if test "$_MSC_VER"; then + ac_configure_args="$ac_configure_args CFLAGS=" + fi + + # Force disable DSS support in jemalloc. + ac_configure_args="$ac_configure_args ac_cv_func_sbrk=false" + + # Make Linux builds munmap freed chunks instead of recycling them. + ac_configure_args="$ac_configure_args --enable-munmap" + + # Disable cache oblivious behavior that appears to have a performance + # impact on Firefox. + ac_configure_args="$ac_configure_args --disable-cache-oblivious" + + if ! test -e memory/jemalloc; then + mkdir -p memory/jemalloc + fi + + # jemalloc's configure runs git to determine the version. But when building + # from a gecko git clone, the git commands it uses is going to pick gecko's + # information, not jemalloc's, which is useless. So pretend we don't have git + # at all. That will make jemalloc's configure pick the in-tree VERSION file. + (PATH="$srcdir/memory/jemalloc/helper:$PATH"; + AC_OUTPUT_SUBDIRS(memory/jemalloc/src) + ) || exit 1 + ac_configure_args="$_SUBDIR_CONFIG_ARGS" + fi + +fi + +]) diff --git a/build/autoconf/lto.m4 b/build/autoconf/lto.m4 new file mode 100644 index 000000000..572a04b48 --- /dev/null +++ b/build/autoconf/lto.m4 @@ -0,0 +1,19 @@ +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 check if the build is using lto. This is really primitive and only detects llvm based +dnl compilers right now. +AC_DEFUN([MOZ_DOING_LTO], +[ + cat > conftest.c <<EOF + int foo = 1; +EOF + $1=no + if ${CC-cc} ${CFLAGS} -S conftest.c -o conftest.s >/dev/null 2>&1; then + if grep '^target triple =' conftest.s; then + $1=yes + fi + fi + rm -f conftest.[cs] +]) diff --git a/build/autoconf/mozheader.m4 b/build/autoconf/mozheader.m4 new file mode 100644 index 000000000..e99e35a40 --- /dev/null +++ b/build/autoconf/mozheader.m4 @@ -0,0 +1,32 @@ +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 MOZ_CHECK_HEADER(HEADER-FILE, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, INCLUDES]]]) +AC_DEFUN([MOZ_CHECK_HEADER], +[ dnl Do the transliteration at runtime so arg 1 can be a shell variable. + ac_safe=`echo "$1" | sed 'y%./+-%__p_%'` + AC_MSG_CHECKING([for $1]) + AC_CACHE_VAL(ac_cv_header_$ac_safe, + [ AC_TRY_COMPILE([$4 +#include <$1>], , + eval "ac_cv_header_$ac_safe=yes", + eval "ac_cv_header_$ac_safe=no") ]) + if eval "test \"`echo '$ac_cv_header_'$ac_safe`\" = yes"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , , [$3]) + fi +]) + +dnl MOZ_CHECK_HEADERS(HEADER-FILE... [, ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND [, INCLUDES]]]) +AC_DEFUN([MOZ_CHECK_HEADERS], +[ for ac_hdr in $1 + do + MOZ_CHECK_HEADER($ac_hdr, + [ ac_tr_hdr=HAVE_`echo $ac_hdr | sed 'y%abcdefghijklmnopqrstuvwxyz./-%ABCDEFGHIJKLMNOPQRSTUVWXYZ___%'` + AC_DEFINE_UNQUOTED($ac_tr_hdr) $2], $3, [$4]) + done +]) diff --git a/build/autoconf/mozprog.m4 b/build/autoconf/mozprog.m4 new file mode 100644 index 000000000..08747b495 --- /dev/null +++ b/build/autoconf/mozprog.m4 @@ -0,0 +1,42 @@ +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/. + +AC_DEFUN([MOZ_PROG_CHECKMSYS], +[AC_REQUIRE([AC_INIT_BINSH])dnl +if test `uname -s | grep -c MINGW 2>/dev/null` != "0"; then + msyshost=1 +fi +]) + +AC_DEFUN([MOZ_PATH_PROG], +[ AC_PATH_PROG($1,$2,$3,$4) + if test "$msyshost"; then + case "[$]$1" in + /*) + tmp_DIRNAME=`dirname "[$]$1"` + tmp_BASENAME=`basename "[$]$1"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + $1="$tmp_PWD/$tmp_BASENAME" + if test -e "[$]$1.exe"; then + $1="[$]$1.exe" + fi + esac + fi +]) + +AC_DEFUN([MOZ_PATH_PROGS], +[ AC_PATH_PROGS($1,$2,$3,$4) + if test "$msyshost"; then + case "[$]$1" in + /*) + tmp_DIRNAME=`dirname "[$]$1"` + tmp_BASENAME=`basename "[$]$1"` + tmp_PWD=`cd "$tmp_DIRNAME" && pwd -W` + $1="$tmp_PWD/$tmp_BASENAME" + if test -e "[$]$1.exe"; then + $1="[$]$1.exe" + fi + esac + fi +]) diff --git a/build/autoconf/nspr-build.m4 b/build/autoconf/nspr-build.m4 new file mode 100644 index 000000000..b733579c2 --- /dev/null +++ b/build/autoconf/nspr-build.m4 @@ -0,0 +1,200 @@ +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/. + +AC_DEFUN([MOZ_CONFIG_NSPR], [ + +ifelse([$1],,define(CONFIGURING_JS,yes)) + +dnl Possible ways this can be called: +dnl from toplevel configure: +dnl JS_STANDALONE= MOZ_BUILD_APP!=js +dnl from js/src/configure invoked by toplevel configure: +dnl JS_STANDALONE= MOZ_BUILD_APP=js +dnl from standalone js/src/configure: +dnl JS_STANDALONE=1 MOZ_BUILD_APP=js + +dnl ======================================================== +dnl = Find the right NSPR to use. +dnl ======================================================== +MOZ_ARG_WITH_STRING(nspr-cflags, +[ --with-nspr-cflags=FLAGS + Pass FLAGS to CC when building code that uses NSPR. + Use this when there's no accurate nspr-config + script available. This is the case when building + SpiderMonkey as part of the Mozilla tree: the + top-level configure script computes NSPR flags + that accomodate the quirks of that environment.], + NSPR_CFLAGS=$withval) +MOZ_ARG_WITH_STRING(nspr-libs, +[ --with-nspr-libs=LIBS Pass LIBS to LD when linking code that uses NSPR. + See --with-nspr-cflags for more details.], + NSPR_LIBS=$withval) + +ifdef([CONFIGURING_JS],[ + MOZ_ARG_ENABLE_BOOL(nspr-build, +[ --enable-nspr-build Build NSPR from source tree], + MOZ_BUILD_NSPR=1, + MOZ_BUILD_NSPR=) +]) + +if test "$MOZ_BUILD_APP" != js || test -n "$JS_STANDALONE"; then + _IS_OUTER_CONFIGURE=1 +fi + +MOZ_ARG_WITH_BOOL(system-nspr, +[ --with-system-nspr Use an NSPR that is already built and installed. + Use the 'nspr-config' script in the current path, + or look for the script in the directories given with + --with-nspr-exec-prefix or --with-nspr-prefix. + (Those flags are only checked if you specify + --with-system-nspr.)], + _USE_SYSTEM_NSPR=1 ) + +JS_POSIX_NSPR=unset +ifdef([CONFIGURING_JS],[ + if test -n "$JS_STANDALONE"; then + case "$target" in + *linux*|*darwin*|*dragonfly*|*freebsd*|*netbsd*|*openbsd*) + if test -z "$_HAS_NSPR"; then + JS_POSIX_NSPR_DEFAULT=1 + fi + ;; + esac + fi + + MOZ_ARG_ENABLE_BOOL(posix-nspr-emulation, +[ --enable-posix-nspr-emulation + Enable emulation of NSPR for POSIX systems], + JS_POSIX_NSPR=1, + JS_POSIX_NSPR=) +]) + +dnl Pass at most one of +dnl --with-system-nspr +dnl --with-nspr-cflags/libs +dnl --enable-nspr-build +dnl --enable-posix-nspr-emulation + +AC_MSG_CHECKING([NSPR selection]) +nspr_opts= +which_nspr=default +if test -n "$_USE_SYSTEM_NSPR"; then + nspr_opts="x$nspr_opts" + which_nspr="system" +fi +if test -n "$NSPR_CFLAGS" -o -n "$NSPR_LIBS"; then + nspr_opts="x$nspr_opts" + which_nspr="command-line" +fi +if test -n "$MOZ_BUILD_NSPR"; then + nspr_opts="x$nspr_opts" + which_nspr="source-tree" +fi +if test "$JS_POSIX_NSPR" = unset; then + JS_POSIX_NSPR= +else + nspr_opts="x$nspr_opts" + which_nspr="posix-wrapper" +fi + +if test -z "$nspr_opts"; then + if test "$MOZ_BUILD_APP" != js; then + dnl Toplevel configure defaults to using nsprpub from the source tree + MOZ_BUILD_NSPR=1 + which_nspr="source-tree" + else + dnl JS configure defaults to emulated NSPR if available, falling back + dnl to nsprpub. + JS_POSIX_NSPR="$JS_POSIX_NSPR_DEFAULT" + if test -z "$JS_POSIX_NSPR"; then + MOZ_BUILD_NSPR=1 + which_nspr="source-tree" + else + which_nspr="posix-wrapper" + fi + fi +fi + +if test -z "$nspr_opts" || test "$nspr_opts" = x; then + AC_MSG_RESULT($which_nspr) +else + AC_MSG_ERROR([only one way of using NSPR may be selected. See 'configure --help'.]) +fi + +AC_SUBST(MOZ_BUILD_NSPR) + +if test "$MOZ_BUILD_APP" = js; then + if test "$JS_POSIX_NSPR" = 1; then + AC_DEFINE(JS_POSIX_NSPR) + fi + AC_SUBST(JS_POSIX_NSPR) +fi + +# A (sub)configure invoked by the toplevel configure will always receive +# --with-nspr-libs on the command line. It will never need to figure out +# anything itself. +if test -n "$_IS_OUTER_CONFIGURE"; then + +if test -n "$_USE_SYSTEM_NSPR"; then + AM_PATH_NSPR($NSPR_MINVER, [MOZ_SYSTEM_NSPR=1], [AC_MSG_ERROR([you do not have NSPR installed or your version is older than $NSPR_MINVER.])]) +fi + +if test -n "$MOZ_SYSTEM_NSPR" -o -n "$NSPR_CFLAGS" -o -n "$NSPR_LIBS"; then + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $NSPR_CFLAGS" + AC_TRY_COMPILE([#include "prtypes.h"], + [#ifndef PR_STATIC_ASSERT + #error PR_STATIC_ASSERT not defined or requires including prtypes.h + #endif], + , + AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT or including prtypes.h does not provide it])) + AC_TRY_COMPILE([#include "prtypes.h"], + [#ifndef PR_UINT64 + #error PR_UINT64 not defined or requires including prtypes.h + #endif], + , + AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it])) + CFLAGS=$_SAVE_CFLAGS +elif test -z "$JS_POSIX_NSPR"; then + NSPR_INCLUDE_DIR="${DIST}/include/nspr" + NSPR_CFLAGS="-I${NSPR_INCLUDE_DIR}" + if test -n "$GNU_CC"; then + if test -n "$MOZ_FOLD_LIBS"; then + NSPR_LIB_DIR=${DIST}/lib + else + NSPR_LIB_DIR=${DIST}/bin + fi + NSPR_LIBS="-L${NSPR_LIB_DIR} -lnspr${NSPR_VERSION} -lplc${NSPR_VERSION} -lplds${NSPR_VERSION}" + else + # NSS needs actual static libs to link to, and this is where they are. + NSPR_LIBS="${DIST}/lib/nspr${NSPR_VERSION}.lib ${DIST}/lib/plc${NSPR_VERSION}.lib ${DIST}/lib/plds${NSPR_VERSION}.lib " + NSPR_LIB_DIR="${DIST}/lib" + fi +fi + +AC_SUBST_LIST(NSPR_CFLAGS) +AC_SUBST(NSPR_INCLUDE_DIR) +AC_SUBST(NSPR_LIB_DIR) + +PKGCONF_REQUIRES_PRIVATE="Requires.private: nspr" +if test -n "$MOZ_SYSTEM_NSPR"; then + _SAVE_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS $NSPR_CFLAGS" + AC_TRY_COMPILE([#include "prlog.h"], + [#ifndef PR_STATIC_ASSERT + #error PR_STATIC_ASSERT not defined + #endif], + , + AC_MSG_ERROR([system NSPR does not support PR_STATIC_ASSERT])) + CFLAGS=$_SAVE_CFLAGS + # piggy back on $MOZ_SYSTEM_NSPR to set a variable for the nspr check for js.pc + PKGCONF_REQUIRES_PRIVATE="Requires.private: nspr >= $NSPR_MINVER" +elif test -n "$JS_POSIX_NSPR"; then + PKGCONF_REQUIRES_PRIVATE= +fi +AC_SUBST([PKGCONF_REQUIRES_PRIVATE]) + +fi # _IS_OUTER_CONFIGURE + +]) diff --git a/build/autoconf/nspr.m4 b/build/autoconf/nspr.m4 new file mode 100644 index 000000000..cccc8828e --- /dev/null +++ b/build/autoconf/nspr.m4 @@ -0,0 +1,110 @@ +# -*- tab-width: 4; -*- +# Configure paths for NSPR +# Public domain - Chris Seawood <cls@seawood.org> 2001-04-05 +# Based upon gtk.m4 (also PD) by Owen Taylor + +dnl AM_PATH_NSPR([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for NSPR, and define NSPR_CFLAGS and NSPR_LIBS +dnl +dnl If the nspr-config script is available, use it to find the +dnl appropriate CFLAGS and LIBS, and to check for the required +dnl version, and run ACTION-IF-FOUND. +dnl +dnl Otherwise, if NO_NSPR_CONFIG_SYSTEM_VERSION is set, we use it, +dnl NO_NSPR_CONFIG_SYSTEM_CFLAGS, and NO_NSPR_CONFIG_SYSTEM_LIBS to +dnl provide default values, and run ACTION-IF-FOUND. (Some systems +dnl ship NSPR without nspr-config, but can glean the appropriate flags +dnl and version.) +dnl +dnl Otherwise, run ACTION-IF-NOT-FOUND. +AC_DEFUN([AM_PATH_NSPR], +[dnl + +AC_ARG_WITH(nspr-prefix, + [ --with-nspr-prefix=PFX Prefix where NSPR is installed], + nspr_config_prefix="$withval", + nspr_config_prefix="") + +AC_ARG_WITH(nspr-exec-prefix, + [ --with-nspr-exec-prefix=PFX + Exec prefix where NSPR is installed], + nspr_config_exec_prefix="$withval", + nspr_config_exec_prefix="") + + if test -n "$nspr_config_exec_prefix"; then + nspr_config_args="$nspr_config_args --exec-prefix=$nspr_config_exec_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_exec_prefix/bin/nspr-config + fi + fi + if test -n "$nspr_config_prefix"; then + nspr_config_args="$nspr_config_args --prefix=$nspr_config_prefix" + if test -z "$NSPR_CONFIG"; then + NSPR_CONFIG=$nspr_config_prefix/bin/nspr-config + fi + fi + + unset ac_cv_path_NSPR_CONFIG + AC_PATH_PROG(NSPR_CONFIG, nspr-config, no) + min_nspr_version=ifelse([$1], ,4.0.0,$1) + AC_MSG_CHECKING(for NSPR - version >= $min_nspr_version) + + no_nspr="" + if test "$NSPR_CONFIG" != "no"; then + NSPR_CFLAGS=`$NSPR_CONFIG $nspr_config_args --cflags` + NSPR_LIBS=`$NSPR_CONFIG $nspr_config_args --libs` + NSPR_VERSION_STRING=`$NSPR_CONFIG $nspr_config_args --version` + elif test -n "${NO_NSPR_CONFIG_SYSTEM_VERSION}"; then + NSPR_CFLAGS="${NO_NSPR_CONFIG_SYSTEM_CFLAGS}" + NSPR_LIBS="${NO_NSPR_CONFIG_SYSTEM_LDFLAGS}" + NSPR_VERSION_STRING="$NO_NSPR_CONFIG_SYSTEM_VERSION" + else + no_nspr="yes" + fi + + if test -z "$no_nspr"; then + nspr_config_major_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'` + nspr_config_minor_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'` + nspr_config_micro_version=`echo $NSPR_VERSION_STRING | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'` + if test -z "$nspr_config_micro_version"; then + nspr_config_micro_version="0" + fi + + min_nspr_major_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'` + min_nspr_minor_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'` + min_nspr_micro_version=`echo $min_nspr_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'` + if test -z "$min_nspr_micro_version"; then + min_nspr_micro_version="0" + fi + + if test "$nspr_config_major_version" -ne "$min_nspr_major_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -lt "$min_nspr_minor_version"; then + no_nspr="yes" + elif test "$nspr_config_major_version" -eq "$min_nspr_major_version" && + test "$nspr_config_minor_version" -eq "$min_nspr_minor_version" && + test "$nspr_config_micro_version" -lt "$min_nspr_micro_version"; then + no_nspr="yes" + fi + fi + + if test -z "$no_nspr"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , :, [$3]) + fi + + + AC_SUBST_LIST(NSPR_CFLAGS) + AC_SUBST_LIST(NSPR_LIBS) + +]) diff --git a/build/autoconf/nss.m4 b/build/autoconf/nss.m4 new file mode 100644 index 000000000..07efb0078 --- /dev/null +++ b/build/autoconf/nss.m4 @@ -0,0 +1,91 @@ +# -*- tab-width: 4; -*- +# Configure paths for NSS +# Public domain - Chris Seawood <cls@seawood.org> 2001-04-05 +# Based upon gtk.m4 (also PD) by Owen Taylor + +dnl AM_PATH_NSS([MINIMUM-VERSION, [ACTION-IF-FOUND [, ACTION-IF-NOT-FOUND]]]) +dnl Test for NSS, and define NSS_CFLAGS and NSS_LIBS +AC_DEFUN([AM_PATH_NSS], +[dnl + +AC_ARG_WITH(nss-prefix, + [ --with-nss-prefix=PFX Prefix where NSS is installed], + nss_config_prefix="$withval", + nss_config_prefix="") + +AC_ARG_WITH(nss-exec-prefix, + [ --with-nss-exec-prefix=PFX + Exec prefix where NSS is installed], + nss_config_exec_prefix="$withval", + nss_config_exec_prefix="") + + if test -n "$nss_config_exec_prefix"; then + nss_config_args="$nss_config_args --exec-prefix=$nss_config_exec_prefix" + if test -z "$NSS_CONFIG"; then + NSS_CONFIG=$nss_config_exec_prefix/bin/nss-config + fi + fi + if test -n "$nss_config_prefix"; then + nss_config_args="$nss_config_args --prefix=$nss_config_prefix" + if test -z "$NSS_CONFIG"; then + NSS_CONFIG=$nss_config_prefix/bin/nss-config + fi + fi + + unset ac_cv_path_NSS_CONFIG + AC_PATH_PROG(NSS_CONFIG, nss-config, no) + min_nss_version=ifelse([$1], ,3.0.0,$1) + AC_MSG_CHECKING(for NSS - version >= $min_nss_version) + + no_nss="" + if test "$NSS_CONFIG" = "no"; then + no_nss="yes" + else + NSS_CFLAGS=`$NSS_CONFIG $nss_config_args --cflags` + NSS_LIBS=`$NSS_CONFIG $nss_config_args --libs` + + nss_config_major_version=`$NSS_CONFIG $nss_config_args --version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'` + nss_config_minor_version=`$NSS_CONFIG $nss_config_args --version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'` + nss_config_micro_version=`$NSS_CONFIG $nss_config_args --version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'` + if test -z "$nss_config_micro_version"; then + nss_config_micro_version="0" + fi + + min_nss_major_version=`echo $min_nss_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\1/'` + min_nss_minor_version=`echo $min_nss_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\2/'` + min_nss_micro_version=`echo $min_nss_version | \ + sed 's/\([[0-9]]*\)\.\([[0-9]]*\)\(\.\([[0-9]]*\)\)\{0,1\}/\4/'` + if test -z "$min_nss_micro_version"; then + min_nss_micro_version="0" + fi + + if test "$nss_config_major_version" -lt "$min_nss_major_version"; then + no_nss="yes" + elif test "$nss_config_major_version" -eq "$min_nss_major_version" && + test "$nss_config_minor_version" -lt "$min_nss_minor_version"; then + no_nss="yes" + elif test "$nss_config_major_version" -eq "$min_nss_major_version" && + test "$nss_config_minor_version" -eq "$min_nss_minor_version" && + test "$nss_config_micro_version" -lt "$min_nss_micro_version"; then + no_nss="yes" + fi + fi + + if test -z "$no_nss"; then + AC_MSG_RESULT(yes) + ifelse([$2], , :, [$2]) + else + AC_MSG_RESULT(no) + ifelse([$3], , :, [$3]) + fi + + + AC_SUBST_LIST(NSS_CFLAGS) + AC_SUBST_LIST(NSS_LIBS) + +]) diff --git a/build/autoconf/pkg.m4 b/build/autoconf/pkg.m4 new file mode 100644 index 000000000..ca8ef7e5a --- /dev/null +++ b/build/autoconf/pkg.m4 @@ -0,0 +1,61 @@ +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/. + +# PKG_CHECK_MODULES(GSTUFF, gtk+-2.0 >= 1.3 glib = 1.3.4, action-if, action-not) +# defines GSTUFF_LIBS, GSTUFF_CFLAGS, see pkg-config man page +# also defines GSTUFF_PKG_ERRORS on error +# PKG_CONFIG is set by Python configure, if it is empty here it could not +# be found. +AC_DEFUN([PKG_CHECK_MODULES], +[succeeded=no + + if test -z "$PKG_CONFIG"; then + echo "*** The pkg-config script could not be found. Make sure it is" + echo "*** in your path, or set the PKG_CONFIG environment variable" + echo "*** to the full path to pkg-config." + echo "*** Or see http://www.freedesktop.org/software/pkgconfig to get pkg-config." + else + PKG_CONFIG_MIN_VERSION=0.9.0 + if $PKG_CONFIG --atleast-pkgconfig-version $PKG_CONFIG_MIN_VERSION; then + AC_MSG_CHECKING(for $2) + + if $PKG_CONFIG --exists "$2" ; then + AC_MSG_RESULT(yes) + succeeded=yes + + AC_MSG_CHECKING($1_CFLAGS) + $1_CFLAGS=`$PKG_CONFIG --cflags "$2"` + AC_MSG_RESULT($$1_CFLAGS) + + AC_MSG_CHECKING($1_LIBS) + ## Remove evil flags like -Wl,--export-dynamic + $1_LIBS="`$PKG_CONFIG --libs \"$2\" |sed s/-Wl,--export-dynamic//g`" + AC_MSG_RESULT($$1_LIBS) + else + $1_CFLAGS="" + $1_LIBS="" + ## If we have a custom action on failure, don't print errors, but + ## do set a variable so people can do so. + $1_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"` + ifelse([$4], ,echo $$1_PKG_ERRORS,) + fi + + AC_SUBST_LIST($1_CFLAGS) + AC_SUBST_LIST($1_LIBS) + else + echo "*** Your version of pkg-config is too old. You need version $PKG_CONFIG_MIN_VERSION or newer." + echo "*** See http://www.freedesktop.org/software/pkgconfig" + fi + fi + + if test $succeeded = yes; then + ifelse([$3], , :, [$3]) + else + if test "$COMPILE_ENVIRONMENT"; then + ifelse([$4], , AC_MSG_ERROR([Library requirements ($2) not met; consider adjusting the PKG_CONFIG_PATH environment variable if your libraries are in a nonstandard prefix so pkg-config can find them.]), [$4]) + fi + fi +]) + + diff --git a/build/autoconf/sanitize.m4 b/build/autoconf/sanitize.m4 new file mode 100644 index 000000000..3193d5c62 --- /dev/null +++ b/build/autoconf/sanitize.m4 @@ -0,0 +1,106 @@ +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/. + +AC_DEFUN([MOZ_CONFIG_SANITIZE], [ + +dnl ======================================================== +dnl = Use Address Sanitizer +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(address-sanitizer, +[ --enable-address-sanitizer Enable Address Sanitizer (default=no)], + MOZ_ASAN=1, + MOZ_ASAN= ) +if test -n "$MOZ_ASAN"; then + MOZ_LLVM_HACKS=1 + if test -n "$CLANG_CL"; then + # Look for the ASan runtime binary + if test "$CPU_ARCH" = "x86_64"; then + MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-x86_64.dll + else + MOZ_CLANG_RT_ASAN_LIB=clang_rt.asan_dynamic-i386.dll + fi + # We use MOZ_PATH_PROG in order to get a Windows style path. + MOZ_PATH_PROG(MOZ_CLANG_RT_ASAN_LIB_PATH, $MOZ_CLANG_RT_ASAN_LIB) + if test -z "$MOZ_CLANG_RT_ASAN_LIB_PATH"; then + AC_MSG_ERROR([Couldn't find $MOZ_CLANG_RT_ASAN_LIB. It should be available in the same location as clang-cl.]) + fi + AC_SUBST(MOZ_CLANG_RT_ASAN_LIB_PATH) + # Suppressing errors in recompiled code. + if test "$OS_ARCH" = "WINNT"; then + CFLAGS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/asan_blacklist_win.txt $CFLAGS" + CXXFLAGS="-fsanitize-blacklist=$_topsrcdir/build/sanitizers/asan_blacklist_win.txt $CXXFLAGS" + fi + fi + CFLAGS="-fsanitize=address $CFLAGS" + CXXFLAGS="-fsanitize=address $CXXFLAGS" + if test -z "$CLANG_CL"; then + LDFLAGS="-fsanitize=address $LDFLAGS" + fi + AC_DEFINE(MOZ_ASAN) + MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) +fi +AC_SUBST(MOZ_ASAN) + +dnl ======================================================== +dnl = Use Memory Sanitizer +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(memory-sanitizer, +[ --enable-memory-sanitizer Enable Memory Sanitizer (default=no)], + MOZ_MSAN=1, + MOZ_MSAN= ) +if test -n "$MOZ_MSAN"; then + MOZ_LLVM_HACKS=1 + CFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CFLAGS" + CXXFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $CXXFLAGS" + if test -z "$CLANG_CL"; then + LDFLAGS="-fsanitize=memory -fsanitize-memory-track-origins $LDFLAGS" + fi + AC_DEFINE(MOZ_MSAN) + MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) +fi +AC_SUBST(MOZ_MSAN) + +dnl ======================================================== +dnl = Use Thread Sanitizer +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(thread-sanitizer, +[ --enable-thread-sanitizer Enable Thread Sanitizer (default=no)], + MOZ_TSAN=1, + MOZ_TSAN= ) +if test -n "$MOZ_TSAN"; then + MOZ_LLVM_HACKS=1 + CFLAGS="-fsanitize=thread $CFLAGS" + CXXFLAGS="-fsanitize=thread $CXXFLAGS" + if test -z "$CLANG_CL"; then + LDFLAGS="-fsanitize=thread $LDFLAGS" + fi + AC_DEFINE(MOZ_TSAN) + MOZ_PATH_PROG(LLVM_SYMBOLIZER, llvm-symbolizer) +fi +AC_SUBST(MOZ_TSAN) + +# The LLVM symbolizer is used by all sanitizers +AC_SUBST(LLVM_SYMBOLIZER) + +dnl ======================================================== +dnl = Enable hacks required for LLVM instrumentations +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(llvm-hacks, +[ --enable-llvm-hacks Enable workarounds required for several LLVM instrumentations (default=no)], + MOZ_LLVM_HACKS=1, + MOZ_LLVM_HACKS= ) +if test -n "$MOZ_LLVM_HACKS"; then + MOZ_NO_WLZDEFS=1 + MOZ_CFLAGS_NSS=1 +fi +AC_SUBST(MOZ_NO_WLZDEFS) +AC_SUBST(MOZ_CFLAGS_NSS) + +dnl ======================================================== +dnl = Test for whether the compiler is compatible with the +dnl = given sanitize options. +dnl ======================================================== +AC_TRY_LINK(,,,AC_MSG_ERROR([compiler is incompatible with sanitize options])) + +]) diff --git a/build/autoconf/subconfigure.m4 b/build/autoconf/subconfigure.m4 new file mode 100644 index 000000000..8e95e014e --- /dev/null +++ b/build/autoconf/subconfigure.m4 @@ -0,0 +1,46 @@ +dnl We are not running in a real autoconf environment. So we're using real m4 +dnl here, not the crazier environment that autoconf provides. + +dnl Autoconf expects [] for quotes; give it to them +changequote([, ]) + +dnl AC_DEFUN is provided to use instead of define in autoconf. Provide it too. +define([AC_DEFUN], [define($1, [$2])]) + +dnl AC_ARG_ENABLE(FEATURE, HELP-STRING, IF-TRUE[, IF-FALSE]) +dnl We have to ignore the help string due to how help works in autoconf... +AC_DEFUN([MOZ_AC_ARG_ENABLE], +[#] Check whether --enable-[$1] or --disable-[$1] was given. +[if test "[${enable_]patsubst([$1], -, _)+set}" = set; then + enableval="[$enable_]patsubst([$1], -, _)" + $3 +ifelse([$4], , , [else + $4 +])dnl +fi +]) + +dnl AC_MSG_ERROR(error-description) +AC_DEFUN([AC_MSG_ERROR], [{ echo "configure: error: $1" 1>&2; exit 1; }]) + +AC_DEFUN([AC_MSG_WARN], [ echo "configure: warning: $1" 1>&2 ]) + +dnl Add the variable to the list of substitution variables +AC_DEFUN([AC_SUBST], +[ +_subconfigure_ac_subst_args="$_subconfigure_ac_subst_args $1" +]) + +dnl Override for AC_DEFINE. +AC_DEFUN([AC_DEFINE], +[ +cat >>confdefs.h <<\EOF +[#define] $1 ifelse($#, 2, [$2], $#, 3, [$2], 1) +EOF +cat >> confdefs.pytmp <<\EOF + (''' $1 ''', ifelse($#, 2, [r''' $2 '''], $#, 3, [r''' $2 '''], ' 1 ')) +EOF +]) + +dnl AC_OUTPUT_SUBDIRS(subdirectory) +AC_DEFUN([AC_OUTPUT_SUBDIRS], [do_output_subdirs "$1"]) diff --git a/build/autoconf/toolchain.m4 b/build/autoconf/toolchain.m4 new file mode 100644 index 000000000..3109f5dfc --- /dev/null +++ b/build/autoconf/toolchain.m4 @@ -0,0 +1,126 @@ +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 Several autoconf functions AC_REQUIRE AC_PROG_CPP/AC_PROG_CXXCPP +dnl or AC_HEADER_STDC, meaning they are called even when we don't call +dnl them explicitly. +dnl However, theses checks are not necessary and python configure sets +dnl the corresponding variables already, so just skip those tests +dnl entirely. +define([AC_PROG_CPP],[]) +define([AC_PROG_CXXCPP],[]) +define([AC_HEADER_STDC], []) + +AC_DEFUN([MOZ_TOOL_VARIABLES], +[ +GNU_AS= +GNU_LD= + +GNU_CC= +GNU_CXX= +if test "$CC_TYPE" = "gcc"; then + GNU_CC=1 + GNU_CXX=1 +fi + +if test "`echo | $AS -o conftest.out -v 2>&1 | grep -c GNU`" != "0"; then + GNU_AS=1 +fi +rm -f conftest.out +if test "`echo | $LD -v 2>&1 | grep -c GNU`" != "0"; then + GNU_LD=1 +fi + +CLANG_CC= +CLANG_CXX= +CLANG_CL= +if test "$CC_TYPE" = "clang"; then + GNU_CC=1 + GNU_CXX=1 + CLANG_CC=1 + CLANG_CXX=1 +fi +if test "$CC_TYPE" = "clang-cl"; then + CLANG_CL=1 +fi + +if test "$GNU_CC"; then + if `$CC -print-prog-name=ld` -v 2>&1 | grep -c GNU >/dev/null; then + GCC_USE_GNU_LD=1 + fi +fi + +AC_SUBST(CLANG_CXX) +AC_SUBST(CLANG_CL) +]) + +AC_DEFUN([MOZ_CROSS_COMPILER], +[ +echo "cross compiling from $host to $target" + +if test -z "$HOST_AR_FLAGS"; then + HOST_AR_FLAGS="$AR_FLAGS" +fi +AC_CHECK_PROGS(HOST_RANLIB, $HOST_RANLIB ranlib, ranlib, :) +AC_CHECK_PROGS(HOST_AR, $HOST_AR ar, ar, :) + +dnl AC_CHECK_PROGS manually goes through $PATH, and as such fails to handle +dnl absolute or relative paths. Relative paths wouldn't work anyways, but +dnl absolute paths would. Trick AC_CHECK_PROGS into working in that case by +dnl adding / to PATH. This is temporary until this moves to moz.configure +dnl (soon). +_SAVE_PATH=$PATH +case "${TOOLCHAIN_PREFIX}" in +/*) + PATH="/:$PATH" + ;; +esac +AC_PROG_CC +AC_PROG_CXX + +AC_CHECK_PROGS(RANLIB, "${TOOLCHAIN_PREFIX}ranlib", :) +AC_CHECK_PROGS(AR, "${TOOLCHAIN_PREFIX}ar", :) +AC_CHECK_PROGS(AS, "${TOOLCHAIN_PREFIX}as", :) +AC_CHECK_PROGS(LD, "${TOOLCHAIN_PREFIX}ld", :) +AC_CHECK_PROGS(LIPO, "${TOOLCHAIN_PREFIX}lipo", :) +AC_CHECK_PROGS(STRIP, "${TOOLCHAIN_PREFIX}strip", :) +AC_CHECK_PROGS(WINDRES, "${TOOLCHAIN_PREFIX}windres", :) +AC_CHECK_PROGS(OTOOL, "${TOOLCHAIN_PREFIX}otool", :) +AC_CHECK_PROGS(OBJCOPY, "${TOOLCHAIN_PREFIX}objcopy", :) +PATH=$_SAVE_PATH +]) + +AC_DEFUN([MOZ_CXX11], +[ +dnl Updates to the test below should be duplicated further below for the +dnl cross-compiling case. +AC_LANG_CPLUSPLUS +if test "$GNU_CXX"; then + AC_CACHE_CHECK([whether 64-bits std::atomic requires -latomic], + ac_cv_needs_atomic, + AC_TRY_LINK( + [#include <cstdint> + #include <atomic>], + [ std::atomic<uint64_t> foo; foo = 1; ], + ac_cv_needs_atomic=no, + _SAVE_LIBS="$LIBS" + LIBS="$LIBS -latomic" + AC_TRY_LINK( + [#include <cstdint> + #include <atomic>], + [ std::atomic<uint64_t> foo; foo = 1; ], + ac_cv_needs_atomic=yes, + ac_cv_needs_atomic="do not know; assuming no") + LIBS="$_SAVE_LIBS" + ) + ) + if test "$ac_cv_needs_atomic" = yes; then + MOZ_NEEDS_LIBATOMIC=1 + else + MOZ_NEEDS_LIBATOMIC= + fi + AC_SUBST(MOZ_NEEDS_LIBATOMIC) +fi +AC_LANG_C +]) diff --git a/build/autoconf/zlib.m4 b/build/autoconf/zlib.m4 new file mode 100644 index 000000000..49a2ccc8b --- /dev/null +++ b/build/autoconf/zlib.m4 @@ -0,0 +1,54 @@ +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 Usage: MOZ_ZLIB_CHECK([version]) + +AC_DEFUN([MOZ_ZLIB_CHECK], +[ + +MOZZLIB=$1 + +MOZ_ARG_WITH_STRING(system-zlib, +[ --with-system-zlib[=PFX] + Use system libz [installed at prefix PFX]], + ZLIB_DIR=$withval) + +if test -z "$MOZ_ZLIB_LIBS$MOZ_ZLIB_CFLAGS$SKIP_LIBRARY_CHECKS"; then + _SAVE_CFLAGS=$CFLAGS + _SAVE_LDFLAGS=$LDFLAGS + _SAVE_LIBS=$LIBS + + if test -n "${ZLIB_DIR}" -a "${ZLIB_DIR}" != "yes"; then + MOZ_ZLIB_CFLAGS="-I${ZLIB_DIR}/include" + MOZ_ZLIB_LIBS="-L${ZLIB_DIR}/lib" + CFLAGS="$MOZ_ZLIB_CFLAGS $CFLAGS" + LDFLAGS="$MOZ_ZLIB_LIBS $LDFLAGS" + fi + if test -z "$ZLIB_DIR" -o "$ZLIB_DIR" = no; then + MOZ_SYSTEM_ZLIB= + else + AC_CHECK_LIB(z, gzread, [MOZ_SYSTEM_ZLIB=1 MOZ_ZLIB_LIBS="$MOZ_ZLIB_LIBS -lz"], + [MOZ_SYSTEM_ZLIB=]) + if test "$MOZ_SYSTEM_ZLIB" = 1; then + MOZZLIBNUM=`echo $MOZZLIB | awk -F. changequote(<<, >>)'{printf "0x%x\n", (((<<$>>1 * 16 + <<$>>2) * 16) + <<$>>3) * 16 + <<$>>4}'changequote([, ])` + AC_TRY_COMPILE([ #include <stdio.h> + #include <string.h> + #include <zlib.h> ], + [ #if ZLIB_VERNUM < $MOZZLIBNUM + #error "Insufficient zlib version ($MOZZLIBNUM required)." + #endif ], + MOZ_SYSTEM_ZLIB=1, + AC_MSG_ERROR([Insufficient zlib version for --with-system-zlib ($MOZZLIB required)])) + fi + fi + CFLAGS=$_SAVE_CFLAGS + LDFLAGS=$_SAVE_LDFLAGS + LIBS=$_SAVE_LIBS +fi + +AC_SUBST_LIST(MOZ_ZLIB_CFLAGS) +AC_SUBST_LIST(MOZ_ZLIB_LIBS) +AC_SUBST(MOZ_SYSTEM_ZLIB) + +]) diff --git a/build/automation-build.mk b/build/automation-build.mk new file mode 100644 index 000000000..e25f90c5d --- /dev/null +++ b/build/automation-build.mk @@ -0,0 +1,67 @@ +# 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 $(MOZILLA_DIR)/build/binary-location.mk + +browser_path := '"$(browser_path)"' + +_PROFILE_DIR = $(TARGET_DEPTH)/_profile/pgo + +ABSOLUTE_TOPSRCDIR = $(abspath $(MOZILLA_DIR)) +_CERTS_SRC_DIR = $(ABSOLUTE_TOPSRCDIR)/build/pgo/certs + +AUTOMATION_PPARGS = \ + -DBROWSER_PATH=$(browser_path) \ + -DXPC_BIN_PATH='"$(DIST)/bin"' \ + -DBIN_SUFFIX='"$(BIN_SUFFIX)"' \ + -DPROFILE_DIR='"$(_PROFILE_DIR)"' \ + -DCERTS_SRC_DIR='"$(_CERTS_SRC_DIR)"' \ + -DPERL='"$(PERL)"' \ + $(NULL) + +ifeq ($(OS_ARCH),Darwin) +AUTOMATION_PPARGS += -DIS_MAC=1 +else +AUTOMATION_PPARGS += -DIS_MAC=0 +endif + +ifeq ($(OS_ARCH),Linux) +AUTOMATION_PPARGS += -DIS_LINUX=1 +else +AUTOMATION_PPARGS += -DIS_LINUX=0 +endif + +ifeq ($(host_os), cygwin) +AUTOMATION_PPARGS += -DIS_CYGWIN=1 +endif + +ifeq ($(ENABLE_TESTS), 1) +AUTOMATION_PPARGS += -DIS_TEST_BUILD=1 +else +AUTOMATION_PPARGS += -DIS_TEST_BUILD=0 +endif + +ifeq ($(MOZ_DEBUG), 1) +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=1 +else +AUTOMATION_PPARGS += -DIS_DEBUG_BUILD=0 +endif + +ifdef MOZ_CRASHREPORTER +AUTOMATION_PPARGS += -DCRASHREPORTER=1 +else +AUTOMATION_PPARGS += -DCRASHREPORTER=0 +endif + +ifdef MOZ_ASAN +AUTOMATION_PPARGS += -DIS_ASAN=1 +else +AUTOMATION_PPARGS += -DIS_ASAN=0 +endif + +automation.py: $(MOZILLA_DIR)/build/automation.py.in $(MOZILLA_DIR)/build/automation-build.mk + $(call py_action,preprocessor, \ + $(AUTOMATION_PPARGS) $(DEFINES) $(ACDEFINES) $< -o $@) + +GARBAGE += automation.py automation.pyc diff --git a/build/automation.py.in b/build/automation.py.in new file mode 100644 index 000000000..1c63977e8 --- /dev/null +++ b/build/automation.py.in @@ -0,0 +1,609 @@ +# +# 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 with_statement +import logging +import os +import re +import select +import signal +import subprocess +import sys +import tempfile +from datetime import datetime, timedelta + +SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) +sys.path.insert(0, SCRIPT_DIR) + +# -------------------------------------------------------------- +# TODO: this is a hack for mozbase without virtualenv, remove with bug 849900 +# These paths refer to relative locations to test.zip, not the OBJDIR or SRCDIR +here = os.path.dirname(os.path.realpath(__file__)) +mozbase = os.path.realpath(os.path.join(os.path.dirname(here), 'mozbase')) + +if os.path.isdir(mozbase): + for package in os.listdir(mozbase): + package_path = os.path.join(mozbase, package) + if package_path not in sys.path: + sys.path.append(package_path) + +import mozcrash +from mozscreenshot import printstatus, dump_screen + + +# --------------------------------------------------------------- + +_DEFAULT_PREFERENCE_FILE = os.path.join(SCRIPT_DIR, 'prefs_general.js') +_DEFAULT_APPS_FILE = os.path.join(SCRIPT_DIR, 'webapps_mochitest.json') + +_DEFAULT_WEB_SERVER = "127.0.0.1" +_DEFAULT_HTTP_PORT = 8888 +_DEFAULT_SSL_PORT = 4443 +_DEFAULT_WEBSOCKET_PORT = 9988 + +# from nsIPrincipal.idl +_APP_STATUS_NOT_INSTALLED = 0 +_APP_STATUS_INSTALLED = 1 +_APP_STATUS_PRIVILEGED = 2 +_APP_STATUS_CERTIFIED = 3 + +#expand _DIST_BIN = __XPC_BIN_PATH__ +#expand _IS_WIN32 = len("__WIN32__") != 0 +#expand _IS_MAC = __IS_MAC__ != 0 +#expand _IS_LINUX = __IS_LINUX__ != 0 +#ifdef IS_CYGWIN +#expand _IS_CYGWIN = __IS_CYGWIN__ == 1 +#else +_IS_CYGWIN = False +#endif +#expand _BIN_SUFFIX = __BIN_SUFFIX__ + +#expand _DEFAULT_APP = "./" + __BROWSER_PATH__ +#expand _CERTS_SRC_DIR = __CERTS_SRC_DIR__ +#expand _IS_TEST_BUILD = __IS_TEST_BUILD__ +#expand _IS_DEBUG_BUILD = __IS_DEBUG_BUILD__ +#expand _CRASHREPORTER = __CRASHREPORTER__ == 1 +#expand _IS_ASAN = __IS_ASAN__ == 1 + + +if _IS_WIN32: + import ctypes, ctypes.wintypes, time, msvcrt +else: + import errno + +def resetGlobalLog(log): + while _log.handlers: + _log.removeHandler(_log.handlers[0]) + handler = logging.StreamHandler(log) + _log.setLevel(logging.INFO) + _log.addHandler(handler) + +# We use the logging system here primarily because it'll handle multiple +# threads, which is needed to process the output of the server and application +# processes simultaneously. +_log = logging.getLogger() +resetGlobalLog(sys.stdout) + + +################# +# PROFILE SETUP # +################# + +class Automation(object): + """ + Runs the browser from a script, and provides useful utilities + for setting up the browser environment. + """ + + DIST_BIN = _DIST_BIN + IS_WIN32 = _IS_WIN32 + IS_MAC = _IS_MAC + IS_LINUX = _IS_LINUX + IS_CYGWIN = _IS_CYGWIN + BIN_SUFFIX = _BIN_SUFFIX + + UNIXISH = not IS_WIN32 and not IS_MAC + + DEFAULT_APP = _DEFAULT_APP + CERTS_SRC_DIR = _CERTS_SRC_DIR + IS_TEST_BUILD = _IS_TEST_BUILD + IS_DEBUG_BUILD = _IS_DEBUG_BUILD + CRASHREPORTER = _CRASHREPORTER + IS_ASAN = _IS_ASAN + + # timeout, in seconds + DEFAULT_TIMEOUT = 60.0 + DEFAULT_WEB_SERVER = _DEFAULT_WEB_SERVER + DEFAULT_HTTP_PORT = _DEFAULT_HTTP_PORT + DEFAULT_SSL_PORT = _DEFAULT_SSL_PORT + DEFAULT_WEBSOCKET_PORT = _DEFAULT_WEBSOCKET_PORT + + def __init__(self): + self.log = _log + self.lastTestSeen = "automation.py" + self.haveDumpedScreen = False + + def setServerInfo(self, + webServer = _DEFAULT_WEB_SERVER, + httpPort = _DEFAULT_HTTP_PORT, + sslPort = _DEFAULT_SSL_PORT, + webSocketPort = _DEFAULT_WEBSOCKET_PORT): + self.webServer = webServer + self.httpPort = httpPort + self.sslPort = sslPort + self.webSocketPort = webSocketPort + + @property + def __all__(self): + return [ + "UNIXISH", + "IS_WIN32", + "IS_MAC", + "log", + "runApp", + "Process", + "DIST_BIN", + "DEFAULT_APP", + "CERTS_SRC_DIR", + "environment", + "IS_TEST_BUILD", + "IS_DEBUG_BUILD", + "DEFAULT_TIMEOUT", + ] + + class Process(subprocess.Popen): + """ + Represents our view of a subprocess. + It adds a kill() method which allows it to be stopped explicitly. + """ + + def __init__(self, + args, + bufsize=0, + executable=None, + stdin=None, + stdout=None, + stderr=None, + preexec_fn=None, + close_fds=False, + shell=False, + cwd=None, + env=None, + universal_newlines=False, + startupinfo=None, + creationflags=0): + _log.info("INFO | automation.py | Launching: %s", subprocess.list2cmdline(args)) + subprocess.Popen.__init__(self, args, bufsize, executable, + stdin, stdout, stderr, + preexec_fn, close_fds, + shell, cwd, env, + universal_newlines, startupinfo, creationflags) + self.log = _log + + def kill(self): + if Automation().IS_WIN32: + import platform + pid = "%i" % self.pid + if platform.release() == "2000": + # Windows 2000 needs 'kill.exe' from the + #'Windows 2000 Resource Kit tools'. (See bug 475455.) + try: + subprocess.Popen(["kill", "-f", pid]).wait() + except: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Missing 'kill' utility to kill process with pid=%s. Kill it manually!", pid) + else: + # Windows XP and later. + subprocess.Popen(["taskkill", "/F", "/PID", pid]).wait() + else: + os.kill(self.pid, signal.SIGKILL) + + def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, dmdPath=None, lsanPath=None): + if xrePath == None: + xrePath = self.DIST_BIN + if env == None: + env = dict(os.environ) + + ldLibraryPath = os.path.abspath(os.path.join(SCRIPT_DIR, xrePath)) + dmdLibrary = None + preloadEnvVar = None + if self.UNIXISH or self.IS_MAC: + envVar = "LD_LIBRARY_PATH" + preloadEnvVar = "LD_PRELOAD" + if self.IS_MAC: + envVar = "DYLD_LIBRARY_PATH" + dmdLibrary = "libdmd.dylib" + else: # unixish + env['MOZILLA_FIVE_HOME'] = xrePath + dmdLibrary = "libdmd.so" + if envVar in env: + ldLibraryPath = ldLibraryPath + ":" + env[envVar] + env[envVar] = ldLibraryPath + elif self.IS_WIN32: + env["PATH"] = env["PATH"] + ";" + str(ldLibraryPath) + dmdLibrary = "dmd.dll" + preloadEnvVar = "MOZ_REPLACE_MALLOC_LIB" + + if dmdPath and dmdLibrary and preloadEnvVar: + env[preloadEnvVar] = os.path.join(dmdPath, dmdLibrary) + + if crashreporter and not debugger: + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + env['MOZ_CRASHREPORTER'] = '1' + else: + env['MOZ_CRASHREPORTER_DISABLE'] = '1' + + # Crash on non-local network connections by default. + # MOZ_DISABLE_NONLOCAL_CONNECTIONS can be set to "0" to temporarily + # enable non-local connections for the purposes of local testing. Don't + # override the user's choice here. See bug 1049688. + env.setdefault('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1') + + env['GNOME_DISABLE_CRASH_DIALOG'] = '1' + env['XRE_NO_WINDOWS_CRASH_DIALOG'] = '1' + + # Set WebRTC logging in case it is not set yet + env.setdefault('MOZ_LOG', 'signaling:3,mtransport:4,DataChannel:4,jsep:4,MediaPipelineFactory:4') + env.setdefault('R_LOG_LEVEL', '6') + env.setdefault('R_LOG_DESTINATION', 'stderr') + env.setdefault('R_LOG_VERBOSE', '1') + + # ASan specific environment stuff + if self.IS_ASAN and (self.IS_LINUX or self.IS_MAC): + # Symbolizer support + llvmsym = os.path.join(xrePath, "llvm-symbolizer") + if os.path.isfile(llvmsym): + env["ASAN_SYMBOLIZER_PATH"] = llvmsym + self.log.info("INFO | automation.py | ASan using symbolizer at %s", llvmsym) + else: + self.log.info("TEST-UNEXPECTED-FAIL | automation.py | Failed to find ASan symbolizer at %s", llvmsym) + + try: + totalMemory = int(os.popen("free").readlines()[1].split()[1]) + + # Only 4 GB RAM or less available? Use custom ASan options to reduce + # the amount of resources required to do the tests. Standard options + # will otherwise lead to OOM conditions on the current test slaves. + if totalMemory <= 1024 * 1024 * 4: + self.log.info("INFO | automation.py | ASan running in low-memory configuration") + env["ASAN_OPTIONS"] = "quarantine_size=50331648:malloc_context_size=5" + else: + self.log.info("INFO | automation.py | ASan running in default memory configuration") + except OSError,err: + self.log.info("Failed determine available memory, disabling ASan low-memory configuration: %s", err.strerror) + except: + self.log.info("Failed determine available memory, disabling ASan low-memory configuration") + + return env + + def killPid(self, pid): + try: + os.kill(pid, getattr(signal, "SIGKILL", signal.SIGTERM)) + except WindowsError: + self.log.info("Failed to kill process %d." % pid) + + if IS_WIN32: + PeekNamedPipe = ctypes.windll.kernel32.PeekNamedPipe + GetLastError = ctypes.windll.kernel32.GetLastError + + def readWithTimeout(self, f, timeout): + """ + Try to read a line of output from the file object |f|. |f| must be a + pipe, like the |stdout| member of a subprocess.Popen object created + with stdout=PIPE. Returns a tuple (line, did_timeout), where |did_timeout| + is True if the read timed out, and False otherwise. If no output is + received within |timeout| seconds, returns a blank line. + """ + + if timeout is None: + timeout = 0 + + x = msvcrt.get_osfhandle(f.fileno()) + l = ctypes.c_long() + done = time.time() + timeout + + buffer = "" + while timeout == 0 or time.time() < done: + if self.PeekNamedPipe(x, None, 0, None, ctypes.byref(l), None) == 0: + err = self.GetLastError() + if err == 38 or err == 109: # ERROR_HANDLE_EOF || ERROR_BROKEN_PIPE + return ('', False) + else: + self.log.error("readWithTimeout got error: %d", err) + # read a character at a time, checking for eol. Return once we get there. + index = 0 + while index < l.value: + char = f.read(1) + buffer += char + if char == '\n': + return (buffer, False) + index = index + 1 + time.sleep(0.01) + return (buffer, True) + + def isPidAlive(self, pid): + STILL_ACTIVE = 259 + PROCESS_QUERY_LIMITED_INFORMATION = 0x1000 + pHandle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid) + if not pHandle: + return False + pExitCode = ctypes.wintypes.DWORD() + ctypes.windll.kernel32.GetExitCodeProcess(pHandle, ctypes.byref(pExitCode)) + ctypes.windll.kernel32.CloseHandle(pHandle) + return pExitCode.value == STILL_ACTIVE + + else: + + def readWithTimeout(self, f, timeout): + """Try to read a line of output from the file object |f|. If no output + is received within |timeout| seconds, return a blank line. + Returns a tuple (line, did_timeout), where |did_timeout| is True + if the read timed out, and False otherwise.""" + (r, w, e) = select.select([f], [], [], timeout) + if len(r) == 0: + return ('', True) + return (f.readline(), False) + + def isPidAlive(self, pid): + try: + # kill(pid, 0) checks for a valid PID without actually sending a signal + # The method throws OSError if the PID is invalid, which we catch below. + os.kill(pid, 0) + + # Wait on it to see if it's a zombie. This can throw OSError.ECHILD if + # the process terminates before we get to this point. + wpid, wstatus = os.waitpid(pid, os.WNOHANG) + return wpid == 0 + except OSError, err: + # Catch the errors we might expect from os.kill/os.waitpid, + # and re-raise any others + if err.errno == errno.ESRCH or err.errno == errno.ECHILD: + return False + raise + + def dumpScreen(self, utilityPath): + if self.haveDumpedScreen: + self.log.info("Not taking screenshot here: see the one that was previously logged") + return + + self.haveDumpedScreen = True; + dump_screen(utilityPath, self.log) + + + def killAndGetStack(self, processPID, utilityPath, debuggerInfo): + """Kill the process, preferrably in a way that gets us a stack trace. + Also attempts to obtain a screenshot before killing the process.""" + if not debuggerInfo: + self.dumpScreen(utilityPath) + self.killAndGetStackNoScreenshot(processPID, utilityPath, debuggerInfo) + + def killAndGetStackNoScreenshot(self, processPID, utilityPath, debuggerInfo): + """Kill the process, preferrably in a way that gets us a stack trace.""" + if self.CRASHREPORTER and not debuggerInfo: + if not self.IS_WIN32: + # ABRT will get picked up by Breakpad's signal handler + os.kill(processPID, signal.SIGABRT) + return + else: + # We should have a "crashinject" program in our utility path + crashinject = os.path.normpath(os.path.join(utilityPath, "crashinject.exe")) + if os.path.exists(crashinject): + status = subprocess.Popen([crashinject, str(processPID)]).wait() + printstatus("crashinject", status) + if status == 0: + return + self.log.info("Can't trigger Breakpad, just killing process") + self.killPid(processPID) + + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None): + """ Look for timeout or crashes and return the status after the process terminates """ + stackFixerFunction = None + didTimeout = False + hitMaxTime = False + if proc.stdout is None: + self.log.info("TEST-INFO: Not logging stdout or stderr due to debugger connection") + else: + logsource = proc.stdout + + if self.IS_DEBUG_BUILD and symbolsPath and os.path.exists(symbolsPath): + # Run each line through a function in fix_stack_using_bpsyms.py (uses breakpad symbol files) + # This method is preferred for Tinderbox builds, since native symbols may have been stripped. + sys.path.insert(0, utilityPath) + import fix_stack_using_bpsyms as stackFixerModule + stackFixerFunction = lambda line: stackFixerModule.fixSymbols(line, symbolsPath) + del sys.path[0] + elif self.IS_DEBUG_BUILD and self.IS_MAC: + # Run each line through a function in fix_macosx_stack.py (uses atos) + sys.path.insert(0, utilityPath) + import fix_macosx_stack as stackFixerModule + stackFixerFunction = lambda line: stackFixerModule.fixSymbols(line) + del sys.path[0] + elif self.IS_DEBUG_BUILD and self.IS_LINUX: + # Run each line through a function in fix_linux_stack.py (uses addr2line) + # This method is preferred for developer machines, so we don't have to run "make buildsymbols". + sys.path.insert(0, utilityPath) + import fix_linux_stack as stackFixerModule + stackFixerFunction = lambda line: stackFixerModule.fixSymbols(line) + del sys.path[0] + + # With metro browser runs this script launches the metro test harness which launches the browser. + # The metro test harness hands back the real browser process id via log output which we need to + # pick up on and parse out. This variable tracks the real browser process id if we find it. + browserProcessId = -1 + + (line, didTimeout) = self.readWithTimeout(logsource, timeout) + while line != "" and not didTimeout: + if stackFixerFunction: + line = stackFixerFunction(line) + + if outputHandler is None: + self.log.info(line.rstrip().decode("UTF-8", "ignore")) + else: + outputHandler(line) + + if "TEST-START" in line and "|" in line: + self.lastTestSeen = line.split("|")[1].strip() + if not debuggerInfo and "TEST-UNEXPECTED-FAIL" in line and "Test timed out" in line: + self.dumpScreen(utilityPath) + + (line, didTimeout) = self.readWithTimeout(logsource, timeout) + + if not hitMaxTime and maxTime and datetime.now() - startTime > timedelta(seconds = maxTime): + # Kill the application. + hitMaxTime = True + self.log.info("TEST-UNEXPECTED-FAIL | %s | application ran for longer than allowed maximum time of %d seconds", self.lastTestSeen, int(maxTime)) + self.log.error("Force-terminating active process(es)."); + self.killAndGetStack(proc.pid, utilityPath, debuggerInfo) + if didTimeout: + if line: + self.log.info(line.rstrip().decode("UTF-8", "ignore")) + self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output", self.lastTestSeen, int(timeout)) + self.log.error("Force-terminating active process(es)."); + if browserProcessId == -1: + browserProcessId = proc.pid + self.killAndGetStack(browserProcessId, utilityPath, debuggerInfo) + + status = proc.wait() + printstatus("Main app process", status) + if status == 0: + self.lastTestSeen = "Main app process exited normally" + if status != 0 and not didTimeout and not hitMaxTime: + self.log.info("TEST-UNEXPECTED-FAIL | %s | Exited with code %d during test run", self.lastTestSeen, status) + return status + + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + """ build the application command line """ + + cmd = os.path.abspath(app) + if self.IS_MAC and os.path.exists(cmd + "-bin"): + # Prefer 'app-bin' in case 'app' is a shell script. + # We can remove this hack once bug 673899 etc are fixed. + cmd += "-bin" + + args = [] + + if debuggerInfo: + args.extend(debuggerInfo.args) + args.append(cmd) + cmd = os.path.abspath(debuggerInfo.path) + + if self.IS_MAC: + args.append("-foreground") + + if self.IS_CYGWIN: + profileDirectory = commands.getoutput("cygpath -w \"" + profileDir + "/\"") + else: + profileDirectory = profileDir + "/" + + args.extend(("-no-remote", "-profile", profileDirectory)) + if testURL is not None: + args.append((testURL)) + args.extend(extraArgs) + return cmd, args + + def checkForZombies(self, processLog, utilityPath, debuggerInfo): + """ Look for hung processes """ + if not os.path.exists(processLog): + self.log.info('Automation Error: PID log not found: %s', processLog) + # Whilst no hung process was found, the run should still display as a failure + return True + + foundZombie = False + self.log.info('INFO | zombiecheck | Reading PID log: %s', processLog) + processList = [] + pidRE = re.compile(r'launched child process (\d+)$') + processLogFD = open(processLog) + for line in processLogFD: + self.log.info(line.rstrip()) + m = pidRE.search(line) + if m: + processList.append(int(m.group(1))) + processLogFD.close() + + for processPID in processList: + self.log.info("INFO | zombiecheck | Checking for orphan process with PID: %d", processPID) + if self.isPidAlive(processPID): + foundZombie = True + self.log.info("TEST-UNEXPECTED-FAIL | zombiecheck | child process %d still alive after shutdown", processPID) + self.killAndGetStack(processPID, utilityPath, debuggerInfo) + return foundZombie + + def checkForCrashes(self, minidumpDir, symbolsPath): + return mozcrash.check_for_crashes(minidumpDir, symbolsPath, test_name=self.lastTestSeen) + + def runApp(self, testURL, env, app, profileDir, extraArgs, utilityPath = None, + xrePath = None, certPath = None, + debuggerInfo = None, symbolsPath = None, + timeout = -1, maxTime = None, onLaunch = None, + detectShutdownLeaks = False, screenshotOnFail=False, testPath=None, bisectChunk=None, + valgrindPath=None, valgrindArgs=None, valgrindSuppFiles=None, outputHandler=None): + """ + Run the app, log the duration it took to execute, return the status code. + Kills the app if it runs for longer than |maxTime| seconds, or outputs nothing for |timeout| seconds. + """ + + if utilityPath == None: + utilityPath = self.DIST_BIN + if xrePath == None: + xrePath = self.DIST_BIN + if certPath == None: + certPath = self.CERTS_SRC_DIR + if timeout == -1: + timeout = self.DEFAULT_TIMEOUT + + # copy env so we don't munge the caller's environment + env = dict(env); + env["NO_EM_RESTART"] = "1" + tmpfd, processLog = tempfile.mkstemp(suffix='pidlog') + os.close(tmpfd) + env["MOZ_PROCESS_LOG"] = processLog + + + cmd, args = self.buildCommandLine(app, debuggerInfo, profileDir, testURL, extraArgs) + startTime = datetime.now() + + if debuggerInfo and debuggerInfo.interactive: + # If an interactive debugger is attached, don't redirect output, + # don't use timeouts, and don't capture ctrl-c. + timeout = None + maxTime = None + outputPipe = None + signal.signal(signal.SIGINT, lambda sigid, frame: None) + else: + outputPipe = subprocess.PIPE + + self.lastTestSeen = "automation.py" + proc = self.Process([cmd] + args, + env = self.environment(env, xrePath = xrePath, + crashreporter = not debuggerInfo), + stdout = outputPipe, + stderr = subprocess.STDOUT) + self.log.info("INFO | automation.py | Application pid: %d", proc.pid) + + if onLaunch is not None: + # Allow callers to specify an onLaunch callback to be fired after the + # app is launched. + onLaunch() + + status = self.waitForFinish(proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, + outputHandler=outputHandler) + self.log.info("INFO | automation.py | Application ran for: %s", str(datetime.now() - startTime)) + + # Do a final check for zombie child processes. + zombieProcesses = self.checkForZombies(processLog, utilityPath, debuggerInfo) + + crashed = self.checkForCrashes(os.path.join(profileDir, "minidumps"), symbolsPath) + + if crashed or zombieProcesses: + status = 1 + + if os.path.exists(processLog): + os.unlink(processLog) + + return status + + def elf_arm(self, filename): + data = open(filename, 'rb').read(20) + return data[:4] == "\x7fELF" and ord(data[18]) == 40 # EM_ARM + diff --git a/build/binary-location.mk b/build/binary-location.mk new file mode 100644 index 000000000..19ae4b55f --- /dev/null +++ b/build/binary-location.mk @@ -0,0 +1,19 @@ +# 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/. + +# finds the location of the browser and puts it in the variable $(browser_path) + +ifneq (,$(filter WINNT,$(OS_ARCH))) +program = $(MOZ_APP_NAME)$(BIN_SUFFIX) +else +program = $(MOZ_APP_NAME)-bin$(BIN_SUFFIX) +endif + +TARGET_DIST = $(TARGET_DEPTH)/dist + +ifeq ($(OS_ARCH),Darwin) +browser_path = $(TARGET_DIST)/$(MOZ_MACBUNDLE_NAME)/Contents/MacOS/$(program) +else +browser_path = $(TARGET_DIST)/bin/$(program) +endif diff --git a/build/build-clang/README b/build/build-clang/README new file mode 100644 index 000000000..8e93eeaa6 --- /dev/null +++ b/build/build-clang/README @@ -0,0 +1,45 @@ +build-clang.py +============== + +A script to build clang from source. + +``` +usage: build-clang.py [-h] -c CONFIG [--clean] + +optional arguments: + -h, --help show this help message and exit + -c CONFIG, --config CONFIG + Clang configuration file + --clean Clean the build directory +``` + +Pre-requisites +-------------- +* Working build toolchain. +* Subversion +* CMake +* Ninja +* Python 2.7 + +Please use the latest available CMake for your platform to avoid surprises. + +Config file format +------------------ + +build-clang.py accepts a JSON config format with the following fields: + +* llvm_revision: The LLVM SVN revision to build. +* stages: Use 1, 2, or 3 to select different compiler stages. The default is 3. +* llvm_repo: SVN path to the LLVM repo. +* clang_repo: SVN path to the Clang repo. +* compiler_repo: SVN path to the compiler-rt repo. +* libcxx_repo: SVN path to the libcxx repo. +* libcxxabi_repo: SVN path to the libcxxabi repo. +* python_path: Path to the Python 2.7 installation on the machine building clang. +* gcc_dir: Path to the gcc toolchain installation, only required on Linux. +* cc: Path to the bootsraping C Compiler. +* cxx: Path to the bootsraping C++ Compiler. +* patches: Optional list of patches to apply per platform. Supported platforms: macosx64, linux32, linux64. The default is Release. +* build_type: The type of build to make. Supported types: Release, Debug, RelWithDebInfo or MinSizeRel. +* build_libcxx: Whether to build with libcxx. The default is false. +* assertions: Whether to enable LLVM assertions. The default is false. diff --git a/build/build-clang/build-clang.py b/build/build-clang/build-clang.py new file mode 100755 index 000000000..697bbb9b8 --- /dev/null +++ b/build/build-clang/build-clang.py @@ -0,0 +1,425 @@ +#!/usr/bin/python2.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/. + +import os +import os.path +import shutil +import subprocess +import platform +import json +import argparse +import tempfile +import glob +import errno +import re +from contextlib import contextmanager +import sys +import which + +DEBUG = os.getenv("DEBUG") + + +def symlink(source, link_name): + os_symlink = getattr(os, "symlink", None) + if callable(os_symlink): + os_symlink(source, link_name) + else: + if os.path.isdir(source): + # Fall back to copying the directory :( + copy_dir_contents(source, link_name) + + +def check_run(args): + global DEBUG + if DEBUG: + print >> sys.stderr, ' '.join(args) + r = subprocess.call(args) + assert r == 0 + + +def run_in(path, args): + d = os.getcwd() + global DEBUG + if DEBUG: + print >> sys.stderr, 'cd "%s"' % path + os.chdir(path) + check_run(args) + if DEBUG: + print >> sys.stderr, 'cd "%s"' % d + os.chdir(d) + + +def patch(patch, srcdir): + patch = os.path.realpath(patch) + check_run(['patch', '-d', srcdir, '-p1', '-i', patch, '--fuzz=0', + '-s']) + + +def build_package(package_build_dir, run_cmake, cmake_args): + if not os.path.exists(package_build_dir): + os.mkdir(package_build_dir) + if run_cmake: + run_in(package_build_dir, ["cmake"] + cmake_args) + run_in(package_build_dir, ["ninja", "install"]) + + +@contextmanager +def updated_env(env): + old_env = os.environ.copy() + os.environ.update(env) + yield + os.environ.clear() + os.environ.update(old_env) + + +def build_tar_package(tar, name, base, directory): + name = os.path.realpath(name) + # On Windows, we have to convert this into an msys path so that tar can + # understand it. + if is_windows(): + name = name.replace('\\', '/') + def f(match): + return '/' + match.group(1).lower() + name = re.sub(r'^([A-Z]):', f, name) + run_in(base, [tar, + "-c", + "-%s" % ("J" if ".xz" in name else "j"), + "-f", + name, directory]) + + +def copy_dir_contents(src, dest): + for f in glob.glob("%s/*" % src): + try: + destname = "%s/%s" % (dest, os.path.basename(f)) + if os.path.isdir(f): + shutil.copytree(f, destname) + else: + shutil.copy2(f, destname) + except OSError as e: + if e.errno == errno.ENOTDIR: + shutil.copy2(f, destname) + elif e.errno == errno.EEXIST: + if os.path.isdir(f): + copy_dir_contents(f, destname) + else: + os.remove(destname) + shutil.copy2(f, destname) + else: + raise Exception('Directory not copied. Error: %s' % e) + + +def mkdir_p(path): + try: + os.makedirs(path) + except OSError as e: + if e.errno != errno.EEXIST or not os.path.isdir(path): + raise + + +def install_libgcc(gcc_dir, clang_dir): + out = subprocess.check_output([os.path.join(gcc_dir, "bin", "gcc"), + '-print-libgcc-file-name']) + + libgcc_dir = os.path.dirname(out.rstrip()) + clang_lib_dir = os.path.join(clang_dir, "lib", "gcc", + "x86_64-unknown-linux-gnu", + os.path.basename(libgcc_dir)) + mkdir_p(clang_lib_dir) + copy_dir_contents(libgcc_dir, clang_lib_dir) + libgcc_dir = os.path.join(gcc_dir, "lib64") + clang_lib_dir = os.path.join(clang_dir, "lib") + copy_dir_contents(libgcc_dir, clang_lib_dir) + include_dir = os.path.join(gcc_dir, "include") + clang_include_dir = os.path.join(clang_dir, "include") + copy_dir_contents(include_dir, clang_include_dir) + + +def svn_co(source_dir, url, directory, revision): + run_in(source_dir, ["svn", "co", "-q", "-r", revision, url, directory]) + + +def svn_update(directory, revision): + run_in(directory, ["svn", "update", "-q", "-r", revision]) + + +def get_platform(): + p = platform.system() + if p == "Darwin": + return "macosx64" + elif p == "Linux": + if platform.architecture() == "AMD64": + return "linux64" + else: + return "linux32" + elif p == "Windows": + if platform.architecture() == "AMD64": + return "win64" + else: + return "win32" + else: + raise NotImplementedError("Not supported platform") + + +def is_darwin(): + return platform.system() == "Darwin" + + +def is_linux(): + return platform.system() == "Linux" + + +def is_windows(): + return platform.system() == "Windows" + + +def build_one_stage(cc, cxx, src_dir, stage_dir, build_libcxx, + build_type, assertions, python_path, gcc_dir): + if not os.path.exists(stage_dir): + os.mkdir(stage_dir) + + build_dir = stage_dir + "/build" + inst_dir = stage_dir + "/clang" + + run_cmake = True + if os.path.exists(build_dir + "/build.ninja"): + run_cmake = False + + # cmake doesn't deal well with backslashes in paths. + def slashify_path(path): + return path.replace('\\', '/') + + cmake_args = ["-GNinja", + "-DCMAKE_C_COMPILER=%s" % slashify_path(cc[0]), + "-DCMAKE_CXX_COMPILER=%s" % slashify_path(cxx[0]), + "-DCMAKE_ASM_COMPILER=%s" % slashify_path(cc[0]), + "-DCMAKE_C_FLAGS=%s" % ' '.join(cc[1:]), + "-DCMAKE_CXX_FLAGS=%s" % ' '.join(cxx[1:]), + "-DCMAKE_BUILD_TYPE=%s" % build_type, + "-DLLVM_TARGETS_TO_BUILD=X86;ARM", + "-DLLVM_ENABLE_ASSERTIONS=%s" % ("ON" if assertions else "OFF"), + "-DPYTHON_EXECUTABLE=%s" % slashify_path(python_path), + "-DCMAKE_INSTALL_PREFIX=%s" % inst_dir, + "-DLLVM_TOOL_LIBCXX_BUILD=%s" % ("ON" if build_libcxx else "OFF"), + "-DLIBCXX_LIBCPPABI_VERSION=\"\"", + src_dir]; + build_package(build_dir, run_cmake, cmake_args) + + if is_linux(): + install_libgcc(gcc_dir, inst_dir) + + +def get_compiler(config, key): + if key not in config: + raise ValueError("Config file needs to set %s" % key) + + f = config[key] + if os.path.isabs(f): + if not os.path.exists(f): + raise ValueError("%s must point to an existing path" % key) + return f + + # Assume that we have the name of some program that should be on PATH. + try: + return which.which(f) + except which.WhichError: + raise ValueError("%s not found on PATH" % f) + +if __name__ == "__main__": + # The directories end up in the debug info, so the easy way of getting + # a reproducible build is to run it in a know absolute directory. + # We use a directory in /builds/slave because the mozilla infrastructure + # cleans it up automatically. + base_dir = "/builds/slave/moz-toolchain" + if is_windows(): + # TODO: Because Windows taskcluster builds are run with distinct + # user IDs for each job, we can't store things in some globally + # accessible directory: one job will run, checkout LLVM to that + # directory, and then if another job runs, the new user won't be + # able to access the previously-checked out code--or be able to + # delete it. So on Windows, we build in the task-specific home + # directory; we will eventually add -fdebug-prefix-map options + # to the LLVM build to bring back reproducibility. + base_dir = os.path.join(os.getcwd(), 'llvm-sources') + + source_dir = base_dir + "/src" + build_dir = base_dir + "/build" + + llvm_source_dir = source_dir + "/llvm" + clang_source_dir = source_dir + "/clang" + compiler_rt_source_dir = source_dir + "/compiler-rt" + libcxx_source_dir = source_dir + "/libcxx" + libcxxabi_source_dir = source_dir + "/libcxxabi" + + if is_darwin(): + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '10.7' + + exe_ext = "" + if is_windows(): + exe_ext = ".exe" + + cc_name = "clang" + cxx_name = "clang++" + if is_windows(): + cc_name = "clang-cl" + cxx_name = "clang-cl" + + parser = argparse.ArgumentParser() + parser.add_argument('-c', '--config', required=True, + type=argparse.FileType('r'), + help="Clang configuration file") + parser.add_argument('--clean', required=False, + action='store_true', + help="Clean the build directory") + + args = parser.parse_args() + config = json.load(args.config) + + if args.clean: + shutil.rmtree(build_dir) + os.sys.exit(0) + + llvm_revision = config["llvm_revision"] + llvm_repo = config["llvm_repo"] + clang_repo = config["clang_repo"] + compiler_repo = config["compiler_repo"] + libcxx_repo = config["libcxx_repo"] + libcxxabi_repo = config.get("libcxxabi_repo") + stages = 3 + if "stages" in config: + stages = int(config["stages"]) + if stages not in (1, 2, 3): + raise ValueError("We only know how to build 1, 2, or 3 stages") + build_type = "Release" + if "build_type" in config: + build_type = config["build_type"] + if build_type not in ("Release", "Debug", "RelWithDebInfo", "MinSizeRel"): + raise ValueError("We only know how to do Release, Debug, RelWithDebInfo or MinSizeRel builds") + build_libcxx = False + if "build_libcxx" in config: + build_libcxx = config["build_libcxx"] + if build_libcxx not in (True, False): + raise ValueError("Only boolean values are accepted for build_libcxx.") + assertions = False + if "assertions" in config: + assertions = config["assertions"] + if assertions not in (True, False): + raise ValueError("Only boolean values are accepted for assertions.") + python_path = None + if "python_path" not in config: + raise ValueError("Config file needs to set python_path") + python_path = config["python_path"] + gcc_dir = None + if "gcc_dir" in config: + gcc_dir = config["gcc_dir"] + if not os.path.exists(gcc_dir): + raise ValueError("gcc_dir must point to an existing path") + if is_linux() and gcc_dir is None: + raise ValueError("Config file needs to set gcc_dir") + cc = get_compiler(config, "cc") + cxx = get_compiler(config, "cxx") + + if not os.path.exists(source_dir): + os.makedirs(source_dir) + svn_co(source_dir, llvm_repo, llvm_source_dir, llvm_revision) + svn_co(source_dir, clang_repo, clang_source_dir, llvm_revision) + svn_co(source_dir, compiler_repo, compiler_rt_source_dir, llvm_revision) + svn_co(source_dir, libcxx_repo, libcxx_source_dir, llvm_revision) + if libcxxabi_repo: + svn_co(source_dir, libcxxabi_repo, libcxxabi_source_dir, llvm_revision) + for p in config.get("patches", {}).get(get_platform(), []): + patch(p, source_dir) + else: + svn_update(llvm_source_dir, llvm_revision) + svn_update(clang_source_dir, llvm_revision) + svn_update(compiler_rt_source_dir, llvm_revision) + svn_update(libcxx_source_dir, llvm_revision) + if libcxxabi_repo: + svn_update(libcxxabi_source_dir, llvm_revision) + + symlinks = [(source_dir + "/clang", + llvm_source_dir + "/tools/clang"), + (source_dir + "/compiler-rt", + llvm_source_dir + "/projects/compiler-rt"), + (source_dir + "/libcxx", + llvm_source_dir + "/projects/libcxx"), + (source_dir + "/libcxxabi", + llvm_source_dir + "/projects/libcxxabi")] + for l in symlinks: + # On Windows, we have to re-copy the whole directory every time. + if not is_windows() and os.path.islink(l[1]): + continue + if os.path.isdir(l[1]): + shutil.rmtree(l[1]) + elif os.path.exists(l[1]): + os.unlink(l[1]) + if os.path.exists(l[0]): + symlink(l[0], l[1]) + + if not os.path.exists(build_dir): + os.makedirs(build_dir) + + stage1_dir = build_dir + '/stage1' + stage1_inst_dir = stage1_dir + '/clang' + + final_stage_dir = stage1_dir + + if is_darwin(): + extra_cflags = [] + extra_cxxflags = ["-stdlib=libc++"] + extra_cflags2 = [] + extra_cxxflags2 = ["-stdlib=libc++"] + elif is_linux(): + extra_cflags = ["-static-libgcc"] + extra_cxxflags = ["-static-libgcc", "-static-libstdc++"] + extra_cflags2 = ["-fPIC"] + extra_cxxflags2 = ["-fPIC", "-static-libstdc++"] + + if os.environ.has_key('LD_LIBRARY_PATH'): + os.environ['LD_LIBRARY_PATH'] = '%s/lib64/:%s' % (gcc_dir, os.environ['LD_LIBRARY_PATH']); + else: + os.environ['LD_LIBRARY_PATH'] = '%s/lib64/' % gcc_dir + elif is_windows(): + extra_cflags = [] + extra_cxxflags = [] + # clang-cl would like to figure out what it's supposed to be emulating + # by looking at an MSVC install, but we don't really have that here. + # Force things on. + extra_cflags2 = [] + extra_cxxflags2 = ['-fms-compatibility-version=19.00.24213', '-Xclang', '-std=c++14'] + + build_one_stage( + [cc] + extra_cflags, + [cxx] + extra_cxxflags, + llvm_source_dir, stage1_dir, build_libcxx, + build_type, assertions, python_path, gcc_dir) + + if stages > 1: + stage2_dir = build_dir + '/stage2' + stage2_inst_dir = stage2_dir + '/clang' + final_stage_dir = stage2_dir + build_one_stage( + [stage1_inst_dir + "/bin/%s%s" % + (cc_name, exe_ext)] + extra_cflags2, + [stage1_inst_dir + "/bin/%s%s" % + (cxx_name, exe_ext)] + extra_cxxflags2, + llvm_source_dir, stage2_dir, build_libcxx, + build_type, assertions, python_path, gcc_dir) + + if stages > 2: + stage3_dir = build_dir + '/stage3' + final_stage_dir = stage3_dir + build_one_stage( + [stage2_inst_dir + "/bin/%s%s" % + (cc_name, exe_ext)] + extra_cflags2, + [stage2_inst_dir + "/bin/%s%s" % + (cxx_name, exe_ext)] + extra_cxxflags2, + llvm_source_dir, stage3_dir, build_libcxx, + build_type, assertions, python_path, gcc_dir) + + if is_darwin() or is_windows(): + build_tar_package("tar", "clang.tar.bz2", final_stage_dir, "clang") + else: + build_tar_package("tar", "clang.tar.xz", final_stage_dir, "clang") diff --git a/build/build-clang/clang-static-analysis-linux64.json b/build/build-clang/clang-static-analysis-linux64.json new file mode 100644 index 000000000..d3ae6c8ad --- /dev/null +++ b/build/build-clang/clang-static-analysis-linux64.json @@ -0,0 +1,30 @@ +{ + "llvm_revision": "262557", + "stages": "3", + "build_libcxx": true, + "build_type": "Release", + "assertions": false, + "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_380/final", + "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_380/final", + "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_380/final", + "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_380/final", + "libcxxabi_repo": "https://llvm.org/svn/llvm-project/libcxxabi/tags/RELEASE_380/final", + "python_path": "/usr/bin/python2.7", + "gcc_dir": "/home/worker/workspace/build/src/gcc", + "cc": "/home/worker/workspace/build/src/gcc/bin/gcc", + "cxx": "/home/worker/workspace/build/src/gcc/bin/g++", + "patches": { + "macosx64": [ + "llvm-debug-frame.patch", + "return-empty-string-non-mangled.patch" + ], + "linux64": [ + "llvm-debug-frame.patch", + "return-empty-string-non-mangled.patch" + ], + "linux32": [ + "llvm-debug-frame.patch", + "return-empty-string-non-mangled.patch" + ] + } +} diff --git a/build/build-clang/clang-static-analysis-macosx64.json b/build/build-clang/clang-static-analysis-macosx64.json new file mode 100644 index 000000000..16818825b --- /dev/null +++ b/build/build-clang/clang-static-analysis-macosx64.json @@ -0,0 +1,29 @@ +{ + "llvm_revision": "262557", + "stages": "3", + "build_libcxx": true, + "build_type": "Release", + "assertions": false, + "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/tags/RELEASE_380/final", + "clang_repo": "https://llvm.org/svn/llvm-project/cfe/tags/RELEASE_380/final", + "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/tags/RELEASE_380/final", + "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/tags/RELEASE_380/final", + "python_path": "/usr/local/bin/python2.7", + "cc": "/Users/cltbld/clang/bin/clang", + "cxx": "/Users/cltbld/clang/bin/clang++", + "patches": { + "macosx64": [ + "disable-mac-tsan.patch", + "llvm-debug-frame.patch", + "return-empty-string-non-mangled.patch" + ], + "linux64": [ + "llvm-debug-frame.patch", + "return-empty-string-non-mangled.patch" + ], + "linux32": [ + "llvm-debug-frame.patch", + "return-empty-string-non-mangled.patch" + ] + } +} diff --git a/build/build-clang/clang-static-analysis-win32.json b/build/build-clang/clang-static-analysis-win32.json new file mode 100644 index 000000000..01cddad85 --- /dev/null +++ b/build/build-clang/clang-static-analysis-win32.json @@ -0,0 +1,16 @@ +{ + "llvm_revision": "283955", + "stages": "3", + "build_libcxx": false, + "build_type": "Release", + "assertions": false, + "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk", + "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk", + "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk", + "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk", + "python_path": "c:/mozilla-build/python/python.exe", + "cc": "cl.exe", + "cxx": "cl.exe", + "patches": { + } +} diff --git a/build/build-clang/clang-static-analysis-win64.json b/build/build-clang/clang-static-analysis-win64.json new file mode 100644 index 000000000..cf626f05e --- /dev/null +++ b/build/build-clang/clang-static-analysis-win64.json @@ -0,0 +1,16 @@ +{ + "llvm_revision": "262971", + "stages": "3", + "build_libcxx": false, + "build_type": "Release", + "assertions": false, + "llvm_repo": "https://llvm.org/svn/llvm-project/llvm/trunk", + "clang_repo": "https://llvm.org/svn/llvm-project/cfe/trunk", + "compiler_repo": "https://llvm.org/svn/llvm-project/compiler-rt/trunk", + "libcxx_repo": "https://llvm.org/svn/llvm-project/libcxx/trunk", + "python_path": "c:/mozilla-build/python/python.exe", + "cc": "c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/BIN/amd64/cl.exe", + "cxx": "c:/Program Files (x86)/Microsoft Visual Studio 12.0/VC/BIN/amd64/cl.exe", + "patches": { + } +} diff --git a/build/build-clang/disable-mac-tsan.patch b/build/build-clang/disable-mac-tsan.patch new file mode 100644 index 000000000..3bd0d3c60 --- /dev/null +++ b/build/build-clang/disable-mac-tsan.patch @@ -0,0 +1,11 @@ +--- a/compiler-rt/cmake/config-ix.cmake ++++ b/compiler-rt/cmake/config-ix.cmake +@@ -617,7 +617,7 @@ + endif() + + if (COMPILER_RT_HAS_SANITIZER_COMMON AND TSAN_SUPPORTED_ARCH AND +- OS_NAME MATCHES "Darwin|Linux|FreeBSD") ++ OS_NAME MATCHES "Linux|FreeBSD") + set(COMPILER_RT_HAS_TSAN TRUE) + else() + set(COMPILER_RT_HAS_TSAN FALSE) diff --git a/build/build-clang/llvm-debug-frame.patch b/build/build-clang/llvm-debug-frame.patch new file mode 100644 index 000000000..a8c57ce6f --- /dev/null +++ b/build/build-clang/llvm-debug-frame.patch @@ -0,0 +1,13 @@ +Index: lib/CodeGen/AsmPrinter/AsmPrinter.cpp +=================================================================== +--- a/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (revision 226419) ++++ b/llvm/lib/CodeGen/AsmPrinter/AsmPrinter.cpp (working copy) +@@ -210,6 +210,8 @@ + OutStreamer->EmitFileDirective(M.getModuleIdentifier()); + } + ++ OutStreamer->EmitCFISections(true, true); ++ + GCModuleInfo *MI = getAnalysisIfAvailable<GCModuleInfo>(); + assert(MI && "AsmPrinter didn't require GCModuleInfo?"); + for (auto &I : *MI) diff --git a/build/build-clang/query-selector-visibility.patch b/build/build-clang/query-selector-visibility.patch new file mode 100644 index 000000000..46a9642bd --- /dev/null +++ b/build/build-clang/query-selector-visibility.patch @@ -0,0 +1,79 @@ +commit 865b9340996f9f9d04b73b187248737dc6fd845e +Author: Michael Wu <mwu@mozilla.com> +Date: Mon Sep 14 17:47:21 2015 -0400 + + Add support for querying the visibility of a cursor + +diff --git a/clang/include/clang-c/Index.h b/clang/include/clang-c/Index.h +index fad9cfa..311bfcb 100644 +--- a/clang/include/clang-c/Index.h ++++ b/clang/include/clang-c/Index.h +@@ -2440,6 +2440,24 @@ enum CXLinkageKind { + CINDEX_LINKAGE enum CXLinkageKind clang_getCursorLinkage(CXCursor cursor); + + /** ++ * \brief Describe the visibility of the entity referred to by a cursor. ++ */ ++enum CXVisibilityKind { ++ /** \brief This value indicates that no visibility information is available ++ * for a provided CXCursor. */ ++ CXVisibility_Invalid, ++ ++ /** \brief Symbol not seen by the linker. */ ++ CXVisibility_Hidden, ++ /** \brief Symbol seen by the linker but resolves to a symbol inside this object. */ ++ CXVisibility_Protected, ++ /** \brief Symbol seen by the linker and acts like a normal symbol. */ ++ CXVisibility_Default, ++}; ++ ++CINDEX_LINKAGE enum CXVisibilityKind clang_getCursorVisibility(CXCursor cursor); ++ ++/** + * \brief Determine the availability of the entity that this cursor refers to, + * taking the current target platform into account. + * +diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp +index 8225a6c..9fa18d3 100644 +--- a/clang/tools/libclang/CIndex.cpp ++++ b/clang/tools/libclang/CIndex.cpp +@@ -6361,6 +6361,27 @@ CXLinkageKind clang_getCursorLinkage(CXCursor cursor) { + } // end: extern "C" + + //===----------------------------------------------------------------------===// ++// Operations for querying visibility of a cursor. ++//===----------------------------------------------------------------------===// ++ ++extern "C" { ++CXVisibilityKind clang_getCursorVisibility(CXCursor cursor) { ++ if (!clang_isDeclaration(cursor.kind)) ++ return CXVisibility_Invalid; ++ ++ const Decl *D = cxcursor::getCursorDecl(cursor); ++ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(D)) ++ switch (ND->getVisibility()) { ++ case HiddenVisibility: return CXVisibility_Hidden; ++ case ProtectedVisibility: return CXVisibility_Protected; ++ case DefaultVisibility: return CXVisibility_Default; ++ }; ++ ++ return CXVisibility_Invalid; ++} ++} // end: extern "C" ++ ++//===----------------------------------------------------------------------===// + // Operations for querying language of a cursor. + //===----------------------------------------------------------------------===// + +diff --git a/clang/tools/libclang/libclang.exports b/clang/tools/libclang/libclang.exports +index f6a7175..a919a8e 100644 +--- a/clang/tools/libclang/libclang.exports ++++ b/clang/tools/libclang/libclang.exports +@@ -173,6 +173,7 @@ clang_getCursorSemanticParent + clang_getCursorSpelling + clang_getCursorType + clang_getCursorUSR ++clang_getCursorVisibility + clang_getDeclObjCTypeEncoding + clang_getDefinitionSpellingAndExtent + clang_getDiagnostic diff --git a/build/build-clang/return-empty-string-non-mangled.patch b/build/build-clang/return-empty-string-non-mangled.patch new file mode 100644 index 000000000..33a391703 --- /dev/null +++ b/build/build-clang/return-empty-string-non-mangled.patch @@ -0,0 +1,19 @@ +Author: Michael Wu <mwu@mozilla.com> +Date: Thu Sep 24 11:36:08 2015 -0400 + + Return an empty string when a symbol isn't mangled + +diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp +--- a/clang/tools/libclang/CIndex.cpp ++++ b/clang/tools/libclang/CIndex.cpp +@@ -3990,6 +3990,10 @@ + ASTContext &Ctx = ND->getASTContext(); + std::unique_ptr<MangleContext> MC(Ctx.createMangleContext()); + ++ // Don't mangle if we don't need to. ++ if (!MC->shouldMangleCXXName(ND)) ++ return cxstring::createEmpty(); ++ + std::string FrontendBuf; + llvm::raw_string_ostream FrontendBufOS(FrontendBuf); + if (MC->shouldMangleDeclName(ND)) { diff --git a/build/buildconfig.py b/build/buildconfig.py new file mode 100644 index 000000000..e73903a1a --- /dev/null +++ b/build/buildconfig.py @@ -0,0 +1,20 @@ +# 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 os +import sys +from mozbuild.base import MozbuildObject + +config = MozbuildObject.from_environment() + +for var in ('topsrcdir', 'topobjdir', 'defines', 'non_global_defines', + 'substs'): + value = getattr(config, var) + setattr(sys.modules[__name__], var, value) + +substs = dict(substs) + +for var in os.environ: + if var not in ('CPP', 'CXXCPP', 'SHELL') and var in substs: + substs[var] = os.environ[var] diff --git a/build/checksums.py b/build/checksums.py new file mode 100755 index 000000000..9b13b50cb --- /dev/null +++ b/build/checksums.py @@ -0,0 +1,158 @@ +#!/usr/bin/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/. + +from __future__ import with_statement + +from optparse import OptionParser +import logging +import os +try: + import hashlib +except: + hashlib = None + +def digest_file(filename, digest, chunk_size=1024): + '''Produce a checksum for the file specified by 'filename'. 'filename' + is a string path to a file that is opened and read in this function. The + checksum algorithm is specified by 'digest' and is a valid OpenSSL + algorithm. If the digest used is not valid or Python's hashlib doesn't + work, the None object will be returned instead. The size of blocks + that this function will read from the file object it opens based on + 'filename' can be specified by 'chunk_size', which defaults to 1K''' + assert not os.path.isdir(filename), 'this function only works with files' + logger = logging.getLogger('checksums.py') + if hashlib is not None: + logger.debug('Creating new %s object' % digest) + h = hashlib.new(digest) + with open(filename, 'rb') as f: + while True: + data = f.read(chunk_size) + if not data: + logger.debug('Finished reading in file') + break + h.update(data) + hash = h.hexdigest() + logger.debug('Hash for %s is %s' % (filename, hash)) + return hash + else: + # In this case we could subprocess.Popen and .communicate with + # sha1sum or md5sum + logger.warn('The python module for hashlib is missing!') + return None + + +def process_files(files, output_filename, digests, strip): + '''This function takes a list of file names, 'files'. It will then + compute the checksum for each of the files by opening the files. + Once each file is read and its checksum is computed, this function + will write the information to the file specified by 'output_filename'. + The path written in the output file will have anything specified by 'strip' + removed from the path. The output file is closed before returning nothing + The algorithm to compute checksums with can be specified by 'digests' + and needs to be a list of valid OpenSSL algorithms. + + The output file is written in the format: + <hash> <algorithm> <filesize> <filepath> + Example: + d1fa09a<snip>e4220 sha1 14250744 firefox-4.0b6pre.en-US.mac64.dmg + ''' + + logger = logging.getLogger('checksums.py') + if os.path.exists(output_filename): + logger.debug('Overwriting existing checksums file "%s"' % + output_filename) + else: + logger.debug('Creating a new checksums file "%s"' % output_filename) + with open(output_filename, 'w+') as output: + for file in files: + if os.path.isdir(file): + logger.warn('%s is a directory, skipping' % file) + else: + for digest in digests: + hash = digest_file(file, digest) + if hash is None: + logger.warn('Unable to generate a hash for %s. ' + + 'Skipping.' % file) + continue + if file.startswith(strip): + short_file = file[len(strip):] + short_file = short_file.lstrip('/') + else: + short_file = file + print >>output, '%s %s %s %s' % (hash, digest, + os.path.getsize(file), + short_file) + +def setup_logging(level=logging.DEBUG): + '''This function sets up the logging module using a speficiable logging + module logging level. The default log level is DEBUG. + + The output is in the format: + <level> - <message> + Example: + DEBUG - Finished reading in file +''' + + logger = logging.getLogger('checksums.py') + logger.setLevel(logging.DEBUG) + handler = logging.StreamHandler() + handler.setLevel(level) + formatter = logging.Formatter("%(levelname)s - %(message)s") + handler.setFormatter(formatter) + logger.addHandler(handler) + +def main(): + '''This is a main function that parses arguments, sets up logging + and generates a checksum file''' + # Parse command line arguments + parser = OptionParser() + parser.add_option('-d', '--digest', help='checksum algorithm to use', + action='append', dest='digests') + parser.add_option('-o', '--output', help='output file to use', + action='store', dest='outfile', default='checksums') + parser.add_option('-v', '--verbose', + help='Be noisy (takes precedence over quiet)', + action='store_true', dest='verbose', default=False) + parser.add_option('-q', '--quiet', help='Be quiet', action='store_true', + dest='quiet', default=False) + parser.add_option('-s', '--strip', + help='strip this path from the filenames', + dest='strip', default=os.getcwd()) + options, args = parser.parse_args() + + #Figure out which logging level to use + if options.verbose: + loglevel = logging.DEBUG + elif options.quiet: + loglevel = logging.ERROR + else: + loglevel = logging.INFO + + #Set up logging + setup_logging(loglevel) + logger = logging.getLogger('checksums.py') + + # Validate the digest type to use + if not options.digests: + options.digests = ['sha1'] + try: + for digest in options.digests: + hashlib.new(digest) + except ValueError, ve: + logger.error('Could not create a "%s" hash object (%s)' % + (digest, ve.args[0])) + exit(1) + + # Validate the files to checksum + files = [] + for i in args: + if os.path.exists(i): + files.append(i) + else: + logger.info('File "%s" was not found on the filesystem' % i) + process_files(files, options.outfile, options.digests, options.strip) + +if __name__ == '__main__': + main() diff --git a/build/clang-plugin/.clang-format b/build/clang-plugin/.clang-format new file mode 100644 index 000000000..9b3aa8b72 --- /dev/null +++ b/build/clang-plugin/.clang-format @@ -0,0 +1 @@ +BasedOnStyle: LLVM diff --git a/build/clang-plugin/Makefile.in b/build/clang-plugin/Makefile.in new file mode 100644 index 000000000..ff1e39d1b --- /dev/null +++ b/build/clang-plugin/Makefile.in @@ -0,0 +1,42 @@ +# 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/. + +# LLVM_CXXFLAGS comes with its own optimization flags. +MOZ_OPTIMIZE = + +include $(topsrcdir)/config/config.mk + +# In the current moz.build world, we need to override essentially every +# variable to limit ourselves to what we need to build the clang plugin. +ifeq ($(HOST_OS_ARCH),WINNT) +OS_CXXFLAGS := $(LLVM_CXXFLAGS) -GR- -EHsc +else +OS_CXXFLAGS := $(LLVM_CXXFLAGS) -fno-rtti -fno-exceptions +DSO_LDOPTS := -shared +endif +OS_COMPILE_CXXFLAGS := +OS_LDFLAGS := $(LLVM_LDFLAGS) $(CLANG_LDFLAGS) + +ifeq ($(HOST_OS_ARCH)_$(OS_ARCH),Linux_Darwin) +# Use the host compiler instead of the target compiler. +CXX := $(HOST_CXX) +# expandlibs doesn't know the distinction between host and target toolchains, +# and on cross linux/darwin builds, the options to give to the linker for file +# lists differ between both, so don't use file lists. +EXPAND_MKSHLIB_ARGS := +# Don't pass OSX linker arguments. +MOZ_FIX_LINK_PATHS := +endif + +# Use the default OS X deployment target to enable using the libc++ headers +# correctly. Note that the binary produced here is a host tool and doesn't need +# to be distributed. +MACOSX_DEPLOYMENT_TARGET := + +# Temporarily relax the requirements for libstdc++ symbol versions on static +# analysis plugin in order to use a recent clang by accepting libstdc++ from +# gcc 4.4.0 (GLIBCXX_3.4.11). +ifdef CHECK_STDCXX +CHECK_STDCXX = $(call CHECK_SYMBOLS,$(1),GLIBCXX,libstdc++,v[1] > 3 || (v[1] == 3 && v[2] == 4 && v[3] > 11)) +endif diff --git a/build/clang-plugin/clang-plugin.cpp b/build/clang-plugin/clang-plugin.cpp new file mode 100644 index 000000000..f420d3e8b --- /dev/null +++ b/build/clang-plugin/clang-plugin.cpp @@ -0,0 +1,2331 @@ +/* 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 respects the LLVM coding standard described at + * http://llvm.org/docs/CodingStandards.html */ + +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/Version.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendPluginRegistry.h" +#include "clang/Frontend/MultiplexConsumer.h" +#include "clang/Sema/Sema.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include <memory> +#include <iterator> + +#define CLANG_VERSION_FULL (CLANG_VERSION_MAJOR * 100 + CLANG_VERSION_MINOR) + +using namespace llvm; +using namespace clang; + +#if CLANG_VERSION_FULL >= 306 +typedef std::unique_ptr<ASTConsumer> ASTConsumerPtr; +#else +typedef ASTConsumer *ASTConsumerPtr; +#endif + +#ifndef HAVE_NEW_ASTMATCHER_NAMES +// In clang 3.8, a number of AST matchers were renamed to better match the +// respective AST node. We use the new names, and #define them to the old +// ones for compatibility with older versions. +#define cxxConstructExpr constructExpr +#define cxxConstructorDecl constructorDecl +#define cxxMethodDecl methodDecl +#define cxxNewExpr newExpr +#define cxxRecordDecl recordDecl +#endif + +#ifndef HAS_ACCEPTS_IGNORINGPARENIMPCASTS +#define hasIgnoringParenImpCasts(x) has(x) +#else +// Before clang 3.9 "has" would behave like has(ignoringParenImpCasts(x)), +// however doing that explicitly would not compile. +#define hasIgnoringParenImpCasts(x) has(ignoringParenImpCasts(x)) +#endif + +// Check if the given expression contains an assignment expression. +// This can either take the form of a Binary Operator or a +// Overloaded Operator Call. +bool hasSideEffectAssignment(const Expr *Expression) { + if (auto OpCallExpr = dyn_cast_or_null<CXXOperatorCallExpr>(Expression)) { + auto BinOp = OpCallExpr->getOperator(); + if (BinOp == OO_Equal || (BinOp >= OO_PlusEqual && BinOp <= OO_PipeEqual)) { + return true; + } + } else if (auto BinOpExpr = dyn_cast_or_null<BinaryOperator>(Expression)) { + if (BinOpExpr->isAssignmentOp()) { + return true; + } + } + + // Recurse to children. + for (const Stmt *SubStmt : Expression->children()) { + auto ChildExpr = dyn_cast_or_null<Expr>(SubStmt); + if (ChildExpr && hasSideEffectAssignment(ChildExpr)) { + return true; + } + } + + return false; +} + +namespace { + +using namespace clang::ast_matchers; +class DiagnosticsMatcher { +public: + DiagnosticsMatcher(); + + ASTConsumerPtr makeASTConsumer() { return AstMatcher.newASTConsumer(); } + +private: + class ScopeChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class ArithmeticArgChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class TrivialCtorDtorChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NaNExprChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoAddRefReleaseOnReturnChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class RefCountedInsideLambdaChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + void emitDiagnostics(SourceLocation Loc, StringRef Name, QualType Type); + + private: + class ThisVisitor : public RecursiveASTVisitor<ThisVisitor> { + public: + explicit ThisVisitor(RefCountedInsideLambdaChecker& Checker) + : Checker(Checker) {} + + bool VisitCXXThisExpr(CXXThisExpr *This); + private: + RefCountedInsideLambdaChecker& Checker; + }; + + ASTContext *Context; + }; + + class ExplicitOperatorBoolChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoDuplicateRefCntMemberChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NeedsNoVTableTypeChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NonMemMovableTemplateArgChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NonMemMovableMemberChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class ExplicitImplicitChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoAutoTypeChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NoExplicitMoveConstructorChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class RefCountedCopyConstructorChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class AssertAssignmentChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class KungFuDeathGripChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class SprintfLiteralChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class OverrideBaseCallChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + private: + void evaluateExpression(const Stmt *StmtExpr, + std::list<const CXXMethodDecl*> &MethodList); + void getRequiredBaseMethod(const CXXMethodDecl* Method, + std::list<const CXXMethodDecl*>& MethodsList); + void findBaseMethodCall(const CXXMethodDecl* Method, + std::list<const CXXMethodDecl*>& MethodsList); + bool isRequiredBaseMethod(const CXXMethodDecl *Method); + }; + +/* + * This is a companion checker for OverrideBaseCallChecker that rejects + * the usage of MOZ_REQUIRED_BASE_METHOD on non-virtual base methods. + */ + class OverrideBaseCallUsageChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + class NonParamInsideFunctionDeclChecker : public MatchFinder::MatchCallback { + public: + virtual void run(const MatchFinder::MatchResult &Result); + }; + + ScopeChecker Scope; + ArithmeticArgChecker ArithmeticArg; + TrivialCtorDtorChecker TrivialCtorDtor; + NaNExprChecker NaNExpr; + NoAddRefReleaseOnReturnChecker NoAddRefReleaseOnReturn; + RefCountedInsideLambdaChecker RefCountedInsideLambda; + ExplicitOperatorBoolChecker ExplicitOperatorBool; + NoDuplicateRefCntMemberChecker NoDuplicateRefCntMember; + NeedsNoVTableTypeChecker NeedsNoVTableType; + NonMemMovableTemplateArgChecker NonMemMovableTemplateArg; + NonMemMovableMemberChecker NonMemMovableMember; + ExplicitImplicitChecker ExplicitImplicit; + NoAutoTypeChecker NoAutoType; + NoExplicitMoveConstructorChecker NoExplicitMoveConstructor; + RefCountedCopyConstructorChecker RefCountedCopyConstructor; + AssertAssignmentChecker AssertAttribution; + KungFuDeathGripChecker KungFuDeathGrip; + SprintfLiteralChecker SprintfLiteral; + OverrideBaseCallChecker OverrideBaseCall; + OverrideBaseCallUsageChecker OverrideBaseCallUsage; + NonParamInsideFunctionDeclChecker NonParamInsideFunctionDecl; + MatchFinder AstMatcher; +}; + +namespace { + +std::string getDeclarationNamespace(const Decl *Declaration) { + const DeclContext *DC = + Declaration->getDeclContext()->getEnclosingNamespaceContext(); + const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(DC); + if (!ND) { + return ""; + } + + while (const DeclContext *ParentDC = ND->getParent()) { + if (!isa<NamespaceDecl>(ParentDC)) { + break; + } + ND = cast<NamespaceDecl>(ParentDC); + } + + const auto &Name = ND->getName(); + return Name; +} + +bool isInIgnoredNamespaceForImplicitCtor(const Decl *Declaration) { + std::string Name = getDeclarationNamespace(Declaration); + if (Name == "") { + return false; + } + + return Name == "std" || // standard C++ lib + Name == "__gnu_cxx" || // gnu C++ lib + Name == "boost" || // boost + Name == "webrtc" || // upstream webrtc + Name == "rtc" || // upstream webrtc 'base' package + Name.substr(0, 4) == "icu_" || // icu + Name == "google" || // protobuf + Name == "google_breakpad" || // breakpad + Name == "soundtouch" || // libsoundtouch + Name == "stagefright" || // libstagefright + Name == "MacFileUtilities" || // MacFileUtilities + Name == "dwarf2reader" || // dwarf2reader + Name == "arm_ex_to_module" || // arm_ex_to_module + Name == "testing" || // gtest + Name == "Json"; // jsoncpp +} + +bool isInIgnoredNamespaceForImplicitConversion(const Decl *Declaration) { + std::string Name = getDeclarationNamespace(Declaration); + if (Name == "") { + return false; + } + + return Name == "std" || // standard C++ lib + Name == "__gnu_cxx" || // gnu C++ lib + Name == "google_breakpad" || // breakpad + Name == "testing"; // gtest +} + +bool isIgnoredPathForImplicitCtor(const Decl *Declaration) { + SourceLocation Loc = Declaration->getLocation(); + const SourceManager &SM = Declaration->getASTContext().getSourceManager(); + SmallString<1024> FileName = SM.getFilename(Loc); + llvm::sys::fs::make_absolute(FileName); + llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), + End = llvm::sys::path::rend(FileName); + for (; Begin != End; ++Begin) { + if (Begin->compare_lower(StringRef("skia")) == 0 || + Begin->compare_lower(StringRef("angle")) == 0 || + Begin->compare_lower(StringRef("harfbuzz")) == 0 || + Begin->compare_lower(StringRef("hunspell")) == 0 || + Begin->compare_lower(StringRef("scoped_ptr.h")) == 0 || + Begin->compare_lower(StringRef("graphite2")) == 0 || + Begin->compare_lower(StringRef("icu")) == 0) { + return true; + } + if (Begin->compare_lower(StringRef("chromium")) == 0) { + // Ignore security/sandbox/chromium but not ipc/chromium. + ++Begin; + return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0; + } + } + return false; +} + +bool isIgnoredPathForImplicitConversion(const Decl *Declaration) { + Declaration = Declaration->getCanonicalDecl(); + SourceLocation Loc = Declaration->getLocation(); + const SourceManager &SM = Declaration->getASTContext().getSourceManager(); + SmallString<1024> FileName = SM.getFilename(Loc); + llvm::sys::fs::make_absolute(FileName); + llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), + End = llvm::sys::path::rend(FileName); + for (; Begin != End; ++Begin) { + if (Begin->compare_lower(StringRef("graphite2")) == 0) { + return true; + } + if (Begin->compare_lower(StringRef("chromium")) == 0) { + // Ignore security/sandbox/chromium but not ipc/chromium. + ++Begin; + return Begin != End && Begin->compare_lower(StringRef("sandbox")) == 0; + } + } + return false; +} + +bool isIgnoredPathForSprintfLiteral(const CallExpr *Call, const SourceManager &SM) { + SourceLocation Loc = Call->getLocStart(); + SmallString<1024> FileName = SM.getFilename(Loc); + llvm::sys::fs::make_absolute(FileName); + llvm::sys::path::reverse_iterator Begin = llvm::sys::path::rbegin(FileName), + End = llvm::sys::path::rend(FileName); + for (; Begin != End; ++Begin) { + if (Begin->compare_lower(StringRef("angle")) == 0 || + Begin->compare_lower(StringRef("chromium")) == 0 || + Begin->compare_lower(StringRef("crashreporter")) == 0 || + Begin->compare_lower(StringRef("google-breakpad")) == 0 || + Begin->compare_lower(StringRef("harfbuzz")) == 0 || + Begin->compare_lower(StringRef("libstagefright")) == 0 || + Begin->compare_lower(StringRef("mtransport")) == 0 || + Begin->compare_lower(StringRef("protobuf")) == 0 || + Begin->compare_lower(StringRef("skia")) == 0 || + // Gtest uses snprintf as GTEST_SNPRINTF_ with sizeof + Begin->compare_lower(StringRef("testing")) == 0) { + return true; + } + if (Begin->compare_lower(StringRef("webrtc")) == 0) { + // Ignore trunk/webrtc, but not media/webrtc + ++Begin; + return Begin != End && Begin->compare_lower(StringRef("trunk")) == 0; + } + } + return false; +} + +bool isInterestingDeclForImplicitConversion(const Decl *Declaration) { + return !isInIgnoredNamespaceForImplicitConversion(Declaration) && + !isIgnoredPathForImplicitConversion(Declaration); +} + +bool isIgnoredExprForMustUse(const Expr *E) { + if (const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(E)) { + switch (OpCall->getOperator()) { + case OO_Equal: + case OO_PlusEqual: + case OO_MinusEqual: + case OO_StarEqual: + case OO_SlashEqual: + case OO_PercentEqual: + case OO_CaretEqual: + case OO_AmpEqual: + case OO_PipeEqual: + case OO_LessLessEqual: + case OO_GreaterGreaterEqual: + return true; + default: + return false; + } + } + + if (const BinaryOperator *Op = dyn_cast<BinaryOperator>(E)) { + return Op->isAssignmentOp(); + } + + return false; +} + +template<typename T> +StringRef getNameChecked(const T& D) { + return D->getIdentifier() ? D->getName() : ""; +} + +bool typeIsRefPtr(QualType Q) { + CXXRecordDecl *D = Q->getAsCXXRecordDecl(); + if (!D || !D->getIdentifier()) { + return false; + } + + StringRef name = D->getName(); + if (name == "RefPtr" || name == "nsCOMPtr") { + return true; + } + return false; +} + +// The method defined in clang for ignoring implicit nodes doesn't work with +// some AST trees. To get around this, we define our own implementation of +// IgnoreImplicit. +const Stmt *IgnoreImplicit(const Stmt *s) { + while (true) { + if (auto *ewc = dyn_cast<ExprWithCleanups>(s)) { + s = ewc->getSubExpr(); + } else if (auto *mte = dyn_cast<MaterializeTemporaryExpr>(s)) { + s = mte->GetTemporaryExpr(); + } else if (auto *bte = dyn_cast<CXXBindTemporaryExpr>(s)) { + s = bte->getSubExpr(); + } else if (auto *ice = dyn_cast<ImplicitCastExpr>(s)) { + s = ice->getSubExpr(); + } else { + break; + } + } + + return s; +} + +const Expr *IgnoreImplicit(const Expr *e) { + return cast<Expr>(IgnoreImplicit(static_cast<const Stmt *>(e))); +} +} + +class CustomTypeAnnotation { + enum ReasonKind { + RK_None, + RK_Direct, + RK_ArrayElement, + RK_BaseClass, + RK_Field, + RK_TemplateInherited, + }; + struct AnnotationReason { + QualType Type; + ReasonKind Kind; + const FieldDecl *Field; + + bool valid() const { return Kind != RK_None; } + }; + typedef DenseMap<void *, AnnotationReason> ReasonCache; + + const char *Spelling; + const char *Pretty; + ReasonCache Cache; + +public: + CustomTypeAnnotation(const char *Spelling, const char *Pretty) + : Spelling(Spelling), Pretty(Pretty){}; + + virtual ~CustomTypeAnnotation() {} + + // Checks if this custom annotation "effectively affects" the given type. + bool hasEffectiveAnnotation(QualType T) { + return directAnnotationReason(T).valid(); + } + void dumpAnnotationReason(DiagnosticsEngine &Diag, QualType T, + SourceLocation Loc); + + void reportErrorIfPresent(DiagnosticsEngine &Diag, QualType T, + SourceLocation Loc, unsigned ErrorID, + unsigned NoteID) { + if (hasEffectiveAnnotation(T)) { + Diag.Report(Loc, ErrorID) << T; + Diag.Report(Loc, NoteID); + dumpAnnotationReason(Diag, T, Loc); + } + } + +private: + bool hasLiteralAnnotation(QualType T) const; + AnnotationReason directAnnotationReason(QualType T); + AnnotationReason tmplArgAnnotationReason(ArrayRef<TemplateArgument> Args); + +protected: + // Allow subclasses to apply annotations to external code: + virtual bool hasFakeAnnotation(const TagDecl *D) const { return false; } +}; + +static CustomTypeAnnotation StackClass = + CustomTypeAnnotation("moz_stack_class", "stack"); +static CustomTypeAnnotation GlobalClass = + CustomTypeAnnotation("moz_global_class", "global"); +static CustomTypeAnnotation NonHeapClass = + CustomTypeAnnotation("moz_nonheap_class", "non-heap"); +static CustomTypeAnnotation HeapClass = + CustomTypeAnnotation("moz_heap_class", "heap"); +static CustomTypeAnnotation NonTemporaryClass = + CustomTypeAnnotation("moz_non_temporary_class", "non-temporary"); +static CustomTypeAnnotation MustUse = + CustomTypeAnnotation("moz_must_use_type", "must-use"); +static CustomTypeAnnotation NonParam = + CustomTypeAnnotation("moz_non_param", "non-param"); + +class MemMoveAnnotation final : public CustomTypeAnnotation { +public: + MemMoveAnnotation() + : CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able") {} + + virtual ~MemMoveAnnotation() {} + +protected: + bool hasFakeAnnotation(const TagDecl *D) const override { + // Annotate everything in ::std, with a few exceptions; see bug + // 1201314 for discussion. + if (getDeclarationNamespace(D) == "std") { + // This doesn't check that it's really ::std::pair and not + // ::std::something_else::pair, but should be good enough. + StringRef Name = getNameChecked(D); + if (Name == "pair" || Name == "atomic" || Name == "__atomic_base") { + return false; + } + return true; + } + return false; + } +}; + +static MemMoveAnnotation NonMemMovable = MemMoveAnnotation(); + +class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> { + DiagnosticsEngine &Diag; + const CompilerInstance &CI; + DiagnosticsMatcher Matcher; + +public: + MozChecker(const CompilerInstance &CI) : Diag(CI.getDiagnostics()), CI(CI) {} + + ASTConsumerPtr getOtherConsumer() { return Matcher.makeASTConsumer(); } + + virtual void HandleTranslationUnit(ASTContext &Ctx) override { + TraverseDecl(Ctx.getTranslationUnitDecl()); + } + + static bool hasCustomAnnotation(const Decl *D, const char *Spelling) { + iterator_range<specific_attr_iterator<AnnotateAttr>> Attrs = + D->specific_attrs<AnnotateAttr>(); + + for (AnnotateAttr *Attr : Attrs) { + if (Attr->getAnnotation() == Spelling) { + return true; + } + } + + return false; + } + + void handleUnusedExprResult(const Stmt *Statement) { + const Expr *E = dyn_cast_or_null<Expr>(Statement); + if (E) { + E = E->IgnoreImplicit(); // Ignore ExprWithCleanup etc. implicit wrappers + QualType T = E->getType(); + if (MustUse.hasEffectiveAnnotation(T) && !isIgnoredExprForMustUse(E)) { + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Unused value of must-use type %0"); + + Diag.Report(E->getLocStart(), ErrorID) << T; + MustUse.dumpAnnotationReason(Diag, T, E->getLocStart()); + } + } + } + + bool VisitCXXRecordDecl(CXXRecordDecl *D) { + // We need definitions, not declarations + if (!D->isThisDeclarationADefinition()) + return true; + + // Look through all of our immediate bases to find methods that need to be + // overridden + typedef std::vector<CXXMethodDecl *> OverridesVector; + OverridesVector MustOverrides; + for (CXXRecordDecl::base_class_iterator Base = D->bases_begin(), + E = D->bases_end(); + Base != E; ++Base) { + // The base is either a class (CXXRecordDecl) or it's a templated class... + CXXRecordDecl *Parent = Base->getType() + .getDesugaredType(D->getASTContext()) + ->getAsCXXRecordDecl(); + // The parent might not be resolved to a type yet. In this case, we can't + // do any checking here. For complete correctness, we should visit + // template instantiations, but this case is likely to be rare, so we will + // ignore it until it becomes important. + if (!Parent) { + continue; + } + Parent = Parent->getDefinition(); + for (CXXRecordDecl::method_iterator M = Parent->method_begin(); + M != Parent->method_end(); ++M) { + if (hasCustomAnnotation(*M, "moz_must_override")) + MustOverrides.push_back(*M); + } + } + + for (OverridesVector::iterator It = MustOverrides.begin(); + It != MustOverrides.end(); ++It) { + bool Overridden = false; + for (CXXRecordDecl::method_iterator M = D->method_begin(); + !Overridden && M != D->method_end(); ++M) { + // The way that Clang checks if a method M overrides its parent method + // is if the method has the same name but would not overload. + if (getNameChecked(M) == getNameChecked(*It) && + !CI.getSema().IsOverload(*M, (*It), false)) { + Overridden = true; + break; + } + } + if (!Overridden) { + unsigned OverrideID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "%0 must override %1"); + unsigned OverrideNote = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "function to override is here"); + Diag.Report(D->getLocation(), OverrideID) << D->getDeclName() + << (*It)->getDeclName(); + Diag.Report((*It)->getLocation(), OverrideNote); + } + } + + return true; + } + + bool VisitSwitchCase(SwitchCase *Statement) { + handleUnusedExprResult(Statement->getSubStmt()); + return true; + } + bool VisitCompoundStmt(CompoundStmt *Statement) { + for (CompoundStmt::body_iterator It = Statement->body_begin(), + E = Statement->body_end(); + It != E; ++It) { + handleUnusedExprResult(*It); + } + return true; + } + bool VisitIfStmt(IfStmt *Statement) { + handleUnusedExprResult(Statement->getThen()); + handleUnusedExprResult(Statement->getElse()); + return true; + } + bool VisitWhileStmt(WhileStmt *Statement) { + handleUnusedExprResult(Statement->getBody()); + return true; + } + bool VisitDoStmt(DoStmt *Statement) { + handleUnusedExprResult(Statement->getBody()); + return true; + } + bool VisitForStmt(ForStmt *Statement) { + handleUnusedExprResult(Statement->getBody()); + handleUnusedExprResult(Statement->getInit()); + handleUnusedExprResult(Statement->getInc()); + return true; + } + bool VisitBinComma(BinaryOperator *Op) { + handleUnusedExprResult(Op->getLHS()); + return true; + } +}; + +/// A cached data of whether classes are refcounted or not. +typedef DenseMap<const CXXRecordDecl *, std::pair<const Decl *, bool>> + RefCountedMap; +RefCountedMap RefCountedClasses; + +bool classHasAddRefRelease(const CXXRecordDecl *D) { + const RefCountedMap::iterator &It = RefCountedClasses.find(D); + if (It != RefCountedClasses.end()) { + return It->second.second; + } + + bool SeenAddRef = false; + bool SeenRelease = false; + for (CXXRecordDecl::method_iterator Method = D->method_begin(); + Method != D->method_end(); ++Method) { + const auto &Name = getNameChecked(Method); + if (Name == "AddRef") { + SeenAddRef = true; + } else if (Name == "Release") { + SeenRelease = true; + } + } + RefCountedClasses[D] = std::make_pair(D, SeenAddRef && SeenRelease); + return SeenAddRef && SeenRelease; +} + +bool isClassRefCounted(QualType T); + +bool isClassRefCounted(const CXXRecordDecl *D) { + // Normalize so that D points to the definition if it exists. + if (!D->hasDefinition()) + return false; + D = D->getDefinition(); + // Base class: anyone with AddRef/Release is obviously a refcounted class. + if (classHasAddRefRelease(D)) + return true; + + // Look through all base cases to figure out if the parent is a refcounted + // class. + for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(); + Base != D->bases_end(); ++Base) { + bool Super = isClassRefCounted(Base->getType()); + if (Super) { + return true; + } + } + + return false; +} + +bool isClassRefCounted(QualType T) { + while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) + T = ArrTy->getElementType(); + CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); + return Clazz ? isClassRefCounted(Clazz) : false; +} + +template <class T> bool ASTIsInSystemHeader(const ASTContext &AC, const T &D) { + auto &SourceManager = AC.getSourceManager(); + auto ExpansionLoc = SourceManager.getExpansionLoc(D.getLocStart()); + if (ExpansionLoc.isInvalid()) { + return false; + } + return SourceManager.isInSystemHeader(ExpansionLoc); +} + +const FieldDecl *getClassRefCntMember(const CXXRecordDecl *D) { + for (RecordDecl::field_iterator Field = D->field_begin(), E = D->field_end(); + Field != E; ++Field) { + if (getNameChecked(Field) == "mRefCnt") { + return *Field; + } + } + return 0; +} + +const FieldDecl *getBaseRefCntMember(QualType T); + +const FieldDecl *getBaseRefCntMember(const CXXRecordDecl *D) { + const FieldDecl *RefCntMember = getClassRefCntMember(D); + if (RefCntMember && isClassRefCounted(D)) { + return RefCntMember; + } + + for (CXXRecordDecl::base_class_const_iterator Base = D->bases_begin(), + E = D->bases_end(); + Base != E; ++Base) { + RefCntMember = getBaseRefCntMember(Base->getType()); + if (RefCntMember) { + return RefCntMember; + } + } + return 0; +} + +const FieldDecl *getBaseRefCntMember(QualType T) { + while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) + T = ArrTy->getElementType(); + CXXRecordDecl *Clazz = T->getAsCXXRecordDecl(); + return Clazz ? getBaseRefCntMember(Clazz) : 0; +} + +bool typeHasVTable(QualType T) { + while (const clang::ArrayType *ArrTy = T->getAsArrayTypeUnsafe()) + T = ArrTy->getElementType(); + CXXRecordDecl *Offender = T->getAsCXXRecordDecl(); + return Offender && Offender->hasDefinition() && Offender->isDynamicClass(); +} +} + +namespace clang { +namespace ast_matchers { + +/// This matcher will match any function declaration that is declared as a heap +/// allocator. +AST_MATCHER(FunctionDecl, heapAllocator) { + return MozChecker::hasCustomAnnotation(&Node, "moz_heap_allocator"); +} + +/// This matcher will match any declaration that is marked as not accepting +/// arithmetic expressions in its arguments. +AST_MATCHER(Decl, noArithmeticExprInArgs) { + return MozChecker::hasCustomAnnotation(&Node, "moz_no_arith_expr_in_arg"); +} + +/// This matcher will match any C++ class that is marked as having a trivial +/// constructor and destructor. +AST_MATCHER(CXXRecordDecl, hasTrivialCtorDtor) { + return MozChecker::hasCustomAnnotation(&Node, "moz_trivial_ctor_dtor"); +} + +/// This matcher will match any function declaration that is marked to prohibit +/// calling AddRef or Release on its return value. +AST_MATCHER(FunctionDecl, hasNoAddRefReleaseOnReturnAttr) { + return MozChecker::hasCustomAnnotation(&Node, + "moz_no_addref_release_on_return"); +} + +/// This matcher will match all arithmetic binary operators. +AST_MATCHER(BinaryOperator, binaryArithmeticOperator) { + BinaryOperatorKind OpCode = Node.getOpcode(); + return OpCode == BO_Mul || OpCode == BO_Div || OpCode == BO_Rem || + OpCode == BO_Add || OpCode == BO_Sub || OpCode == BO_Shl || + OpCode == BO_Shr || OpCode == BO_And || OpCode == BO_Xor || + OpCode == BO_Or || OpCode == BO_MulAssign || OpCode == BO_DivAssign || + OpCode == BO_RemAssign || OpCode == BO_AddAssign || + OpCode == BO_SubAssign || OpCode == BO_ShlAssign || + OpCode == BO_ShrAssign || OpCode == BO_AndAssign || + OpCode == BO_XorAssign || OpCode == BO_OrAssign; +} + +/// This matcher will match all arithmetic unary operators. +AST_MATCHER(UnaryOperator, unaryArithmeticOperator) { + UnaryOperatorKind OpCode = Node.getOpcode(); + return OpCode == UO_PostInc || OpCode == UO_PostDec || OpCode == UO_PreInc || + OpCode == UO_PreDec || OpCode == UO_Plus || OpCode == UO_Minus || + OpCode == UO_Not; +} + +/// This matcher will match == and != binary operators. +AST_MATCHER(BinaryOperator, binaryEqualityOperator) { + BinaryOperatorKind OpCode = Node.getOpcode(); + return OpCode == BO_EQ || OpCode == BO_NE; +} + +/// This matcher will match floating point types. +AST_MATCHER(QualType, isFloat) { return Node->isRealFloatingType(); } + +/// This matcher will match locations in system headers. This is adopted from +/// isExpansionInSystemHeader in newer clangs, but modified in order to work +/// with old clangs that we use on infra. +AST_MATCHER(BinaryOperator, isInSystemHeader) { + return ASTIsInSystemHeader(Finder->getASTContext(), Node); +} + +/// This matcher will match a list of files. These files contain +/// known NaN-testing expressions which we would like to whitelist. +AST_MATCHER(BinaryOperator, isInWhitelistForNaNExpr) { + const char* whitelist[] = { + "SkScalar.h", + "json_writer.cpp" + }; + + SourceLocation Loc = Node.getOperatorLoc(); + auto &SourceManager = Finder->getASTContext().getSourceManager(); + SmallString<1024> FileName = SourceManager.getFilename(Loc); + + for (auto itr = std::begin(whitelist); itr != std::end(whitelist); itr++) { + if (llvm::sys::path::rbegin(FileName)->equals(*itr)) { + return true; + } + } + + return false; +} + +/// This matcher will match all accesses to AddRef or Release methods. +AST_MATCHER(MemberExpr, isAddRefOrRelease) { + ValueDecl *Member = Node.getMemberDecl(); + CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(Member); + if (Method) { + const auto &Name = getNameChecked(Method); + return Name == "AddRef" || Name == "Release"; + } + return false; +} + +/// This matcher will select classes which are refcounted. +AST_MATCHER(CXXRecordDecl, hasRefCntMember) { + return isClassRefCounted(&Node) && getClassRefCntMember(&Node); +} + +AST_MATCHER(QualType, hasVTable) { return typeHasVTable(Node); } + +AST_MATCHER(CXXRecordDecl, hasNeedsNoVTableTypeAttr) { + return MozChecker::hasCustomAnnotation(&Node, "moz_needs_no_vtable_type"); +} + +/// This matcher will select classes which are non-memmovable +AST_MATCHER(QualType, isNonMemMovable) { + return NonMemMovable.hasEffectiveAnnotation(Node); +} + +/// This matcher will select classes which require a memmovable template arg +AST_MATCHER(CXXRecordDecl, needsMemMovableTemplateArg) { + return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_type"); +} + +/// This matcher will select classes which require all members to be memmovable +AST_MATCHER(CXXRecordDecl, needsMemMovableMembers) { + return MozChecker::hasCustomAnnotation(&Node, "moz_needs_memmovable_members"); +} + +AST_MATCHER(CXXConstructorDecl, isInterestingImplicitCtor) { + const CXXConstructorDecl *Declaration = Node.getCanonicalDecl(); + return + // Skip ignored namespaces and paths + !isInIgnoredNamespaceForImplicitCtor(Declaration) && + !isIgnoredPathForImplicitCtor(Declaration) && + // We only want Converting constructors + Declaration->isConvertingConstructor(false) && + // We don't want copy of move constructors, as those are allowed to be + // implicit + !Declaration->isCopyOrMoveConstructor() && + // We don't want deleted constructors. + !Declaration->isDeleted(); +} + +// We can't call this "isImplicit" since it clashes with an existing matcher in +// clang. +AST_MATCHER(CXXConstructorDecl, isMarkedImplicit) { + return MozChecker::hasCustomAnnotation(&Node, "moz_implicit"); +} + +AST_MATCHER(CXXRecordDecl, isConcreteClass) { return !Node.isAbstract(); } + +AST_MATCHER(QualType, autoNonAutoableType) { + if (const AutoType *T = Node->getContainedAutoType()) { + if (const CXXRecordDecl *Rec = T->getAsCXXRecordDecl()) { + return MozChecker::hasCustomAnnotation(Rec, "moz_non_autoable"); + } + } + return false; +} + +AST_MATCHER(CXXConstructorDecl, isExplicitMoveConstructor) { + return Node.isExplicit() && Node.isMoveConstructor(); +} + +AST_MATCHER(CXXConstructorDecl, isCompilerProvidedCopyConstructor) { + return !Node.isUserProvided() && Node.isCopyConstructor(); +} + +AST_MATCHER(CallExpr, isAssertAssignmentTestFunc) { + static const std::string AssertName = "MOZ_AssertAssignmentTest"; + const FunctionDecl *Method = Node.getDirectCallee(); + + return Method + && Method->getDeclName().isIdentifier() + && Method->getName() == AssertName; +} + +AST_MATCHER(CallExpr, isSnprintfLikeFunc) { + static const std::string Snprintf = "snprintf"; + static const std::string Vsnprintf = "vsnprintf"; + const FunctionDecl *Func = Node.getDirectCallee(); + + if (!Func || isa<CXXMethodDecl>(Func)) { + return false; + } + + StringRef Name = getNameChecked(Func); + if (Name != Snprintf && Name != Vsnprintf) { + return false; + } + + return !isIgnoredPathForSprintfLiteral(&Node, Finder->getASTContext().getSourceManager()); +} + +AST_MATCHER(CXXRecordDecl, isLambdaDecl) { + return Node.isLambda(); +} + +AST_MATCHER(QualType, isRefPtr) { + return typeIsRefPtr(Node); +} + +AST_MATCHER(CXXRecordDecl, hasBaseClasses) { + const CXXRecordDecl *Decl = Node.getCanonicalDecl(); + + // Must have definition and should inherit other classes + return Decl && Decl->hasDefinition() && Decl->getNumBases(); +} + +AST_MATCHER(CXXMethodDecl, isRequiredBaseMethod) { + const CXXMethodDecl *Decl = Node.getCanonicalDecl(); + return Decl + && MozChecker::hasCustomAnnotation(Decl, "moz_required_base_method"); +} + +AST_MATCHER(CXXMethodDecl, isNonVirtual) { + const CXXMethodDecl *Decl = Node.getCanonicalDecl(); + return Decl && !Decl->isVirtual(); +} +} +} + +namespace { + +void CustomTypeAnnotation::dumpAnnotationReason(DiagnosticsEngine &Diag, + QualType T, + SourceLocation Loc) { + unsigned InheritsID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%1 is a %0 type because it inherits from a %0 type %2"); + unsigned MemberID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "%1 is a %0 type because member %2 is a %0 type %3"); + unsigned ArrayID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%1 is a %0 type because it is an array of %0 type %2"); + unsigned TemplID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "%1 is a %0 type because it has a template argument %0 type %2"); + + AnnotationReason Reason = directAnnotationReason(T); + for (;;) { + switch (Reason.Kind) { + case RK_ArrayElement: + Diag.Report(Loc, ArrayID) << Pretty << T << Reason.Type; + break; + case RK_BaseClass: { + const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl(); + assert(Declaration && "This type should be a C++ class"); + + Diag.Report(Declaration->getLocation(), InheritsID) << Pretty << T + << Reason.Type; + break; + } + case RK_Field: + Diag.Report(Reason.Field->getLocation(), MemberID) + << Pretty << T << Reason.Field << Reason.Type; + break; + case RK_TemplateInherited: { + const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl(); + assert(Declaration && "This type should be a C++ class"); + + Diag.Report(Declaration->getLocation(), TemplID) << Pretty << T + << Reason.Type; + break; + } + default: + // FIXME (bug 1203263): note the original annotation. + return; + } + + T = Reason.Type; + Reason = directAnnotationReason(T); + } +} + +bool CustomTypeAnnotation::hasLiteralAnnotation(QualType T) const { +#if CLANG_VERSION_FULL >= 306 + if (const TagDecl *D = T->getAsTagDecl()) { +#else + if (const CXXRecordDecl *D = T->getAsCXXRecordDecl()) { +#endif + return hasFakeAnnotation(D) || MozChecker::hasCustomAnnotation(D, Spelling); + } + return false; +} + +CustomTypeAnnotation::AnnotationReason +CustomTypeAnnotation::directAnnotationReason(QualType T) { + if (hasLiteralAnnotation(T)) { + AnnotationReason Reason = {T, RK_Direct, nullptr}; + return Reason; + } + + // Check if we have a cached answer + void *Key = T.getAsOpaquePtr(); + ReasonCache::iterator Cached = Cache.find(T.getAsOpaquePtr()); + if (Cached != Cache.end()) { + return Cached->second; + } + + // Check if we have a type which we can recurse into + if (const clang::ArrayType *Array = T->getAsArrayTypeUnsafe()) { + if (hasEffectiveAnnotation(Array->getElementType())) { + AnnotationReason Reason = {Array->getElementType(), RK_ArrayElement, + nullptr}; + Cache[Key] = Reason; + return Reason; + } + } + + // Recurse into Base classes + if (const CXXRecordDecl *Declaration = T->getAsCXXRecordDecl()) { + if (Declaration->hasDefinition()) { + Declaration = Declaration->getDefinition(); + + for (const CXXBaseSpecifier &Base : Declaration->bases()) { + if (hasEffectiveAnnotation(Base.getType())) { + AnnotationReason Reason = {Base.getType(), RK_BaseClass, nullptr}; + Cache[Key] = Reason; + return Reason; + } + } + + // Recurse into members + for (const FieldDecl *Field : Declaration->fields()) { + if (hasEffectiveAnnotation(Field->getType())) { + AnnotationReason Reason = {Field->getType(), RK_Field, Field}; + Cache[Key] = Reason; + return Reason; + } + } + + // Recurse into template arguments if the annotation + // MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS is present + if (MozChecker::hasCustomAnnotation( + Declaration, "moz_inherit_type_annotations_from_template_args")) { + const ClassTemplateSpecializationDecl *Spec = + dyn_cast<ClassTemplateSpecializationDecl>(Declaration); + if (Spec) { + const TemplateArgumentList &Args = Spec->getTemplateArgs(); + + AnnotationReason Reason = tmplArgAnnotationReason(Args.asArray()); + if (Reason.Kind != RK_None) { + Cache[Key] = Reason; + return Reason; + } + } + } + } + } + + AnnotationReason Reason = {QualType(), RK_None, nullptr}; + Cache[Key] = Reason; + return Reason; +} + +CustomTypeAnnotation::AnnotationReason +CustomTypeAnnotation::tmplArgAnnotationReason(ArrayRef<TemplateArgument> Args) { + for (const TemplateArgument &Arg : Args) { + if (Arg.getKind() == TemplateArgument::Type) { + QualType Type = Arg.getAsType(); + if (hasEffectiveAnnotation(Type)) { + AnnotationReason Reason = {Type, RK_TemplateInherited, nullptr}; + return Reason; + } + } else if (Arg.getKind() == TemplateArgument::Pack) { + AnnotationReason Reason = tmplArgAnnotationReason(Arg.getPackAsArray()); + if (Reason.Kind != RK_None) { + return Reason; + } + } + } + + AnnotationReason Reason = {QualType(), RK_None, nullptr}; + return Reason; +} + +bool isPlacementNew(const CXXNewExpr *Expression) { + // Regular new expressions aren't placement new + if (Expression->getNumPlacementArgs() == 0) + return false; + const FunctionDecl *Declaration = Expression->getOperatorNew(); + if (Declaration && MozChecker::hasCustomAnnotation(Declaration, + "moz_heap_allocator")) { + return false; + } + return true; +} + +DiagnosticsMatcher::DiagnosticsMatcher() { + AstMatcher.addMatcher(varDecl().bind("node"), &Scope); + AstMatcher.addMatcher(cxxNewExpr().bind("node"), &Scope); + AstMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &Scope); + AstMatcher.addMatcher( + callExpr(callee(functionDecl(heapAllocator()))).bind("node"), + &Scope); + AstMatcher.addMatcher(parmVarDecl().bind("parm_vardecl"), &Scope); + + AstMatcher.addMatcher( + callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()), + anyOf(hasDescendant( + binaryOperator( + allOf(binaryArithmeticOperator(), + hasLHS(hasDescendant(declRefExpr())), + hasRHS(hasDescendant(declRefExpr())))) + .bind("node")), + hasDescendant( + unaryOperator( + allOf(unaryArithmeticOperator(), + hasUnaryOperand(allOf( + hasType(builtinType()), + anyOf(hasDescendant(declRefExpr()), + declRefExpr()))))) + .bind("node"))))) + .bind("call"), + &ArithmeticArg); + AstMatcher.addMatcher( + cxxConstructExpr( + allOf(hasDeclaration(noArithmeticExprInArgs()), + anyOf(hasDescendant( + binaryOperator( + allOf(binaryArithmeticOperator(), + hasLHS(hasDescendant(declRefExpr())), + hasRHS(hasDescendant(declRefExpr())))) + .bind("node")), + hasDescendant( + unaryOperator( + allOf(unaryArithmeticOperator(), + hasUnaryOperand(allOf( + hasType(builtinType()), + anyOf(hasDescendant(declRefExpr()), + declRefExpr()))))) + .bind("node"))))) + .bind("call"), + &ArithmeticArg); + + AstMatcher.addMatcher(cxxRecordDecl(hasTrivialCtorDtor()).bind("node"), + &TrivialCtorDtor); + + AstMatcher.addMatcher( + binaryOperator( + allOf(binaryEqualityOperator(), + hasLHS(hasIgnoringParenImpCasts( + declRefExpr(hasType(qualType((isFloat())))).bind("lhs"))), + hasRHS(hasIgnoringParenImpCasts( + declRefExpr(hasType(qualType((isFloat())))).bind("rhs"))), + unless(anyOf(isInSystemHeader(), isInWhitelistForNaNExpr())))) + .bind("node"), + &NaNExpr); + + // First, look for direct parents of the MemberExpr. + AstMatcher.addMatcher( + callExpr( + callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")), + hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr())) + .bind("member"))) + .bind("node"), + &NoAddRefReleaseOnReturn); + // Then, look for MemberExpr that need to be casted to the right type using + // an intermediary CastExpr before we get to the CallExpr. + AstMatcher.addMatcher( + callExpr( + callee(functionDecl(hasNoAddRefReleaseOnReturnAttr()).bind("func")), + hasParent(castExpr( + hasParent(memberExpr(isAddRefOrRelease(), hasParent(callExpr())) + .bind("member"))))) + .bind("node"), + &NoAddRefReleaseOnReturn); + + // We want to reject any code which captures a pointer to an object of a + // refcounted type, and then lets that value escape. As a primitive analysis, + // we reject any occurances of the lambda as a template parameter to a class + // (which could allow it to escape), as well as any presence of such a lambda + // in a return value (either from lambdas, or in c++14, auto functions). + // + // We check these lambdas' capture lists for raw pointers to refcounted types. + AstMatcher.addMatcher( + functionDecl(returns(recordType(hasDeclaration(cxxRecordDecl( + isLambdaDecl()).bind("decl"))))), + &RefCountedInsideLambda); + AstMatcher.addMatcher(lambdaExpr().bind("lambdaExpr"), + &RefCountedInsideLambda); + AstMatcher.addMatcher( + classTemplateSpecializationDecl(hasAnyTemplateArgument(refersToType( + recordType(hasDeclaration(cxxRecordDecl( + isLambdaDecl()).bind("decl")))))), + &RefCountedInsideLambda); + + // Older clang versions such as the ones used on the infra recognize these + // conversions as 'operator _Bool', but newer clang versions recognize these + // as 'operator bool'. + AstMatcher.addMatcher( + cxxMethodDecl(anyOf(hasName("operator bool"), hasName("operator _Bool"))) + .bind("node"), + &ExplicitOperatorBool); + + AstMatcher.addMatcher(cxxRecordDecl().bind("decl"), &NoDuplicateRefCntMember); + + AstMatcher.addMatcher( + classTemplateSpecializationDecl( + allOf(hasAnyTemplateArgument(refersToType(hasVTable())), + hasNeedsNoVTableTypeAttr())) + .bind("node"), + &NeedsNoVTableType); + + // Handle non-mem-movable template specializations + AstMatcher.addMatcher( + classTemplateSpecializationDecl( + allOf(needsMemMovableTemplateArg(), + hasAnyTemplateArgument(refersToType(isNonMemMovable())))) + .bind("specialization"), + &NonMemMovableTemplateArg); + + // Handle non-mem-movable members + AstMatcher.addMatcher( + cxxRecordDecl(needsMemMovableMembers()) + .bind("decl"), + &NonMemMovableMember); + + AstMatcher.addMatcher(cxxConstructorDecl(isInterestingImplicitCtor(), + ofClass(allOf(isConcreteClass(), + decl().bind("class"))), + unless(isMarkedImplicit())) + .bind("ctor"), + &ExplicitImplicit); + + AstMatcher.addMatcher(varDecl(hasType(autoNonAutoableType())).bind("node"), + &NoAutoType); + + AstMatcher.addMatcher( + cxxConstructorDecl(isExplicitMoveConstructor()).bind("node"), + &NoExplicitMoveConstructor); + + AstMatcher.addMatcher( + cxxConstructExpr( + hasDeclaration(cxxConstructorDecl(isCompilerProvidedCopyConstructor(), + ofClass(hasRefCntMember())))) + .bind("node"), + &RefCountedCopyConstructor); + + AstMatcher.addMatcher( + callExpr(isAssertAssignmentTestFunc()).bind("funcCall"), + &AssertAttribution); + + AstMatcher.addMatcher(varDecl(hasType(isRefPtr())).bind("decl"), + &KungFuDeathGrip); + + AstMatcher.addMatcher( + callExpr(isSnprintfLikeFunc(), + allOf(hasArgument(0, ignoringParenImpCasts(declRefExpr().bind("buffer"))), + anyOf(hasArgument(1, sizeOfExpr(hasIgnoringParenImpCasts(declRefExpr().bind("size")))), + hasArgument(1, integerLiteral().bind("immediate")), + hasArgument(1, declRefExpr(to(varDecl(hasType(isConstQualified()), + hasInitializer(integerLiteral().bind("constant"))))))))) + .bind("funcCall"), + &SprintfLiteral + ); + + AstMatcher.addMatcher(cxxRecordDecl(hasBaseClasses()).bind("class"), + &OverrideBaseCall); + + AstMatcher.addMatcher( + cxxMethodDecl(isNonVirtual(), isRequiredBaseMethod()).bind("method"), + &OverrideBaseCallUsage); + + AstMatcher.addMatcher( + functionDecl(anyOf(allOf(isDefinition(), + hasAncestor(classTemplateSpecializationDecl() + .bind("spec"))), + isDefinition())) + .bind("func"), + &NonParamInsideFunctionDecl); + AstMatcher.addMatcher( + lambdaExpr().bind("lambda"), + &NonParamInsideFunctionDecl); +} + +// These enum variants determine whether an allocation has occured in the code. +enum AllocationVariety { + AV_None, + AV_Global, + AV_Automatic, + AV_Temporary, + AV_Heap, +}; + +// XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it +// probably will be used at some point in the future, in order to produce better +// error messages. +typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *> + AutomaticTemporaryMap; +AutomaticTemporaryMap AutomaticTemporaries; + +void DiagnosticsMatcher::ScopeChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + + // There are a variety of different reasons why something could be allocated + AllocationVariety Variety = AV_None; + SourceLocation Loc; + QualType T; + + if (const ParmVarDecl *D = + Result.Nodes.getNodeAs<ParmVarDecl>("parm_vardecl")) { + if (D->hasUnparsedDefaultArg() || D->hasUninstantiatedDefaultArg()) { + return; + } + if (const Expr *Default = D->getDefaultArg()) { + if (const MaterializeTemporaryExpr *E = + dyn_cast<MaterializeTemporaryExpr>(Default)) { + // We have just found a ParmVarDecl which has, as its default argument, + // a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as + // automatic, by adding it to the AutomaticTemporaryMap. + // Reporting on this type will occur when the MaterializeTemporaryExpr + // is matched against. + AutomaticTemporaries[E] = D; + } + } + return; + } + + // Determine the type of allocation which we detected + if (const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node")) { + if (D->hasGlobalStorage()) { + Variety = AV_Global; + } else { + Variety = AV_Automatic; + } + T = D->getType(); + Loc = D->getLocStart(); + } else if (const CXXNewExpr *E = Result.Nodes.getNodeAs<CXXNewExpr>("node")) { + // New allocates things on the heap. + // We don't consider placement new to do anything, as it doesn't actually + // allocate the storage, and thus gives us no useful information. + if (!isPlacementNew(E)) { + Variety = AV_Heap; + T = E->getAllocatedType(); + Loc = E->getLocStart(); + } + } else if (const MaterializeTemporaryExpr *E = + Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("node")) { + // Temporaries can actually have varying storage durations, due to temporary + // lifetime extension. We consider the allocation variety of this temporary + // to be the same as the allocation variety of its lifetime. + + // XXX We maybe should mark these lifetimes as being due to a temporary + // which has had its lifetime extended, to improve the error messages. + switch (E->getStorageDuration()) { + case SD_FullExpression: { + // Check if this temporary is allocated as a default argument! + // if it is, we want to pretend that it is automatic. + AutomaticTemporaryMap::iterator AutomaticTemporary = + AutomaticTemporaries.find(E); + if (AutomaticTemporary != AutomaticTemporaries.end()) { + Variety = AV_Automatic; + } else { + Variety = AV_Temporary; + } + } break; + case SD_Automatic: + Variety = AV_Automatic; + break; + case SD_Thread: + case SD_Static: + Variety = AV_Global; + break; + case SD_Dynamic: + assert(false && "I don't think that this ever should occur..."); + Variety = AV_Heap; + break; + } + T = E->getType().getUnqualifiedType(); + Loc = E->getLocStart(); + } else if (const CallExpr *E = Result.Nodes.getNodeAs<CallExpr>("node")) { + T = E->getType()->getPointeeType(); + if (!T.isNull()) { + // This will always allocate on the heap, as the heapAllocator() check + // was made in the matcher + Variety = AV_Heap; + Loc = E->getLocStart(); + } + } + + // Error messages for incorrect allocations. + unsigned StackID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 only valid on the stack"); + unsigned GlobalID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 only valid as global"); + unsigned HeapID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 only valid on the heap"); + unsigned NonHeapID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 is not valid on the heap"); + unsigned NonTemporaryID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "variable of type %0 is not valid in a temporary"); + + unsigned StackNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "value incorrectly allocated in an automatic variable"); + unsigned GlobalNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "value incorrectly allocated in a global variable"); + unsigned HeapNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "value incorrectly allocated on the heap"); + unsigned TemporaryNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "value incorrectly allocated in a temporary"); + + // Report errors depending on the annotations on the input types. + switch (Variety) { + case AV_None: + return; + + case AV_Global: + StackClass.reportErrorIfPresent(Diag, T, Loc, StackID, GlobalNoteID); + HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, GlobalNoteID); + break; + + case AV_Automatic: + GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, StackNoteID); + HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, StackNoteID); + break; + + case AV_Temporary: + GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, TemporaryNoteID); + HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, TemporaryNoteID); + NonTemporaryClass.reportErrorIfPresent(Diag, T, Loc, NonTemporaryID, + TemporaryNoteID); + break; + + case AV_Heap: + GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, HeapNoteID); + StackClass.reportErrorIfPresent(Diag, T, Loc, StackID, HeapNoteID); + NonHeapClass.reportErrorIfPresent(Diag, T, Loc, NonHeapID, HeapNoteID); + break; + } +} + +void DiagnosticsMatcher::ArithmeticArgChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "cannot pass an arithmetic expression of built-in types to %0"); + const Expr *Expression = Result.Nodes.getNodeAs<Expr>("node"); + if (const CallExpr *Call = Result.Nodes.getNodeAs<CallExpr>("call")) { + Diag.Report(Expression->getLocStart(), ErrorID) << Call->getDirectCallee(); + } else if (const CXXConstructExpr *Ctr = + Result.Nodes.getNodeAs<CXXConstructExpr>("call")) { + Diag.Report(Expression->getLocStart(), ErrorID) << Ctr->getConstructor(); + } +} + +void DiagnosticsMatcher::TrivialCtorDtorChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "class %0 must have trivial constructors and destructors"); + const CXXRecordDecl *Node = Result.Nodes.getNodeAs<CXXRecordDecl>("node"); + + // We need to accept non-constexpr trivial constructors as well. This occurs + // when a struct contains pod members, which will not be initialized. As + // constexpr values are initialized, the constructor is non-constexpr. + bool BadCtor = !(Node->hasConstexprDefaultConstructor() || + Node->hasTrivialDefaultConstructor()); + bool BadDtor = !Node->hasTrivialDestructor(); + if (BadCtor || BadDtor) + Diag.Report(Node->getLocStart(), ErrorID) << Node; +} + +void DiagnosticsMatcher::NaNExprChecker::run( + const MatchFinder::MatchResult &Result) { + if (!Result.Context->getLangOpts().CPlusPlus) { + // mozilla::IsNaN is not usable in C, so there is no point in issuing these + // warnings. + return; + } + + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "comparing a floating point value to itself for " + "NaN checking can lead to incorrect results"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "consider using mozilla::IsNaN instead"); + const BinaryOperator *Expression = Result.Nodes.getNodeAs<BinaryOperator>( + "node"); + const DeclRefExpr *LHS = Result.Nodes.getNodeAs<DeclRefExpr>("lhs"); + const DeclRefExpr *RHS = Result.Nodes.getNodeAs<DeclRefExpr>("rhs"); + const ImplicitCastExpr *LHSExpr = dyn_cast<ImplicitCastExpr>( + Expression->getLHS()); + const ImplicitCastExpr *RHSExpr = dyn_cast<ImplicitCastExpr>( + Expression->getRHS()); + // The AST subtree that we are looking for will look like this: + // -BinaryOperator ==/!= + // |-ImplicitCastExpr LValueToRValue + // | |-DeclRefExpr + // |-ImplicitCastExpr LValueToRValue + // |-DeclRefExpr + // The check below ensures that we are dealing with the correct AST subtree + // shape, and + // also that both of the found DeclRefExpr's point to the same declaration. + if (LHS->getFoundDecl() == RHS->getFoundDecl() && LHSExpr && RHSExpr && + std::distance(LHSExpr->child_begin(), LHSExpr->child_end()) == 1 && + std::distance(RHSExpr->child_begin(), RHSExpr->child_end()) == 1 && + *LHSExpr->child_begin() == LHS && *RHSExpr->child_begin() == RHS) { + Diag.Report(Expression->getLocStart(), ErrorID); + Diag.Report(Expression->getLocStart(), NoteID); + } +} + +void DiagnosticsMatcher::NoAddRefReleaseOnReturnChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "%1 cannot be called on the return value of %0"); + const Stmt *Node = Result.Nodes.getNodeAs<Stmt>("node"); + const FunctionDecl *Func = Result.Nodes.getNodeAs<FunctionDecl>("func"); + const MemberExpr *Member = Result.Nodes.getNodeAs<MemberExpr>("member"); + const CXXMethodDecl *Method = + dyn_cast<CXXMethodDecl>(Member->getMemberDecl()); + + Diag.Report(Node->getLocStart(), ErrorID) << Func << Method; +} + +void DiagnosticsMatcher::RefCountedInsideLambdaChecker::run( + const MatchFinder::MatchResult &Result) { + Context = Result.Context; + static DenseSet<const CXXRecordDecl*> CheckedDecls; + + const CXXRecordDecl *Lambda = Result.Nodes.getNodeAs<CXXRecordDecl>("decl"); + + if (const LambdaExpr *OuterLambda = + Result.Nodes.getNodeAs<LambdaExpr>("lambdaExpr")) { + const CXXMethodDecl *OpCall = OuterLambda->getCallOperator(); + QualType ReturnTy = OpCall->getReturnType(); + if (const CXXRecordDecl *Record = ReturnTy->getAsCXXRecordDecl()) { + Lambda = Record; + } + } + + if (!Lambda || !Lambda->isLambda()) { + return; + } + + // Don't report errors on the same declarations more than once. + if (CheckedDecls.count(Lambda)) { + return; + } + CheckedDecls.insert(Lambda); + + bool StrongRefToThisCaptured = false; + + for (const LambdaCapture& Capture : Lambda->captures()) { + // Check if any of the captures are ByRef. If they are, we have nothing to + // report, as it's OK to capture raw pointers to refcounted objects so long as + // the Lambda doesn't escape the current scope, which is required by ByRef + // captures already. + if (Capture.getCaptureKind() == LCK_ByRef) { + return; + } + + // Check if this capture is byvalue, and captures a strong reference to this. + // XXX: Do we want to make sure that this type which we are capturing is a "Smart Pointer" somehow? + if (!StrongRefToThisCaptured && + Capture.capturesVariable() && + Capture.getCaptureKind() == LCK_ByCopy) { + const VarDecl *Var = Capture.getCapturedVar(); + if (Var->hasInit()) { + const Stmt *Init = Var->getInit(); + + // Ignore single argument constructors, and trivial nodes. + while (true) { + auto NewInit = IgnoreImplicit(Init); + if (auto ConstructExpr = dyn_cast<CXXConstructExpr>(NewInit)) { + if (ConstructExpr->getNumArgs() == 1) { + NewInit = ConstructExpr->getArg(0); + } + } + if (Init == NewInit) { + break; + } + Init = NewInit; + } + + if (isa<CXXThisExpr>(Init)) { + StrongRefToThisCaptured = true; + } + } + } + } + + // Now we can go through and produce errors for any captured variables or this pointers. + for (const LambdaCapture& Capture : Lambda->captures()) { + if (Capture.capturesVariable()) { + QualType Pointee = Capture.getCapturedVar()->getType()->getPointeeType(); + + if (!Pointee.isNull() && isClassRefCounted(Pointee)) { + emitDiagnostics(Capture.getLocation(), Capture.getCapturedVar()->getName(), Pointee); + return; + } + } + + // The situation with captures of `this` is more complex. All captures of + // `this` look the same-ish (they are LCK_This). We want to complain about + // captures of `this` where `this` is a refcounted type, and the capture is + // actually used in the body of the lambda (if the capture isn't used, then + // we don't care, because it's only being captured in order to give access + // to private methods). + // + // In addition, we don't complain about this, even if it is used, if it was + // captured implicitly when the LambdaCaptureDefault was LCD_ByRef, as that + // expresses the intent that the lambda won't leave the enclosing scope. + bool ImplicitByRefDefaultedCapture = + Capture.isImplicit() && Lambda->getLambdaCaptureDefault() == LCD_ByRef; + if (Capture.capturesThis() && + !ImplicitByRefDefaultedCapture && + !StrongRefToThisCaptured) { + ThisVisitor V(*this); + bool NotAborted = V.TraverseDecl(const_cast<CXXMethodDecl *>(Lambda->getLambdaCallOperator())); + if (!NotAborted) { + return; + } + } + } +} + +void DiagnosticsMatcher::RefCountedInsideLambdaChecker::emitDiagnostics( + SourceLocation Loc, StringRef Name, QualType Type) { + DiagnosticsEngine& Diag = Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Refcounted variable '%0' of type %1 cannot be captured by a lambda"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Please consider using a smart pointer"); + + Diag.Report(Loc, ErrorID) << Name << Type; + Diag.Report(Loc, NoteID); +} + +bool DiagnosticsMatcher::RefCountedInsideLambdaChecker::ThisVisitor::VisitCXXThisExpr(CXXThisExpr *This) { + QualType Pointee = This->getType()->getPointeeType(); + if (!Pointee.isNull() && isClassRefCounted(Pointee)) { + Checker.emitDiagnostics(This->getLocStart(), "this", Pointee); + return false; + } + + return true; +} + +void DiagnosticsMatcher::ExplicitOperatorBoolChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "bad implicit conversion operator for %0"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "consider adding the explicit keyword to %0"); + const CXXConversionDecl *Method = + Result.Nodes.getNodeAs<CXXConversionDecl>("node"); + const CXXRecordDecl *Clazz = Method->getParent(); + + if (!Method->isExplicitSpecified() && + !MozChecker::hasCustomAnnotation(Method, "moz_implicit") && + !ASTIsInSystemHeader(Method->getASTContext(), *Method) && + isInterestingDeclForImplicitConversion(Method)) { + Diag.Report(Method->getLocStart(), ErrorID) << Clazz; + Diag.Report(Method->getLocStart(), NoteID) << "'operator bool'"; + } +} + +void DiagnosticsMatcher::NoDuplicateRefCntMemberChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + const CXXRecordDecl *D = Result.Nodes.getNodeAs<CXXRecordDecl>("decl"); + const FieldDecl *RefCntMember = getClassRefCntMember(D); + const FieldDecl *FoundRefCntBase = nullptr; + + if (!D->hasDefinition()) + return; + D = D->getDefinition(); + + // If we don't have an mRefCnt member, and we have less than 2 superclasses, + // we don't have to run this loop, as neither case will ever apply. + if (!RefCntMember && D->getNumBases() < 2) { + return; + } + + // Check every superclass for whether it has a base with a refcnt member, and + // warn for those which do + for (auto &Base : D->bases()) { + // Determine if this base class has an mRefCnt member + const FieldDecl *BaseRefCntMember = getBaseRefCntMember(Base.getType()); + + if (BaseRefCntMember) { + if (RefCntMember) { + // We have an mRefCnt, and superclass has an mRefCnt + unsigned Error = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Refcounted record %0 has multiple mRefCnt members"); + unsigned Note1 = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Superclass %0 also has an mRefCnt member"); + unsigned Note2 = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "Consider using the _INHERITED macros for AddRef and Release here"); + + Diag.Report(D->getLocStart(), Error) << D; + Diag.Report(BaseRefCntMember->getLocStart(), Note1) + << BaseRefCntMember->getParent(); + Diag.Report(RefCntMember->getLocStart(), Note2); + } + + if (FoundRefCntBase) { + unsigned Error = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Refcounted record %0 has multiple superclasses with mRefCnt members"); + unsigned Note = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "Superclass %0 has an mRefCnt member"); + + // superclass has mRefCnt, and another superclass also has an mRefCnt + Diag.Report(D->getLocStart(), Error) << D; + Diag.Report(BaseRefCntMember->getLocStart(), Note) + << BaseRefCntMember->getParent(); + Diag.Report(FoundRefCntBase->getLocStart(), Note) + << FoundRefCntBase->getParent(); + } + + // Record that we've found a base with a mRefCnt member + FoundRefCntBase = BaseRefCntMember; + } + } +} + +void DiagnosticsMatcher::NeedsNoVTableTypeChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "%0 cannot be instantiated because %1 has a VTable"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "bad instantiation of %0 requested here"); + + const ClassTemplateSpecializationDecl *Specialization = + Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("node"); + + // Get the offending template argument + QualType Offender; + const TemplateArgumentList &Args = + Specialization->getTemplateInstantiationArgs(); + for (unsigned i = 0; i < Args.size(); ++i) { + Offender = Args[i].getAsType(); + if (typeHasVTable(Offender)) { + break; + } + } + + Diag.Report(Specialization->getLocStart(), ErrorID) << Specialization + << Offender; + Diag.Report(Specialization->getPointOfInstantiation(), NoteID) + << Specialization; +} + +void DiagnosticsMatcher::NonMemMovableTemplateArgChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Cannot instantiate %0 with non-memmovable template argument %1"); + unsigned Note1ID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "instantiation of %0 requested here"); + + // Get the specialization + const ClassTemplateSpecializationDecl *Specialization = + Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("specialization"); + SourceLocation RequestLoc = Specialization->getPointOfInstantiation(); + + // Report an error for every template argument which is non-memmovable + const TemplateArgumentList &Args = + Specialization->getTemplateInstantiationArgs(); + for (unsigned i = 0; i < Args.size(); ++i) { + QualType ArgType = Args[i].getAsType(); + if (NonMemMovable.hasEffectiveAnnotation(ArgType)) { + Diag.Report(Specialization->getLocation(), ErrorID) << Specialization + << ArgType; + // XXX It would be really nice if we could get the instantiation stack + // information + // from Sema such that we could print a full template instantiation stack, + // however, + // it seems as though that information is thrown out by the time we get + // here so we + // can only report one level of template specialization (which in many + // cases won't + // be useful) + Diag.Report(RequestLoc, Note1ID) << Specialization; + NonMemMovable.dumpAnnotationReason(Diag, ArgType, RequestLoc); + } + } +} + +void DiagnosticsMatcher::NonMemMovableMemberChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "class %0 cannot have non-memmovable member %1 of type %2"); + + // Get the specialization + const CXXRecordDecl* Declaration = + Result.Nodes.getNodeAs<CXXRecordDecl>("decl"); + + // Report an error for every member which is non-memmovable + for (const FieldDecl *Field : Declaration->fields()) { + QualType Type = Field->getType(); + if (NonMemMovable.hasEffectiveAnnotation(Type)) { + Diag.Report(Field->getLocation(), ErrorID) << Declaration + << Field + << Type; + NonMemMovable.dumpAnnotationReason(Diag, Type, Declaration->getLocation()); + } + } +} + +void DiagnosticsMatcher::ExplicitImplicitChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "bad implicit conversion constructor for %0"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "consider adding the explicit keyword to the constructor"); + + // We've already checked everything in the matcher, so we just have to report + // the error. + + const CXXConstructorDecl *Ctor = + Result.Nodes.getNodeAs<CXXConstructorDecl>("ctor"); + const CXXRecordDecl *Declaration = + Result.Nodes.getNodeAs<CXXRecordDecl>("class"); + + Diag.Report(Ctor->getLocation(), ErrorID) << Declaration->getDeclName(); + Diag.Report(Ctor->getLocation(), NoteID); +} + +void DiagnosticsMatcher::NoAutoTypeChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Cannot use auto to declare a variable of type %0"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Please write out this type explicitly"); + + const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node"); + + Diag.Report(D->getLocation(), ErrorID) << D->getType(); + Diag.Report(D->getLocation(), NoteID); +} + +void DiagnosticsMatcher::NoExplicitMoveConstructorChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Move constructors may not be marked explicit"); + + // Everything we needed to know was checked in the matcher - we just report + // the error here + const CXXConstructorDecl *D = + Result.Nodes.getNodeAs<CXXConstructorDecl>("node"); + + Diag.Report(D->getLocation(), ErrorID); +} + +void DiagnosticsMatcher::RefCountedCopyConstructorChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Invalid use of compiler-provided copy constructor " + "on refcounted type"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "The default copy constructor also copies the " + "default mRefCnt property, leading to reference " + "count imbalance issues. Please provide your own " + "copy constructor which only copies the fields which " + "need to be copied"); + + // Everything we needed to know was checked in the matcher - we just report + // the error here + const CXXConstructExpr *E = Result.Nodes.getNodeAs<CXXConstructExpr>("node"); + + Diag.Report(E->getLocation(), ErrorID); + Diag.Report(E->getLocation(), NoteID); +} + +void DiagnosticsMatcher::AssertAssignmentChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned AssignInsteadOfComp = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Forbidden assignment in assert expression"); + const CallExpr *FuncCall = Result.Nodes.getNodeAs<CallExpr>("funcCall"); + + if (FuncCall && hasSideEffectAssignment(FuncCall)) { + Diag.Report(FuncCall->getLocStart(), AssignInsteadOfComp); + } +} + +void DiagnosticsMatcher::KungFuDeathGripChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Unused \"kungFuDeathGrip\" %0 objects constructed from %1 are prohibited"); + + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, + "Please switch all accesses to this %0 to go through '%1', or explicitly pass '%1' to `mozilla::Unused`"); + + const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("decl"); + if (D->isReferenced() || !D->hasLocalStorage() || !D->hasInit()) { + return; + } + + // Not interested in parameters. + if (isa<ImplicitParamDecl>(D) || isa<ParmVarDecl>(D)) { + return; + } + + const Expr *E = IgnoreImplicit(D->getInit()); + const CXXConstructExpr *CE = dyn_cast<CXXConstructExpr>(E); + if (CE && CE->getNumArgs() == 0) { + // We don't report an error when we construct and don't use a nsCOMPtr / + // nsRefPtr with no arguments. We don't report it because the error is not + // related to the current check. In the future it may be reported through a + // more generic mechanism. + return; + } + + // We don't want to look at the single argument conversion constructors + // which are inbetween the declaration and the actual object which we are + // assigning into the nsCOMPtr/RefPtr. To do this, we repeatedly + // IgnoreImplicit, then look at the expression. If it is one of these + // conversion constructors, we ignore it and continue to dig. + while ((CE = dyn_cast<CXXConstructExpr>(E)) && CE->getNumArgs() == 1) { + E = IgnoreImplicit(CE->getArg(0)); + } + + // We allow taking a kungFuDeathGrip of `this` because it cannot change + // beneath us, so calling directly through `this` is OK. This is the same + // for local variable declarations. + // + // We also don't complain about unused RefPtrs which are constructed from + // the return value of a new expression, as these are required in order to + // immediately destroy the value created (which was presumably created for + // its side effects), and are not used as a death grip. + if (isa<CXXThisExpr>(E) || isa<DeclRefExpr>(E) || isa<CXXNewExpr>(E)) { + return; + } + + // These types are assigned into nsCOMPtr and RefPtr for their side effects, + // and not as a kungFuDeathGrip. We don't want to consider RefPtr and nsCOMPtr + // types which are initialized with these types as errors. + const TagDecl *TD = E->getType()->getAsTagDecl(); + if (TD && TD->getIdentifier()) { + static const char *IgnoreTypes[] = { + "already_AddRefed", + "nsGetServiceByCID", + "nsGetServiceByCIDWithError", + "nsGetServiceByContractID", + "nsGetServiceByContractIDWithError", + "nsCreateInstanceByCID", + "nsCreateInstanceByContractID", + "nsCreateInstanceFromFactory", + }; + + for (uint32_t i = 0; i < sizeof(IgnoreTypes) / sizeof(IgnoreTypes[0]); ++i) { + if (TD->getName() == IgnoreTypes[i]) { + return; + } + } + } + + // Report the error + const char *ErrThing; + const char *NoteThing; + if (isa<MemberExpr>(E)) { + ErrThing = "members"; + NoteThing = "member"; + } else { + ErrThing = "temporary values"; + NoteThing = "value"; + } + + // We cannot provide the note if we don't have an initializer + Diag.Report(D->getLocStart(), ErrorID) << D->getType() << ErrThing; + Diag.Report(E->getLocStart(), NoteID) << NoteThing << getNameChecked(D); +} + +void DiagnosticsMatcher::SprintfLiteralChecker::run( + const MatchFinder::MatchResult &Result) { + if (!Result.Context->getLangOpts().CPlusPlus) { + // SprintfLiteral is not usable in C, so there is no point in issuing these + // warnings. + return; + } + + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Use %1 instead of %0 when writing into a character array."); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "This will prevent passing in the wrong size to %0 accidentally."); + + const CallExpr *D = Result.Nodes.getNodeAs<CallExpr>("funcCall"); + + StringRef Name = D->getDirectCallee()->getName(); + const char *Replacement; + if (Name == "snprintf") { + Replacement = "SprintfLiteral"; + } else { + assert(Name == "vsnprintf"); + Replacement = "VsprintfLiteral"; + } + + const DeclRefExpr *Buffer = Result.Nodes.getNodeAs<DeclRefExpr>("buffer"); + const DeclRefExpr *Size = Result.Nodes.getNodeAs<DeclRefExpr>("size"); + if (Size) { + // Match calls like snprintf(x, sizeof(x), ...). + if (Buffer->getFoundDecl() != Size->getFoundDecl()) { + return; + } + + Diag.Report(D->getLocStart(), ErrorID) << Name << Replacement; + Diag.Report(D->getLocStart(), NoteID) << Name; + return; + } + + const QualType QType = Buffer->getType(); + const ConstantArrayType *Type = dyn_cast<ConstantArrayType>(QType.getTypePtrOrNull()); + if (Type) { + // Match calls like snprintf(x, 100, ...), where x is int[100]; + const IntegerLiteral *Literal = Result.Nodes.getNodeAs<IntegerLiteral>("immediate"); + if (!Literal) { + // Match calls like: const int y = 100; snprintf(x, y, ...); + Literal = Result.Nodes.getNodeAs<IntegerLiteral>("constant"); + } + + if (Type->getSize().ule(Literal->getValue())) { + Diag.Report(D->getLocStart(), ErrorID) << Name << Replacement; + Diag.Report(D->getLocStart(), NoteID) << Name; + } + } +} + +bool DiagnosticsMatcher::OverrideBaseCallChecker::isRequiredBaseMethod( + const CXXMethodDecl *Method) { + return MozChecker::hasCustomAnnotation(Method, "moz_required_base_method"); +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::evaluateExpression( + const Stmt *StmtExpr, std::list<const CXXMethodDecl*> &MethodList) { + // Continue while we have methods in our list + if (!MethodList.size()) { + return; + } + + if (auto MemberFuncCall = dyn_cast<CXXMemberCallExpr>(StmtExpr)) { + if (auto Method = dyn_cast<CXXMethodDecl>( + MemberFuncCall->getDirectCallee())) { + findBaseMethodCall(Method, MethodList); + } + } + + for (auto S : StmtExpr->children()) { + if (S) { + evaluateExpression(S, MethodList); + } + } +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::getRequiredBaseMethod( + const CXXMethodDecl *Method, + std::list<const CXXMethodDecl*>& MethodsList) { + + if (isRequiredBaseMethod(Method)) { + MethodsList.push_back(Method); + } else { + // Loop through all it's base methods. + for (auto BaseMethod = Method->begin_overridden_methods(); + BaseMethod != Method->end_overridden_methods(); BaseMethod++) { + getRequiredBaseMethod(*BaseMethod, MethodsList); + } + } +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::findBaseMethodCall( + const CXXMethodDecl* Method, + std::list<const CXXMethodDecl*>& MethodsList) { + + MethodsList.remove(Method); + // Loop also through all it's base methods; + for (auto BaseMethod = Method->begin_overridden_methods(); + BaseMethod != Method->end_overridden_methods(); BaseMethod++) { + findBaseMethodCall(*BaseMethod, MethodsList); + } +} + +void DiagnosticsMatcher::OverrideBaseCallChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned OverrideBaseCallCheckID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "Method %0 must be called in all overrides, but is not called in " + "this override defined for class %1"); + const CXXRecordDecl *Decl = Result.Nodes.getNodeAs<CXXRecordDecl>("class"); + + // Loop through the methods and look for the ones that are overridden. + for (auto Method : Decl->methods()) { + // If this method doesn't override other methods or it doesn't have a body, + // continue to the next declaration. + if (!Method->size_overridden_methods() || !Method->hasBody()) { + continue; + } + + // Preferred the usage of list instead of vector in order to avoid + // calling erase-remove when deleting items + std::list<const CXXMethodDecl*> MethodsList; + // For each overridden method push it to a list if it meets our + // criteria + for (auto BaseMethod = Method->begin_overridden_methods(); + BaseMethod != Method->end_overridden_methods(); BaseMethod++) { + getRequiredBaseMethod(*BaseMethod, MethodsList); + } + + // If no method has been found then no annotation was used + // so checking is not needed + if (!MethodsList.size()) { + continue; + } + + // Loop through the body of our method and search for calls to + // base methods + evaluateExpression(Method->getBody(), MethodsList); + + // If list is not empty pop up errors + for (auto BaseMethod : MethodsList) { + Diag.Report(Method->getLocation(), OverrideBaseCallCheckID) + << BaseMethod->getQualifiedNameAsString() + << Decl->getName(); + } + } +} + +void DiagnosticsMatcher::OverrideBaseCallUsageChecker::run( + const MatchFinder::MatchResult &Result) { + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, + "MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods"); + const CXXMethodDecl *Method = Result.Nodes.getNodeAs<CXXMethodDecl>("method"); + + Diag.Report(Method->getLocation(), ErrorID); +} + +void DiagnosticsMatcher::NonParamInsideFunctionDeclChecker::run( + const MatchFinder::MatchResult &Result) { + static DenseSet<const FunctionDecl*> CheckedFunctionDecls; + + const FunctionDecl *func = Result.Nodes.getNodeAs<FunctionDecl>("func"); + if (!func) { + const LambdaExpr *lambda = Result.Nodes.getNodeAs<LambdaExpr>("lambda"); + if (lambda) { + func = lambda->getCallOperator(); + } + } + + if (!func) { + return; + } + + if (func->isDeleted()) { + return; + } + + // Don't report errors on the same declarations more than once. + if (CheckedFunctionDecls.count(func)) { + return; + } + CheckedFunctionDecls.insert(func); + + const ClassTemplateSpecializationDecl *Spec = + Result.Nodes.getNodeAs<ClassTemplateSpecializationDecl>("spec"); + + DiagnosticsEngine &Diag = Result.Context->getDiagnostics(); + unsigned ErrorID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Error, "Type %0 must not be used as parameter"); + unsigned NoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "Please consider passing a const reference instead"); + unsigned SpecNoteID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, "The bad argument was passed to %0 here"); + + for (ParmVarDecl *p : func->parameters()) { + QualType T = p->getType().withoutLocalFastQualifiers(); + if (NonParam.hasEffectiveAnnotation(T)) { + Diag.Report(p->getLocation(), ErrorID) << T; + Diag.Report(p->getLocation(), NoteID); + + if (Spec) { + Diag.Report(Spec->getPointOfInstantiation(), SpecNoteID) + << Spec->getSpecializedTemplate(); + } + } + } +} + +class MozCheckAction : public PluginASTAction { +public: + ASTConsumerPtr CreateASTConsumer(CompilerInstance &CI, + StringRef FileName) override { +#if CLANG_VERSION_FULL >= 306 + std::unique_ptr<MozChecker> Checker(llvm::make_unique<MozChecker>(CI)); + ASTConsumerPtr Other(Checker->getOtherConsumer()); + + std::vector<ASTConsumerPtr> Consumers; + Consumers.push_back(std::move(Checker)); + Consumers.push_back(std::move(Other)); + return llvm::make_unique<MultiplexConsumer>(std::move(Consumers)); +#else + MozChecker *Checker = new MozChecker(CI); + + ASTConsumer *Consumers[] = {Checker, Checker->getOtherConsumer()}; + return new MultiplexConsumer(Consumers); +#endif + } + + bool ParseArgs(const CompilerInstance &CI, + const std::vector<std::string> &Args) override { + return true; + } +}; +} + +static FrontendPluginRegistry::Add<MozCheckAction> X("moz-check", + "check moz action"); +// Export the registry on Windows. +#ifdef LLVM_EXPORT_REGISTRY +LLVM_EXPORT_REGISTRY(FrontendPluginRegistry) +#endif diff --git a/build/clang-plugin/moz.build b/build/clang-plugin/moz.build new file mode 100644 index 000000000..d9b20cd90 --- /dev/null +++ b/build/clang-plugin/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; 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/. + +SharedLibrary('clang-plugin') + +SOURCES += [ + 'clang-plugin.cpp', +] + +DISABLE_STL_WRAPPING = True +NO_VISIBILITY_FLAGS = True + +# libc++ is required to build plugins against clang on OS X. +if CONFIG['HOST_OS_ARCH'] == 'Darwin': + CXXFLAGS += ['-stdlib=libc++'] + LDFLAGS += ['-lc++'] + +DIRS += [ + 'tests', +] diff --git a/build/clang-plugin/tests/Makefile.in b/build/clang-plugin/tests/Makefile.in new file mode 100644 index 000000000..74faf065c --- /dev/null +++ b/build/clang-plugin/tests/Makefile.in @@ -0,0 +1,18 @@ +# 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 without any warning flags, and with clang verify flag for a +# syntax-only build (no codegen), without a limit on the number of errors. +OS_CFLAGS := $(filter-out -W%,$(OS_CFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 -std=c11 +OS_CXXFLAGS := $(filter-out -W%,$(OS_CXXFLAGS)) -fsyntax-only -Xclang -verify -ferror-limit=0 + +include $(topsrcdir)/config/rules.mk + +target:: $(OBJS) + +# We don't actually build anything. +.PHONY: $(OBJS) + +# Don't actually build a library, since we don't actually build objects. +$(LIBRARY): EXPAND_LIBS_GEN=true diff --git a/build/clang-plugin/tests/NonParameterTestCases.h b/build/clang-plugin/tests/NonParameterTestCases.h new file mode 100644 index 000000000..d38a14d94 --- /dev/null +++ b/build/clang-plugin/tests/NonParameterTestCases.h @@ -0,0 +1,61 @@ +MAYBE_STATIC void raw(Param x) {} + +MAYBE_STATIC void raw(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void raw(HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +MAYBE_STATIC void const_(const NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC void const_(const HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +MAYBE_STATIC void array(NonParam x[]) {} +MAYBE_STATIC void array(NonParamUnion x[]) {} +MAYBE_STATIC void array(NonParamClass x[]) {} +MAYBE_STATIC void array(NonParamEnum x[]) {} +MAYBE_STATIC void array(NonParamEnumClass x[]) {} +MAYBE_STATIC void array(HasNonParamStruct x[]) {} +MAYBE_STATIC void array(HasNonParamUnion x[]) {} +MAYBE_STATIC void array(HasNonParamStructUnion x[]) {} + +MAYBE_STATIC void ptr(NonParam* x) {} +MAYBE_STATIC void ptr(NonParamUnion* x) {} +MAYBE_STATIC void ptr(NonParamClass* x) {} +MAYBE_STATIC void ptr(NonParamEnum* x) {} +MAYBE_STATIC void ptr(NonParamEnumClass* x) {} +MAYBE_STATIC void ptr(HasNonParamStruct* x) {} +MAYBE_STATIC void ptr(HasNonParamUnion* x) {} +MAYBE_STATIC void ptr(HasNonParamStructUnion* x) {} + +MAYBE_STATIC void ref(NonParam& x) {} +MAYBE_STATIC void ref(NonParamUnion& x) {} +MAYBE_STATIC void ref(NonParamClass& x) {} +MAYBE_STATIC void ref(NonParamEnum& x) {} +MAYBE_STATIC void ref(NonParamEnumClass& x) {} +MAYBE_STATIC void ref(HasNonParamStruct& x) {} +MAYBE_STATIC void ref(HasNonParamUnion& x) {} +MAYBE_STATIC void ref(HasNonParamStructUnion& x) {} + +MAYBE_STATIC void constRef(const NonParam& x) {} +MAYBE_STATIC void constRef(const NonParamUnion& x) {} +MAYBE_STATIC void constRef(const NonParamClass& x) {} +MAYBE_STATIC void constRef(const NonParamEnum& x) {} +MAYBE_STATIC void constRef(const NonParamEnumClass& x) {} +MAYBE_STATIC void constRef(const HasNonParamStruct& x) {} +MAYBE_STATIC void constRef(const HasNonParamUnion& x) {} +MAYBE_STATIC void constRef(const HasNonParamStructUnion& x) {} + +MAYBE_STATIC inline void inlineRaw(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamUnion x) {} //expected-error {{Type 'NonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamClass x) {} //expected-error {{Type 'NonParamClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamEnum x) {} //expected-error {{Type 'NonParamEnum' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +MAYBE_STATIC inline void inlineRaw(NonParamEnumClass x) {} //expected-error {{Type 'NonParamEnumClass' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} diff --git a/build/clang-plugin/tests/TestAssertWithAssignment.cpp b/build/clang-plugin/tests/TestAssertWithAssignment.cpp new file mode 100644 index 000000000..f0f049e4a --- /dev/null +++ b/build/clang-plugin/tests/TestAssertWithAssignment.cpp @@ -0,0 +1,68 @@ +#include "mozilla/MacroArgs.h" + +static __attribute__((always_inline)) bool MOZ_AssertAssignmentTest(bool expr) { + return expr; +} + +#define MOZ_UNLIKELY(x) (__builtin_expect(!!(x), 0)) +#define MOZ_CRASH() do { } while(0) +#define MOZ_CHECK_ASSERT_ASSIGNMENT(expr) MOZ_AssertAssignmentTest(!!(expr)) + +#define MOZ_ASSERT_HELPER1(expr) \ + do { \ + if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \ + MOZ_CRASH();\ + } \ + } while(0) \ + +/* Now the two-argument form. */ +#define MOZ_ASSERT_HELPER2(expr, explain) \ + do { \ + if (MOZ_UNLIKELY(!MOZ_CHECK_ASSERT_ASSIGNMENT(expr))) { \ + MOZ_CRASH();\ + } \ + } while(0) \ + +#define MOZ_RELEASE_ASSERT_GLUE(a, b) a b +#define MOZ_RELEASE_ASSERT(...) \ + MOZ_RELEASE_ASSERT_GLUE( \ + MOZ_PASTE_PREFIX_AND_ARG_COUNT(MOZ_ASSERT_HELPER, __VA_ARGS__), \ + (__VA_ARGS__)) + +#define MOZ_ASSERT(...) MOZ_RELEASE_ASSERT(__VA_ARGS__) + +void FunctionTest(int p) { + MOZ_ASSERT(p = 1); // expected-error {{Forbidden assignment in assert expression}} +} + +void FunctionTest2(int p) { + MOZ_ASSERT(((p = 1))); // expected-error {{Forbidden assignment in assert expression}} +} + +void FunctionTest3(int p) { + MOZ_ASSERT(p != 3); +} + +class TestOverloading { + int value; +public: + explicit TestOverloading(int _val) : value(_val) {} + // different operators + explicit operator bool() const { return true; } + TestOverloading& operator=(const int _val) { value = _val; return *this; } + + int& GetInt() {return value;} +}; + +void TestOverloadingFunc() { + TestOverloading p(2); + int f; + + MOZ_ASSERT(p); + MOZ_ASSERT(p = 3); // expected-error {{Forbidden assignment in assert expression}} + MOZ_ASSERT(p, "p is not valid"); + MOZ_ASSERT(p = 3, "p different than 3"); // expected-error {{Forbidden assignment in assert expression}} + MOZ_ASSERT(p.GetInt() = 2); // expected-error {{Forbidden assignment in assert expression}} + MOZ_ASSERT(p.GetInt() == 2); + MOZ_ASSERT(p.GetInt() == 2, f = 3); +} diff --git a/build/clang-plugin/tests/TestBadImplicitConversionCtor.cpp b/build/clang-plugin/tests/TestBadImplicitConversionCtor.cpp new file mode 100644 index 000000000..ca2472582 --- /dev/null +++ b/build/clang-plugin/tests/TestBadImplicitConversionCtor.cpp @@ -0,0 +1,50 @@ +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +struct Foo { + Foo(int); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + Foo(int, char=0); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + Foo(...); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + template<class T> + Foo(float); // expected-error {{bad implicit conversion constructor for 'Foo'}} expected-note {{consider adding the explicit keyword to the constructor}} + Foo(int, unsigned); + Foo(Foo&); + Foo(const Foo&); + Foo(volatile Foo&); + Foo(const volatile Foo&); + Foo(Foo&&); + Foo(const Foo&&); + Foo(volatile Foo&&); + Foo(const volatile Foo&&); +}; + +struct Bar { + explicit Bar(int); + explicit Bar(int, char=0); + explicit Bar(...); +}; + +struct Baz { + MOZ_IMPLICIT Baz(int); + MOZ_IMPLICIT Baz(int, char=0); + MOZ_IMPLICIT Baz(...); +}; + +struct Barn { + Barn(int) = delete; + Barn(int, char=0) = delete; + Barn(...) = delete; +}; + +struct Abstract { + Abstract(int); + Abstract(int, char=0); + Abstract(...); + virtual void f() = 0; +}; + +template<class T> +struct Template { + Template(int); // expected-error {{bad implicit conversion constructor for 'Template'}} expected-note {{consider adding the explicit keyword to the constructor}} + template<class U> + Template(float); // expected-error {{bad implicit conversion constructor for 'Template'}} expected-note {{consider adding the explicit keyword to the constructor}} +}; diff --git a/build/clang-plugin/tests/TestCustomHeap.cpp b/build/clang-plugin/tests/TestCustomHeap.cpp new file mode 100644 index 000000000..9514ff2c4 --- /dev/null +++ b/build/clang-plugin/tests/TestCustomHeap.cpp @@ -0,0 +1,28 @@ +#define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class"))) +#define MOZ_HEAP_ALLOCATOR \ + _Pragma("GCC diagnostic push") \ + _Pragma("GCC diagnostic ignored \"-Wgcc-compat\"") \ + __attribute__((annotate("moz_heap_allocator"))) \ + _Pragma("GCC diagnostic pop") + +#include <stdlib.h> +#include <memory> + +struct MOZ_NONHEAP_CLASS X { +}; + +void *operator new(size_t x, int qual) MOZ_HEAP_ALLOCATOR { + return ::operator new(x); +} + +template <typename T> +T *customAlloc() MOZ_HEAP_ALLOCATOR { + T *arg = static_cast<T*>(malloc(sizeof(T))); + return new (arg) T(); +} + +template <typename T> +void misuseX(T q) { + X *foo = customAlloc<X>(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + X *foo2 = new (100) X(); // expected-error {{variable of type 'X' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} +} diff --git a/build/clang-plugin/tests/TestExplicitOperatorBool.cpp b/build/clang-plugin/tests/TestExplicitOperatorBool.cpp new file mode 100644 index 000000000..bc4b43a7d --- /dev/null +++ b/build/clang-plugin/tests/TestExplicitOperatorBool.cpp @@ -0,0 +1,11 @@ +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +struct Bad { + operator bool(); // expected-error {{bad implicit conversion operator for 'Bad'}} expected-note {{consider adding the explicit keyword to 'operator bool'}} +}; +struct Good { + explicit operator bool(); +}; +struct Okay { + MOZ_IMPLICIT operator bool(); +}; diff --git a/build/clang-plugin/tests/TestGlobalClass.cpp b/build/clang-plugin/tests/TestGlobalClass.cpp new file mode 100644 index 000000000..1825b9707 --- /dev/null +++ b/build/clang-plugin/tests/TestGlobalClass.cpp @@ -0,0 +1,52 @@ +#define MOZ_GLOBAL_CLASS __attribute__((annotate("moz_global_class"))) +#include <stddef.h> + +struct MOZ_GLOBAL_CLASS Global { + int i; + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template <class T> +struct MOZ_GLOBAL_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseGlobalClass(int len) { + Global notValid; // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}} + Global alsoNotValid[2]; // expected-error {{variable of type 'Global [2]' only valid as global}} expected-note {{'Global [2]' is a global type because it is an array of global type 'Global'}} expected-note {{value incorrectly allocated in an automatic variable}} + static Global valid; + static Global alsoValid[2]; + + gobble(¬Valid); + gobble(&valid); + gobble(&alsoValid[0]); + + gobble(new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + gobble(new Global[10]); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + gobble(len <= 5 ? &valid : new Global); // expected-error {{variable of type 'Global' only valid as global}} expected-note {{value incorrectly allocated on the heap}} + + char buffer[sizeof(Global)]; + gobble(new (buffer) Global); +} + +Global valid; +struct RandomClass { + Global nonstaticMember; // expected-note {{'RandomClass' is a global type because member 'nonstaticMember' is a global type 'Global'}} + static Global staticMember; +}; +struct MOZ_GLOBAL_CLASS RandomGlobalClass { + Global nonstaticMember; + static Global staticMember; +}; + +struct BadInherit : Global {}; // expected-note {{'BadInherit' is a global type because it inherits from a global type 'Global'}} +struct MOZ_GLOBAL_CLASS GoodInherit : Global {}; + +void misuseGlobalClassEvenMore(int len) { + BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}} + RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid as global}} expected-note {{value incorrectly allocated in an automatic variable}} +} diff --git a/build/clang-plugin/tests/TestHeapClass.cpp b/build/clang-plugin/tests/TestHeapClass.cpp new file mode 100644 index 000000000..36e762973 --- /dev/null +++ b/build/clang-plugin/tests/TestHeapClass.cpp @@ -0,0 +1,64 @@ +#define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class"))) +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +#include <stddef.h> + +struct MOZ_HEAP_CLASS Heap { + int i; + Heap() {} + MOZ_IMPLICIT Heap(int a) {} + Heap(int a, int b) {} + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template <class T> +struct MOZ_HEAP_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void gobbleref(const Heap&) { } + +void misuseHeapClass(int len) { + Heap invalid; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} + Heap alsoInvalid[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}} + static Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} + static Heap alsoInvalidStatic[2]; // expected-error {{variable of type 'Heap [2]' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} expected-note {{'Heap [2]' is a heap type because it is an array of heap type 'Heap'}} + + gobble(&invalid); + gobble(&invalidStatic); + gobble(&alsoInvalid[0]); + + gobbleref(Heap()); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(Heap(10, 20)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(Heap(10)); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(10); // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a temporary}} + + gobble(new Heap); + gobble(new Heap[10]); + gobble(new TemplateClass<int>); + gobble(len <= 5 ? &invalid : new Heap); + + char buffer[sizeof(Heap)]; + gobble(new (buffer) Heap); +} + +Heap invalidStatic; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} +struct RandomClass { + Heap nonstaticMember; // expected-note {{'RandomClass' is a heap type because member 'nonstaticMember' is a heap type 'Heap'}} + static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} +}; +struct MOZ_HEAP_CLASS RandomHeapClass { + Heap nonstaticMember; + static Heap staticMember; // expected-error {{variable of type 'Heap' only valid on the heap}} expected-note {{value incorrectly allocated in a global variable}} +}; + +struct BadInherit : Heap {}; // expected-note {{'BadInherit' is a heap type because it inherits from a heap type 'Heap'}} +struct MOZ_HEAP_CLASS GoodInherit : Heap {}; + +void useStuffWrongly() { + BadInherit i; // expected-error {{variable of type 'BadInherit' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} + RandomClass r; // expected-error {{variable of type 'RandomClass' only valid on the heap}} expected-note {{value incorrectly allocated in an automatic variable}} +} diff --git a/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp b/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp new file mode 100644 index 000000000..a46ff39fd --- /dev/null +++ b/build/clang-plugin/tests/TestInheritTypeAnnotationsFromTemplateArgs.cpp @@ -0,0 +1,46 @@ +#define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS \ + __attribute__((annotate("moz_inherit_type_annotations_from_template_args"))) +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable"))) +#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type"))) + +class Normal {}; +class MOZ_STACK_CLASS Stack {}; +class IndirectStack : Stack {}; // expected-note {{'IndirectStack' is a stack type because it inherits from a stack type 'Stack'}} +class ContainsStack { Stack m; }; // expected-note {{'ContainsStack' is a stack type because member 'm' is a stack type 'Stack'}} +class MOZ_NON_MEMMOVABLE Pointery {}; +class IndirectPointery : Pointery {}; // expected-note {{'IndirectPointery' is a non-memmove()able type because it inherits from a non-memmove()able type 'Pointery'}} +class ContainsPointery { Pointery m; }; // expected-note {{'ContainsPointery' is a non-memmove()able type because member 'm' is a non-memmove()able type 'Pointery'}} + +template<class T> +class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS Template {}; // expected-note-re 5 {{'Template<{{.*}}>' is a stack type because it has a template argument stack type '{{.*}}'}} expected-note-re 5 {{'Template<{{.*}}>' is a non-memmove()able type because it has a template argument non-memmove()able type '{{.*}}'}} +class IndirectTemplate : Template<Stack> {}; // expected-note {{'IndirectTemplate' is a stack type because it inherits from a stack type 'Template<Stack>'}} +class ContainsTemplate { Template<Stack> m; }; // expected-note {{'ContainsTemplate' is a stack type because member 'm' is a stack type 'Template<Stack>'}} + +static Template<Stack> a; // expected-error {{variable of type 'Template<Stack>' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static Template<IndirectStack> b; // expected-error {{variable of type 'Template<IndirectStack>' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static Template<ContainsStack> c; // expected-error {{variable of type 'Template<ContainsStack>' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static IndirectTemplate d; // expected-error {{variable of type 'IndirectTemplate' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static ContainsTemplate e; // expected-error {{variable of type 'ContainsTemplate' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +static Template<Normal> f; + +template<class T> +class MOZ_NEEDS_MEMMOVABLE_TYPE Mover { // expected-error-re 8 {{Cannot instantiate 'Mover<{{.*}}>' with non-memmovable template argument '{{.*}}'}} + char mForceInstantiation[sizeof(T)]; +}; +class IndirectTemplatePointery : Template<Pointery> {}; // expected-note {{'IndirectTemplatePointery' is a non-memmove()able type because it inherits from a non-memmove()able type 'Template<Pointery>'}} +class ContainsTemplatePointery { Template<Pointery> m; }; // expected-note {{'ContainsTemplatePointery' is a non-memmove()able type because member 'm' is a non-memmove()able type 'Template<Pointery>'}} + +static Mover<Template<Pointery>> n; // expected-note {{instantiation of 'Mover<Template<Pointery> >' requested here}} +static Mover<Template<IndirectPointery>> o; // expected-note {{instantiation of 'Mover<Template<IndirectPointery> >' requested here}} +static Mover<Template<ContainsPointery>> p; // expected-note {{instantiation of 'Mover<Template<ContainsPointery> >' requested here}} +static Mover<IndirectTemplatePointery> q; // expected-note {{instantiation of 'Mover<IndirectTemplatePointery>' requested here}} +static Mover<ContainsTemplatePointery> r; // expected-note {{instantiation of 'Mover<ContainsTemplatePointery>' requested here}} +static Mover<Template<Normal>> s; + +template<class T, class... Ts> +class MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS ManyTs {}; // expected-note-re 3 {{'ManyTs<{{.*}}>' is a non-memmove()able type because it has a template argument non-memmove()able type '{{.*}}'}} + +static Mover<ManyTs<Pointery>> t; // expected-note {{instantiation of 'Mover<ManyTs<Pointery> >' requested here}} +static Mover<ManyTs<Normal, Pointery>> u; // expected-note {{instantiation of 'Mover<ManyTs<Normal, Pointery> >' requested here}} +static Mover<ManyTs<Normal, Normal, Pointery>> v; // expected-note {{instantiation of 'Mover<ManyTs<Normal, Normal, Pointery> >' requested here}} diff --git a/build/clang-plugin/tests/TestKungFuDeathGrip.cpp b/build/clang-plugin/tests/TestKungFuDeathGrip.cpp new file mode 100644 index 000000000..0b94d8a88 --- /dev/null +++ b/build/clang-plugin/tests/TestKungFuDeathGrip.cpp @@ -0,0 +1,107 @@ +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +template <typename T> +class already_AddRefed { +public: + already_AddRefed(); + T* mPtr; +}; + +template <typename T> +class RefPtr { +public: + RefPtr(); + MOZ_IMPLICIT RefPtr(T* aIn); + MOZ_IMPLICIT RefPtr(already_AddRefed<T> aIn); + ~RefPtr(); + T* mPtr; +}; + +template <typename T> +class nsCOMPtr { +public: + nsCOMPtr(); + MOZ_IMPLICIT nsCOMPtr(T* aIn); + MOZ_IMPLICIT nsCOMPtr(already_AddRefed<T> aIn); + ~nsCOMPtr(); + T* mPtr; +}; + +class Type { +public: + static nsCOMPtr<Type> someStaticCOMPtr; + + void f(nsCOMPtr<Type> ignoredArgument, Type *param) { + nsCOMPtr<Type> never_referenced; + nsCOMPtr<Type> kfdg_t1(this); + nsCOMPtr<Type> kfdg_t2 = this; + + nsCOMPtr<Type> kfdg_m1(p); // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m1', or explicitly pass 'kfdg_m1' to `mozilla::Unused`}} + nsCOMPtr<Type> kfdg_m2 = p; // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m2', or explicitly pass 'kfdg_m2' to `mozilla::Unused`}} + nsCOMPtr<Type> kfdg_m3(p); + kfdg_m3.mPtr->f(nullptr, nullptr); + nsCOMPtr<Type> kfdg_m4 = p; + kfdg_m4.mPtr->f(nullptr, nullptr); + + nsCOMPtr<Type> kfdg_a1((already_AddRefed<Type>())); + nsCOMPtr<Type> kfdg_a2 = already_AddRefed<Type>(); + + nsCOMPtr<Type> kfdg_p1(param); + nsCOMPtr<Type> kfdg_p2 = param; + + + RefPtr<Type> never_referenced2; + RefPtr<Type> kfdg_t3(this); + RefPtr<Type> kfdg_t4 = this; + + RefPtr<Type> kfdg_m5(p); // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m5', or explicitly pass 'kfdg_m5' to `mozilla::Unused`}} + RefPtr<Type> kfdg_m6 = p; // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m6', or explicitly pass 'kfdg_m6' to `mozilla::Unused`}} + RefPtr<Type> kfdg_m7(p); + kfdg_m7.mPtr->f(nullptr, nullptr); + RefPtr<Type> kfdg_m8 = p; + kfdg_m8.mPtr->f(nullptr, nullptr); + + RefPtr<Type> kfdg_a3((already_AddRefed<Type>())); + RefPtr<Type> kfdg_a4 = already_AddRefed<Type>(); + + RefPtr<Type> kfdg_p3(param); + RefPtr<Type> kfdg_p4 = param; + } + + Type *p; +}; + +void f(nsCOMPtr<Type> ignoredArgument, Type *param) { + nsCOMPtr<Type> never_referenced; + Type t; + // Type *p = nullptr; + nsCOMPtr<Type> kfdg_m1(t.p); // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m1', or explicitly pass 'kfdg_m1' to `mozilla::Unused`}} + nsCOMPtr<Type> kfdg_m2 = t.p; // expected-error {{Unused "kungFuDeathGrip" 'nsCOMPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m2', or explicitly pass 'kfdg_m2' to `mozilla::Unused`}} + nsCOMPtr<Type> kfdg_m3(t.p); + kfdg_m3.mPtr->f(nullptr, nullptr); + nsCOMPtr<Type> kfdg_m4 = t.p; + kfdg_m4.mPtr->f(nullptr, nullptr); + + nsCOMPtr<Type> kfdg_a1((already_AddRefed<Type>())); + nsCOMPtr<Type> kfdg_a2 = already_AddRefed<Type>(); + + nsCOMPtr<Type> kfdg_p1(param); + nsCOMPtr<Type> kfdg_p2 = param; + + + RefPtr<Type> never_referenced2; + RefPtr<Type> kfdg_m5(t.p); // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m5', or explicitly pass 'kfdg_m5' to `mozilla::Unused`}} + RefPtr<Type> kfdg_m6 = t.p; // expected-error {{Unused "kungFuDeathGrip" 'RefPtr<Type>' objects constructed from members are prohibited}} expected-note {{Please switch all accesses to this member to go through 'kfdg_m6', or explicitly pass 'kfdg_m6' to `mozilla::Unused`}} + RefPtr<Type> kfdg_m7(t.p); + kfdg_m7.mPtr->f(nullptr, nullptr); + RefPtr<Type> kfdg_m8 = t.p; + kfdg_m8.mPtr->f(nullptr, nullptr); + + RefPtr<Type> kfdg_a3((already_AddRefed<Type>())); + RefPtr<Type> kfdg_a4 = already_AddRefed<Type>(); + + RefPtr<Type> kfdg_p3(param); + RefPtr<Type> kfdg_p4 = param; +} + +nsCOMPtr<Type> Type::someStaticCOMPtr(nullptr); diff --git a/build/clang-plugin/tests/TestMultipleAnnotations.cpp b/build/clang-plugin/tests/TestMultipleAnnotations.cpp new file mode 100644 index 000000000..aa927259d --- /dev/null +++ b/build/clang-plugin/tests/TestMultipleAnnotations.cpp @@ -0,0 +1,17 @@ +#define MOZ_MUST_USE_TYPE __attribute__((annotate("moz_must_use_type"))) +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) + +class MOZ_MUST_USE_TYPE MOZ_STACK_CLASS TestClass {}; + +TestClass foo; // expected-error {{variable of type 'TestClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} + +TestClass f() +{ + TestClass bar; + return bar; +} + +void g() +{ + f(); // expected-error {{Unused value of must-use type 'TestClass'}} +} diff --git a/build/clang-plugin/tests/TestMustOverride.cpp b/build/clang-plugin/tests/TestMustOverride.cpp new file mode 100644 index 000000000..8e053f6c2 --- /dev/null +++ b/build/clang-plugin/tests/TestMustOverride.cpp @@ -0,0 +1,63 @@ +#define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override"))) +// Ignore warnings not related to static analysis here +#pragma GCC diagnostic ignored "-Woverloaded-virtual" + +struct S { + virtual void f() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + virtual void g() MOZ_MUST_OVERRIDE; + virtual void h() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; +struct C : S { // expected-error {{'C' must override 'f'}} expected-error {{'C' must override 'h'}} + virtual void g() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + virtual void h(int); + void q() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; +struct D : C { // expected-error {{'D' must override 'g'}} expected-error {{'D' must override 'q'}} + virtual void f(); +}; + +struct Base { + virtual void VirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + void NonVirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + static void StaticMethod() MOZ_MUST_OVERRIDE; +}; + +struct DoesNotPropagate : Base { + virtual void VirtMethod(); + void NonVirtMethod(); + static void StaticMethod(); +}; + +struct Final : DoesNotPropagate { }; + +struct Propagates : Base { + virtual void VirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + void NonVirtMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + static void StaticMethod() MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} +}; + +struct FailsFinal : Propagates { }; // expected-error {{'FailsFinal' must override 'VirtMethod'}} expected-error {{'FailsFinal' must override 'NonVirtMethod'}} expected-error {{'FailsFinal' must override 'StaticMethod'}} + +struct WrongOverload : Base { // expected-error {{'WrongOverload' must override 'VirtMethod'}} expected-error {{'WrongOverload' must override 'NonVirtMethod'}} + virtual void VirtMethod() const; + void NonVirtMethod(int param); + static void StaticMethod(); +}; + +namespace A { namespace B { namespace C { + struct Param {}; + struct Base { + void f(Param p) MOZ_MUST_OVERRIDE; // expected-note {{function to override is here}} + }; +}}} + +struct Param {}; + +struct Derived : A::B::C::Base { + typedef A::B::C::Param Typedef; + void f(Typedef t); +}; + +struct BadDerived : A::B::C::Base { // expected-error {{'BadDerived' must override 'f'}} + void f(Param p); +}; diff --git a/build/clang-plugin/tests/TestMustUse.cpp b/build/clang-plugin/tests/TestMustUse.cpp new file mode 100644 index 000000000..7878a4cde --- /dev/null +++ b/build/clang-plugin/tests/TestMustUse.cpp @@ -0,0 +1,201 @@ +#define MOZ_MUST_USE_TYPE __attribute__((annotate("moz_must_use_type"))) + +struct Temporary { ~Temporary(); }; +class MOZ_MUST_USE_TYPE MustUse {}; +class MayUse {}; + +MustUse producesMustUse(); +MustUse *producesMustUsePointer(); +MustUse &producesMustUseRef(); + +MustUse producesMustUse(const Temporary& t); +MustUse *producesMustUsePointer(const Temporary& t); +MustUse &producesMustUseRef(const Temporary& t); + +MayUse producesMayUse(); +MayUse *producesMayUsePointer(); +MayUse &producesMayUseRef(); + +void use(MustUse*); +void use(MustUse&); +void use(MustUse&&); +void use(MayUse*); +void use(MayUse&); +void use(MayUse&&); +void use(bool); + +void foo() { + MustUse u; + + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + { + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } + if (true) { + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } else { + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } + + if(true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + else producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + if(true) producesMustUsePointer(); + else producesMustUsePointer(); + if(true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + else producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + if(true) producesMayUse(); + else producesMayUse(); + if(true) producesMayUsePointer(); + else producesMayUsePointer(); + if(true) producesMayUseRef(); + else producesMayUseRef(); + if(true) u = producesMustUse(); + else u = producesMustUse(); + if(true) u = producesMustUse(Temporary()); + else u = producesMustUse(Temporary()); + + while (true) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true) producesMustUsePointer(); + while (true) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true) producesMayUse(); + while (true) producesMayUsePointer(); + while (true) producesMayUseRef(); + while (true) u = producesMustUse(); + while (true) u = producesMustUse(Temporary()); + + do producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true); + do producesMustUsePointer(); + while (true); + do producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + while (true); + do producesMayUse(); + while (true); + do producesMayUsePointer(); + while (true); + do producesMayUseRef(); + while (true); + do u = producesMustUse(); + while (true); + do u = producesMustUse(Temporary()); + while (true); + + for (;;) producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;) producesMustUsePointer(); + for (;;) producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;) producesMayUse(); + for (;;) producesMayUsePointer(); + for (;;) producesMayUseRef(); + for (;;) u = producesMustUse(); + for (;;) u = producesMustUse(Temporary()); + + for (producesMustUse();;); // expected-error {{Unused value of must-use type 'MustUse'}} + for (producesMustUsePointer();;); + for (producesMustUseRef();;); // expected-error {{Unused value of must-use type 'MustUse'}} + for (producesMayUse();;); + for (producesMayUsePointer();;); + for (producesMayUseRef();;); + for (u = producesMustUse();;); + for (u = producesMustUse(Temporary());;); + + for (;;producesMustUse()); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;producesMustUsePointer()); + for (;;producesMustUseRef()); // expected-error {{Unused value of must-use type 'MustUse'}} + for (;;producesMayUse()); + for (;;producesMayUsePointer()); + for (;;producesMayUseRef()); + for (;;u = producesMustUse()); + for (;;u = producesMustUse(Temporary())); + + use((producesMustUse(), false)); // expected-error {{Unused value of must-use type 'MustUse'}} + use((producesMustUsePointer(), false)); + use((producesMustUseRef(), false)); // expected-error {{Unused value of must-use type 'MustUse'}} + use((producesMayUse(), false)); + use((producesMayUsePointer(), false)); + use((producesMayUseRef(), false)); + use((u = producesMustUse(), false)); + use((u = producesMustUse(Temporary()), false)); + + switch (1) { + case 1: + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + case 2: + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + case 3: + producesMustUsePointer(); + case 4: + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + case 5: + producesMayUse(); + case 6: + producesMayUsePointer(); + case 7: + producesMayUseRef(); + case 8: + u = producesMustUse(); + case 9: + u = producesMustUse(Temporary()); + default: + producesMustUse(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMustUsePointer(); + producesMustUseRef(); // expected-error {{Unused value of must-use type 'MustUse'}} + producesMayUse(); + producesMayUsePointer(); + producesMayUseRef(); + u = producesMustUse(); + u = producesMustUse(Temporary()); + } + + use(producesMustUse()); + use(producesMustUsePointer()); + use(producesMustUseRef()); + use(producesMayUse()); + use(producesMayUsePointer()); + use(producesMayUseRef()); + use(u = producesMustUse()); + use(u = producesMustUse(Temporary())); + + MustUse a = producesMustUse(); + MustUse *b = producesMustUsePointer(); + MustUse &c = producesMustUseRef(); + MayUse d = producesMayUse(); + MayUse *e = producesMayUsePointer(); + MayUse &f = producesMayUseRef(); + MustUse g = u = producesMustUse(); + MustUse h = u = producesMustUse(Temporary()); +} diff --git a/build/clang-plugin/tests/TestNANTestingExpr.cpp b/build/clang-plugin/tests/TestNANTestingExpr.cpp new file mode 100644 index 000000000..943577d4a --- /dev/null +++ b/build/clang-plugin/tests/TestNANTestingExpr.cpp @@ -0,0 +1,16 @@ +void test(bool x); +void foo() { + float f, f2; + typedef double mydouble; + mydouble d; + double d2; + test(f == f); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(d == d); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(f != f); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(d != d); // expected-error{{comparing a floating point value to itself for NaN checking can lead to incorrect results}} expected-note{{consider using mozilla::IsNaN instead}} + test(f != d); + test(d == (d - f)); + test(f == f2); + test(d == d2); + test(d + 1 == d); +} diff --git a/build/clang-plugin/tests/TestNANTestingExprC.c b/build/clang-plugin/tests/TestNANTestingExprC.c new file mode 100644 index 000000000..ab2fead22 --- /dev/null +++ b/build/clang-plugin/tests/TestNANTestingExprC.c @@ -0,0 +1,17 @@ +/* expected-no-diagnostics */ +void test(int x); +void foo() { + float f, f2; + typedef double mydouble; + mydouble d; + double d2; + test(f == f); + test(d == d); + test(f != f); + test(d != d); + test(f != d); + test(d == (d - f)); + test(f == f2); + test(d == d2); + test(d + 1 == d); +} diff --git a/build/clang-plugin/tests/TestNeedsNoVTableType.cpp b/build/clang-plugin/tests/TestNeedsNoVTableType.cpp new file mode 100644 index 000000000..531a1c82a --- /dev/null +++ b/build/clang-plugin/tests/TestNeedsNoVTableType.cpp @@ -0,0 +1,94 @@ +#define MOZ_NEEDS_NO_VTABLE_TYPE __attribute__((annotate("moz_needs_no_vtable_type"))) + +template <class T> +struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer { // expected-error {{'PickyConsumer<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer<G>' cannot be instantiated because 'G' has a VTable}} + T *m; +}; + +template <class T> +struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_A { // expected-error {{'PickyConsumer_A<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_A<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_A<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_A<G>' cannot be instantiated because 'G' has a VTable}} + T *m; +}; +template <class T> +struct PickyConsumerWrapper { + PickyConsumer_A<T> m; // expected-note {{bad instantiation of 'PickyConsumer_A<B>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<E>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<F>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_A<G>' requested here}} +}; + +template <class T> +struct MOZ_NEEDS_NO_VTABLE_TYPE PickyConsumer_B { // expected-error {{'PickyConsumer_B<B>' cannot be instantiated because 'B' has a VTable}} expected-error {{'PickyConsumer_B<E>' cannot be instantiated because 'E' has a VTable}} expected-error {{'PickyConsumer_B<F>' cannot be instantiated because 'F' has a VTable}} expected-error {{'PickyConsumer_B<G>' cannot be instantiated because 'G' has a VTable}} + T *m; +}; +template <class T> +struct PickyConsumerSubclass : PickyConsumer_B<T> {}; // expected-note {{bad instantiation of 'PickyConsumer_B<B>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<E>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<F>' requested here}} expected-note {{bad instantiation of 'PickyConsumer_B<G>' requested here}} + +template <class T> +struct NonPickyConsumer { + T *m; +}; + +struct A {}; +struct B : virtual A {}; +struct C : A {}; +struct D { + void d(); +}; +struct E { + virtual void e(); +}; +struct F : E { + virtual void e() final; +}; +struct G { + virtual void e() = 0; +}; + +void f() { + { + PickyConsumer<A> a1; + PickyConsumerWrapper<A> a2; + PickyConsumerSubclass<A> a3; + NonPickyConsumer<A> a4; + } + + { + PickyConsumer<B> a1; // expected-note {{bad instantiation of 'PickyConsumer<B>' requested here}} + PickyConsumerWrapper<B> a2; + PickyConsumerSubclass<B> a3; + NonPickyConsumer<B> a4; + } + + { + PickyConsumer<C> a1; + PickyConsumerWrapper<C> a2; + PickyConsumerSubclass<C> a3; + NonPickyConsumer<C> a4; + } + + { + PickyConsumer<D> a1; + PickyConsumerWrapper<D> a2; + PickyConsumerSubclass<D> a3; + NonPickyConsumer<D> a4; + } + + { + PickyConsumer<E> a1; // expected-note {{bad instantiation of 'PickyConsumer<E>' requested here}} + PickyConsumerWrapper<E> a2; + PickyConsumerSubclass<E> a3; + NonPickyConsumer<E> a4; + } + + { + PickyConsumer<F> a1; // expected-note {{bad instantiation of 'PickyConsumer<F>' requested here}} + PickyConsumerWrapper<F> a2; + PickyConsumerSubclass<F> a3; + NonPickyConsumer<F> a4; + } + + { + PickyConsumer<G> a1; // expected-note {{bad instantiation of 'PickyConsumer<G>' requested here}} + PickyConsumerWrapper<G> a2; + PickyConsumerSubclass<G> a3; + NonPickyConsumer<G> a4; + } +} diff --git a/build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp b/build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp new file mode 100644 index 000000000..2e1f83377 --- /dev/null +++ b/build/clang-plugin/tests/TestNoAddRefReleaseOnReturn.cpp @@ -0,0 +1,110 @@ +#define MOZ_NO_ADDREF_RELEASE_ON_RETURN __attribute__((annotate("moz_no_addref_release_on_return"))) + +struct Test { + void AddRef(); + void Release(); + void foo(); +}; + +struct TestD : Test {}; + +struct S { + Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +struct SD { + TestD* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + TestD& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + TestD h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +template<class T> +struct X { + T* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + T& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + T h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +template<class T> +struct SP { + T* operator->() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +}; + +Test* f() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +Test& g() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +Test h() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + +TestD* fd() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +TestD& gd() MOZ_NO_ADDREF_RELEASE_ON_RETURN; +TestD hd() MOZ_NO_ADDREF_RELEASE_ON_RETURN; + +void test() { + S s; + s.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + s.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + s.f()->foo(); + s.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + s.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + s.g().foo(); + s.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + s.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + s.h().foo(); + SD sd; + sd.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + sd.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + sd.f()->foo(); + sd.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + sd.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + sd.g().foo(); + sd.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + sd.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + sd.h().foo(); + X<Test> x; + x.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + x.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + x.f()->foo(); + x.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + x.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + x.g().foo(); + x.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + x.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + x.h().foo(); + X<TestD> xd; + xd.f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + xd.f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + xd.f()->foo(); + xd.g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + xd.g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + xd.g().foo(); + xd.h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + xd.h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + xd.h().foo(); + SP<Test> sp; + sp->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'operator->'}} + sp->Release(); // expected-error{{'Release' cannot be called on the return value of 'operator->'}} + sp->foo(); + SP<TestD> spd; + spd->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'operator->'}} + spd->Release(); // expected-error{{'Release' cannot be called on the return value of 'operator->'}} + spd->foo(); + f()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'f'}} + f()->Release(); // expected-error{{'Release' cannot be called on the return value of 'f'}} + f()->foo(); + g().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'g'}} + g().Release(); // expected-error{{'Release' cannot be called on the return value of 'g'}} + g().foo(); + h().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'h'}} + h().Release(); // expected-error{{'Release' cannot be called on the return value of 'h'}} + h().foo(); + fd()->AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'fd'}} + fd()->Release(); // expected-error{{'Release' cannot be called on the return value of 'fd'}} + fd()->foo(); + gd().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'gd'}} + gd().Release(); // expected-error{{'Release' cannot be called on the return value of 'gd'}} + gd().foo(); + hd().AddRef(); // expected-error{{'AddRef' cannot be called on the return value of 'hd'}} + hd().Release(); // expected-error{{'Release' cannot be called on the return value of 'hd'}} + hd().foo(); +} diff --git a/build/clang-plugin/tests/TestNoArithmeticExprInArgument.cpp b/build/clang-plugin/tests/TestNoArithmeticExprInArgument.cpp new file mode 100644 index 000000000..d147b1701 --- /dev/null +++ b/build/clang-plugin/tests/TestNoArithmeticExprInArgument.cpp @@ -0,0 +1,32 @@ +#define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT __attribute__((annotate("moz_no_arith_expr_in_arg"))) + +struct X { + explicit X(int) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT; + void baz(int) MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT; +}; + +int operator+(int, X); +int operator+(X, int); +int operator++(X); + +void badArithmeticsInArgs() { + int a = 1; + typedef int myint; + myint b = 2; + X goodObj1(a); + goodObj1.baz(b); + X badObj1(a + b); // expected-error{{cannot pass an arithmetic expression of built-in types to 'X'}} + X badObj2 = X(a ? 0 : ++a); // expected-error{{cannot pass an arithmetic expression of built-in types to 'X'}} + X badObj3(~a); // expected-error{{cannot pass an arithmetic expression of built-in types to 'X'}} + badObj1.baz(a - 1 - b); // expected-error{{cannot pass an arithmetic expression of built-in types to 'baz'}} + badObj1.baz(++a); // expected-error{{cannot pass an arithmetic expression of built-in types to 'baz'}} + badObj1.baz(a++); // expected-error{{cannot pass an arithmetic expression of built-in types to 'baz'}} + badObj1.baz(a || b); + badObj1.baz(a + goodObj1); + badObj1.baz(goodObj1 + a); + badObj1.baz(++goodObj1); + badObj1.baz(-1); + badObj1.baz(-1.0); + badObj1.baz(1 + 2); + badObj1.baz(1 << (sizeof(int)/2)); +} diff --git a/build/clang-plugin/tests/TestNoAutoType.cpp b/build/clang-plugin/tests/TestNoAutoType.cpp new file mode 100644 index 000000000..6c6e65f24 --- /dev/null +++ b/build/clang-plugin/tests/TestNoAutoType.cpp @@ -0,0 +1,41 @@ +#define MOZ_NON_AUTOABLE __attribute__((annotate("moz_non_autoable"))) + +template<class T> +struct MOZ_NON_AUTOABLE ExplicitTypeTemplate {}; +struct MOZ_NON_AUTOABLE ExplicitType {}; +struct NonExplicitType {}; + +void f() { + { + ExplicitType a; + auto b = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType'}} expected-note {{Please write out this type explicitly}} + auto &br = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType &'}} expected-note {{Please write out this type explicitly}} + const auto &brc = a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitType &'}} expected-note {{Please write out this type explicitly}} + auto *bp = &a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType *'}} expected-note {{Please write out this type explicitly}} + const auto *bpc = &a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitType *'}} expected-note {{Please write out this type explicitly}} + } + + { + ExplicitTypeTemplate<int> a; + auto b = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitTypeTemplate<int>'}} expected-note {{Please write out this type explicitly}} + auto &br = a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitTypeTemplate<int> &'}} expected-note {{Please write out this type explicitly}} + const auto &brc = a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitTypeTemplate<int> &'}} expected-note {{Please write out this type explicitly}} + auto *bp = &a; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitTypeTemplate<int> *'}} expected-note {{Please write out this type explicitly}} + const auto *bpc = &a; // expected-error {{Cannot use auto to declare a variable of type 'const ExplicitTypeTemplate<int> *'}} expected-note {{Please write out this type explicitly}} + } + + { + NonExplicitType c; + auto d = c; + auto &dr = c; + const auto &drc = c; + auto *dp = &c; + const auto *dpc = &c; + } +} + +ExplicitType A; +auto B = A; // expected-error {{Cannot use auto to declare a variable of type 'ExplicitType'}} expected-note {{Please write out this type explicitly}} + +NonExplicitType C; +auto D = C; diff --git a/build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp b/build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp new file mode 100644 index 000000000..ff68e4fc7 --- /dev/null +++ b/build/clang-plugin/tests/TestNoDuplicateRefCntMember.cpp @@ -0,0 +1,49 @@ +class C1 {}; + +class RC1 { +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note 2 {{Superclass 'RC1' also has an mRefCnt member}} expected-note 3 {{Superclass 'RC1' has an mRefCnt member}} +}; + +class RC2 : public RC1 { // expected-error {{Refcounted record 'RC2' has multiple mRefCnt members}} +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note {{Consider using the _INHERITED macros for AddRef and Release here}} +}; + +class C2 : public RC1 {}; + +class RC3 : public RC1 {}; + +class RC4 : public RC3, public C2 {}; // expected-error {{Refcounted record 'RC4' has multiple superclasses with mRefCnt members}} + +class RC5 : public RC1 {}; + +class RC6 : public C1, public RC5 { // expected-error {{Refcounted record 'RC6' has multiple mRefCnt members}} +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note {{Consider using the _INHERITED macros for AddRef and Release here}} +}; + +class Predecl; + +class OtherRC { +public: + virtual void AddRef(); + virtual void Release(); + +private: + int mRefCnt; // expected-note {{Superclass 'OtherRC' has an mRefCnt member}} +}; + +class MultRCSuper : public RC1, public OtherRC {}; // expected-error {{Refcounted record 'MultRCSuper' has multiple superclasses with mRefCnt members}} diff --git a/build/clang-plugin/tests/TestNoExplicitMoveConstructor.cpp b/build/clang-plugin/tests/TestNoExplicitMoveConstructor.cpp new file mode 100644 index 000000000..5aea6b1a7 --- /dev/null +++ b/build/clang-plugin/tests/TestNoExplicitMoveConstructor.cpp @@ -0,0 +1,25 @@ +class Foo { + Foo(Foo&& f); +}; + +class Bar { + explicit Bar(Bar&& f); // expected-error {{Move constructors may not be marked explicit}} +}; + +class Baz { + template<typename T> + explicit Baz(T&& f) {}; +}; + +class Quxx { + Quxx(); + Quxx(Quxx& q) = delete; + template<typename T> + explicit Quxx(T&& f) {}; +}; + +void f() { + // Move a quxx into a quxx! (This speciailizes Quxx's constructor to look like + // a move constructor - to make sure it doesn't trigger) + Quxx(Quxx()); +} diff --git a/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp new file mode 100644 index 000000000..2c74a34ba --- /dev/null +++ b/build/clang-plugin/tests/TestNoRefcountedInsideLambdas.cpp @@ -0,0 +1,651 @@ +#include <functional> +#include "mozilla/Function.h" +#define MOZ_STRONG_REF __attribute__((annotate("moz_strong_ref"))) + +struct RefCountedBase { + void AddRef(); + void Release(); +}; + +template <class T> +struct SmartPtr { + T* MOZ_STRONG_REF t; + T* operator->() const; +}; + +struct R : RefCountedBase { + void method(); +}; + +void take(...); +void foo() { + R* ptr; + SmartPtr<R> sp; + take([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + take([=](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([=](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + take([ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + take([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + take([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + take([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + take([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); +} + +void b() { + R* ptr; + SmartPtr<R> sp; + std::function<void(R*)>([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + std::function<void(SmartPtr<R>)>([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function<void(R*)>([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + std::function<void(SmartPtr<R>)>([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + std::function<void(R*)>([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); + std::function<void(SmartPtr<R>)>([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function<void(R*)>([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); + std::function<void(SmartPtr<R>)>([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + std::function<void(R*)>([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + std::function<void(SmartPtr<R>)>([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function<void(R*)>([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + std::function<void(SmartPtr<R>)>([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + std::function<void(R*)>([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + std::function<void(SmartPtr<R>)>([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + std::function<void(R*)>([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + std::function<void(SmartPtr<R>)>([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); +} + +void c() { + R* ptr; + SmartPtr<R> sp; + mozilla::function<void(R*)>([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + mozilla::function<void(SmartPtr<R>)>([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function<void(R*)>([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + mozilla::function<void(SmartPtr<R>)>([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + mozilla::function<void(R*)>([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); + mozilla::function<void(SmartPtr<R>)>([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function<void(R*)>([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); + mozilla::function<void(SmartPtr<R>)>([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + mozilla::function<void(R*)>([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + mozilla::function<void(SmartPtr<R>)>([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function<void(R*)>([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + mozilla::function<void(SmartPtr<R>)>([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + mozilla::function<void(R*)>([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + mozilla::function<void(SmartPtr<R>)>([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + mozilla::function<void(R*)>([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + mozilla::function<void(SmartPtr<R>)>([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); +} + +// These tests would check c++14 deduced return types, if they were supported in +// our codebase. They are being kept here for convenience in the future if we do +// add support for c++14 deduced return types +#if 0 +auto d1() { + R* ptr; + SmartPtr<R> sp; + return ([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); +} +auto d2() { + R* ptr; + SmartPtr<R> sp; + return ([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d3() { + R* ptr; + SmartPtr<R> sp; + return ([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); +} +auto d4() { + R* ptr; + SmartPtr<R> sp; + return ([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +auto d5() { + R* ptr; + SmartPtr<R> sp; + return ([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); +} +auto d6() { + R* ptr; + SmartPtr<R> sp; + return ([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d8() { + R* ptr; + SmartPtr<R> sp; + return ([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); +} +auto d9() { + R* ptr; + SmartPtr<R> sp; + return ([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +auto d10() { + R* ptr; + SmartPtr<R> sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); +} +auto d11() { + R* ptr; + SmartPtr<R> sp; + return ([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d12() { + R* ptr; + SmartPtr<R> sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); +} +auto d13() { + R* ptr; + SmartPtr<R> sp; + return ([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +auto d14() { + R* ptr; + SmartPtr<R> sp; + return ([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); +} +auto d15() { + R* ptr; + SmartPtr<R> sp; + return ([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); +} +auto d16() { + R* ptr; + SmartPtr<R> sp; + return ([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); +} +auto d17() { + R* ptr; + SmartPtr<R> sp; + return ([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); +} +#endif + +void e() { + auto e1 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + }; + auto e2 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e3 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + }; + auto e4 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; + auto e5 = []() { + R* ptr; + SmartPtr<R> sp; + return ([=](R* argptr) { + R* localptr; + ptr->method(); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + argptr->method(); + localptr->method(); + }); + }; + auto e6 = []() { + R* ptr; + SmartPtr<R> sp; + return ([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e8 = []() { + R* ptr; + SmartPtr<R> sp; + return ([=](R* argptr) { + R* localptr; + take(ptr); // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + take(argptr); + take(localptr); + }); + }; + auto e9 = []() { + R* ptr; + SmartPtr<R> sp; + return ([=](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; + auto e10 = []() { + R* ptr; + SmartPtr<R> sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + }; + auto e11 = []() { + R* ptr; + SmartPtr<R> sp; + return ([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e12 = []() { + R* ptr; + SmartPtr<R> sp; + return ([ptr](R* argptr) { // expected-error{{Refcounted variable 'ptr' of type 'R' cannot be captured by a lambda}} expected-note{{Please consider using a smart pointer}} + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + }; + auto e13 = []() { + R* ptr; + SmartPtr<R> sp; + return ([sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; + auto e14 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&ptr](R* argptr) { + R* localptr; + ptr->method(); + argptr->method(); + localptr->method(); + }); + }; + auto e15 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + sp->method(); + argsp->method(); + localsp->method(); + }); + }; + auto e16 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&ptr](R* argptr) { + R* localptr; + take(ptr); + take(argptr); + take(localptr); + }); + }; + auto e17 = []() { + R* ptr; + SmartPtr<R> sp; + return ([&sp](SmartPtr<R> argsp) { + SmartPtr<R> localsp; + take(sp); + take(argsp); + take(localsp); + }); + }; +} diff --git a/build/clang-plugin/tests/TestNonHeapClass.cpp b/build/clang-plugin/tests/TestNonHeapClass.cpp new file mode 100644 index 000000000..26fe6404e --- /dev/null +++ b/build/clang-plugin/tests/TestNonHeapClass.cpp @@ -0,0 +1,62 @@ +#define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class"))) +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#include <stddef.h> + +struct MOZ_NONHEAP_CLASS NonHeap { + int i; + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template <class T> +struct MOZ_NONHEAP_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseNonHeapClass(int len) { + NonHeap valid; + NonHeap alsoValid[2]; + static NonHeap validStatic; + static NonHeap alsoValidStatic[2]; + + gobble(&valid); + gobble(&validStatic); + gobble(&alsoValid[0]); + + gobble(new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(new NonHeap[10]); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(len <= 5 ? &valid : new NonHeap); // expected-error {{variable of type 'NonHeap' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + + char buffer[sizeof(NonHeap)]; + gobble(new (buffer) NonHeap); +} + +NonHeap validStatic; +struct RandomClass { + NonHeap nonstaticMember; // expected-note {{'RandomClass' is a non-heap type because member 'nonstaticMember' is a non-heap type 'NonHeap'}} + static NonHeap staticMember; +}; +struct MOZ_NONHEAP_CLASS RandomNonHeapClass { + NonHeap nonstaticMember; + static NonHeap staticMember; +}; + +struct BadInherit : NonHeap {}; // expected-note {{'BadInherit' is a non-heap type because it inherits from a non-heap type 'NonHeap'}} +struct MOZ_NONHEAP_CLASS GoodInherit : NonHeap {}; + +void useStuffWrongly() { + gobble(new BadInherit); // expected-error {{variable of type 'BadInherit' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} + gobble(new RandomClass); // expected-error {{variable of type 'RandomClass' is not valid on the heap}} expected-note {{value incorrectly allocated on the heap}} +} + +// Stack class overrides non-heap typees. +struct MOZ_STACK_CLASS StackClass {}; +struct MOZ_NONHEAP_CLASS InferredStackClass : GoodInherit { + NonHeap nonstaticMember; + StackClass stackClass; // expected-note {{'InferredStackClass' is a stack type because member 'stackClass' is a stack type 'StackClass'}} +}; + +InferredStackClass global; // expected-error {{variable of type 'InferredStackClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} diff --git a/build/clang-plugin/tests/TestNonMemMovable.cpp b/build/clang-plugin/tests/TestNonMemMovable.cpp new file mode 100644 index 000000000..a726d0ab6 --- /dev/null +++ b/build/clang-plugin/tests/TestNonMemMovable.cpp @@ -0,0 +1,830 @@ +#define MOZ_NON_MEMMOVABLE __attribute__((annotate("moz_non_memmovable"))) +#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type"))) +#define MOZ_NEEDS_MEMMOVABLE_MEMBERS __attribute__((annotate("moz_needs_memmovable_members"))) + +/* + These are a bunch of structs with variable levels of memmovability. + They will be used as template parameters to the various NeedyTemplates +*/ +struct MOZ_NON_MEMMOVABLE NonMovable {}; +struct Movable {}; + +// Subclasses +struct S_NonMovable : NonMovable {}; // expected-note 51 {{'S_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'NonMovable'}} +struct S_Movable : Movable {}; + +// Members +struct W_NonMovable { + NonMovable m; // expected-note 34 {{'W_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'NonMovable'}} +}; +struct W_Movable { + Movable m; +}; + +// Wrapped Subclasses +struct WS_NonMovable { + S_NonMovable m; // expected-note 34 {{'WS_NonMovable' is a non-memmove()able type because member 'm' is a non-memmove()able type 'S_NonMovable'}} +}; +struct WS_Movable { + S_Movable m; +}; + +// Combinations of the above +struct SW_NonMovable : W_NonMovable {}; // expected-note 17 {{'SW_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'W_NonMovable'}} +struct SW_Movable : W_Movable {}; + +struct SWS_NonMovable : WS_NonMovable {}; // expected-note 17 {{'SWS_NonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'WS_NonMovable'}} +struct SWS_Movable : WS_Movable {}; + +// Basic templated wrapper +template <class T> +struct Template_Inline { + T m; // expected-note-re 56 {{'Template_Inline<{{.*}}>' is a non-memmove()able type because member 'm' is a non-memmove()able type '{{.*}}'}} +}; + +template <class T> +struct Template_Ref { + T* m; +}; + +template <class T> +struct Template_Unused {}; + +template <class T> +struct MOZ_NON_MEMMOVABLE Template_NonMovable {}; + +/* + These tests take the following form: + DECLARATIONS => Declarations of the templates which are either marked with MOZ_NEEDS_MEMMOVABLE_TYPE + or which instantiate a MOZ_NEEDS_MEMMOVABLE_TYPE through some mechanism. + BAD N => Instantiations of the wrapper template with each of the non-memmovable types. + The prefix S_ means subclass, W_ means wrapped. Each of these rows should produce an error + on the NeedyTemplate in question, and a note at the instantiation location of that template. + Unfortunately, on every case more complicated than bad1, the instantiation location is + within another template. Thus, the notes are expected on the template in question which + actually instantiates the MOZ_NEEDS_MEMMOVABLE_TYPE template. + GOOD N => Instantiations of the wrapper template with each of the memmovable types. + This is meant as a sanity check to ensure that we don't reject valid instantiations of + templates. + + + Note 1: Each set uses it's own types to ensure that they don't re-use each-other's template specializations. + If they did, then some of the error messages would not be emitted (as error messages are emitted for template + specializations, rather than for variable declarations) + + Note 2: Every instance of NeedyTemplate contains a member of type T. This is to ensure that T is actually + instantiated (if T is a template) by clang. If T isn't instantiated, then we can't actually tell if it is + NON_MEMMOVABLE. (This is OK in practice, as you cannot memmove a type which you don't know the size of). + + Note 3: There are a set of tests for specializations of NeedyTemplate at the bottom. For each set of tests, + these tests contribute two expected errors to the templates. +*/ + +// +// 1 - Unwrapped MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate1 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate1<{{.*}}>' with non-memmovable template argument '{{.*}}'}} + +void bad1() { + NeedyTemplate1<NonMovable> a1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<S_NonMovable> a2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<W_NonMovable> a3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<WS_NonMovable> a4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<SW_NonMovable> a5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<SWS_NonMovable> a6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + + NeedyTemplate1<Template_Inline<NonMovable> > b1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_Inline<S_NonMovable> > b2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_Inline<W_NonMovable> > b3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_Inline<WS_NonMovable> > b4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_Inline<SW_NonMovable> > b5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_Inline<SWS_NonMovable> > b6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + + NeedyTemplate1<Template_NonMovable<NonMovable> > c1; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<S_NonMovable> > c2; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<W_NonMovable> > c3; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<WS_NonMovable> > c4; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<SW_NonMovable> > c5; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<SWS_NonMovable> > c6; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<Movable> > c7; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<S_Movable> > c8; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<W_Movable> > c9; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<WS_Movable> > c10; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<SW_Movable> > c11; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} + NeedyTemplate1<Template_NonMovable<SWS_Movable> > c12; // expected-note-re {{instantiation of 'NeedyTemplate1<{{.*}}>' requested here}} +} + +void good1() { + NeedyTemplate1<Movable> a1; + NeedyTemplate1<S_Movable> a2; + NeedyTemplate1<W_Movable> a3; + NeedyTemplate1<WS_Movable> a4; + NeedyTemplate1<SW_Movable> a5; + NeedyTemplate1<SWS_Movable> a6; + + NeedyTemplate1<Template_Inline<Movable> > b1; + NeedyTemplate1<Template_Inline<S_Movable> > b2; + NeedyTemplate1<Template_Inline<W_Movable> > b3; + NeedyTemplate1<Template_Inline<WS_Movable> > b4; + NeedyTemplate1<Template_Inline<SW_Movable> > b5; + NeedyTemplate1<Template_Inline<SWS_Movable> > b6; + + NeedyTemplate1<Template_Unused<Movable> > c1; + NeedyTemplate1<Template_Unused<S_Movable> > c2; + NeedyTemplate1<Template_Unused<W_Movable> > c3; + NeedyTemplate1<Template_Unused<WS_Movable> > c4; + NeedyTemplate1<Template_Unused<SW_Movable> > c5; + NeedyTemplate1<Template_Unused<SWS_Movable> > c6; + NeedyTemplate1<Template_Unused<NonMovable> > c7; + NeedyTemplate1<Template_Unused<S_NonMovable> > c8; + NeedyTemplate1<Template_Unused<W_NonMovable> > c9; + NeedyTemplate1<Template_Unused<WS_NonMovable> > c10; + NeedyTemplate1<Template_Unused<SW_NonMovable> > c11; + NeedyTemplate1<Template_Unused<SWS_NonMovable> > c12; + + NeedyTemplate1<Template_Ref<Movable> > d1; + NeedyTemplate1<Template_Ref<S_Movable> > d2; + NeedyTemplate1<Template_Ref<W_Movable> > d3; + NeedyTemplate1<Template_Ref<WS_Movable> > d4; + NeedyTemplate1<Template_Ref<SW_Movable> > d5; + NeedyTemplate1<Template_Ref<SWS_Movable> > d6; + NeedyTemplate1<Template_Ref<NonMovable> > d7; + NeedyTemplate1<Template_Ref<S_NonMovable> > d8; + NeedyTemplate1<Template_Ref<W_NonMovable> > d9; + NeedyTemplate1<Template_Ref<WS_NonMovable> > d10; + NeedyTemplate1<Template_Ref<SW_NonMovable> > d11; + NeedyTemplate1<Template_Ref<SWS_NonMovable> > d12; +} + +// +// 2 - Subclassed MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate2 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate2<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template <class T> +struct S_NeedyTemplate2 : NeedyTemplate2<T> {}; // expected-note-re 26 {{instantiation of 'NeedyTemplate2<{{.*}}>' requested here}} + +void bad2() { + S_NeedyTemplate2<NonMovable> a1; + S_NeedyTemplate2<S_NonMovable> a2; + S_NeedyTemplate2<W_NonMovable> a3; + S_NeedyTemplate2<WS_NonMovable> a4; + S_NeedyTemplate2<SW_NonMovable> a5; + S_NeedyTemplate2<SWS_NonMovable> a6; + + S_NeedyTemplate2<Template_Inline<NonMovable> > b1; + S_NeedyTemplate2<Template_Inline<S_NonMovable> > b2; + S_NeedyTemplate2<Template_Inline<W_NonMovable> > b3; + S_NeedyTemplate2<Template_Inline<WS_NonMovable> > b4; + S_NeedyTemplate2<Template_Inline<SW_NonMovable> > b5; + S_NeedyTemplate2<Template_Inline<SWS_NonMovable> > b6; + + S_NeedyTemplate2<Template_NonMovable<NonMovable> > c1; + S_NeedyTemplate2<Template_NonMovable<S_NonMovable> > c2; + S_NeedyTemplate2<Template_NonMovable<W_NonMovable> > c3; + S_NeedyTemplate2<Template_NonMovable<WS_NonMovable> > c4; + S_NeedyTemplate2<Template_NonMovable<SW_NonMovable> > c5; + S_NeedyTemplate2<Template_NonMovable<SWS_NonMovable> > c6; + S_NeedyTemplate2<Template_NonMovable<Movable> > c7; + S_NeedyTemplate2<Template_NonMovable<S_Movable> > c8; + S_NeedyTemplate2<Template_NonMovable<W_Movable> > c9; + S_NeedyTemplate2<Template_NonMovable<WS_Movable> > c10; + S_NeedyTemplate2<Template_NonMovable<SW_Movable> > c11; + S_NeedyTemplate2<Template_NonMovable<SWS_Movable> > c12; +} + +void good2() { + S_NeedyTemplate2<Movable> a1; + S_NeedyTemplate2<S_Movable> a2; + S_NeedyTemplate2<W_Movable> a3; + S_NeedyTemplate2<WS_Movable> a4; + S_NeedyTemplate2<SW_Movable> a5; + S_NeedyTemplate2<SWS_Movable> a6; + + S_NeedyTemplate2<Template_Inline<Movable> > b1; + S_NeedyTemplate2<Template_Inline<S_Movable> > b2; + S_NeedyTemplate2<Template_Inline<W_Movable> > b3; + S_NeedyTemplate2<Template_Inline<WS_Movable> > b4; + S_NeedyTemplate2<Template_Inline<SW_Movable> > b5; + S_NeedyTemplate2<Template_Inline<SWS_Movable> > b6; + + S_NeedyTemplate2<Template_Unused<Movable> > c1; + S_NeedyTemplate2<Template_Unused<S_Movable> > c2; + S_NeedyTemplate2<Template_Unused<W_Movable> > c3; + S_NeedyTemplate2<Template_Unused<WS_Movable> > c4; + S_NeedyTemplate2<Template_Unused<SW_Movable> > c5; + S_NeedyTemplate2<Template_Unused<SWS_Movable> > c6; + S_NeedyTemplate2<Template_Unused<NonMovable> > c7; + S_NeedyTemplate2<Template_Unused<S_NonMovable> > c8; + S_NeedyTemplate2<Template_Unused<W_NonMovable> > c9; + S_NeedyTemplate2<Template_Unused<WS_NonMovable> > c10; + S_NeedyTemplate2<Template_Unused<SW_NonMovable> > c11; + S_NeedyTemplate2<Template_Unused<SWS_NonMovable> > c12; + + S_NeedyTemplate2<Template_Ref<Movable> > d1; + S_NeedyTemplate2<Template_Ref<S_Movable> > d2; + S_NeedyTemplate2<Template_Ref<W_Movable> > d3; + S_NeedyTemplate2<Template_Ref<WS_Movable> > d4; + S_NeedyTemplate2<Template_Ref<SW_Movable> > d5; + S_NeedyTemplate2<Template_Ref<SWS_Movable> > d6; + S_NeedyTemplate2<Template_Ref<NonMovable> > d7; + S_NeedyTemplate2<Template_Ref<S_NonMovable> > d8; + S_NeedyTemplate2<Template_Ref<W_NonMovable> > d9; + S_NeedyTemplate2<Template_Ref<WS_NonMovable> > d10; + S_NeedyTemplate2<Template_Ref<SW_NonMovable> > d11; + S_NeedyTemplate2<Template_Ref<SWS_NonMovable> > d12; +} + +// +// 3 - Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate3 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate3<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template <class T> +struct W_NeedyTemplate3 { + NeedyTemplate3<T> m; // expected-note-re 26 {{instantiation of 'NeedyTemplate3<{{.*}}>' requested here}} +}; +void bad3() { + W_NeedyTemplate3<NonMovable> a1; + W_NeedyTemplate3<S_NonMovable> a2; + W_NeedyTemplate3<W_NonMovable> a3; + W_NeedyTemplate3<WS_NonMovable> a4; + W_NeedyTemplate3<SW_NonMovable> a5; + W_NeedyTemplate3<SWS_NonMovable> a6; + + W_NeedyTemplate3<Template_Inline<NonMovable> > b1; + W_NeedyTemplate3<Template_Inline<S_NonMovable> > b2; + W_NeedyTemplate3<Template_Inline<W_NonMovable> > b3; + W_NeedyTemplate3<Template_Inline<WS_NonMovable> > b4; + W_NeedyTemplate3<Template_Inline<SW_NonMovable> > b5; + W_NeedyTemplate3<Template_Inline<SWS_NonMovable> > b6; + + W_NeedyTemplate3<Template_NonMovable<NonMovable> > c1; + W_NeedyTemplate3<Template_NonMovable<S_NonMovable> > c2; + W_NeedyTemplate3<Template_NonMovable<W_NonMovable> > c3; + W_NeedyTemplate3<Template_NonMovable<WS_NonMovable> > c4; + W_NeedyTemplate3<Template_NonMovable<SW_NonMovable> > c5; + W_NeedyTemplate3<Template_NonMovable<SWS_NonMovable> > c6; + W_NeedyTemplate3<Template_NonMovable<Movable> > c7; + W_NeedyTemplate3<Template_NonMovable<S_Movable> > c8; + W_NeedyTemplate3<Template_NonMovable<W_Movable> > c9; + W_NeedyTemplate3<Template_NonMovable<WS_Movable> > c10; + W_NeedyTemplate3<Template_NonMovable<SW_Movable> > c11; + W_NeedyTemplate3<Template_NonMovable<SWS_Movable> > c12; +} + +void good3() { + W_NeedyTemplate3<Movable> a1; + W_NeedyTemplate3<S_Movable> a2; + W_NeedyTemplate3<W_Movable> a3; + W_NeedyTemplate3<WS_Movable> a4; + W_NeedyTemplate3<SW_Movable> a5; + W_NeedyTemplate3<SWS_Movable> a6; + + W_NeedyTemplate3<Template_Inline<Movable> > b1; + W_NeedyTemplate3<Template_Inline<S_Movable> > b2; + W_NeedyTemplate3<Template_Inline<W_Movable> > b3; + W_NeedyTemplate3<Template_Inline<WS_Movable> > b4; + W_NeedyTemplate3<Template_Inline<SW_Movable> > b5; + W_NeedyTemplate3<Template_Inline<SWS_Movable> > b6; + + W_NeedyTemplate3<Template_Unused<Movable> > c1; + W_NeedyTemplate3<Template_Unused<S_Movable> > c2; + W_NeedyTemplate3<Template_Unused<W_Movable> > c3; + W_NeedyTemplate3<Template_Unused<WS_Movable> > c4; + W_NeedyTemplate3<Template_Unused<SW_Movable> > c5; + W_NeedyTemplate3<Template_Unused<SWS_Movable> > c6; + W_NeedyTemplate3<Template_Unused<NonMovable> > c7; + W_NeedyTemplate3<Template_Unused<S_NonMovable> > c8; + W_NeedyTemplate3<Template_Unused<W_NonMovable> > c9; + W_NeedyTemplate3<Template_Unused<WS_NonMovable> > c10; + W_NeedyTemplate3<Template_Unused<SW_NonMovable> > c11; + W_NeedyTemplate3<Template_Unused<SWS_NonMovable> > c12; + + W_NeedyTemplate3<Template_Ref<Movable> > d1; + W_NeedyTemplate3<Template_Ref<S_Movable> > d2; + W_NeedyTemplate3<Template_Ref<W_Movable> > d3; + W_NeedyTemplate3<Template_Ref<WS_Movable> > d4; + W_NeedyTemplate3<Template_Ref<SW_Movable> > d5; + W_NeedyTemplate3<Template_Ref<SWS_Movable> > d6; + W_NeedyTemplate3<Template_Ref<NonMovable> > d7; + W_NeedyTemplate3<Template_Ref<S_NonMovable> > d8; + W_NeedyTemplate3<Template_Ref<W_NonMovable> > d9; + W_NeedyTemplate3<Template_Ref<WS_NonMovable> > d10; + W_NeedyTemplate3<Template_Ref<SW_NonMovable> > d11; + W_NeedyTemplate3<Template_Ref<SWS_NonMovable> > d12; +} + +// +// 4 - Wrapped Subclassed MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate4 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate4<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template <class T> +struct S_NeedyTemplate4 : NeedyTemplate4<T> {}; // expected-note-re 26 {{instantiation of 'NeedyTemplate4<{{.*}}>' requested here}} +template <class T> +struct WS_NeedyTemplate4 { + S_NeedyTemplate4<T> m; +}; +void bad4() { + WS_NeedyTemplate4<NonMovable> a1; + WS_NeedyTemplate4<S_NonMovable> a2; + WS_NeedyTemplate4<W_NonMovable> a3; + WS_NeedyTemplate4<WS_NonMovable> a4; + WS_NeedyTemplate4<SW_NonMovable> a5; + WS_NeedyTemplate4<SWS_NonMovable> a6; + + WS_NeedyTemplate4<Template_Inline<NonMovable> > b1; + WS_NeedyTemplate4<Template_Inline<S_NonMovable> > b2; + WS_NeedyTemplate4<Template_Inline<W_NonMovable> > b3; + WS_NeedyTemplate4<Template_Inline<WS_NonMovable> > b4; + WS_NeedyTemplate4<Template_Inline<SW_NonMovable> > b5; + WS_NeedyTemplate4<Template_Inline<SWS_NonMovable> > b6; + + WS_NeedyTemplate4<Template_NonMovable<NonMovable> > c1; + WS_NeedyTemplate4<Template_NonMovable<S_NonMovable> > c2; + WS_NeedyTemplate4<Template_NonMovable<W_NonMovable> > c3; + WS_NeedyTemplate4<Template_NonMovable<WS_NonMovable> > c4; + WS_NeedyTemplate4<Template_NonMovable<SW_NonMovable> > c5; + WS_NeedyTemplate4<Template_NonMovable<SWS_NonMovable> > c6; + WS_NeedyTemplate4<Template_NonMovable<Movable> > c7; + WS_NeedyTemplate4<Template_NonMovable<S_Movable> > c8; + WS_NeedyTemplate4<Template_NonMovable<W_Movable> > c9; + WS_NeedyTemplate4<Template_NonMovable<WS_Movable> > c10; + WS_NeedyTemplate4<Template_NonMovable<SW_Movable> > c11; + WS_NeedyTemplate4<Template_NonMovable<SWS_Movable> > c12; +} + +void good4() { + WS_NeedyTemplate4<Movable> a1; + WS_NeedyTemplate4<S_Movable> a2; + WS_NeedyTemplate4<W_Movable> a3; + WS_NeedyTemplate4<WS_Movable> a4; + WS_NeedyTemplate4<SW_Movable> a5; + WS_NeedyTemplate4<SWS_Movable> a6; + + WS_NeedyTemplate4<Template_Inline<Movable> > b1; + WS_NeedyTemplate4<Template_Inline<S_Movable> > b2; + WS_NeedyTemplate4<Template_Inline<W_Movable> > b3; + WS_NeedyTemplate4<Template_Inline<WS_Movable> > b4; + WS_NeedyTemplate4<Template_Inline<SW_Movable> > b5; + WS_NeedyTemplate4<Template_Inline<SWS_Movable> > b6; + + WS_NeedyTemplate4<Template_Unused<Movable> > c1; + WS_NeedyTemplate4<Template_Unused<S_Movable> > c2; + WS_NeedyTemplate4<Template_Unused<W_Movable> > c3; + WS_NeedyTemplate4<Template_Unused<WS_Movable> > c4; + WS_NeedyTemplate4<Template_Unused<SW_Movable> > c5; + WS_NeedyTemplate4<Template_Unused<SWS_Movable> > c6; + WS_NeedyTemplate4<Template_Unused<NonMovable> > c7; + WS_NeedyTemplate4<Template_Unused<S_NonMovable> > c8; + WS_NeedyTemplate4<Template_Unused<W_NonMovable> > c9; + WS_NeedyTemplate4<Template_Unused<WS_NonMovable> > c10; + WS_NeedyTemplate4<Template_Unused<SW_NonMovable> > c11; + WS_NeedyTemplate4<Template_Unused<SWS_NonMovable> > c12; + + WS_NeedyTemplate4<Template_Ref<Movable> > d1; + WS_NeedyTemplate4<Template_Ref<S_Movable> > d2; + WS_NeedyTemplate4<Template_Ref<W_Movable> > d3; + WS_NeedyTemplate4<Template_Ref<WS_Movable> > d4; + WS_NeedyTemplate4<Template_Ref<SW_Movable> > d5; + WS_NeedyTemplate4<Template_Ref<SWS_Movable> > d6; + WS_NeedyTemplate4<Template_Ref<NonMovable> > d7; + WS_NeedyTemplate4<Template_Ref<S_NonMovable> > d8; + WS_NeedyTemplate4<Template_Ref<W_NonMovable> > d9; + WS_NeedyTemplate4<Template_Ref<WS_NonMovable> > d10; + WS_NeedyTemplate4<Template_Ref<SW_NonMovable> > d11; + WS_NeedyTemplate4<Template_Ref<SWS_NonMovable> > d12; +} + +// +// 5 - Subclassed Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate5 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate5<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template <class T> +struct W_NeedyTemplate5 { + NeedyTemplate5<T> m; // expected-note-re 26 {{instantiation of 'NeedyTemplate5<{{.*}}>' requested here}} +}; +template <class T> +struct SW_NeedyTemplate5 : W_NeedyTemplate5<T> {}; +void bad5() { + SW_NeedyTemplate5<NonMovable> a1; + SW_NeedyTemplate5<S_NonMovable> a2; + SW_NeedyTemplate5<W_NonMovable> a3; + SW_NeedyTemplate5<WS_NonMovable> a4; + SW_NeedyTemplate5<SW_NonMovable> a5; + SW_NeedyTemplate5<SWS_NonMovable> a6; + + SW_NeedyTemplate5<Template_Inline<NonMovable> > b1; + SW_NeedyTemplate5<Template_Inline<S_NonMovable> > b2; + SW_NeedyTemplate5<Template_Inline<W_NonMovable> > b3; + SW_NeedyTemplate5<Template_Inline<WS_NonMovable> > b4; + SW_NeedyTemplate5<Template_Inline<SW_NonMovable> > b5; + SW_NeedyTemplate5<Template_Inline<SWS_NonMovable> > b6; + + SW_NeedyTemplate5<Template_NonMovable<NonMovable> > c1; + SW_NeedyTemplate5<Template_NonMovable<S_NonMovable> > c2; + SW_NeedyTemplate5<Template_NonMovable<W_NonMovable> > c3; + SW_NeedyTemplate5<Template_NonMovable<WS_NonMovable> > c4; + SW_NeedyTemplate5<Template_NonMovable<SW_NonMovable> > c5; + SW_NeedyTemplate5<Template_NonMovable<SWS_NonMovable> > c6; + SW_NeedyTemplate5<Template_NonMovable<Movable> > c7; + SW_NeedyTemplate5<Template_NonMovable<S_Movable> > c8; + SW_NeedyTemplate5<Template_NonMovable<W_Movable> > c9; + SW_NeedyTemplate5<Template_NonMovable<WS_Movable> > c10; + SW_NeedyTemplate5<Template_NonMovable<SW_Movable> > c11; + SW_NeedyTemplate5<Template_NonMovable<SWS_Movable> > c12; +} + +void good5() { + SW_NeedyTemplate5<Movable> a1; + SW_NeedyTemplate5<S_Movable> a2; + SW_NeedyTemplate5<W_Movable> a3; + SW_NeedyTemplate5<WS_Movable> a4; + SW_NeedyTemplate5<SW_Movable> a5; + SW_NeedyTemplate5<SWS_Movable> a6; + + SW_NeedyTemplate5<Template_Inline<Movable> > b1; + SW_NeedyTemplate5<Template_Inline<S_Movable> > b2; + SW_NeedyTemplate5<Template_Inline<W_Movable> > b3; + SW_NeedyTemplate5<Template_Inline<WS_Movable> > b4; + SW_NeedyTemplate5<Template_Inline<SW_Movable> > b5; + SW_NeedyTemplate5<Template_Inline<SWS_Movable> > b6; + + SW_NeedyTemplate5<Template_Unused<Movable> > c1; + SW_NeedyTemplate5<Template_Unused<S_Movable> > c2; + SW_NeedyTemplate5<Template_Unused<W_Movable> > c3; + SW_NeedyTemplate5<Template_Unused<WS_Movable> > c4; + SW_NeedyTemplate5<Template_Unused<SW_Movable> > c5; + SW_NeedyTemplate5<Template_Unused<SWS_Movable> > c6; + SW_NeedyTemplate5<Template_Unused<NonMovable> > c7; + SW_NeedyTemplate5<Template_Unused<S_NonMovable> > c8; + SW_NeedyTemplate5<Template_Unused<W_NonMovable> > c9; + SW_NeedyTemplate5<Template_Unused<WS_NonMovable> > c10; + SW_NeedyTemplate5<Template_Unused<SW_NonMovable> > c11; + SW_NeedyTemplate5<Template_Unused<SWS_NonMovable> > c12; + + SW_NeedyTemplate5<Template_Ref<Movable> > d1; + SW_NeedyTemplate5<Template_Ref<S_Movable> > d2; + SW_NeedyTemplate5<Template_Ref<W_Movable> > d3; + SW_NeedyTemplate5<Template_Ref<WS_Movable> > d4; + SW_NeedyTemplate5<Template_Ref<SW_Movable> > d5; + SW_NeedyTemplate5<Template_Ref<SWS_Movable> > d6; + SW_NeedyTemplate5<Template_Ref<NonMovable> > d7; + SW_NeedyTemplate5<Template_Ref<S_NonMovable> > d8; + SW_NeedyTemplate5<Template_Ref<W_NonMovable> > d9; + SW_NeedyTemplate5<Template_Ref<WS_NonMovable> > d10; + SW_NeedyTemplate5<Template_Ref<SW_NonMovable> > d11; + SW_NeedyTemplate5<Template_Ref<SWS_NonMovable> > d12; +} + +// +// 6 - MOZ_NEEDS_MEMMOVABLE_TYPE instantiated with default template argument +// +// Note: This has an extra error, because it also includes a test with the default template argument. +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate6 {T m;}; // expected-error-re 27 {{Cannot instantiate 'NeedyTemplate6<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template <class T> +struct W_NeedyTemplate6 { + NeedyTemplate6<T> m; // expected-note-re 27 {{instantiation of 'NeedyTemplate6<{{.*}}>' requested here}} +}; +template <class T> +struct SW_NeedyTemplate6 : W_NeedyTemplate6<T> {}; +// We create a different NonMovable type here, as NeedyTemplate6 will already be instantiated with NonMovable +struct MOZ_NON_MEMMOVABLE NonMovable2 {}; +template <class T = NonMovable2> +struct Defaulted_SW_NeedyTemplate6 { + SW_NeedyTemplate6<T> m; +}; +void bad6() { + Defaulted_SW_NeedyTemplate6<NonMovable> a1; + Defaulted_SW_NeedyTemplate6<S_NonMovable> a2; + Defaulted_SW_NeedyTemplate6<W_NonMovable> a3; + Defaulted_SW_NeedyTemplate6<WS_NonMovable> a4; + Defaulted_SW_NeedyTemplate6<SW_NonMovable> a5; + Defaulted_SW_NeedyTemplate6<SWS_NonMovable> a6; + + Defaulted_SW_NeedyTemplate6<Template_Inline<NonMovable> > b1; + Defaulted_SW_NeedyTemplate6<Template_Inline<S_NonMovable> > b2; + Defaulted_SW_NeedyTemplate6<Template_Inline<W_NonMovable> > b3; + Defaulted_SW_NeedyTemplate6<Template_Inline<WS_NonMovable> > b4; + Defaulted_SW_NeedyTemplate6<Template_Inline<SW_NonMovable> > b5; + Defaulted_SW_NeedyTemplate6<Template_Inline<SWS_NonMovable> > b6; + + Defaulted_SW_NeedyTemplate6<Template_NonMovable<NonMovable> > c1; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<S_NonMovable> > c2; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<W_NonMovable> > c3; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<WS_NonMovable> > c4; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<SW_NonMovable> > c5; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<SWS_NonMovable> > c6; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<Movable> > c7; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<S_Movable> > c8; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<W_Movable> > c9; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<WS_Movable> > c10; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<SW_Movable> > c11; + Defaulted_SW_NeedyTemplate6<Template_NonMovable<SWS_Movable> > c12; + + Defaulted_SW_NeedyTemplate6<> c13; +} + +void good6() { + Defaulted_SW_NeedyTemplate6<Movable> a1; + Defaulted_SW_NeedyTemplate6<S_Movable> a2; + Defaulted_SW_NeedyTemplate6<W_Movable> a3; + Defaulted_SW_NeedyTemplate6<WS_Movable> a4; + Defaulted_SW_NeedyTemplate6<SW_Movable> a5; + Defaulted_SW_NeedyTemplate6<SWS_Movable> a6; + + Defaulted_SW_NeedyTemplate6<Template_Inline<Movable> > b1; + Defaulted_SW_NeedyTemplate6<Template_Inline<S_Movable> > b2; + Defaulted_SW_NeedyTemplate6<Template_Inline<W_Movable> > b3; + Defaulted_SW_NeedyTemplate6<Template_Inline<WS_Movable> > b4; + Defaulted_SW_NeedyTemplate6<Template_Inline<SW_Movable> > b5; + Defaulted_SW_NeedyTemplate6<Template_Inline<SWS_Movable> > b6; + + Defaulted_SW_NeedyTemplate6<Template_Unused<Movable> > c1; + Defaulted_SW_NeedyTemplate6<Template_Unused<S_Movable> > c2; + Defaulted_SW_NeedyTemplate6<Template_Unused<W_Movable> > c3; + Defaulted_SW_NeedyTemplate6<Template_Unused<WS_Movable> > c4; + Defaulted_SW_NeedyTemplate6<Template_Unused<SW_Movable> > c5; + Defaulted_SW_NeedyTemplate6<Template_Unused<SWS_Movable> > c6; + Defaulted_SW_NeedyTemplate6<Template_Unused<NonMovable> > c7; + Defaulted_SW_NeedyTemplate6<Template_Unused<S_NonMovable> > c8; + Defaulted_SW_NeedyTemplate6<Template_Unused<W_NonMovable> > c9; + Defaulted_SW_NeedyTemplate6<Template_Unused<WS_NonMovable> > c10; + Defaulted_SW_NeedyTemplate6<Template_Unused<SW_NonMovable> > c11; + Defaulted_SW_NeedyTemplate6<Template_Unused<SWS_NonMovable> > c12; + + Defaulted_SW_NeedyTemplate6<Template_Ref<Movable> > d1; + Defaulted_SW_NeedyTemplate6<Template_Ref<S_Movable> > d2; + Defaulted_SW_NeedyTemplate6<Template_Ref<W_Movable> > d3; + Defaulted_SW_NeedyTemplate6<Template_Ref<WS_Movable> > d4; + Defaulted_SW_NeedyTemplate6<Template_Ref<SW_Movable> > d5; + Defaulted_SW_NeedyTemplate6<Template_Ref<SWS_Movable> > d6; + Defaulted_SW_NeedyTemplate6<Template_Ref<NonMovable> > d7; + Defaulted_SW_NeedyTemplate6<Template_Ref<S_NonMovable> > d8; + Defaulted_SW_NeedyTemplate6<Template_Ref<W_NonMovable> > d9; + Defaulted_SW_NeedyTemplate6<Template_Ref<WS_NonMovable> > d10; + Defaulted_SW_NeedyTemplate6<Template_Ref<SW_NonMovable> > d11; + Defaulted_SW_NeedyTemplate6<Template_Ref<SWS_NonMovable> > d12; +} + +// +// 7 - MOZ_NEEDS_MEMMOVABLE_TYPE instantiated as default template argument +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate7 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate7<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template <class T, class Q = NeedyTemplate7<T> > +struct Defaulted_Templated_NeedyTemplate7 {Q m;}; // expected-note-re 26 {{instantiation of 'NeedyTemplate7<{{.*}}>' requested here}} +void bad7() { + Defaulted_Templated_NeedyTemplate7<NonMovable> a1; + Defaulted_Templated_NeedyTemplate7<S_NonMovable> a2; + Defaulted_Templated_NeedyTemplate7<W_NonMovable> a3; + Defaulted_Templated_NeedyTemplate7<WS_NonMovable> a4; + Defaulted_Templated_NeedyTemplate7<SW_NonMovable> a5; + Defaulted_Templated_NeedyTemplate7<SWS_NonMovable> a6; + + Defaulted_Templated_NeedyTemplate7<Template_Inline<NonMovable> > b1; + Defaulted_Templated_NeedyTemplate7<Template_Inline<S_NonMovable> > b2; + Defaulted_Templated_NeedyTemplate7<Template_Inline<W_NonMovable> > b3; + Defaulted_Templated_NeedyTemplate7<Template_Inline<WS_NonMovable> > b4; + Defaulted_Templated_NeedyTemplate7<Template_Inline<SW_NonMovable> > b5; + Defaulted_Templated_NeedyTemplate7<Template_Inline<SWS_NonMovable> > b6; + + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<NonMovable> > c1; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<S_NonMovable> > c2; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<W_NonMovable> > c3; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<WS_NonMovable> > c4; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SW_NonMovable> > c5; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SWS_NonMovable> > c6; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<Movable> > c7; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<S_Movable> > c8; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<W_Movable> > c9; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<WS_Movable> > c10; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SW_Movable> > c11; + Defaulted_Templated_NeedyTemplate7<Template_NonMovable<SWS_Movable> > c12; +} + +void good7() { + Defaulted_Templated_NeedyTemplate7<Movable> a1; + Defaulted_Templated_NeedyTemplate7<S_Movable> a2; + Defaulted_Templated_NeedyTemplate7<W_Movable> a3; + Defaulted_Templated_NeedyTemplate7<WS_Movable> a4; + Defaulted_Templated_NeedyTemplate7<SW_Movable> a5; + Defaulted_Templated_NeedyTemplate7<SWS_Movable> a6; + + Defaulted_Templated_NeedyTemplate7<Template_Inline<Movable> > b1; + Defaulted_Templated_NeedyTemplate7<Template_Inline<S_Movable> > b2; + Defaulted_Templated_NeedyTemplate7<Template_Inline<W_Movable> > b3; + Defaulted_Templated_NeedyTemplate7<Template_Inline<WS_Movable> > b4; + Defaulted_Templated_NeedyTemplate7<Template_Inline<SW_Movable> > b5; + Defaulted_Templated_NeedyTemplate7<Template_Inline<SWS_Movable> > b6; + + Defaulted_Templated_NeedyTemplate7<Template_Unused<Movable> > c1; + Defaulted_Templated_NeedyTemplate7<Template_Unused<S_Movable> > c2; + Defaulted_Templated_NeedyTemplate7<Template_Unused<W_Movable> > c3; + Defaulted_Templated_NeedyTemplate7<Template_Unused<WS_Movable> > c4; + Defaulted_Templated_NeedyTemplate7<Template_Unused<SW_Movable> > c5; + Defaulted_Templated_NeedyTemplate7<Template_Unused<SWS_Movable> > c6; + Defaulted_Templated_NeedyTemplate7<Template_Unused<NonMovable> > c7; + Defaulted_Templated_NeedyTemplate7<Template_Unused<S_NonMovable> > c8; + Defaulted_Templated_NeedyTemplate7<Template_Unused<W_NonMovable> > c9; + Defaulted_Templated_NeedyTemplate7<Template_Unused<WS_NonMovable> > c10; + Defaulted_Templated_NeedyTemplate7<Template_Unused<SW_NonMovable> > c11; + Defaulted_Templated_NeedyTemplate7<Template_Unused<SWS_NonMovable> > c12; + + Defaulted_Templated_NeedyTemplate7<Template_Ref<Movable> > d1; + Defaulted_Templated_NeedyTemplate7<Template_Ref<S_Movable> > d2; + Defaulted_Templated_NeedyTemplate7<Template_Ref<W_Movable> > d3; + Defaulted_Templated_NeedyTemplate7<Template_Ref<WS_Movable> > d4; + Defaulted_Templated_NeedyTemplate7<Template_Ref<SW_Movable> > d5; + Defaulted_Templated_NeedyTemplate7<Template_Ref<SWS_Movable> > d6; + Defaulted_Templated_NeedyTemplate7<Template_Ref<NonMovable> > d7; + Defaulted_Templated_NeedyTemplate7<Template_Ref<S_NonMovable> > d8; + Defaulted_Templated_NeedyTemplate7<Template_Ref<W_NonMovable> > d9; + Defaulted_Templated_NeedyTemplate7<Template_Ref<WS_NonMovable> > d10; + Defaulted_Templated_NeedyTemplate7<Template_Ref<SW_NonMovable> > d11; + Defaulted_Templated_NeedyTemplate7<Template_Ref<SWS_NonMovable> > d12; +} + +// +// 8 - Wrapped MOZ_NEEDS_MEMMOVABLE_TYPE instantiated as default template argument +// + +template <class T> +struct MOZ_NEEDS_MEMMOVABLE_TYPE NeedyTemplate8 {T m;}; // expected-error-re 26 {{Cannot instantiate 'NeedyTemplate8<{{.*}}>' with non-memmovable template argument '{{.*}}'}} +template <class T, class Q = NeedyTemplate8<T> > +struct Defaulted_Templated_NeedyTemplate8 {Q m;}; // expected-note-re 26 {{instantiation of 'NeedyTemplate8<{{.*}}>' requested here}} +template <class T> +struct W_Defaulted_Templated_NeedyTemplate8 { + Defaulted_Templated_NeedyTemplate8<T> m; +}; +void bad8() { + W_Defaulted_Templated_NeedyTemplate8<NonMovable> a1; + W_Defaulted_Templated_NeedyTemplate8<S_NonMovable> a2; + W_Defaulted_Templated_NeedyTemplate8<W_NonMovable> a3; + W_Defaulted_Templated_NeedyTemplate8<WS_NonMovable> a4; + W_Defaulted_Templated_NeedyTemplate8<SW_NonMovable> a5; + W_Defaulted_Templated_NeedyTemplate8<SWS_NonMovable> a6; + + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<NonMovable> > b1; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<S_NonMovable> > b2; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<W_NonMovable> > b3; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<WS_NonMovable> > b4; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SW_NonMovable> > b5; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SWS_NonMovable> > b6; + + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<NonMovable> > c1; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<S_NonMovable> > c2; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<W_NonMovable> > c3; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<WS_NonMovable> > c4; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SW_NonMovable> > c5; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SWS_NonMovable> > c6; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<Movable> > c7; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<S_Movable> > c8; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<W_Movable> > c9; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<WS_Movable> > c10; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SW_Movable> > c11; + W_Defaulted_Templated_NeedyTemplate8<Template_NonMovable<SWS_Movable> > c12; +} + +void good8() { + W_Defaulted_Templated_NeedyTemplate8<Movable> a1; + W_Defaulted_Templated_NeedyTemplate8<S_Movable> a2; + W_Defaulted_Templated_NeedyTemplate8<W_Movable> a3; + W_Defaulted_Templated_NeedyTemplate8<WS_Movable> a4; + W_Defaulted_Templated_NeedyTemplate8<SW_Movable> a5; + W_Defaulted_Templated_NeedyTemplate8<SWS_Movable> a6; + + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<Movable> > b1; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<S_Movable> > b2; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<W_Movable> > b3; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<WS_Movable> > b4; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SW_Movable> > b5; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SWS_Movable> > b6; + + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<Movable> > c1; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<S_Movable> > c2; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<W_Movable> > c3; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<WS_Movable> > c4; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SW_Movable> > c5; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SWS_Movable> > c6; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<NonMovable> > c7; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<S_NonMovable> > c8; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<W_NonMovable> > c9; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<WS_NonMovable> > c10; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SW_NonMovable> > c11; + W_Defaulted_Templated_NeedyTemplate8<Template_Unused<SWS_NonMovable> > c12; + + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<Movable> > d1; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<S_Movable> > d2; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<W_Movable> > d3; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<WS_Movable> > d4; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SW_Movable> > d5; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SWS_Movable> > d6; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<NonMovable> > d7; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<S_NonMovable> > d8; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<W_NonMovable> > d9; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<WS_NonMovable> > d10; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SW_NonMovable> > d11; + W_Defaulted_Templated_NeedyTemplate8<Template_Ref<SWS_NonMovable> > d12; +} + +/* + SpecializedNonMovable is a non-movable class which has an explicit specialization of NeedyTemplate + for it. Instantiations of NeedyTemplateN<SpecializedNonMovable> should be legal as the explicit + specialization isn't annotated with MOZ_NEEDS_MEMMOVABLE_TYPE. + + However, as it is MOZ_NON_MEMMOVABLE, derived classes and members shouldn't be able to be used to + instantiate NeedyTemplate. +*/ + +struct MOZ_NON_MEMMOVABLE SpecializedNonMovable {}; +struct S_SpecializedNonMovable : SpecializedNonMovable {}; // expected-note 8 {{'S_SpecializedNonMovable' is a non-memmove()able type because it inherits from a non-memmove()able type 'SpecializedNonMovable'}} + +// Specialize all of the NeedyTemplates with SpecializedNonMovable. +template <> +struct NeedyTemplate1<SpecializedNonMovable> {}; +template <> +struct NeedyTemplate2<SpecializedNonMovable> {}; +template <> +struct NeedyTemplate3<SpecializedNonMovable> {}; +template <> +struct NeedyTemplate4<SpecializedNonMovable> {}; +template <> +struct NeedyTemplate5<SpecializedNonMovable> {}; +template <> +struct NeedyTemplate6<SpecializedNonMovable> {}; +template <> +struct NeedyTemplate7<SpecializedNonMovable> {}; +template <> +struct NeedyTemplate8<SpecializedNonMovable> {}; + +void specialization() { + /* + SpecializedNonMovable has a specialization for every variant of NeedyTemplate, + so these templates are valid, even though SpecializedNonMovable isn't + memmovable + */ + NeedyTemplate1<SpecializedNonMovable> a1; + S_NeedyTemplate2<SpecializedNonMovable> a2; + W_NeedyTemplate3<SpecializedNonMovable> a3; + WS_NeedyTemplate4<SpecializedNonMovable> a4; + SW_NeedyTemplate5<SpecializedNonMovable> a5; + Defaulted_SW_NeedyTemplate6<SpecializedNonMovable> a6; + Defaulted_Templated_NeedyTemplate7<SpecializedNonMovable> a7; + W_Defaulted_Templated_NeedyTemplate8<SpecializedNonMovable> a8; + + /* + These entries contain an element which is SpecializedNonMovable, and are non-movable + as there is no valid specialization, and their member is non-memmovable + */ + NeedyTemplate1<Template_Inline<SpecializedNonMovable> > b1; // expected-note {{instantiation of 'NeedyTemplate1<Template_Inline<SpecializedNonMovable> >' requested here}} + S_NeedyTemplate2<Template_Inline<SpecializedNonMovable> > b2; + W_NeedyTemplate3<Template_Inline<SpecializedNonMovable> > b3; + WS_NeedyTemplate4<Template_Inline<SpecializedNonMovable> > b4; + SW_NeedyTemplate5<Template_Inline<SpecializedNonMovable> > b5; + Defaulted_SW_NeedyTemplate6<Template_Inline<SpecializedNonMovable> > b6; + Defaulted_Templated_NeedyTemplate7<Template_Inline<SpecializedNonMovable> > b7; + W_Defaulted_Templated_NeedyTemplate8<Template_Inline<SpecializedNonMovable> > b8; + + /* + The subclass of SpecializedNonMovable, is also non-memmovable, + as there is no valid specialization. + */ + NeedyTemplate1<S_SpecializedNonMovable> c1; // expected-note {{instantiation of 'NeedyTemplate1<S_SpecializedNonMovable>' requested here}} + S_NeedyTemplate2<S_SpecializedNonMovable> c2; + W_NeedyTemplate3<S_SpecializedNonMovable> c3; + WS_NeedyTemplate4<S_SpecializedNonMovable> c4; + SW_NeedyTemplate5<S_SpecializedNonMovable> c5; + Defaulted_SW_NeedyTemplate6<S_SpecializedNonMovable> c6; + Defaulted_Templated_NeedyTemplate7<S_SpecializedNonMovable> c7; + W_Defaulted_Templated_NeedyTemplate8<S_SpecializedNonMovable> c8; +} + +class MOZ_NEEDS_MEMMOVABLE_MEMBERS NeedsMemMovableMembers { + Movable m1; + NonMovable m2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'm2' of type 'NonMovable'}} + S_Movable sm1; + S_NonMovable sm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'sm2' of type 'S_NonMovable'}} + W_Movable wm1; + W_NonMovable wm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wm2' of type 'W_NonMovable'}} + SW_Movable swm1; + SW_NonMovable swm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swm2' of type 'SW_NonMovable'}} + WS_Movable wsm1; + WS_NonMovable wsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'wsm2' of type 'WS_NonMovable'}} + SWS_Movable swsm1; + SWS_NonMovable swsm2; // expected-error {{class 'NeedsMemMovableMembers' cannot have non-memmovable member 'swsm2' of type 'SWS_NonMovable'}} +}; + +class NeedsMemMovableMembersDerived : public NeedsMemMovableMembers {}; diff --git a/build/clang-plugin/tests/TestNonMemMovableStd.cpp b/build/clang-plugin/tests/TestNonMemMovableStd.cpp new file mode 100644 index 000000000..9fce52496 --- /dev/null +++ b/build/clang-plugin/tests/TestNonMemMovableStd.cpp @@ -0,0 +1,21 @@ +#define MOZ_NEEDS_MEMMOVABLE_TYPE __attribute__((annotate("moz_needs_memmovable_type"))) + +template<class T> +class MOZ_NEEDS_MEMMOVABLE_TYPE Mover { T mForceInst; }; // expected-error-re 4 {{Cannot instantiate 'Mover<{{.*}}>' with non-memmovable template argument '{{.*}}'}} + +namespace std { +// In theory defining things in std:: like this invokes undefined +// behavior, but in practice it's good enough for this test case. +template<class C> class basic_string { }; +typedef basic_string<char> string; +template<class T, class U> class pair { T mT; U mU; }; // expected-note {{std::pair<bool, std::basic_string<char> >' is a non-memmove()able type because member 'mU' is a non-memmove()able type 'std::basic_string<char>'}} +class arbitrary_name { }; +} + +class HasString { std::string m; }; // expected-note {{'HasString' is a non-memmove()able type because member 'm' is a non-memmove()able type 'std::string' (aka 'basic_string<char>')}} + +static Mover<std::string> bad; // expected-note {{instantiation of 'Mover<std::basic_string<char> >' requested here}} +static Mover<HasString> bad_mem; // expected-note {{instantiation of 'Mover<HasString>' requested here}} +static Mover<std::arbitrary_name> assumed_bad; // expected-note {{instantiation of 'Mover<std::arbitrary_name>' requested here}} +static Mover<std::pair<bool, int>> good; +static Mover<std::pair<bool, std::string>> not_good; // expected-note {{instantiation of 'Mover<std::pair<bool, std::basic_string<char> > >' requested here}} diff --git a/build/clang-plugin/tests/TestNonParameterChecker.cpp b/build/clang-plugin/tests/TestNonParameterChecker.cpp new file mode 100644 index 000000000..87ff89238 --- /dev/null +++ b/build/clang-plugin/tests/TestNonParameterChecker.cpp @@ -0,0 +1,179 @@ +#define MOZ_NON_PARAM __attribute__((annotate("moz_non_param"))) + +struct Param {}; +struct MOZ_NON_PARAM NonParam {}; +union MOZ_NON_PARAM NonParamUnion {}; +class MOZ_NON_PARAM NonParamClass {}; +enum MOZ_NON_PARAM NonParamEnum { X, Y, Z }; +enum class MOZ_NON_PARAM NonParamEnumClass { X, Y, Z }; + +struct HasNonParamStruct { NonParam x; int y; }; +union HasNonParamUnion { NonParam x; int y; }; +struct HasNonParamStructUnion { HasNonParamUnion z; }; + +#define MAYBE_STATIC +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC + +// Do not check typedef and using. +typedef void (*funcTypeParam)(Param x); +typedef void (*funcTypeNonParam)(NonParam x); + +using usingFuncTypeParam = void (*)(Param x); +using usingFuncTypeNonParam = void (*)(NonParam x); + +class class_ +{ + explicit class_(Param x) {} + explicit class_(NonParam x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamStruct x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamUnion x) {} //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + explicit class_(HasNonParamStructUnion x) {} //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +#define MAYBE_STATIC +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC +}; + +class classWithStatic +{ +#define MAYBE_STATIC static +#include "NonParameterTestCases.h" +#undef MAYBE_STATIC +}; + +template <typename T> +class tmplClassForParam +{ +public: + void raw(T x) {} + void rawDefault(T x = T()) {} + void const_(const T x) {} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +template <typename T> +class tmplClassForNonParam +{ +public: + void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void rawDefault(T x = T()) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void const_(const T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +template <typename T> +class tmplClassForHasNonParamStruct +{ +public: + void raw(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void rawDefault(T x = T()) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void const_(const T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + void ptr(T* x) {} + void ref(T& x) {} + void constRef(const T& x) {} + + void notCalled(T x) {} +}; + +void testTemplateClass() +{ + tmplClassForParam<Param> paramClass; + Param param; + paramClass.raw(param); + paramClass.rawDefault(); + paramClass.const_(param); + paramClass.ptr(¶m); + paramClass.ref(param); + paramClass.constRef(param); + + tmplClassForNonParam<NonParam> nonParamClass; //expected-note 3 {{The bad argument was passed to 'tmplClassForNonParam' here}} + NonParam nonParam; + nonParamClass.raw(nonParam); + nonParamClass.rawDefault(); + nonParamClass.const_(nonParam); + nonParamClass.ptr(&nonParam); + nonParamClass.ref(nonParam); + nonParamClass.constRef(nonParam); + + tmplClassForHasNonParamStruct<HasNonParamStruct> hasNonParamStructClass;//expected-note 3 {{The bad argument was passed to 'tmplClassForHasNonParamStruct' here}} + HasNonParamStruct hasNonParamStruct; + hasNonParamStructClass.raw(hasNonParamStruct); + hasNonParamStructClass.rawDefault(); + hasNonParamStructClass.const_(hasNonParamStruct); + hasNonParamStructClass.ptr(&hasNonParamStruct); + hasNonParamStructClass.ref(hasNonParamStruct); + hasNonParamStructClass.constRef(hasNonParamStruct); +} + +template <typename T> +class NestedTemplateInner +{ +public: + void raw(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +}; + +template <typename T> +class nestedTemplateOuter +{ +public: + void constRef(const T& x) { + NestedTemplateInner<T> inner; //expected-note {{The bad argument was passed to 'NestedTemplateInner' here}} + inner.raw(x); + } +}; + +void testNestedTemplateClass() +{ + nestedTemplateOuter<NonParam> outer; + NonParam nonParam; + outer.constRef(nonParam); // FIXME: this line needs note "The bad argument was passed to 'constRef' here" +} + +template <typename T> +void tmplFuncForParam(T x) {} +template <typename T> +void tmplFuncForNonParam(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template <typename T> +void tmplFuncForNonParamImplicit(T x) {} //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template <typename T> +void tmplFuncForHasNonParamStruct(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +template <typename T> +void tmplFuncForHasNonParamStructImplicit(T x) {} //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + +void testTemplateFunc() +{ + Param param; + tmplFuncForParam<Param>(param); + + NonParam nonParam; + tmplFuncForNonParam<NonParam>(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParam' here" + tmplFuncForNonParamImplicit(nonParam); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForNonParamImplicit' here" + + HasNonParamStruct hasNonParamStruct; + tmplFuncForHasNonParamStruct<HasNonParamStruct>(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStruct' here" + tmplFuncForHasNonParamStructImplicit(hasNonParamStruct); // FIXME: this line needs note "The bad argument was passed to 'tmplFuncForHasNonParamStructImplicit' here" +} + +void testLambda() +{ + auto paramLambda = [](Param x) -> void {}; + auto nonParamLambda = [](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamStructLambda = [](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamUnionLambda = [](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + auto nonParamStructUnionLambda = [](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + + (void)[](Param x) -> void {}; + (void)[](NonParam x) -> void {}; //expected-error {{Type 'NonParam' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamStruct x) -> void {}; //expected-error {{Type 'HasNonParamStruct' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamUnion x) -> void {}; //expected-error {{Type 'HasNonParamUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} + (void)[](HasNonParamStructUnion x) -> void {}; //expected-error {{Type 'HasNonParamStructUnion' must not be used as parameter}} expected-note {{Please consider passing a const reference instead}} +} diff --git a/build/clang-plugin/tests/TestNonTemporaryClass.cpp b/build/clang-plugin/tests/TestNonTemporaryClass.cpp new file mode 100644 index 000000000..682c8ad53 --- /dev/null +++ b/build/clang-plugin/tests/TestNonTemporaryClass.cpp @@ -0,0 +1,70 @@ +#define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class"))) +#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit"))) + +#include <stddef.h> + +struct MOZ_NON_TEMPORARY_CLASS NonTemporary { + int i; + NonTemporary() {} + MOZ_IMPLICIT NonTemporary(int a) {} + NonTemporary(int a, int b) {} + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template <class T> +struct MOZ_NON_TEMPORARY_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void gobbleref(const NonTemporary&) { } + +template <class T> +void gobbleanyref(const T&) { } + +void misuseNonTemporaryClass(int len) { + NonTemporary invalid; + NonTemporary alsoInvalid[2]; + static NonTemporary invalidStatic; + static NonTemporary alsoInvalidStatic[2]; + + gobble(&invalid); + gobble(&invalidStatic); + gobble(&alsoInvalid[0]); + + gobbleref(NonTemporary()); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(NonTemporary(10, 20)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(NonTemporary(10)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleref(10); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleanyref(TemplateClass<int>()); // expected-error {{variable of type 'TemplateClass<int>' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + + gobble(new NonTemporary); + gobble(new NonTemporary[10]); + gobble(new TemplateClass<int>); + gobble(len <= 5 ? &invalid : new NonTemporary); + + char buffer[sizeof(NonTemporary)]; + gobble(new (buffer) NonTemporary); +} + +void defaultArg(const NonTemporary& arg = NonTemporary()) { +} + +NonTemporary invalidStatic; +struct RandomClass { + NonTemporary nonstaticMember; // expected-note {{'RandomClass' is a non-temporary type because member 'nonstaticMember' is a non-temporary type 'NonTemporary'}} + static NonTemporary staticMember; +}; +struct MOZ_NON_TEMPORARY_CLASS RandomNonTemporaryClass { + NonTemporary nonstaticMember; + static NonTemporary staticMember; +}; + +struct BadInherit : NonTemporary {}; // expected-note {{'BadInherit' is a non-temporary type because it inherits from a non-temporary type 'NonTemporary'}} + +void useStuffWrongly() { + gobbleanyref(BadInherit()); // expected-error {{variable of type 'BadInherit' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} + gobbleanyref(RandomClass()); // expected-error {{variable of type 'RandomClass' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}} +} diff --git a/build/clang-plugin/tests/TestOverrideBaseCall.cpp b/build/clang-plugin/tests/TestOverrideBaseCall.cpp new file mode 100644 index 000000000..6fdaaad04 --- /dev/null +++ b/build/clang-plugin/tests/TestOverrideBaseCall.cpp @@ -0,0 +1,175 @@ +#define MOZ_REQUIRED_BASE_METHOD __attribute__((annotate("moz_required_base_method"))) + +class Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + } + + virtual int foRet() MOZ_REQUIRED_BASE_METHOD { + return 0; + } +}; + +class BaseOne : public Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + Base::fo(); + } +}; + +class BaseSecond : public Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + Base::fo(); + } +}; + +class Deriv : public BaseOne, public BaseSecond { +public: + void func() { + } + + void fo() { + func(); + BaseSecond::fo(); + BaseOne::fo(); + } +}; + +class DerivSimple : public Base { +public: + void fo() { // expected-error {{Method Base::fo must be called in all overrides, but is not called in this override defined for class DerivSimple}} + } +}; + +class BaseVirtualOne : public virtual Base { +}; + +class BaseVirtualSecond: public virtual Base { +}; + +class DerivVirtual : public BaseVirtualOne, public BaseVirtualSecond { +public: + void fo() { + Base::fo(); + } +}; + +class DerivIf : public Base { +public: + void fo() { + if (true) { + Base::fo(); + } + } +}; + +class DerivIfElse : public Base { +public: + void fo() { + if (true) { + Base::fo(); + } else { + Base::fo(); + } + } +}; + +class DerivFor : public Base { +public: + void fo() { + for (int i = 0; i < 10; i++) { + Base::fo(); + } + } +}; + +class DerivDoWhile : public Base { +public: + void fo() { + do { + Base::fo(); + } while(false); + } +}; + +class DerivWhile : public Base { +public: + void fo() { + while (true) { + Base::fo(); + break; + } + } +}; + +class DerivAssignment : public Base { +public: + int foRet() { + return foRet(); + } +}; + +class BaseOperator { +private: + int value; +public: + BaseOperator() : value(0) { + } + virtual BaseOperator& operator++() MOZ_REQUIRED_BASE_METHOD { + value++; + return *this; + } +}; + +class DerivOperatorErr : public BaseOperator { +private: + int value; +public: + DerivOperatorErr() : value(0) { + } + DerivOperatorErr& operator++() { // expected-error {{Method BaseOperator::operator++ must be called in all overrides, but is not called in this override defined for class DerivOperatorErr}} + value++; + return *this; + } +}; + +class DerivOperator : public BaseOperator { +private: + int value; +public: + DerivOperator() : value(0) { + } + DerivOperator& operator++() { + BaseOperator::operator++(); + value++; + return *this; + } +}; + +class DerivPrime : public Base { +public: + void fo() { + Base::fo(); + } +}; + +class DerivSecondErr : public DerivPrime { +public: + void fo() { // expected-error {{Method Base::fo must be called in all overrides, but is not called in this override defined for class DerivSecondErr}} + } +}; + +class DerivSecond : public DerivPrime { +public: + void fo() { + Base::fo(); + } +}; + +class DerivSecondIndirect : public DerivPrime { +public: + void fo() { + DerivPrime::fo(); + } +}; diff --git a/build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp b/build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp new file mode 100644 index 000000000..e268122c6 --- /dev/null +++ b/build/clang-plugin/tests/TestOverrideBaseCallAnnotation.cpp @@ -0,0 +1,47 @@ +#define MOZ_REQUIRED_BASE_METHOD __attribute__((annotate("moz_required_base_method"))) + +class Base { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + } +}; + +class BaseNonVirtual { +public: + void fo() MOZ_REQUIRED_BASE_METHOD { // expected-error {{MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods}} + } +}; + +class Deriv : public BaseNonVirtual { +public: + virtual void fo() MOZ_REQUIRED_BASE_METHOD { + } +}; + +class DerivVirtual : public Base { +public: + void fo() MOZ_REQUIRED_BASE_METHOD { + Base::fo(); + } +}; + +class BaseOperator { +public: + BaseOperator& operator++() MOZ_REQUIRED_BASE_METHOD { // expected-error {{MOZ_REQUIRED_BASE_METHOD can be used only on virtual methods}} + return *this; + } +}; + +class DerivOperator : public BaseOperator { +public: + virtual DerivOperator& operator++() { + return *this; + } +}; + +class DerivPrimeOperator : public DerivOperator { +public: + DerivPrimeOperator& operator++() { + return *this; + } +}; diff --git a/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp b/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp new file mode 100644 index 000000000..d3bd73084 --- /dev/null +++ b/build/clang-plugin/tests/TestRefCountedCopyConstructor.cpp @@ -0,0 +1,25 @@ +// Implicit copy construct which is unused +class RC1 { + void AddRef(); + void Release(); + int mRefCnt; +}; + +// Explicit copy constructor which is used +class RC2 { +public: + RC2(); + RC2(const RC2&); +private: + void AddRef(); + void Release(); + int mRefCnt; +}; + +void f() { + RC1* r1 = new RC1(); + RC1* r1p = new RC1(*r1); // expected-error {{Invalid use of compiler-provided copy constructor on refcounted type}} expected-note {{The default copy constructor also copies the default mRefCnt property, leading to reference count imbalance issues. Please provide your own copy constructor which only copies the fields which need to be copied}} + + RC2* r2 = new RC2(); + RC2* r2p = new RC2(*r2); +} diff --git a/build/clang-plugin/tests/TestSprintfLiteral.cpp b/build/clang-plugin/tests/TestSprintfLiteral.cpp new file mode 100644 index 000000000..9b7675433 --- /dev/null +++ b/build/clang-plugin/tests/TestSprintfLiteral.cpp @@ -0,0 +1,47 @@ +#include <cstdio> + +void bad() { + char x[100]; + snprintf(x, sizeof(x), "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + snprintf(x, 100, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + snprintf(x, 101, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + const int hundred = 100; + snprintf(x, hundred, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} + const int hundredandone = 101; + snprintf(x, hundredandone, "bar"); // expected-error {{Use SprintfLiteral instead of snprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to snprintf accidentally.}} +} + +void ok() { + char x[100]; + int y; + snprintf(x, sizeof(y), "what"); + + snprintf(x, 50, "what"); + + int nothundred = 100; + nothundred = 99; + snprintf(x, nothundred, "what"); +} + +void vargs_bad(va_list args) { + char x[100]; + vsnprintf(x, sizeof(x), "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + vsnprintf(x, 100, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + vsnprintf(x, 101, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + const int hundred = 100; + vsnprintf(x, hundred, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} + const int hundredandone = 101; + vsnprintf(x, hundredandone, "bar", args); // expected-error {{Use VsprintfLiteral instead of vsnprintf when writing into a character array.}} expected-note {{This will prevent passing in the wrong size to vsnprintf accidentally.}} +} + +void vargs_good(va_list args) { + char x[100]; + int y; + vsnprintf(x, sizeof(y), "what", args); + + vsnprintf(x, 50, "what", args); + + int nothundred = 100; + nothundred = 99; + vsnprintf(x, nothundred, "what", args); +} diff --git a/build/clang-plugin/tests/TestStackClass.cpp b/build/clang-plugin/tests/TestStackClass.cpp new file mode 100644 index 000000000..41afa39e1 --- /dev/null +++ b/build/clang-plugin/tests/TestStackClass.cpp @@ -0,0 +1,50 @@ +#define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class"))) +#include <stddef.h> + +struct MOZ_STACK_CLASS Stack { + int i; + void *operator new(size_t x) throw() { return 0; } + void *operator new(size_t blah, char *buffer) { return buffer; } +}; + +template <class T> +struct MOZ_STACK_CLASS TemplateClass { + T i; +}; + +void gobble(void *) { } + +void misuseStackClass(int len) { + Stack valid; + Stack alsoValid[2]; + static Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} + static Stack alsoNotValid[2]; // expected-error {{variable of type 'Stack [2]' only valid on the stack}} expected-note {{'Stack [2]' is a stack type because it is an array of stack type 'Stack'}} expected-note {{value incorrectly allocated in a global variable}} + + gobble(&valid); + gobble(¬Valid); + gobble(&alsoValid[0]); + + gobble(new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + gobble(new Stack[10]); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + gobble(new TemplateClass<int>); // expected-error {{variable of type 'TemplateClass<int>' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + gobble(len <= 5 ? &valid : new Stack); // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated on the heap}} + + char buffer[sizeof(Stack)]; + gobble(new (buffer) Stack); +} + +Stack notValid; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +struct RandomClass { + Stack nonstaticMember; // expected-note {{'RandomClass' is a stack type because member 'nonstaticMember' is a stack type 'Stack'}} + static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +}; +struct MOZ_STACK_CLASS RandomStackClass { + Stack nonstaticMember; + static Stack staticMember; // expected-error {{variable of type 'Stack' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +}; + +struct BadInherit : Stack {}; // expected-note {{'BadInherit' is a stack type because it inherits from a stack type 'Stack'}} +struct MOZ_STACK_CLASS GoodInherit : Stack {}; + +BadInherit moreInvalid; // expected-error {{variable of type 'BadInherit' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} +RandomClass evenMoreInvalid; // expected-error {{variable of type 'RandomClass' only valid on the stack}} expected-note {{value incorrectly allocated in a global variable}} diff --git a/build/clang-plugin/tests/TestTrivialCtorDtor.cpp b/build/clang-plugin/tests/TestTrivialCtorDtor.cpp new file mode 100644 index 000000000..cef06b0ac --- /dev/null +++ b/build/clang-plugin/tests/TestTrivialCtorDtor.cpp @@ -0,0 +1,83 @@ +#define MOZ_TRIVIAL_CTOR_DTOR __attribute__((annotate("moz_trivial_ctor_dtor"))) + +struct MOZ_TRIVIAL_CTOR_DTOR EmptyClass{}; + +template <class T> +struct MOZ_TRIVIAL_CTOR_DTOR TemplateEmptyClass{}; + +struct MOZ_TRIVIAL_CTOR_DTOR NonEmptyClass { + void *m; +}; + +template <class T> +struct MOZ_TRIVIAL_CTOR_DTOR TemplateNonEmptyClass { + T* m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadUserDefinedCtor { // expected-error {{class 'BadUserDefinedCtor' must have trivial constructors and destructors}} + BadUserDefinedCtor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadUserDefinedDtor { // expected-error {{class 'BadUserDefinedDtor' must have trivial constructors and destructors}} + ~BadUserDefinedDtor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadVirtualDtor { // expected-error {{class 'BadVirtualDtor' must have trivial constructors and destructors}} + virtual ~BadVirtualDtor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkVirtualMember { + virtual void f(); +}; + +void foo(); +struct MOZ_TRIVIAL_CTOR_DTOR BadNonEmptyCtorDtor { // expected-error {{class 'BadNonEmptyCtorDtor' must have trivial constructors and destructors}} + BadNonEmptyCtorDtor() { foo(); } + ~BadNonEmptyCtorDtor() { foo(); } +}; + +struct NonTrivialCtor { + NonTrivialCtor() { foo(); } +}; + +struct NonTrivialDtor { + ~NonTrivialDtor() { foo(); } +}; + +struct VirtualMember { + virtual void f(); +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialCtorInBase : NonTrivialCtor { // expected-error {{class 'BadNonTrivialCtorInBase' must have trivial constructors and destructors}} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialDtorInBase : NonTrivialDtor { // expected-error {{class 'BadNonTrivialDtorInBase' must have trivial constructors and destructors}} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialCtorInMember { // expected-error {{class 'BadNonTrivialCtorInMember' must have trivial constructors and destructors}} + NonTrivialCtor m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR BadNonTrivialDtorInMember { // expected-error {{class 'BadNonTrivialDtorInMember' must have trivial constructors and destructors}} + NonTrivialDtor m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkVirtualMemberInMember { + VirtualMember m; +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkConstExprConstructor { + constexpr OkConstExprConstructor() {} +}; + +struct MOZ_TRIVIAL_CTOR_DTOR OkConstExprConstructorInMember { + OkConstExprConstructor m; +}; + +// XXX: This error is unfortunate, but is unlikely to come up in real code. +// In this situation, it should be possible to define a constexpr constructor +// which explicitly initializes the members. +struct MOZ_TRIVIAL_CTOR_DTOR BadUnfortunateError { // expected-error {{class 'BadUnfortunateError' must have trivial constructors and destructors}} + OkConstExprConstructor m; + void *n; +}; diff --git a/build/clang-plugin/tests/moz.build b/build/clang-plugin/tests/moz.build new file mode 100644 index 000000000..3f7bdcba1 --- /dev/null +++ b/build/clang-plugin/tests/moz.build @@ -0,0 +1,45 @@ +# -*- Mode: python; 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/. + +# dummy library name to avoid skipping building the sources here. +Library('clang-plugin-tests') + +SOURCES += [ + 'TestAssertWithAssignment.cpp', + 'TestBadImplicitConversionCtor.cpp', + 'TestCustomHeap.cpp', + 'TestExplicitOperatorBool.cpp', + 'TestGlobalClass.cpp', + 'TestHeapClass.cpp', + 'TestInheritTypeAnnotationsFromTemplateArgs.cpp', + 'TestKungFuDeathGrip.cpp', + 'TestMultipleAnnotations.cpp', + 'TestMustOverride.cpp', + 'TestMustUse.cpp', + 'TestNANTestingExpr.cpp', + 'TestNANTestingExprC.c', + 'TestNeedsNoVTableType.cpp', + 'TestNoAddRefReleaseOnReturn.cpp', + 'TestNoArithmeticExprInArgument.cpp', + 'TestNoAutoType.cpp', + 'TestNoDuplicateRefCntMember.cpp', + 'TestNoExplicitMoveConstructor.cpp', + 'TestNonHeapClass.cpp', + 'TestNonMemMovable.cpp', + 'TestNonMemMovableStd.cpp', + 'TestNonParameterChecker.cpp', + 'TestNonTemporaryClass.cpp', + 'TestNoRefcountedInsideLambdas.cpp', + 'TestOverrideBaseCall.cpp', + 'TestOverrideBaseCallAnnotation.cpp', + 'TestRefCountedCopyConstructor.cpp', + 'TestSprintfLiteral.cpp', + 'TestStackClass.cpp', + 'TestTrivialCtorDtor.cpp', +] + +DISABLE_STL_WRAPPING = True +NO_VISIBILITY_FLAGS = True diff --git a/build/compare-mozconfig/compare-mozconfigs-wrapper.py b/build/compare-mozconfig/compare-mozconfigs-wrapper.py new file mode 100644 index 000000000..e1888eed7 --- /dev/null +++ b/build/compare-mozconfig/compare-mozconfigs-wrapper.py @@ -0,0 +1,68 @@ +#!/usr/bin/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/. + +from __future__ import unicode_literals + +import logging +import mozunit +import subprocess +import sys +import unittest + +from os import path +from buildconfig import substs + +log = logging.getLogger(__name__) + +def determine_platform(): + platform_mapping = {'WINNT': {'x86_64': 'win64', + 'i686': 'win32'}, + 'Darwin': {'x86_64': 'macosx-universal', + 'i386':'macosx-universal'}, + 'Linux': {'x86_64': 'linux64', + 'i686': 'linux32'}} + + os_type = substs['OS_TARGET'] + cpu_type = substs['TARGET_CPU'] + return platform_mapping.get(os_type, {}).get(cpu_type, None) + + +class TestCompareMozconfigs(unittest.TestCase): + def test_compare_mozconfigs(self): + """ A wrapper script that calls compare-mozconfig.py + based on the platform that the machine is building for""" + platform = determine_platform() + + if platform is not None: + python_exe = substs['PYTHON'] + topsrcdir = substs['top_srcdir'] + + # construct paths and args for compare-mozconfig + browser_dir = path.join(topsrcdir, 'browser') + script_path = path.join(topsrcdir, 'build/compare-mozconfig/compare-mozconfigs.py') + whitelist_path = path.join(browser_dir, 'config/mozconfigs/whitelist') + beta_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'beta') + release_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'release') + nightly_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'nightly') + + log.info("Comparing beta against nightly mozconfigs") + ret_code = subprocess.call([python_exe, script_path, '--whitelist', + whitelist_path, '--no-download', + platform + ',' + beta_mozconfig_path + + ',' + nightly_mozconfig_path]) + + if ret_code > 0: + return ret_code + + log.info("Comparing release against nightly mozconfigs") + ret_code = subprocess.call([python_exe, script_path, '--whitelist', + whitelist_path, '--no-download', + platform + ',' + release_mozconfig_path + + ',' + nightly_mozconfig_path]) + + self.assertEqual(0, ret_code) + +if __name__ == '__main__': + mozunit.main() diff --git a/build/compare-mozconfig/compare-mozconfigs.py b/build/compare-mozconfig/compare-mozconfigs.py new file mode 100644 index 000000000..876e336fe --- /dev/null +++ b/build/compare-mozconfig/compare-mozconfigs.py @@ -0,0 +1,170 @@ +#!/usr/bin/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/. + +# originally from http://hg.mozilla.org/build/tools/file/4ab9c1a4e05b/scripts/release/compare-mozconfigs.py + +from __future__ import unicode_literals + +import logging +import os +import site +import sys +import urllib2 +import difflib + +FAILURE_CODE = 1 +SUCCESS_CODE = 0 + +log = logging.getLogger(__name__) + +class ConfigError(Exception): + pass + +def make_hg_url(hgHost, repoPath, protocol='https', revision=None, + filename=None): + """construct a valid hg url from a base hg url (hg.mozilla.org), + repoPath, revision and possible filename""" + base = '%s://%s' % (protocol, hgHost) + repo = '/'.join(p.strip('/') for p in [base, repoPath]) + if not filename: + if not revision: + return repo + else: + return '/'.join([p.strip('/') for p in [repo, 'rev', revision]]) + else: + assert revision + return '/'.join([p.strip('/') for p in [repo, 'raw-file', revision, + filename]]) + +def readConfig(configfile, keys=[], required=[]): + c = {} + execfile(configfile, c) + for k in keys: + c = c[k] + items = c.keys() + err = False + for key in required: + if key not in items: + err = True + log.error("Required item `%s' missing from %s" % (key, c)) + if err: + raise ConfigError("Missing at least one item in config, see above") + return c + +def verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, platform, + mozconfigWhitelist={}): + """Compares mozconfig to nightly_mozconfig and compare to an optional + whitelist of known differences. mozconfig_pair and nightly_mozconfig_pair + are pairs containing the mozconfig's identifier and the list of lines in + the mozconfig.""" + + # unpack the pairs to get the names, the names are just for + # identifying the mozconfigs when logging the error messages + mozconfig_name, mozconfig_lines = mozconfig_pair + nightly_mozconfig_name, nightly_mozconfig_lines = nightly_mozconfig_pair + + missing_args = mozconfig_lines == [] or nightly_mozconfig_lines == [] + if missing_args: + log.info("Missing mozconfigs to compare for %s" % platform) + return False + + success = True + + diff_instance = difflib.Differ() + diff_result = diff_instance.compare(mozconfig_lines, nightly_mozconfig_lines) + diff_list = list(diff_result) + + for line in diff_list: + clean_line = line[1:].strip() + if (line[0] == '-' or line[0] == '+') and len(clean_line) > 1: + # skip comment lines + if clean_line.startswith('#'): + continue + # compare to whitelist + message = "" + if line[0] == '-': + # handle lines that move around in diff + if '+' + line[1:] in diff_list: + continue + if platform in mozconfigWhitelist.get('release', {}): + if clean_line in \ + mozconfigWhitelist['release'][platform]: + continue + elif line[0] == '+': + if '-' + line[1:] in diff_list: + continue + if platform in mozconfigWhitelist.get('nightly', {}): + if clean_line in \ + mozconfigWhitelist['nightly'][platform]: + continue + else: + log.warning("%s not in %s %s!" % ( + clean_line, platform, + mozconfigWhitelist['nightly'][platform])) + else: + log.error("Skipping line %s!" % line) + continue + message = "found in %s but not in %s: %s" + if line[0] == '-': + log.error(message % (mozconfig_name, + nightly_mozconfig_name, clean_line)) + else: + log.error(message % (nightly_mozconfig_name, + mozconfig_name, clean_line)) + success = False + return success + +def get_mozconfig(path, options): + """Consumes a path and returns a list of lines from + the mozconfig file. If download is required, the path + specified should be relative to the root of the hg + repository e.g browser/config/mozconfigs/linux32/nightly""" + if options.no_download: + return open(path, 'r').readlines() + else: + url = make_hg_url(options.hghost, options.branch, 'http', + options.revision, path) + return urllib2.urlopen(url).readlines() + +if __name__ == '__main__': + from optparse import OptionParser + parser = OptionParser() + + parser.add_option('--branch', dest='branch') + parser.add_option('--revision', dest='revision') + parser.add_option('--hghost', dest='hghost', default='hg.mozilla.org') + parser.add_option('--whitelist', dest='whitelist') + parser.add_option('--no-download', action='store_true', dest='no_download', + default=False) + options, args = parser.parse_args() + + logging.basicConfig(level=logging.INFO) + + missing_args = options.branch is None or options.revision is None + if not options.no_download and missing_args: + logging.error('Not enough arguments to download mozconfigs') + sys.exit(FAILURE_CODE) + + mozconfig_whitelist = readConfig(options.whitelist, ['whitelist']) + + for arg in args: + platform, mozconfig_path, nightly_mozconfig_path = arg.split(',') + + mozconfig_lines = get_mozconfig(mozconfig_path, options) + nightly_mozconfig_lines = get_mozconfig(nightly_mozconfig_path, options) + + mozconfig_pair = (mozconfig_path, mozconfig_lines) + nightly_mozconfig_pair = (nightly_mozconfig_path, + nightly_mozconfig_lines) + + passed = verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, + platform, mozconfig_whitelist) + + if passed: + logging.info('Mozconfig check passed!') + else: + logging.error('Mozconfig check failed!') + sys.exit(FAILURE_CODE) + sys.exit(SUCCESS_CODE) diff --git a/build/defines.sh b/build/defines.sh new file mode 100644 index 000000000..cf98c3d8b --- /dev/null +++ b/build/defines.sh @@ -0,0 +1,3 @@ +# Define indicating that this build is prior to one of the early betas. To be +# unset mid-way through the beta cycle. +EARLY_BETA_OR_EARLIER= diff --git a/build/docs/androideclipse.rst b/build/docs/androideclipse.rst new file mode 100644 index 000000000..a8c048130 --- /dev/null +++ b/build/docs/androideclipse.rst @@ -0,0 +1,90 @@ +.. _build_androideclipse: + +======================== +Android Eclipse Projects +======================== + +The build system contains alpha support for generating Android Eclipse +project files to aid with development. + +To generate Android Eclipse project files, you'll need to have a fully +built and packaged tree:: + + mach build && mach package + +(This is because Eclipse itself packages an APK containing +``omni.ja``, and ``omni.ja`` is only assembled during packaging.) + +Then, simply generate the Android Eclipse build backend:: + + mach build-backend -b AndroidEclipse + +If all goes well, the path to the generated projects should be +printed (currently, ``$OBJDIR/android_eclipse``). + +To use the generated Android Eclipse project files, you'll need to +have a recent version of Eclipse (see `Tested Versions`_) with the +`Eclipse ADT plugin +<http://developer.android.com/tools/sdk/eclipse-adt.html>`_ +installed. You can then import all the projects into Eclipse using +*File > Import ... > General > Existing Projects into Workspace*. + +Updating Project Files +====================== + +As you pull and update the source tree, your Android Eclipse files may +fall out of sync with the build configuration. The tree should still +build fine from within Eclipse, but source files may be missing and in +rare circumstances Eclipse's index may not have the proper build +configuration. + +To account for this, you'll want to periodically regenerate the +Android Eclipse project files. You can do this by running ``mach build +&& mach package && mach build-backend -b AndroidEclipse`` from the +command line. It's a good idea to refresh and clean build all projects +in Eclipse after doing this. + +In future, we'd like to include an Android Eclipse run configuration +or build target that integrates updating the project files. + +Currently, regeneration rewrites the original project files. **If +you've made any customizations to the projects, they will likely get +overwritten.** We would like to improve this user experience in the +future. + +Troubleshooting +=============== + +If Eclipse's builder gets confused, you should always refresh and +clean build all projects. If Eclipse's builder is continually +confused, you can see a log of what is happening at +``$OBJDIR/android_eclipse/build.log``. + +If you run into memory problems executing ``dex``, you should +`Increase Eclipse's memory limits <http://stackoverflow.com/a/11093228>`_. + +The produced Android Eclipse project files are unfortunately not +portable. Please don't move them around. + +Structure of Android Eclipse projects +===================================== + +The Android Eclipse backend generates several projects spanning Fennec +itself and its tests. You'll mostly interact with the *Fennec* project +itself. + +In future, we'd like to expand this documentation to include some of +the technical details of how the Eclipse integration works, and how to +add additional Android Eclipse projects using the ``moz.build`` +system. + +Tested Versions +=============== + +=============== ==================================== ================= +OS Version Working as of +=============== ==================================== ================= +Mac OS X Luna (Build id: 20130919-0819) February 2014 +Mac OS X Kepler (Build id: 20131219-0014) February 2014 +Mac OS X 10.8.5 Kepler (Build id: 20130919-0819) February 2014 +=============== ==================================== ================= diff --git a/build/docs/build-overview.rst b/build/docs/build-overview.rst new file mode 100644 index 000000000..a81531bc9 --- /dev/null +++ b/build/docs/build-overview.rst @@ -0,0 +1,117 @@ +.. _build_overview: + +===================== +Build System Overview +===================== + +This document provides an overview on how the build system works. It is +targeted at people wanting to learn about internals of the build system. +It is not meant for persons who casually interact with the build system. +That being said, knowledge empowers, so consider reading on. + +The build system is composed of many different components working in +harmony to build the source tree. We begin with a graphic overview. + +.. graphviz:: + + digraph build_components { + rankdir="LR"; + "configure" -> "config.status" -> "build backend" -> "build output" + } + +Phase 1: Configuration +====================== + +Phase 1 centers around the ``configure`` script, which is a bash shell script. +The file is generated from a file called ``configure.in`` which is written in M4 +and processed using Autoconf 2.13 to create the final configure script. +You don't have to worry about how you obtain a ``configure`` file: the build +system does this for you. + +The primary job of ``configure`` is to determine characteristics of the system +and compiler, apply options passed into it, and validate everything looks OK to +build. The primary output of the ``configure`` script is an executable file +in the object directory called ``config.status``. ``configure`` also produces +some additional files (like ``autoconf.mk``). However, the most important file +in terms of architecture is ``config.status``. + +The existence of a ``config.status`` file may be familiar to those who have worked +with Autoconf before. However, Mozilla's ``config.status`` is different from almost +any other ``config.status`` you've ever seen: it's written in Python! Instead of +having our ``configure`` script produce a shell script, we have it generating +Python. + +Now is as good a time as any to mention that Python is prevalent in our build +system. If we need to write code for the build system, we do it in Python. +That's just how we roll. For more, see :ref:`python`. + +``config.status`` contains 2 parts: data structures representing the output of +``configure`` and a command-line interface for preparing/configuring/generating +an appropriate build backend. (A build backend is merely a tool used to build +the tree - like GNU Make or Tup). These data structures essentially describe +the current state of the system and what the existing build configuration looks +like. For example, it defines which compiler to use, how to invoke it, which +application features are enabled, etc. You are encouraged to open up +``config.status`` to have a look for yourself! + +Once we have emitted a ``config.status`` file, we pass into the realm of +phase 2. + +Phase 2: Build Backend Preparation and the Build Definition +=========================================================== + +Once ``configure`` has determined what the current build configuration is, +we need to apply this to the source tree so we can actually build. + +What essentially happens is the automatically-produced ``config.status`` Python +script is executed as soon as ``configure`` has generated it. ``config.status`` +is charged with the task of tell a tool how to build the tree. To do this, +``config.status`` must first scan the build system definition. + +The build system definition consists of various ``moz.build`` files in the tree. +There is roughly one ``moz.build`` file per directory or per set of related directories. +Each ``moz.build`` files defines how its part of the build config works. For +example it says *I want these C++ files compiled* or *look for additional +information in these directories.* config.status starts with the ``moz.build`` +file from the root directory and then descends into referenced ``moz.build`` +files by following ``DIRS`` variables or similar. + +As the ``moz.build`` files are read, data structures describing the overall +build system definition are emitted. These data structures are then fed into a +build backend, which then performs actions, such as writing out files to +be read by a build tool. e.g. a ``make`` backend will write a +``Makefile``. + +When ``config.status`` runs, you'll see the following output:: + + Reticulating splines... + Finished reading 1096 moz.build files into 1276 descriptors in 2.40s + Backend executed in 2.39s + 2188 total backend files. 0 created; 1 updated; 2187 unchanged + Total wall time: 5.03s; CPU time: 3.79s; Efficiency: 75% + +What this is saying is that a total of *1096* ``moz.build`` files were read. +Altogether, *1276* data structures describing the build configuration were +derived from them. It took *2.40s* wall time to just read these files and +produce the data structures. The *1276* data structures were fed into the +build backend which then determined it had to manage *2188* files derived +from those data structures. Most of them already existed and didn't need +changed. However, *1* was updated as a result of the new configuration. +The whole process took *5.03s*. Although, only *3.79s* was in +CPU time. That likely means we spent roughly *25%* of the time waiting on +I/O. + +For more on how ``moz.build`` files work, see :ref:`mozbuild-files`. + +Phase 3: Invokation of the Build Backend +======================================== + +When most people think of the build system, they think of phase 3. This is +where we take all the code in the tree and produce Firefox or whatever +application you are creating. Phase 3 effectively takes whatever was +generated by phase 2 and runs it. Since the dawn of Mozilla, this has been +make consuming Makefiles. However, with the transition to moz.build files, +you may soon see non-Make build backends, such as Tup or Visual Studio. + +When building the tree, most of the time is spent in phase 3. This is when +header files are installed, C++ files are compiled, files are preprocessed, etc. diff --git a/build/docs/build-targets.rst b/build/docs/build-targets.rst new file mode 100644 index 000000000..dacd46c7f --- /dev/null +++ b/build/docs/build-targets.rst @@ -0,0 +1,62 @@ +.. _build_targets: + +============= +Build Targets +============= + +When you build with ``mach build``, there are some special targets that can be +built. This page attempts to document them. + +Partial Tree Targets +==================== + +The targets in this section only build part of the tree. Please note that +partial tree builds can be unreliable. Use at your own risk. + +export + Build the *export* tier. The *export* tier builds everything that is + required for C/C++ compilation. It stages all header files, processes + IDLs, etc. + +compile + Build the *compile* tier. The *compile* tier compiles all C/C++ files. + +libs + Build the *libs* tier. The *libs* tier performs linking and performs + most build steps which aren't related to compilation. + +tools + Build the *tools* tier. The *tools* tier mostly deals with supplementary + tools and compiled tests. It will link tools against libXUL, including + compiled test binaries. + +binaries: + Recompiles and relinks C/C++ files. Only works after a complete normal + build, but allows for much faster rebuilds of C/C++ code. For performance + reasons, however, it skips nss, nspr, icu and ffi. This is targeted to + improve local developer workflow when touching C/C++ code. + +install-manifests + Process install manifests. Install manifests handle the installation of + files into the object directory. + + Unless ``NO_REMOVE=1`` is defined in the environment, files not accounted + in the install manifests will be deleted from the object directory. + +install-tests + Processes the tests install manifest. + +Common Actions +============== + +The targets in this section correspond to common build-related actions. Many +of the actions in this section are effectively frontends to shell scripts. +These actions will likely all be replaced by mach commands someday. + +buildsymbols + Create a symbols archive for the current build. + + This must be performed after a successful build. + +check + Run build system tests. diff --git a/build/docs/cppeclipse.rst b/build/docs/cppeclipse.rst new file mode 100644 index 000000000..4492add47 --- /dev/null +++ b/build/docs/cppeclipse.rst @@ -0,0 +1,54 @@ +.. _build_cppeclipse: + +===================== +Cpp Eclipse Projects +===================== + +For additional information on using Eclipse CDT see +`the MDN page +<https://developer.mozilla.org/en-US/docs/Eclipse_CDT>`_. + +The build system contains alpha support for generating C++ Eclipse +project files to aid with development. + +Please report bugs to bugzilla and make them depend on bug 973770. + +To generate a C++ Eclipse project files, you'll need to have a fully +built tree:: + + mach build + +Then, simply generate the Android Eclipse build backend:: + + mach build-backend -b CppEclipse + +If all goes well, the path to the generated workspace should be +printed (currently, ``$OBJDIR/android_eclipse``). + +To use the generated Android Eclipse project files, you'll need to +have a Eclipse CDT 8.3 (We plan to follow the latest Eclipse release) +`Eclipse CDT plugin +<https://www.eclipse.org/cdt/>`_ +installed. You can then import all the projects into Eclipse using +*File > Import ... > General > Existing Projects into Workspace* +-only- if you have not ran the background indexer. + +Updating Project Files +====================== + +As you pull and update the source tree, your C++ Eclipse files may +fall out of sync with the build configuration. The tree should still +build fine from within Eclipse, but source files may be missing and in +rare circumstances Eclipse's index may not have the proper build +configuration. + +To account for this, you'll want to periodically regenerate the +Android Eclipse project files. You can do this by running ``mach build +&& mach build-backend -b CppEclipse`` from the +command line. + +Currently, regeneration rewrites the original project files. **If +you've made any customizations to the projects, they will likely get +overwritten.** We would like to improve this user experience in the +future. + diff --git a/build/docs/defining-binaries.rst b/build/docs/defining-binaries.rst new file mode 100644 index 000000000..97dadbbd5 --- /dev/null +++ b/build/docs/defining-binaries.rst @@ -0,0 +1,351 @@ +.. _defining_binaries: + +====================================== +Defining Binaries for the Build System +====================================== + +One part of what the build system does is compile C/C++ and link the resulting +objects to produce executables and/or libraries. This document describes the +basics of defining what is going to be built and how. All the following +describes constructs to use in moz.build files. + + +Source files +============ + +Source files to be used in a given directory are registered in the ``SOURCES`` +and ``UNIFIED_SOURCES`` variables. ``UNIFIED_SOURCES`` have a special behavior +in that they are aggregated by batches of 16, requiring, for example, that there +are no conflicting variables in those source files. + +``SOURCES`` and ``UNIFIED_SOURCES`` are lists which must be appended to, and +each append requires the given list to be alphanumerically ordered. + +.. code-block:: python + + UNIFIED_SOURCES += [ + 'FirstSource.cpp', + 'SecondSource.cpp', + 'ThirdSource.cpp', + ] + + SOURCES += [ + 'OtherSource.cpp', + ] + +``SOURCES`` and ``UNIFIED_SOURCES`` can contain a mix of different file types, +for C, C++, and Objective C. + + +Static Libraries +================ + +To build a static library, other than defining the source files (see above), one +just needs to define a library name with the ``Library`` template. + +.. code-block:: python + + Library('foo') + +The library file name will be ``libfoo.a`` on UNIX systems and ``foo.lib`` on +Windows. + +If the static library needs to aggregate other static libraries, a list of +``Library`` names can be added to the ``USE_LIBS`` variable. Like ``SOURCES``, it +requires the appended list to be alphanumerically ordered. + +.. code-block:: python + + USE_LIBS += ['bar', 'baz'] + +If there are multiple directories containing the same ``Library`` name, it is +possible to disambiguate by prefixing with the path to the wanted one (relative +or absolute): + +.. code-block:: python + + USE_LIBS += [ + '/path/from/topsrcdir/to/bar', + '../relative/baz', + ] + +Note that the leaf name in those paths is the ``Library`` name, not an actual +file name. + +Note that currently, the build system may not create an actual library for +static libraries. It is an implementation detail that shouldn't need to be +worried about. + +As a special rule, ``USE_LIBS`` is allowed to contain references to shared +libraries. In such cases, programs and shared libraries linking this static +library will inherit those shared library dependencies. + + +Intermediate (Static) Libraries +=============================== + +In many cases in the tree, static libraries are built with the only purpose +of being linked into another, bigger one (like libxul). Instead of adding all +required libraries to ``USE_LIBS`` for the bigger one, it is possible to tell +the build system that the library built in the current directory is meant to +be linked to that bigger library, with the ``FINAL_LIBRARY`` variable. + +.. code-block:: python + + FINAL_LIBRARY = 'xul' + +The ``FINAL_LIBRARY`` value must match a unique ``Library`` name somewhere +in the tree. + +As a special rule, those intermediate libraries don't need a ``Library`` name +for themselves. + + +Shared Libraries +================ + +Sometimes, we want shared libraries, a.k.a. dynamic libraries. Such libraries +are defined similarly to static libraries, using the ``SharedLibrary`` template +instead of ``Library``. + +.. code-block:: python + + SharedLibrary('foo') + +When this template is used, no static library is built. See further below to +build both types of libraries. + +With a ``SharedLibrary`` name of ``foo``, the library file name will be +``libfoo.dylib`` on OSX, ``libfoo.so`` on ELF systems (Linux, etc.), and +``foo.dll`` on Windows. On Windows, there is also an import library named +``foo.lib``, used on the linker command line. ``libfoo.dylib`` and +``libfoo.so`` are considered the import library name for, resp. OSX and ELF +systems. + +On OSX, one may want to create a special kind of dynamic library: frameworks. +This is done with the ``Framework`` template. + +.. code-block:: python + + Framework('foo') + +With a ``Framework`` name of ``foo``, the framework file name will be ``foo``. +This template however affects the behavior on all platforms, so it needs to +be set only on OSX. + + +Executables +=========== + +Executables, a.k.a. programs, are, in the simplest form, defined with the +``Program`` template. + +.. code-block:: python + + Program('foobar') + +On UNIX systems, the executable file name will be ``foobar``, while on Windows, +it will be ``foobar.exe``. + +Like static and shared libraries, the build system can be instructed to link +libraries to the executable with ``USE_LIBS``, listing various ``Library`` +names. + +In some cases, we want to create an executable per source file in the current +directory, in which case we can use the ``SimplePrograms`` template + +.. code-block:: python + + SimplePrograms([ + 'FirstProgram', + 'SecondProgram', + ]) + +Contrary to ``Program``, which requires corresponding ``SOURCES``, when using +``SimplePrograms``, the corresponding ``SOURCES`` are implied. If the +corresponding ``sources`` have an extension different from ``.cpp``, it is +possible to specify the proper extension: + +.. code-block:: python + + SimplePrograms([ + 'ThirdProgram', + 'FourthProgram', + ], ext='.c') + +Please note this construct was added for compatibility with what already lives +in the mozilla tree ; it is recommended not to add new simple programs with +sources with a different extension than ``.cpp``. + +Similar to ``SimplePrograms``, is the ``CppUnitTests`` template, which defines, +with the same rules, C++ unit tests programs. Like ``SimplePrograms``, it takes +an ``ext`` argument to specify the extension for the corresponding ``SOURCES``, +if it's different from ``.cpp``. + + +Linking with system libraries +============================= + +Programs and libraries usually need to link with system libraries, such as a +widget toolkit, etc. Those required dependencies can be given with the +``OS_LIBS`` variable. + +.. code-block:: python + + OS_LIBS += [ + 'foo', + 'bar', + ] + +This expands to ``foo.lib bar.lib`` when building with MSVC, and +``-lfoo -lbar`` otherwise. + +For convenience with ``pkg-config``, ``OS_LIBS`` can also take linker flags +such as ``-L/some/path`` and ``-llib``, such that it is possible to directly +assign ``LIBS`` variables from ``CONFIG``, such as: + +.. code-block:: python + + OS_LIBS += CONFIG['MOZ_PANGO_LIBS'] + +(assuming ``CONFIG['MOZ_PANGO_LIBS']`` is a list, not a string) + +Like ``USE_LIBS``, this variable applies to static and shared libraries, as +well as programs. + + +Libraries from third party build system +======================================= + +Some libraries in the tree are not built by the moz.build-governed build +system, and there is no ``Library`` corresponding to them. + +However, ``USE_LIBS`` allows to reference such libraries by giving a full +path (like when disambiguating identical ``Library`` names). The same naming +rules apply as other uses of ``USE_LIBS``, so only the library name without +prefix and suffix shall be given. + +.. code-block:: python + + USE_LIBS += [ + '/path/from/topsrcdir/to/third-party/bar', + '../relative/third-party/baz', + ] + +Note that ``/path/from/topsrcdir/to/third-party`` and +``../relative/third-party/baz`` must lead under a subconfigured directory (a +directory with an AC_OUTPUT_SUBDIRS in configure.in), or ``security/nss``. + + +Building both static and shared libraries +========================================= + +When both types of libraries are required, one needs to set both +``FORCE_SHARED_LIB`` and ``FORCE_STATIC_LIB`` boolean variables. + +.. code-block:: python + + FORCE_SHARED_LIB = True + FORCE_STATIC_LIB = True + +But because static libraries and Windows import libraries have the same file +names, either the static or the shared library name needs to be different +than the name given to the ``Library`` template. + +The ``STATIC_LIBRARY_NAME`` and ``SHARED_LIBRARY_NAME`` variables can be used +to change either the static or the shared library name. + +.. code-block:: python + + Library('foo') + STATIC_LIBRARY_NAME = 'foo_s' + +With the above, on Windows, ``foo_s.lib`` will be the static library, +``foo.dll`` the shared library, and ``foo.lib`` the import library. + +In some cases, for convenience, it is possible to set both +``STATIC_LIBRARY_NAME`` and ``SHARED_LIBRARY_NAME``. For example: + +.. code-block:: python + + Library('mylib') + STATIC_LIBRARY_NAME = 'mylib_s' + SHARED_LIBRARY_NAME = CONFIG['SHARED_NAME'] + +This allows to use ``mylib`` in the ``USE_LIBS`` of another library or +executable. + +When refering to a ``Library`` name building both types of libraries in +``USE_LIBS``, the shared library is chosen to be linked. But sometimes, +it is wanted to link the static version, in which case the ``Library`` name +needs to be prefixed with ``static:`` in ``USE_LIBS`` + +:: + + a/moz.build: + Library('mylib') + FORCE_SHARED_LIB = True + FORCE_STATIC_LIB = True + STATIC_LIBRARY_NAME = 'mylib_s' + b/moz.build: + Program('myprog') + USE_LIBS += [ + 'static:mylib', + ] + + +Miscellaneous +============= + +The ``SDK_LIBRARY`` boolean variable defines whether the library in the current +directory is going to be installed in the SDK. + +The ``SONAME`` variable declares a "shared object name" for the library. It +defaults to the ``Library`` name or the ``SHARED_LIBRARY_NAME`` if set. When +linking to a library with a ``SONAME``, the resulting library or program will +have a dependency on the library with the name corresponding to the ``SONAME`` +instead of the ``Library`` name. This only impacts ELF systems. + +:: + + a/moz.build: + Library('mylib') + b/moz.build: + Library('otherlib') + SONAME = 'foo' + c/moz.build: + Program('myprog') + USE_LIBS += [ + 'mylib', + 'otherlib', + ] + +On e.g. Linux, the above ``myprog`` will have DT_NEEDED markers for +``libmylib.so`` and ``libfoo.so`` instead of ``libmylib.so`` and +``libotherlib.so`` if there weren't a ``SONAME``. This means the runtime +requirement for ``myprog`` is ``libfoo.so`` instead of ``libotherlib.so``. + + +Gecko-related binaries +====================== + +Some programs or libraries are totally independent of Gecko, and can use the +above mentioned templates. Others are Gecko-related in some way, and may +need XPCOM linkage, mozglue. These things are tedious. A set of additional +templates exists to ease defining such programs and libraries. They are +essentially the same as the above mentioned templates, prefixed with "Gecko": + + - ``GeckoProgram`` + - ``GeckoSimplePrograms`` + - ``GeckoCppUnitTests`` + - ``GeckoSharedLibrary`` + - ``GeckoFramework`` + +There is also ``XPCOMBinaryComponent`` for XPCOM components, which is a +special kind of library. + +All the Gecko-prefixed templates take the same arguments as their +non-Gecko-prefixed counterparts, and can take a few more arguments +for non-standard cases. See the definition of ``GeckoBinary`` in +build/gecko_templates.mozbuild for more details, but most usecases +should not require these additional arguments. diff --git a/build/docs/environment-variables.rst b/build/docs/environment-variables.rst new file mode 100644 index 000000000..c46339159 --- /dev/null +++ b/build/docs/environment-variables.rst @@ -0,0 +1,31 @@ +.. _environment_variables: + +================================================ +Environment Variables Impacting the Build System +================================================ + +Various environment variables have an impact on the behavior of the +build system. This document attempts to document them. + +AUTOCLOBBER + If defines, the build system will automatically clobber as needed. + The default behavior is to print a message and error out when a + clobber is needed. + + This variable is typically defined in a :ref:`mozconfig <mozconfig>` + file via ``mk_add_options``. + +REBUILD_CHECK + If defined, the build system will print information about why + certain files were rebuilt. + + This feature is disabled by default because it makes the build slower. + +MACH_NO_TERMINAL_FOOTER + If defined, the terminal footer displayed when building with mach in + a TTY is disabled. + +MACH_NO_WRITE_TIMES + If defined, mach commands will not prefix output lines with the + elapsed time since program start. This option is equivalent to + passing ``--log-no-times`` to mach. diff --git a/build/docs/files-metadata.rst b/build/docs/files-metadata.rst new file mode 100644 index 000000000..5af4d96fd --- /dev/null +++ b/build/docs/files-metadata.rst @@ -0,0 +1,178 @@ +.. _mozbuild_files_metadata: + +============== +Files Metadata +============== + +:ref:`mozbuild-files` provide a mechanism for attaching metadata to +files. Essentially, you define some flags to set on a file or file +pattern. Later, some tool or process queries for metadata attached to a +file of interest and it does something intelligent with that data. + +Defining Metadata +================= + +Files metadata is defined by using the +:ref:`Files Sub-Context <mozbuild_subcontext_Files>` in ``moz.build`` +files. e.g.:: + + with Files('**/Makefile.in'): + BUG_COMPONENT = ('Core', 'Build Config') + +This working example says, *for all Makefile.in files in every directory +underneath this one - including this directory - set the Bugzilla +component to Core :: Build Config*. + +For more info, read the +:ref:`docs on Files <mozbuild_subcontext_Files>`. + +How Metadata is Read +==================== + +``Files`` metadata is extracted in :ref:`mozbuild_fs_reading_mode`. + +Reading starts by specifying a set of files whose metadata you are +interested in. For each file, the filesystem is walked to the root +of the source directory. Any ``moz.build`` encountered during this +walking are marked as relevant to the file. + +Let's say you have the following filesystem content:: + + /moz.build + /root_file + /dir1/moz.build + /dir1/foo + /dir1/subdir1/foo + /dir2/foo + +For ``/root_file``, the relevant ``moz.build`` files are just +``/moz.build``. + +For ``/dir1/foo`` and ``/dir1/subdir1/foo``, the relevant files are +``/moz.build`` and ``/dir1/moz.build``. + +For ``/dir2``, the relevant file is just ``/moz.build``. + +Once the list of relevant ``moz.build`` files is obtained, each +``moz.build`` file is evaluated. Root ``moz.build`` file first, +leaf-most files last. This follows the rules of +:ref:`mozbuild_fs_reading_mode`, with the set of evaluated ``moz.build`` +files being controlled by filesystem content, not ``DIRS`` variables. + +The file whose metadata is being resolved maps to a set of ``moz.build`` +files which in turn evaluates to a list of contexts. For file metadata, +we only care about one of these contexts: +:ref:`Files <mozbuild_subcontext_Files>`. + +We start with an empty ``Files`` instance to represent the file. As +we encounter a *files sub-context*, we see if it is appropriate to +this file. If it is, we apply its values. This process is repeated +until all *files sub-contexts* have been applied or skipped. The final +state of the ``Files`` instance is used to represent the metadata for +this particular file. + +It may help to visualize this. Say we have 2 ``moz.build`` files:: + + # /moz.build + with Files('*.cpp'): + BUG_COMPONENT = ('Core', 'XPCOM') + + with Files('**/*.js'): + BUG_COMPONENT = ('Firefox', 'General') + + # /foo/moz.build + with Files('*.js'): + BUG_COMPONENT = ('Another', 'Component') + +Querying for metadata for the file ``/foo/test.js`` will reveal 3 +relevant ``Files`` sub-contexts. They are evaluated as follows: + +1. ``/moz.build - Files('*.cpp')``. Does ``/*.cpp`` match + ``/foo/test.js``? **No**. Ignore this context. +2. ``/moz.build - Files('**/*.js')``. Does ``/**/*.js`` match + ``/foo/test.js``? **Yes**. Apply ``BUG_COMPONENT = ('Firefox', 'General')`` + to us. +3. ``/foo/moz.build - Files('*.js')``. Does ``/foo/*.js`` match + ``/foo/test.js``? **Yes**. Apply + ``BUG_COMPONENT = ('Another', 'Component')``. + +At the end of execution, we have +``BUG_COMPONENT = ('Another', 'Component')`` as the metadata for +``/foo/test.js``. + +One way to look at file metadata is as a stack of data structures. +Each ``Files`` sub-context relevant to a given file is applied on top +of the previous state, starting from an empty state. The final state +wins. + +.. _mozbuild_files_metadata_finalizing: + +Finalizing Values +================= + +The default behavior of ``Files`` sub-context evaluation is to apply new +values on top of old. In most circumstances, this results in desired +behavior. However, there are circumstances where this may not be +desired. There is thus a mechanism to *finalize* or *freeze* values. + +Finalizing values is useful for scenarios where you want to prevent +wildcard matches from overwriting previously-set values. This is useful +for one-off files. + +Let's take ``Makefile.in`` files as an example. The build system module +policy dictates that ``Makefile.in`` files are part of the ``Build +Config`` module and should be reviewed by peers of that module. However, +there exist ``Makefile.in`` files in many directories in the source +tree. Without finalization, a ``*`` or ``**`` wildcard matching rule +would match ``Makefile.in`` files and overwrite their metadata. + +Finalizing of values is performed by setting the ``FINAL`` variable +on ``Files`` sub-contexts. See the +:ref:`Files documentation <mozbuild_subcontext_Files>` for more. + +Here is an example with ``Makefile.in`` files, showing how it is +possible to finalize the ``BUG_COMPONENT`` value.:: + + # /moz.build + with Files('**/Makefile.in'): + BUG_COMPONENT = ('Core', 'Build Config') + FINAL = True + + # /foo/moz.build + with Files('**'): + BUG_COMPONENT = ('Another', 'Component') + +If we query for metadata of ``/foo/Makefile.in``, both ``Files`` +sub-contexts match the file pattern. However, since ``BUG_COMPONENT`` is +marked as finalized by ``/moz.build``, the assignment from +``/foo/moz.build`` is ignored. The final value for ``BUG_COMPONENT`` +is ``('Core', 'Build Config')``. + +Here is another example:: + + with Files('*.cpp'): + BUG_COMPONENT = ('One-Off', 'For C++') + FINAL = True + + with Files('**'): + BUG_COMPONENT = ('Regular', 'Component') + +For every files except ``foo.cpp``, the bug component will be resolved +as ``Regular :: Component``. However, ``foo.cpp`` has its value of +``One-Off :: For C++`` preserved because it is finalized. + +.. important:: + + ``FINAL`` only applied to variables defined in a context. + + If you want to mark one variable as finalized but want to leave + another mutable, you'll need to use 2 ``Files`` contexts. + +Guidelines for Defining Metadata +================================ + +In general, values defined towards the root of the source tree are +generic and become more specific towards the leaves. For example, +the ``BUG_COMPONENT`` for ``/browser`` might be ``Firefox :: General`` +whereas ``/browser/components/preferences`` would list +``Firefox :: Preferences``. diff --git a/build/docs/glossary.rst b/build/docs/glossary.rst new file mode 100644 index 000000000..f846effe5 --- /dev/null +++ b/build/docs/glossary.rst @@ -0,0 +1,48 @@ +======== +Glossary +======== + +.. glossary:: + :sorted: + + object directory + A directory holding the output of the build system. The build + system attempts to isolate all file modifications to this + directory. By convention, object directories are commonly + directories under the source directory prefixed with **obj-**. + e.g. **obj-firefox**. + + mozconfig + A shell script used to configure the build system. + + configure + A generated shell script which detects the current system + environment, applies a requested set of build configuration + options, and writes out metadata to be consumed by the build + system. + + config.status + An executable file produced by **configure** that takes the + generated build config and writes out files used to build the + tree. Traditionally, config.status writes out a bunch of + Makefiles. + + install manifest + A file containing metadata describing file installation rules. + A large part of the build system consists of copying files + around to appropriate places. We write out special files + describing the set of required operations so we can process the + actions effeciently. These files are install manifests. + + clobber build + A build performed with an initially empty object directory. All + build actions must be performed. + + incremental build + A build performed with the result of a previous build in an + object directory. The build should not have to work as hard because + it will be able to reuse the work from previous builds. + + mozinfo + An API for accessing a common and limited subset of the build and + run-time configuration. See :ref:`mozinfo`. diff --git a/build/docs/index.rst b/build/docs/index.rst new file mode 100644 index 000000000..63e053757 --- /dev/null +++ b/build/docs/index.rst @@ -0,0 +1,50 @@ +============ +Build System +============ + +Important Concepts +================== +.. toctree:: + :maxdepth: 1 + + glossary + build-overview + supported-configurations + Mozconfig Files <mozconfigs> + mozbuild-files + mozbuild-symbols + files-metadata + Profile Guided Optimization <pgo> + slow + environment-variables + build-targets + python + test_manifests + mozinfo + preprocessor + jar-manifests + defining-binaries + toolchains + locales + rust + +integrated development environment (IDE) +======================================== +.. toctree:: + :maxdepth: 1 + + androideclipse + cppeclipse + visualstudio + +mozbuild +======== + +mozbuild is a Python package containing a lot of the code for the +Mozilla build system. + +.. toctree:: + :maxdepth: 1 + + mozbuild/index + mozbuild/dumbmake diff --git a/build/docs/jar-manifests.rst b/build/docs/jar-manifests.rst new file mode 100644 index 000000000..89776a101 --- /dev/null +++ b/build/docs/jar-manifests.rst @@ -0,0 +1,97 @@ +.. _jar_manifests: + +============= +JAR Manifests +============= + +JAR Manifests are plaintext files in the tree that are used to package chrome +files into the correct JARs, and create +`Chrome Registration <https://developer.mozilla.org/en-US/docs/Chrome_Registration>`_ +manifests. JAR Manifests are commonly named ``jar.mn``. They are +declared in ``moz.build`` files using the ``JAR_MANIFESTS`` variable. + +``jar.mn`` files are automatically processed by the build system when building a +source directory that contains one. The ``jar``.mn is run through the +:ref:`preprocessor` before being passed to the manifest processor. In order to +have ``@variables@`` expanded (such as ``@AB_CD@``) throughout the file, add +the line ``#filter substitution`` at the top of your ``jar.mn`` file. + +The format of a jar.mn is fairly simple; it consists of a heading specifying +which JAR file is being packaged, followed by indented lines listing files and +chrome registration instructions. + +To see a simple ``jar.mn`` file at work, see ``toolkit/profile/jar.mn``. A much +more complex ``jar.mn`` is at ``toolkit/locales/jar.mn``. + +Shipping Chrome Files +===================== + +To ship chrome files in a JAR, an indented line indicates a file to be packaged:: + + <jarfile>.jar: + path/in/jar/file_name.xul (source/tree/location/file_name.xul) + +The JAR location may be preceded with a base path between square brackets:: + [base/path] <jarfile>.jar: + path/in/jar/file_name.xul (source/tree/location/file_name.xul) + +In this case, the jar will be directly located under the given ``base/bath``, +while without a base path, it will be under a ``chrome`` directory. + +If the JAR manifest and packaged file live in the same directory, the path and +parenthesis can be omitted. In other words, the following two lines are +equivalent:: + + path/in/jar/same_place.xhtml (same_place.xhtml) + path/in/jar/same_place.xhtml + +The source tree location may also be an *absolute* path (taken from the +top of the source tree:: + + path/in/jar/file_name.xul (/path/in/sourcetree/file_name.xul) + +An asterisk marker (``*``) at the beginning of the line indicates that the +file should be processed by the :ref:`preprocessor` before being packaged:: + + * path/in/jar/preprocessed.xul (source/tree/location/file_name.xul) + +Preprocessed files always replace existing files, to ensure that changes in +``#expand`` or ``#include`` directives are picked up. + +There is a special source-directory format for localized files (note the +percent sign in the source file location): this format reads ``localized.dtd`` +from the ``en-US`` directory if building an English version, and reads the +file from the alternate localization source tree +``/l10n/<locale>/path/localized.dtd`` if building a localized version:: + + locale/path/localized.dtd (%localized/path/localized.dtd) + +The source tree location can also use wildcards, in which case the path in +jar is expected to be a base directory. Paths before the wildcard are not +made part of the destination path:: + + path/in/jar/ (source/tree/location/*.xul) + +The above will install all xul files under ``source/tree/location`` as +``path/in/jar/*.xul``. + +Register Chrome +=============== + +`Chrome Registration <https://developer.mozilla.org/en-US/docs/Chrome_Registration>`_ +instructions are marked with a percent sign (``%``) at the beginning of the +line, and must be part of the definition of a JAR file. Any additional percents +signs are replaced with an appropriate relative URL of the JAR file being +packaged:: + + % content global %path/in/jar/ + % overlay chrome://blah/content/blah.xul chrome://foo/content/overlay.xul + +There are two possible locations for a manifest file. If the chrome is being +built into a standalone application, the ``jar.mn`` processor creates a +``<jarfilename>.manifest`` next to the JAR file itself. This is the default +behavior. + +If the build specifies ``USE_EXTENSION_MANIFEST = 1``, the ``jar.mn`` processor +creates a single ``chrome.manifest`` file suitable for registering chrome as +an extension. diff --git a/build/docs/locales.rst b/build/docs/locales.rst new file mode 100644 index 000000000..4705ed963 --- /dev/null +++ b/build/docs/locales.rst @@ -0,0 +1,100 @@ +.. _localization: + +=================== +Localization (l10n) +=================== + +Single-locale language repacks +============================== + +To save on build time, the build system and automation collaborate to allow +downloading a packaged en-US Firefox, performing some locale-specific +post-processing, and re-packaging a locale-specific Firefox. Such artifacts +are termed "single-locale language repacks". There is another concept of a +"multi-locale language build", which is more like a regular build and less +like a re-packaging post-processing step. + +There are scripts in-tree in mozharness to orchestrate these re-packaging +steps for `Desktop +<https://dxr.mozilla.org/mozilla-central/source/testing/mozharness/scripts/desktop_l10n.py>`_ +and `Android +<https://dxr.mozilla.org/mozilla-central/source/testing/mozharness/scripts/mobile_l10n.py>`_ +but they rely heavily on buildbot information so they are almost impossible to +run locally. + +The following instructions are extracted from the `Android script with hg hash +494289c7 +<https://dxr.mozilla.org/mozilla-central/rev/494289c72ba3997183e7b5beaca3e0447ecaf96d/testing/mozharness/scripts/mobile_l10n.py>`_, +and may need to be updated and slightly modified for Desktop. + +Step by step instructions for Android +------------------------------------- + +This assumes that ``$AB_CD`` is the locale you want to repack with; I tested +with "ar" and "en-GB". + +.. warning:: l10n repacks do not work with artifact builds. Repackaging + compiles no code so supporting ``--disable-compile-environment`` would not + save much, if any, time. + +#. You must have a built and packaged object directory, or a pre-built + ``en-US`` package. + + .. code-block:: shell + + ./mach build + ./mach package + +#. Clone ``l10n-central/$AB_CD`` so that it is a sibling to your + ``mozilla-central`` directory. + + .. code-block:: shell + + $ ls -al + mozilla-central + ... + $ mkdir -p l10n-central + $ hg clone https://hg.mozilla.org/l10n-central/$AB_CD l10n-central/$AB_CD + $ ls -al + mozilla-central + l10n-central/$AB_CD + ... + +#. Copy your ``mozconfig`` to ``mozconfig.l10n`` and add the following. + + :: + + ac_add_options --with-l10n-base=../../l10n-central + ac_add_options --disable-tests + mk_add_options MOZ_OBJDIR=./objdir-l10n + +#. Configure and prepare the l10n object directory. + + .. code-block:: shell + + MOZCONFIG=mozconfig.l10n ./mach configure + MOZCONFIG=mozconfig.l10n ./mach build -C config export + MOZCONFIG=mozconfig.l10n ./mach build buildid.h + +#. Copy your built package and unpack it into the l10n object directory. + + .. code-block:: shell + + cp $OBJDIR/dist/fennec-*en-US*.apk ./objdir-l10n/dist + MOZCONFIG=mozconfig.l10n ./mach build -C mobile/android/locales unpack + +#. Run the ``compare-locales`` script to write locale-specific changes into + ``objdir-l10n/merged``. + + .. code-block:: shell + + MOZCONFIG=mozconfig.l10n ./mach compare-locales --merge-dir objdir-l10n/merged $AB_CD + +#. Finally, repackage using the locale-specific changes. + + .. code-block:: shell + + MOZCONFIG=mozconfig.l10n LOCALE_MERGEDIR=`realpath objdir-l10n/merged` ./mach build -C mobile/android/locales installers-$AB_CD + + (Note the absolute path for ``LOCALE_MERGEDIR``.) You should find a + re-packaged build at ``objdir-l10n/dist/fennec-*$AB_CD*.apk``. diff --git a/build/docs/mozbuild-files.rst b/build/docs/mozbuild-files.rst new file mode 100644 index 000000000..3550c51ef --- /dev/null +++ b/build/docs/mozbuild-files.rst @@ -0,0 +1,176 @@ +.. _mozbuild-files: + +=============== +moz.build Files +=============== + +``moz.build`` files are the mechanism by which tree metadata (notably +the build configuration) is defined. + +Directories in the tree contain ``moz.build`` files which declare +functionality for their respective part of the tree. This includes +things such as the list of C++ files to compile, where to find tests, +etc. + +``moz.build`` files are actually Python scripts. However, their +execution is governed by special rules. This is explained below. + +moz.build Python Sandbox +======================== + +As mentioned above, ``moz.build`` files are Python scripts. However, +they are executed in a special Python *sandbox* that significantly +changes and limits the execution environment. The environment is so +different, it's doubtful most ``moz.build`` files would execute without +error if executed by a vanilla Python interpreter (e.g. ``python +moz.build``. + +The following properties make execution of ``moz.build`` files special: + +1. The execution environment exposes a limited subset of Python. +2. There is a special set of global symbols and an enforced naming + convention of symbols. +3. Some symbols are inherited from previously-executed ``moz.build`` + files. + +The limited subset of Python is actually an extremely limited subset. +Only a few symbols from ``__builtins__`` are exposed. These include +``True``, ``False``, and ``None``. Global functions like ``import``, +``print``, and ``open`` aren't available. Without these, ``moz.build`` +files can do very little. *This is by design*. + +The execution sandbox treats all ``UPPERCASE`` variables specially. Any +``UPPERCASE`` variable must be known to the sandbox before the script +executes. Any attempt to read or write to an unknown ``UPPERCASE`` +variable will result in an exception being raised. Furthermore, the +types of all ``UPPERCASE`` variables is strictly enforced. Attempts to +assign an incompatible type to an ``UPPERCASE`` variable will result in +an exception being raised. + +The strictness of behavior with ``UPPERCASE`` variables is a very +intentional design decision. By ensuring strict behavior, any operation +involving an ``UPPERCASE`` variable is guaranteed to have well-defined +side-effects. Previously, when the build configuration was defined in +``Makefiles``, assignments to variables that did nothing would go +unnoticed. ``moz.build`` files fix this problem by eliminating the +potential for false promises. + +After a ``moz.build`` file has completed execution, only the +``UPPERCASE`` variables are used to retrieve state. + +The set of variables and functions available to the Python sandbox is +defined by the :py:mod:`mozbuild.frontend.context` module. The +data structures in this module are consumed by the +:py:class:`mozbuild.frontend.reader.MozbuildSandbox` class to construct +the sandbox. There are tests to ensure that the set of symbols exposed +to an empty sandbox are all defined in the ``context`` module. +This module also contains documentation for each symbol, so nothing can +sneak into the sandbox without being explicitly defined and documented. + +Reading and Traversing moz.build Files +====================================== + +The process for reading ``moz.build`` files roughly consists of: + +1. Start at the root ``moz.build`` (``<topsrcdir>/moz.build``). +2. Evaluate the ``moz.build`` file in a new sandbox. +3. Emit the main *context* and any *sub-contexts* from the executed + sandbox. +4. Extract a set of ``moz.build`` files to execute next. +5. For each additional ``moz.build`` file, goto #2 and repeat until all + referenced files have executed. + +From the perspective of the consumer, the output of reading is a stream +of :py:class:`mozbuild.frontend.reader.context.Context` instances. Each +``Context`` defines a particular aspect of data. Consumers iterate over +these objects and do something with the data inside. Each object is +essentially a dictionary of all the ``UPPERCASE`` variables populated +during its execution. + +.. note:: + + Historically, there was only one ``context`` per ``moz.build`` file. + As the number of things tracked by ``moz.build`` files grew and more + and more complex processing was desired, it was necessary to split these + contexts into multiple logical parts. It is now common to emit + multiple contexts per ``moz.build`` file. + +Build System Reading Mode +------------------------- + +The traditional mode of evaluation of ``moz.build`` files is what's +called *build system traversal mode.* In this mode, the ``CONFIG`` +variable in each ``moz.build`` sandbox is populated from data coming +from ``config.status``, which is produced by ``configure``. + +During evaluation, ``moz.build`` files often make decisions conditional +on the state of the build configuration. e.g. *only compile foo.cpp if +feature X is enabled*. + +In this mode, traversal of ``moz.build`` files is governed by variables +like ``DIRS`` and ``TEST_DIRS``. For example, to execute a child +directory, ``foo``, you would add ``DIRS += ['foo']`` to a ``moz.build`` +file and ``foo/moz.build`` would be evaluated. + +.. _mozbuild_fs_reading_mode: + +Filesystem Reading Mode +----------------------- + +There is an alternative reading mode that doesn't involve the build +system and doesn't use ``DIRS`` variables to control traversal into +child directories. This mode is called *filesystem reading mode*. + +In this reading mode, the ``CONFIG`` variable is a dummy, mostly empty +object. Accessing all but a few special variables will return an empty +value. This means that nearly all ``if CONFIG['FOO']:`` branches will +not be taken. + +Instead of using content from within the evaluated ``moz.build`` +file to drive traversal into subsequent ``moz.build`` files, the set +of files to evaluate is controlled by the thing doing the reading. + +A single ``moz.build`` file is not guaranteed to be executable in +isolation. Instead, we must evaluate all *parent* ``moz.build`` files +first. For example, in order to evaluate ``/foo/moz.build``, one must +execute ``/moz.build`` and have its state influence the execution of +``/foo/moz.build``. + +Filesystem reading mode is utilized to power the +:ref:`mozbuild_files_metadata` feature. + +Technical Details +----------------- + +The code for reading ``moz.build`` files lives in +:py:mod:`mozbuild.frontend.reader`. The Python sandboxes evaluation results +(:py:class:`mozbuild.frontend.context.Context`) are passed into +:py:mod:`mozbuild.frontend.emitter`, which converts them to classes defined +in :py:mod:`mozbuild.frontend.data`. Each class in this module defines a +domain-specific component of tree metdata. e.g. there will be separate +classes that represent a JavaScript file vs a compiled C++ file or test +manifests. This means downstream consumers of this data can filter on class +types to only consume what they are interested in. + +There is no well-defined mapping between ``moz.build`` file instances +and the number of :py:mod:`mozbuild.frontend.data` classes derived from +each. Depending on the content of the ``moz.build`` file, there may be 1 +object derived or 100. + +The purpose of the ``emitter`` layer between low-level sandbox execution +and metadata representation is to facilitate a unified normalization and +verification step. There are multiple downstream consumers of the +``moz.build``-derived data and many will perform the same actions. This +logic can be complicated, so we have a component dedicated to it. + +:py:class:`mozbuild.frontend.reader.BuildReader`` and +:py:class:`mozbuild.frontend.reader.TreeMetadataEmitter`` have a +stream-based API courtesy of generators. When you hook them up properly, +the :py:mod:`mozbuild.frontend.data` classes are emitted before all +``moz.build`` files have been read. This means that downstream errors +are raised soon after sandbox execution. + +Lots of the code for evaluating Python sandboxes is applicable to +non-Mozilla systems. In theory, it could be extracted into a standalone +and generic package. However, until there is a need, there will +likely be some tightly coupled bits. diff --git a/build/docs/mozbuild-symbols.rst b/build/docs/mozbuild-symbols.rst new file mode 100644 index 000000000..4e9a8853a --- /dev/null +++ b/build/docs/mozbuild-symbols.rst @@ -0,0 +1,7 @@ +.. _mozbuild_symbols: + +======================== +mozbuild Sandbox Symbols +======================== + +.. mozbuildsymbols:: mozbuild.frontend.context diff --git a/build/docs/mozbuild/dumbmake.rst b/build/docs/mozbuild/dumbmake.rst new file mode 100644 index 000000000..7840695ad --- /dev/null +++ b/build/docs/mozbuild/dumbmake.rst @@ -0,0 +1,38 @@ +dumbmake +======== + +*dumbmake* is a simple dependency tracker for make. + +It turns lists of make targets into longer lists of make targets that +include dependencies. For example: + + netwerk, package + +might be turned into + + netwerk, netwerk/build, toolkit/library, package + +The dependency list is read from the plain text file +`topsrcdir/build/dumbmake-dependencies`. The format best described by +example: + + build_this + when_this_changes + +Interpret this to mean that `build_this` is a dependency of +`when_this_changes`. More formally, a line (CHILD) indented more than +the preceding line (PARENT) means that CHILD should trigger building +PARENT. That is, building CHILD will trigger building first CHILD and +then PARENT. + +This structure is recursive: + + build_this_when_either_change + build_this_only_when + this_changes + +This means that `build_this_when_either_change` is a dependency of +`build_this_only_when` and `this_changes`, and `build_this_only_when` +is a dependency of `this_changes`. Building `this_changes` will build +first `this_changes`, then `build_this_only_when`, and finally +`build_this_when_either_change`. diff --git a/build/docs/mozbuild/index.rst b/build/docs/mozbuild/index.rst new file mode 100644 index 000000000..86f38940b --- /dev/null +++ b/build/docs/mozbuild/index.rst @@ -0,0 +1,41 @@ +======== +mozbuild +======== + +mozbuild is a Python package providing functionality used by Mozilla's +build system. + +Modules Overview +================ + +* mozbuild.backend -- Functionality for producing and interacting with build + backends. A build backend is an entity that consumes build system metadata + (from mozbuild.frontend) and does something useful with it (typically writing + out files that can be used by a build tool to build the tree). +* mozbuild.compilation -- Functionality related to compiling. This + includes managing compiler warnings. +* mozbuild.frontend -- Functionality for reading build frontend files + (what defines the build system) and converting them to data structures + which are fed into build backends to produce backend configurations. +* mozpack -- Functionality related to packaging builds. + +Overview +======== + +The build system consists of frontend files that define what to do. They +say things like "compile X" "copy Y." + +The mozbuild.frontend package contains code for reading these frontend +files and converting them to static data structures. The set of produced +static data structures for the tree constitute the current build +configuration. + +There exist entities called build backends. From a high level, build +backends consume the build configuration and do something with it. They +typically produce tool-specific files such as make files which can be used +to build the tree. + +Piecing it all together, we have frontend files that are parsed into data +structures. These data structures are fed into a build backend. The output +from build backends is used by builders to build the tree. + diff --git a/build/docs/mozconfigs.rst b/build/docs/mozconfigs.rst new file mode 100644 index 000000000..1befca9ec --- /dev/null +++ b/build/docs/mozconfigs.rst @@ -0,0 +1,70 @@ +.. _mozconfig: + +=============== +mozconfig Files +=============== + +mozconfig files are used to configure how a build works. + +mozconfig files are actually shell scripts. They are executed in a +special context with specific variables and functions exposed to them. + +API +=== + +Functions +--------- + +The following special functions are available to a mozconfig script. + +ac_add_options +^^^^^^^^^^^^^^ + +This function is used to declare extra options/arguments to pass into +configure. + +e.g.:: + + ac_add_options --disable-tests + ac_add_options --enable-optimize + +mk_add_options +^^^^^^^^^^^^^^ + +This function is used to inject statements into client.mk for execution. +It is typically used to define variables, notably the object directory. + +e.g.:: + + mk_add_options AUTOCLOBBER=1 + +ac_add_options +^^^^^^^^^^^^^^ + +This is a variant of ac_add_options() which only adds configure options +for a specified application. This is only used when building multiple +applications through client.mk. This function is typically not needed. + +Special mk_add_options Variables +-------------------------------- + +For historical reasons, the method for communicating certain +well-defined variables is via mk_add_options(). In this section, we +document what those special variables are. + +MOZ_OBJDIR +^^^^^^^^^^ + +This variable is used to define the :term:`object directory` for the current +build. + +Finding the active mozconfig +============================ + +Multiple mozconfig files can exist to provide different configuration +options for different tasks. The rules for finding the active mozconfig +are defined in the +:py:func:`mozbuild.mozconfig.MozconfigLoader.find_mozconfig` method: + +.. autoclass:: mozbuild.mozconfig.MozconfigLoader + :members: find_mozconfig diff --git a/build/docs/mozinfo.rst b/build/docs/mozinfo.rst new file mode 100644 index 000000000..d74ca75e6 --- /dev/null +++ b/build/docs/mozinfo.rst @@ -0,0 +1,177 @@ +.. _mozinfo: + +======= +mozinfo +======= + +``mozinfo`` is a solution for representing a subset of build +configuration and run-time data. + +``mozinfo`` data is typically accessed through a ``mozinfo.json`` file +which is written to the :term:`object directory` during build +configuration. The code for writing this file lives in +:py:mod:`mozbuild.mozinfo`. + +``mozinfo.json`` is an object/dictionary of simple string values. + +The attributes in ``mozinfo.json`` are used for many purposes. One use +is to filter tests for applicability to the current build. For more on +this, see :ref:`test_manifests`. + +.. _mozinfo_attributes: + +mozinfo.json Attributes +================================= + +``mozinfo`` currently records the following attributes. + +appname + The application being built. + + Value comes from ``MOZ_APP_NAME`` from ``config.status``. + + Optional. + +asan + Whether address sanitization is enabled. + + Values are ``true`` and ``false``. + + Always defined. + +bin_suffix + The file suffix for binaries produced with this build. + + Values may be an empty string, as not all platforms have a binary + suffix. + + Always defined. + +bits + The number of bits in the CPU this build targets. + + Values are typically ``32`` or ``64``. + + Universal Mac builds do not have this key defined. + + Unkown processor architectures (see ``processor`` below) may not have + this key defined. + + Optional. + +buildapp + The path to the XUL application being built. + + For desktop Firefox, this is ``browser``. For Fennec, it's + ``mobile/android``. For B2G, it's ``b2g``. + +crashreporter + Whether the crash reporter is enabled for this build. + + Values are ``true`` and ``false``. + + Always defined. + +datareporting + Whether data reporting (MOZ_DATA_REPORTING) is enabled for this build. + + Values are ``true`` and ``false``. + + Always defined. + +debug + Whether this is a debug build. + + Values are ``true`` and ``false``. + + Always defined. + +healthreport + Whether the Health Report feature is enabled. + + Values are ``true`` and ``false``. + + Always defined. + +mozconfig + The path of the :ref:`mozconfig file <mozconfig>` used to produce this build. + + Optional. + +nightly_build + Whether this is a nightly build. + + Values are ``true`` and ``false``. + + Always defined. + +os + The operating system the build is produced for. Values for tier-1 + supported platforms are ``linux``, ``win``, ``mac``, ``b2g``, and + ``android``. For other platforms, the value is the lowercase version + of the ``OS_TARGET`` variable from ``config.status``. + + Always defined. + +processor + Information about the processor architecture this build targets. + + Values come from ``TARGET_CPU``, however some massaging may be + performed. + + If the build is a universal build on Mac (it targets both 32-bit and + 64-bit), the value is ``universal-x86-x86_64``. + + If the value starts with ``arm``, the value is ``arm``. + + If the value starts with a string of the form ``i[3-9]86]``, the + value is ``x86``. + + Always defined. + +release_or_beta + Whether this is a release or beta build. + + Values are ``true`` and ``false``. + + Always defined. + +sm_promise + Whether spidermonkey promises have been enabled or not. This is set + by adding --enable-sm-promise to the mozconfig file. + + Values are ``true`` and ``false``. + + Always defined. + +tests_enabled + Whether tests are enabled for this build. + + Values are ``true`` and ``false``. + + Always defined. + +toolkit + The widget toolkit in case. The value comes from the + ``MOZ_WIDGET_TOOLKIT`` ``config.status`` variable. + + Always defined. + +topsrcdir + The path to the source directory the build came from. + + Always defined. + +wave + Whether Wave audio support is enabled. + + Values are ``true`` and ``false``. + + Always defined. + +webm + Whether WebM support is enabled. + + Values are ``true`` and ``false``. + + Always defined. diff --git a/build/docs/pgo.rst b/build/docs/pgo.rst new file mode 100644 index 000000000..51d03c625 --- /dev/null +++ b/build/docs/pgo.rst @@ -0,0 +1,40 @@ +.. _pgo: + +=========================== +Profile Guided Optimization +=========================== + +:abbr:`PGO (Profile Guided Optimization)` is the process of adding +probes to a compiled binary, running said binary, then using the +run-time information to *recompile* the binary to (hopefully) make it +faster. + +How PGO Builds Work +=================== + +The supported interface for invoking a PGO build is to evaluate the +*build* target of client.mk with *MOZ_PGO* defined. e.g.:: + + $ make -f client.mk MOZ_PGO=1 + +This is equivalent to:: + + $ make -f client.mk profiledbuild + +Which is roughly equivalent to: + +#. Perform a build with *MOZ_PROFILE_GENERATE=1* and *MOZ_PGO_INSTRUMENTED=1* +#. Package with *MOZ_PGO_INSTRUMENTED=1* +#. Performing a run of the instrumented binaries +#. $ make maybe_clobber_profiledbuild +#. Perform a build with *MOZ_PROFILE_USE=1* + +Differences between toolchains +============================== + +There are some implementation differences depending on the compiler +toolchain being used. + +The *maybe_clobber_profiledbuild* step gets its name because of a +difference. On Windows, this step merely moves some *.pgc* files around. +Using GCC or Clang, it is equivalent to a *make clean*. diff --git a/build/docs/preprocessor.rst b/build/docs/preprocessor.rst new file mode 100644 index 000000000..ab2d8ecf7 --- /dev/null +++ b/build/docs/preprocessor.rst @@ -0,0 +1,244 @@ +.. _preprocessor: + +================= +Text Preprocessor +================= + +The build system contains a text preprocessor similar to the C preprocessor, +meant for processing files which have no built-in preprocessor such as XUL +and JavaScript documents. It is implemented at ``python/mozbuild/mozbuild/preprocessor.py`` and +is typically invoked via :ref:`jar_manifests`. + +While used to preprocess CSS files, the directives are changed to begin with +``%`` instead of ``#`` to avoid conflict of the id selectors. + +Directives +========== + +Variable Definition +------------------- + +define +^^^^^^ + +:: + + #define variable + #define variable value + +Defines a preprocessor variable. + +Note that, unlike the C preprocessor, instances of this variable later in the +source are not automatically replaced (see #filter). If value is not supplied, +it defaults to ``1``. + +Note that whitespace is significant, so ``"#define foo one"`` and +``"#define foo one "`` is different (in the second case, ``foo`` is defined to +be a four-character string). + +undef +^^^^^ + +:: + + #undef variable + +Undefines a preprocessor variable. + +Conditionals +------------ + +if +^^ + +:: + + #if variable + #if !variable + #if variable==string + #if variable!=string + +Disables output if the conditional is false. This can be nested to arbitrary +depths. Note that in the equality checks, the variable must come first, and +the comparison operator must not be surrounded by any whitespace. + +else +^^^^ + +:: + + #else + +Reverses the state of the previous conditional block; for example, if the +last ``#if`` was true (output was enabled), an ``#else`` makes it off +(output gets disabled). + +.. warning:: An ``#else`` is relative to the last conditional block only, + unlike the C preprocessor. + + It does not matter whether any blocks before it were true. This behavior + changed on trunk (Gecko 1.9) on 2006-12-07; see Bug 277122 for details. + +:: + + #if 1 + always included + #elif 1 + never included + #else + always included + #endif + +endif +^^^^^ + +:: + + #endif + +Ends the conditional block. + +ifdef / ifndef +^^^^^^^^^^^^^^ + +:: + + #ifdef variable + #ifndef variable + +An ``#if`` conditional that is true only if the preprocessor variable +variable is defined (in the case of ``ifdef``) or not defined (``ifndef``). + +elif / elifdef / elifndef +^^^^^^^^^^^^^^^^^^^^^^^^^ + +:: + + #elif variable + #elif !variable + #elif variable == string + #elif variable != string + #elifdef variable + #elifndef variable + +A shorthand to mean an ``#else`` combined with the relevant conditional. +The following two blocks are equivalent:: + + #ifdef foo + block 1 + #elifdef bar + block 2 + #endif + +:: + + #ifdef foo + block 1 + #else + #ifdef bar + block 2 + #endif + #endif + +.. warning:: An ``#elif``, ``#elifdef``, or ``#elifndef`` is relative to + the last conditional block only (as well as the condition it implies), + unlike the C preprocessor. It does not matter whether any blocks before + it were true. This behavior changed on trunk (Gecko 1.9) on 2006-12-07. + See Bug 277122 for details. + +File Inclusion +-------------- + +include +^^^^^^^ + +:: + + #include filename + +The file specified by filename is processed as if the contents was placed +at this position. This also means that preprocessor conditionals can even +be started in one file and ended in another (but is highly discouraged). +There is no limit on depth of inclusion, or repeated inclusion of the same +file, or self inclusion; thus, care should be taken to avoid infinite loops. + +includesubst +^^^^^^^^^^^^ + +:: + + #includesubst @variable@filename + +Same as a ``#include`` except that all instances of variable in the included +file is also expanded as in ``#filter`` substitution + +expand +^^^^^^ + +:: + + #expand string + +All variables wrapped in ``__`` are replaced with their value, for this line +only. If the variable is not defined, it expands to an empty string. For +example, if ``foo`` has the value ``bar``, and ``baz`` is not defined, then:: + + #expand This <__foo__> <__baz__> gets expanded + +Is expanded to:: + + This <bar> <> gets expanded + +filter / unfilter +^^^^^^^^^^^^^^^^^ + +:: + + #filter filter1 filter2 ... filterN + #unfilter filter1 filter2 ... filterN + +``#filter`` turns on the given filter. + +Filters are run in alphabetical order on a per-line basis. + +``#unfilter`` turns off the given filter. Available filters are: + +emptyLines + strips blank lines from the output +slashslash + strips everything from the first two consecutive slash (``/``) + characters until the end of the line +spaces + collapses consecutive sequences of spaces into a single space, + and strips leading and trailing spaces +substitution + all variables wrapped in @ are replaced with their value. If the + variable is not defined, it is a fatal error. Similar to ``#expand`` + and ``#filter`` +attemptSubstitution + all variables wrapped in ``@`` are replaced with their value, or an + empty string if the variable is not defined. Similar to ``#expand``. + +literal +^^^^^^^ + +:: + + #literal string + +Output the string (i.e. the rest of the line) literally, with no other fixups. +This is useful to output lines starting with ``#``, or to temporarily +disable filters. + +Other +----- + +#error +^^^^^^ + +:: + + #error string + +Cause a fatal error at this point, with the error message being the +given string. diff --git a/build/docs/python.rst b/build/docs/python.rst new file mode 100644 index 000000000..098541706 --- /dev/null +++ b/build/docs/python.rst @@ -0,0 +1,178 @@ +.. _python: + +=========================== +Python and the Build System +=========================== + +The Python programming language is used significantly in the build +system. If we need to write code for the build system or for a tool +related to the build system, Python is typically the first choice. + +Python Requirements +=================== + +The tree requires Python 2.7.3 or greater but not Python 3 to build. +All Python packages not in the Python distribution are included in the +source tree. So all you should need is a vanilla Python install and you +should be good to go. + +Only CPython (the Python distribution available from www.python.org) is +supported. + +We require Python 2.7.3 (and not say 2.7.2) to build because Python +2.7.3 contains numerous bug fixes, especially around the area of Unicode +handling. These bug fixes are extremely annoying and have to be worked +around. The build maintainers were tired of doing this, so the minimum +version requirement was upped (bug 870420). + +We intend to eventually support Python 3. This will come by way of dual +2.7/3.x compatibility because a single flag day conversion to 3.x will +be too cumbersome given the amount of Python that would need converted. +We will not know which 3.x minor release we are targeting until this +effort is underway. This is tracked in bug 636155. + +Compiled Python Packages +======================== + +There are some features of the build that rely on compiled Python packages +(packages containing C source). These features are currently all +optional because not every system contains the Python development +headers required to build these extensions. + +We recommend you have the Python development headers installed (``mach +bootstrap`` should do this for you) so you can take advantage of these +features. + +Issues with OS X System Python +============================== + +The Python that ships with OS X has historically been littered with +subtle bugs and suboptimalities. Furthermore, OS X up through 10.8 don't +ship with Python 2.7.3 (10.8 ships with 2.7.2). + +OS X 10.8 and below users will be required to install a new Python +distribution. This may not be necessary for OS X 10.9+. However, we +still recommend installing a separate Python because of the history with +OS X's system Python issues. + +We recommend installing Python through Homebrew or MacPorts. If you run +``mach bootstrap``, this should be done for you. + +Virtualenvs +=========== + +The build system relies heavily on +`virtualenvs <http://www.virtualenv.org/en/latest/>`_. Virtualenvs are +standalone and isolated Python environments. The problem a virtualenv +solves is that of dependencies across multiple Python components. If two +components on a system relied on different versions of a package, there +could be a conflict. Instead of managing multiple versions of a package +simultaneously, Python and virtualenvs take the route that it is easier +to just keep them separate so there is no potential for conflicts. + +Very early in the build process, a virtualenv is created inside the +:term:`object directory`. The virtualenv is configured such that it can +find all the Python packages in the source tree. The code for this lives +in :py:mod:`mozbuild.virtualenv`. + +Deficiencies +------------ + +There are numerous deficiencies with the way virtualenvs are handled in +the build system. + +* mach reinvents the virtualenv. + + There is code in ``build/mach_bootstrap.py`` that configures ``sys.path`` + much the same way the virtualenv does. There are various bugs tracking + this. However, no clear solution has yet been devised. It's not a huge + problem and thus not a huge priority. + +* They aren't preserved across copies and packaging. + + If you attempt to copy an entire tree from one machine to another or + from one directory to another, chances are the virtualenv will fall + apart. It would be nice if we could preserve it somehow. Instead of + actually solving portable virtualenvs, all we really need to solve is + encapsulating the logic for populating the virtualenv along with all + dependent files in the appropriate place. + +* .pyc files written to source directory. + + We rely heavily on ``.pth`` files in our virtualenv. A ``.pth`` file + is a special file that contains a list of paths. Python will take the + set of listed paths encountered in ``.pth`` files and add them to + ``sys.path``. + + When Python compiles a ``.py`` file to bytecode, it writes out a + ``.pyc`` file so it doesn't have to perform this compilation again. + It puts these ``.pyc`` files alongside the ``.pyc`` file. Python + provides very little control for determing where these ``.pyc`` files + go, even in Python 3 (which offers customer importers). + + With ``.pth`` files pointing back to directories in the source tree + and not the object directory, ``.pyc`` files are created in the source + tree. This is bad because when Python imports a module, it first looks + for a ``.pyc`` file before the ``.py`` file. If there is a ``.pyc`` + file but no ``.py`` file, it will happily import the module. This + wreaks havoc during file moves, refactoring, etc. + + There are various proposals for fixing this. See bug 795995. + +Installing Python Manually +========================== + +We highly recommend you use your system's package manager or a +well-supported 3rd party package manager to install Python for you. If +these are not available to you, we recommend the following tools for +installing Python: + +* `buildout.python <https://github.com/collective/buildout.python>`_ +* `pyenv <https://github.com/yyuu/pyenv>`_ +* An official installer from http://www.python.org. + +If all else fails, consider compiling Python from source manually. But this +should be viewed as the least desirable option. + +Common Issues with Python +========================= + +Upgrading your Python distribution breaks the virtualenv +-------------------------------------------------------- + +If you upgrade the Python distribution (e.g. install Python 2.7.5 +from 2.7.3, chances are parts of the virtualenv will break. +This commonly manifests as a cryptic ``Cannot import XXX`` exception. +More often than not, the module being imported contains binary/compiled +components. + +If you upgrade or reinstall your Python distribution, we recommend +clobbering your build. + +Packages installed at the system level conflict with build system's +------------------------------------------------------------------- + +It is common for people to install Python packages using ``sudo`` (e.g. +``sudo pip install psutil``) or with the system's package manager +(e.g. ``apt-get install python-mysql``. + +A problem with this is that packages installed at the system level may +conflict with the package provided by the source tree. As of bug 907902 +and changeset f18eae7c3b27 (September 16, 2013), this should no longer +be an issue since the virtualenv created as part of the build doesn't +add the system's ``site-packages`` directory to ``sys.path``. However, +poorly installed packages may still find a way to creep into the mix and +interfere with our virtualenv. + +As a general principle, we recommend against using your system's package +manager or using ``sudo`` to install Python packages. Instead, create +virtualenvs and isolated Python environments for all of your Python +projects. + +Python on $PATH is not appropriate +---------------------------------- + +Tools like ``mach`` will look for Python by performing ``/usr/bin/env +python`` or equivalent. Please be sure the appropriate Python 2.7.3+ +path is on $PATH. On OS X, this likely means you'll need to modify your +shell's init script to put something ahead of ``/usr/bin``. diff --git a/build/docs/rust.rst b/build/docs/rust.rst new file mode 100644 index 000000000..970cf3268 --- /dev/null +++ b/build/docs/rust.rst @@ -0,0 +1,79 @@ +.. _rust: + +============================== +Including Rust Code in Firefox +============================== + +The build system has support for building and linking Rust crates. +Rust code is built using ``cargo`` in the typical way, so it is +straightforward to take an existing Rust crate and integrate it +into Firefox. + +.. important:: + + Rust code is not currently enabled by default in Firefox builds. + This should change soon (`bug 1283898 <https://bugzilla.mozilla.org/show_bug.cgi?id=1283898>`_), + but the option to build without Rust code will likely last a little longer + (`bug 1284816 <https://bugzilla.mozilla.org/show_bug.cgi?id=1284816>`_), + so Rust code cannot currently be used for required components. + + +Linking Rust Crates into libxul +=============================== + +Rust crates that you want to link into libxul should be listed in the +``dependencies`` section of `toolkit/library/rust/shared/Cargo.toml <https://dxr.mozilla.org/mozilla-central/source/toolkit/library/rust/shared/Cargo.toml>`_. +You'll also need to add an ``extern crate`` reference to `toolkit/library/rust/shared/lib.rs <https://dxr.mozilla.org/mozilla-central/source/toolkit/library/rust/shared/lib.rs>`_. +This ensures that the Rust code will be linked properly into libxul as well +as the copy of libxul used for gtests. + +Linking Rust Crates into something else +======================================= + +There currently is not any Rust code being linked into binaries other than +libxul. If you would like to do so, you'll need to create a directory with +a ``Cargo.toml`` file for your crate, and a ``moz.build`` file that contains: + +.. code-block:: python + + RustLibrary('crate_name') + +Where *crate_name* matches the name from the ``[package]`` section of your +``Cargo.toml``. You can refer to `the moz.build file <https://dxr.mozilla.org/mozilla-central/rev/3f4c3a3cabaf94958834d3a8935adfb4a887942d/toolkit/library/rust/moz.build#7>`_ and `the Cargo.toml file <https://dxr.mozilla.org/mozilla-central/rev/3f4c3a3cabaf94958834d3a8935adfb4a887942d/toolkit/library/rust/Cargo.toml>`_ that are used for libxul. + +You can then add ``USE_LIBS += ['crate_name']`` to the ``moz.build`` file +that defines the binary as you would with any other library in the tree. + +.. important:: + + You cannot link a Rust crate into an intermediate library that will wind + up being linked into libxul. The build system enforces that only a single + ``RustLibrary`` may be linked into a binary. If you need to do this, you + will have to add a ``RustLibrary`` to link to any standalone binaries that + link the intermediate library, and also add the Rust crate to the libxul + dependencies as in `linking Rust Crates into libxul`_. + +Where Should I put my Crate? +============================ + +If your crate's canonical home is mozilla-central, you can put it next to the +other code in the module it belongs to. + +If your crate is mirrored into mozilla-central from another repository, and +will not be actively developed in mozilla-central, you can simply list it +as a ``crates.io``-style dependency with a version number, and let it be +vendored into the ``third_party/rust`` directory. + +If your crate is mirrored into mozilla-central from another repository, but +will be actively developed in both locations, you should send mail to the +dev-builds mailing list to start a discussion on how to meet your needs. + + +Crate dependencies +================== + +All dependencies for in-tree Rust crates are vendored into the +``third_party/rust`` directory. Currently if you add a dependency on a new +crate you must run ``mach vendor rust`` to vendor the dependencies into +that directory. In the future we hope to make it so that you only need to +vendor the dependencies in order to build your changes in a CI push. diff --git a/build/docs/slow.rst b/build/docs/slow.rst new file mode 100644 index 000000000..546fe26ce --- /dev/null +++ b/build/docs/slow.rst @@ -0,0 +1,179 @@ +.. _slow: + +============================ +Why the Build System is Slow +============================ + +A common complaint about the build system is that it's slow. There are +many reasons contributing to its slowness. We will attempt to document +them here. + +First, it is important to distinguish between a :term:`clobber build` +and an :term:`incremental build`. The reasons for why each are slow can +be different. + +The build does a lot of work +============================ + +It may not be obvious, but the main reason the build system is slow is +because it does a lot of work! The source tree consists of a few +thousand C++ files. On a modern machine, we spend over 120 minutes of CPU +core time compiling files! So, if you are looking for the root cause of +slow clobber builds, look at the sheer volume of C++ files in the tree. + +You don't have enough CPU cores and MHz +======================================= + +The build should be CPU bound. If the build system maintainers are +optimizing the build system perfectly, every CPU core in your machine +should be 100% saturated during a build. While this isn't currently the +case (keep reading below), generally speaking, the more CPU cores you +have in your machine and the more total MHz in your machine, the better. + +**We highly recommend building with no fewer than 4 physical CPU +cores.** Please note the *physical* in this sentence. Hyperthreaded +cores (an Intel Core i7 will report 8 CPU cores but only 4 are physical +for example) only yield at most a 1.25x speedup per core. + +We also recommend using the most modern CPU model possible. Haswell +chips deliver much more performance per CPU cycle than say Sandy Bridge +CPUs. + +This cause impacts both clobber and incremental builds. + +You are building with a slow I/O layer +====================================== + +The build system can be I/O bound if your I/O layer is slow. Linking +libxul on some platforms and build architectures can perform gigabytes +of I/O. + +To minimize the impact of slow I/O on build performance, **we highly +recommend building with an SSD.** Power users with enough memory may opt +to build from a RAM disk. Mechanical disks should be avoided if at all +possible. + +Some may dispute the importance of an SSD on build times. It is true +that the beneficial impact of an SSD can be mitigated if your system has +lots of memory and the build files stay in the page cache. However, +operating system memory management is complicated. You don't really have +control over what or when something is evicted from the page cache. +Therefore, unless your machine is a dedicated build machine or you have +more memory than is needed by everything running on your machine, +chances are you'll run into page cache eviction and you I/O layer will +impact build performance. That being said, an SSD certainly doesn't +hurt build times. And, anyone who has used a machine with an SSD will +tell you how great of an investment it is for performance all around the +operating system. On top of that, some automated tests are I/O bound +(like those touching SQLite databases), so an SSD will make tests +faster. + +This cause impacts both clobber and incremental builds. + +You don't have enough memory +============================ + +The build system allocates a lot of memory, especially when building +many things in parallel. If you don't have enough free system memory, +the build will cause swap activity, slowing down your system and the +build. Even if you never get to the point of swapping, the build system +performs a lot of I/O and having all accessed files in memory and the +page cache can significantly reduce the influence of the I/O layer on +the build system. + +**We recommend building with no less than 8 GB of system memory.** As +always, the more memory you have, the better. For a bare bones machine +doing nothing more than building the source tree, anything more than 16 +GB is likely entering the point of diminishing returns. + +This cause impacts both clobber and incremental builds. + +You are building on Windows +=========================== + +New processes on Windows are about a magnitude slower to spawn than on +UNIX-y systems such as Linux. This is because Windows has optimized new +threads while the \*NIX platforms typically optimize new processes. +Anyway, the build system spawns thousands of new processes during a +build. Parts of the build that rely on rapid spawning of new processes +are slow on Windows as a result. This is most pronounced when running +*configure*. The configure file is a giant shell script and shell +scripts rely heavily on new processes. This is why configure on Windows +can run over a minute slower on Windows. + +Another reason Windows builds are slower is because Windows lacks proper +symlink support. On systems that support symlinks, we can generate a +file into a staging area then symlink it into the final directory very +quickly. On Windows, we have to perform a full file copy. This incurs +much more I/O. And if done poorly, can muck with file modification +times, messing up build dependencies. As of the summer of 2013, the +impact of symlinks is being mitigated through the use +of an :term:`install manifest`. + +These issues impact both clobber and incremental builds. + +Recursive make traversal is slow +================================ + +The build system has traditionally been built by employing recursive +make. Recursive make involves make iterating through directories / make +files sequentially and executing each in turn. This is inefficient for +directories containing few targets/tasks because make could be *starved* +for work when processing these directories. Any time make is starved, +the build isn't using all available CPU cycles and the build is slower +as a result. + +Work has started in bug 907365 to fix this issue by changing the way +make traverses all the make files. + +The impact of slow recursive make traversal is mostly felt on +incremental builds. Traditionally, most of the wall time during a +no-op build is spent in make traversal. + +make is inefficient +=================== + +Compared to modern build backends like Tup or Ninja, make is slow and +inefficient. We can only make make so fast. At some point, we'll hit a +performance plateau and will need to use a different tool to make builds +faster. + +Please note that clobber and incremental builds are different. A clobber +build with make will likely be as fast as a clobber build with e.g. Tup. +However, Tup should vastly outperform make when it comes to incremental +builds. Therefore, this issue is mostly seen when performing incremental +builds. + +C++ header dependency hell +========================== + +Modifying a *.h* file can have significant impact on the build system. +If you modify a *.h* that is used by 1000 C++ files, all of those 1000 +C++ files will be recompiled. + +Our code base has traditionally been sloppy managing the impact of +changed headers on build performance. Bug 785103 tracks improving the +situation. + +This issue mostly impacts the times of an :term:`incremental build`. + +A search/indexing service on your machine is running +==================================================== + +Many operating systems have a background service that automatically +indexes filesystem content to make searching faster. On Windows, you +have the Windows Search Service. On OS X, you have Finder. + +These background services sometimes take a keen interest in the files +being produced as part of the build. Since the build system produces +hundreds of megabytes or even a few gigabytes of file data, you can +imagine how much work this is to index! If this work is being performed +while the build is running, your build will be slower. + +OS X's Finder is notorious for indexing when the build is running. And, +it has a tendency to suck up a whole CPU core. This can make builds +several minutes slower. If you build with ``mach`` and have the optional +``psutil`` package built (it requires Python development headers - see +:ref:`python` for more) and Finder is running during a build, mach will +print a warning at the end of the build, complete with instructions on +how to fix it. diff --git a/build/docs/supported-configurations.rst b/build/docs/supported-configurations.rst new file mode 100644 index 000000000..cc2c1ea72 --- /dev/null +++ b/build/docs/supported-configurations.rst @@ -0,0 +1,55 @@ +.. _build_supported_configurations: + +======================== +Supported Configurations +======================== + +This page attempts to document supported build configurations. + +Windows +======= + +We support building on Windows XP and newer operating systems using +Visual Studio 2010 and newer. + +The following are not fully supported by Mozilla (but may work): + +* Building without the latest *MozillaBuild* Windows development + environment +* Building with Mingw or any other non-Visual Studio toolchain. + +OS X +==== + +We support building on OS X 10.6 and newer with the OS X 10.6 SDK. + +The tree should build with the following OS X releases and SDK versions: + +* 10.6 Snow Leopard +* 10.7 Lion +* 10.8 Mountain Lion +* 10.9 Mavericks + +The tree requires building with Clang 3.3 and newer. This corresponds to +version of 4.2 of Apple's Clang that ships with Xcode. This corresponds +to Xcode 4.6 and newer. Xcode 4.6 only runs on OS X 10.7.4 and newer. +So, OS X 10.6 users will need to install a non-Apple toolchain. Running +``mach bootstrap`` should install an appropriate toolchain from Homebrew +or MacPorts automatically. + +The tree should build with GCC 4.4 and newer on OS X. However, this +build configuration isn't as widely used (and differs from what Mozilla +uses to produce OS X builds), so it's recommended to stick with Clang. + +Linux +===== + +Linux 2.6 and later kernels are supported. + +Most distributions are supported as long as the proper package +dependencies are in place. Running ``mach bootstrap`` should install +packages for popular Linux distributions. ``configure`` will typically +detect missing dependencies and inform you how to disable features to +work around unsatisfied dependencies. + +Clang 3.3 or GCC 4.4 is required to build the tree. diff --git a/build/docs/test_manifests.rst b/build/docs/test_manifests.rst new file mode 100644 index 000000000..8c85579be --- /dev/null +++ b/build/docs/test_manifests.rst @@ -0,0 +1,207 @@ +.. _test_manifests: + +============== +Test Manifests +============== + +Many test suites have their test metadata defined in files called +**test manifests**. + +Test manifests are divided into two flavors: :ref:`manifestparser_manifests` +and :ref:`reftest_manifests`. + +Naming Convention +================= + +The build system does not enforce file naming for test manifest files. +However, the following convention is used. + +mochitest.ini + For the *plain* flavor of mochitests. + +chrome.ini + For the *chrome* flavor of mochitests. + +browser.ini + For the *browser chrome* flavor of mochitests. + +a11y.ini + For the *a11y* flavor of mochitests. + +xpcshell.ini + For *xpcshell* tests. + +.. _manifestparser_manifests: + +ManifestParser Manifests +========================== + +ManifestParser manifests are essentially ini files that conform to a basic +set of assumptions. + +The `reference documentation <http://mozbase.readthedocs.org/en/latest/manifestparser.html>`_ +for manifestparser manifests describes the basic format of test manifests. + +In summary, manifests are ini files with section names describing test files:: + + [test_foo.js] + [test_bar.js] + +Keys under sections can hold metadata about each test:: + + [test_foo.js] + skip-if = os == "win" + [test_foo.js] + skip-if = os == "linux" && debug + [test_baz.js] + fail-if = os == "mac" || os == "android" + +There is a special **DEFAULT** section whose keys/metadata apply to all +sections/tests:: + + [DEFAULT] + property = value + + [test_foo.js] + +In the above example, **test_foo.js** inherits the metadata **property = value** +from the **DEFAULT** section. + +Recognized Metadata +------------------- + +Test manifests can define some common keys/metadata to influence behavior. +Those keys are as follows: + +head + List of files that will be executed before the test file. (Used in + xpcshell tests.) + +tail + List of files that will be executed after the test file. (Used in + xpcshell tests.) + +support-files + List of additional files required to run tests. This is typically + defined in the **DEFAULT** section. + + Unlike other file lists, *support-files* supports a globbing mechanism + to facilitate pulling in many files with minimal typing. This globbing + mechanism is activated if an entry in this value contains a ``*`` + character. A single ``*`` will wildcard match all files in a directory. + A double ``**`` will descend into child directories. For example, + ``data/*`` will match ``data/foo`` but not ``data/subdir/bar`` where + ``data/**`` will match ``data/foo`` and ``data/subdir/bar``. + + Support files starting with ``/`` are placed in a root directory, rather + than a location determined by the manifest location. For mochitests, + this allows for the placement of files at the server root. The source + file is selected from the base name (e.g., ``foo`` for ``/path/foo``). + Files starting with ``/`` cannot be selected using globbing. + + Some support files are used by tests across multiple directories. In + this case, a test depending on a support file from another directory + must note that dependency with the path to the required support file + in its own **support-files** entry. These use a syntax where paths + starting with ``!/`` will indicate the beginning of the path to a + shared support file starting from the root of the srcdir. For example, + if a manifest at ``dom/base/test/mochitest.ini`` has a support file, + ``dom/base/test/server-script.sjs``, and a mochitest in + ``dom/workers/test`` depends on that support file, the test manifest + at ``dom/workers/test/mochitest.ini`` must include + ``!/dom/base/test/server-script.sjs`` in its **support-files** entry. + +generated-files + List of files that are generated as part of the build and don't exist in + the source tree. + + The build system assumes that each manifest file, test file, and file + listed in **head**, **tail**, and **support-files** is static and + provided by the source tree (and not automatically generated as part + of the build). This variable tells the build system not to make this + assumption. + + This variable will likely go away sometime once all generated files are + accounted for in the build config. + + If a generated file is not listed in this key, a clobber build will + likely fail. + +dupe-manifest + Record that this manifest duplicates another manifest. + + The common scenario is two manifest files will include a shared + manifest file via the ``[include:file]`` special section. The build + system enforces that each test file is only provided by a single + manifest. Having this key present bypasses that check. + + The value of this key is ignored. + + +skip-if + Skip this test if the specified condition is true. + See :ref:`manifest_filter_language`. + +fail-if + Expect test failure if the specified condition is true. + See :ref:`manifest_filter_language`. + +run-sequentially + If present, the test should not be run in parallel with other tests. + + Some test harnesses support parallel test execution on separate processes + and/or threads (behavior varies by test harness). If this key is present, + the test harness should not attempt to run this test in parallel with any + other test. + + By convention, the value of this key is a string describing why the test + can't be run in parallel. + +.. _manifest_filter_language: + +Manifest Filter Language +------------------------ + +Some manifest keys accept a special filter syntax as their values. These +values are essentially boolean expressions that are evaluated at test +execution time. + +The expressions can reference a well-defined set of variables, such as +``os`` and ``debug``. These variables are populated from the +``mozinfo.json`` file. For the full list of available variables, see +the :ref:`mozinfo documentation <mozinfo_attributes>`. + +See +`the source <https://hg.mozilla.org/mozilla-central/file/default/testing/mozbase/manifestparser/manifestparser/manifestparser.py>`_ for the full documentation of the +expression syntax until it is documented here. + +.. todo:: + + Document manifest filter language. + +.. _manifest_file_installation: + +File Installation +----------------- + +Files referenced by manifests are automatically installed into the object +directory into paths defined in +:py:func:`mozbuild.frontend.emitter.TreeMetadataEmitter._process_test_manifest`. + +Relative paths resolving to parent directory (e.g. +``support-files = ../foo.txt`` have special behavior. + +For ``support-files``, the file will be installed to the default destination +for that manifest. Only the file's base name is used to construct the final +path: directories are irrelevant. Files starting with ``/`` are an exception, +these are installed relative to the root of the destination; the base name is +instead used to select the file.. + +For all other entry types, the file installation is skipped. + +.. _reftest_manifests: + +Reftest Manifests +================= + +See `MDN <https://developer.mozilla.org/en-US/docs/Creating_reftest-based_unit_tests>`_. diff --git a/build/docs/toolchains.rst b/build/docs/toolchains.rst new file mode 100644 index 000000000..eba640fa0 --- /dev/null +++ b/build/docs/toolchains.rst @@ -0,0 +1,111 @@ +.. _build_toolchains: + +=========================== +Creating Toolchain Archives +=========================== + +There are various scripts in the repository for producing archives +of the build tools (e.g. compilers and linkers) required to build. + +Clang +===== + +See the ``build/build-clang`` directory. Read ``build/build-clang/README`` +for more. + +Windows +======= + +The ``build/windows_toolchain.py`` script is used to build and manage +Windows toolchain archives containing Visual Studio executables, SDKs, +etc. + +The way Firefox build automation works is an archive containing the +toolchain is produced and uploaded to an internal Mozilla server. The +build automation will download, verify, and extract this archive before +building. The archive is self-contained so machines don't need to install +Visual Studio, SDKs, or various other dependencies. Unfortunately, +Microsoft's terms don't allow Mozilla to distribute this archive +publicly. However, the same tool can be used to create your own copy. + +Configuring Your System +----------------------- + +It is **highly** recommended to perform this process on a fresh installation +of Windows 7 or 10 (such as in a VM). Installing all updates through +Windows Update is not only acceptable - it is encouraged. Although it +shouldn't matter. + +Next, install Visual Studio 2015 Community. The download link can be +found at https://www.visualstudio.com/en-us/products/visual-studio-community-vs.aspx. +Be sure to follow these install instructions: + +1. Choose a ``Custom`` installation and click ``Next`` +2. Select ``Programming Languages`` -> ``Visual C++`` (make sure all sub items are + selected) +3. Under ``Windows and Web Development`` uncheck everything except + ``Universal Windows App Development Tools`` and the items under it + (should be ``Tools (1.3.1)...`` and the ``Windows 10 SDK``). + +Once Visual Studio 2015 Community has been installed, from a checkout +of mozilla-central, run something like the following to produce a ZIP +archive:: + + $ ./mach python build/windows_toolchain.py create-zip vs2015u3 + +The produced archive will be the argument to ``create-zip`` + ``.zip``. + +Firefox for Android with Gradle +=============================== + +To build Firefox for Android with Gradle in automation, archives +containing both the Gradle executable and a Maven repository +comprising the exact build dependencies are produced and uploaded to +an internal Mozilla server. The build automation will download, +verify, and extract these archive before building. These archives +provide a self-contained Gradle and Maven repository so that machines +don't need to fetch additional Maven dependencies at build time. +(Gradle and the downloaded Maven dependencies can be both +redistributed publicly.) + +Archiving the Gradle executable is straight-forward, but archiving a +local Maven repository is not. Therefore a special Task Cluster +Docker image and job exist for producing the required archives. The +Docker image definition is rooted in +``taskcluster/docker/android-gradle-build``. The Task Cluster job +definition is in +``testing/taskcluster/tasks/builds/android_api_15_gradle_dependencies.yml``. +The job runs in a container based on the custom Docker image and +spawns a Sonatype Nexus proxying Maven repository process in the +background. The job builds Firefox for Android using Gradle and the +in-tree Gradle configuration rooted at ``build.gradle``. The spawned +proxying Maven repository downloads external dependencies and collects +them. After the Gradle build completes, the job archives the Gradle +version used to build, and the downloaded Maven repository, and +exposes them as Task Cluster artifacts. + +Here is `an example try job fetching these dependencies +<https://treeherder.mozilla.org/#/jobs?repo=try&revision=75bc98935147&selectedJob=17793653>`_. +The resulting task produced a `Gradle archive +<https://queue.taskcluster.net/v1/task/CeYMgAP3Q-KF8h37nMhJjg/runs/0/artifacts/public%2Fbuild%2Fgradle.tar.xz>`_ +and a `Maven repository archive +<https://queue.taskcluster.net/v1/task/CeYMgAP3Q-KF8h37nMhJjg/runs/0/artifacts/public%2Fbuild%2Fjcentral.tar.xz>`_. +These archives were then uploaded (manually) to Mozilla automation +using tooltool for consumption in Gradle builds. + +To update the version of Gradle in the archive produced, update +``gradle/wrapper/gradle-wrapper.properties``. Be sure to also update +the SHA256 checksum to prevent poisoning the build machines! + +To update the versions of Gradle dependencies used, update +``dependencies`` sections in the in-tree Gradle configuration rooted +at ``build.gradle``. Once you are confident your changes build +locally, push a fresh try build with an invocation like:: + + $ hg push-to-try -m "try: -b o -p android-api-15-gradle-dependencies" + +Then `upload your archives to tooltool +<https://wiki.mozilla.org/ReleaseEngineering/Applications/Tooltool#How_To_Upload_To_Tooltool>`_, +update the in-tree manifests in +``mobile/android/config/tooltool-manifests``, and push a fresh try +build. diff --git a/build/docs/visualstudio.rst b/build/docs/visualstudio.rst new file mode 100644 index 000000000..3fbf28e94 --- /dev/null +++ b/build/docs/visualstudio.rst @@ -0,0 +1,100 @@ +.. _build_visualstudio: + +====================== +Visual Studio Projects +====================== + +The build system contains alpha support for generating Visual Studio +project files to aid with development. + +To generate Visual Studio project files, you'll need to have a configured tree:: + + mach configure + +(If you have built recently, your tree is already configured.) + +Then, simply generate the Visual Studio build backend:: + + mach build-backend -b VisualStudio + +If all goes well, the path to the generated Solution (``.sln``) file should be +printed. You should be able to open that solution with Visual Studio 2010 or +newer. + +Currently, output is hard-coded to the Visual Studio 2010 format. If you open +the solution in a newer Visual Studio release, you will be prompted to upgrade +projects. Simply click through the wizard to do that. + +Structure of Solution +===================== + +The Visual Studio solution consists of hundreds of projects spanning thousands +of files. To help with organization, the solution is divided into the following +trees/folders: + +Build Targets + This folder contains common build targets. The *full* project is used to + perform a full build. The *binaries* project is used to build just binaries. + The *visual-studio* project can be built to regenerate the Visual Studio + project files. + + Performing the *clean* action on any of these targets will clean the + *entire* build output. + +Binaries + This folder contains common binaries that can be executed from within + Visual Studio. If you are building the Firefox desktop application, + the *firefox* project will launch firefox.exe. You probably want one of + these set to your startup project. + +Libraries + This folder contains entries for each static library that is produced as + part of the build. These roughly correspond to each directory in the tree + containing C/C++. e.g. code from ``dom/base`` will be contained in the + ``dom_base`` project. + + These projects don't do anything when built. If you build a project here, + the *binaries* build target project is built. + +Updating Project Files +====================== + +As you pull and update the source tree, your Visual Studio files may fall out +of sync with the build configuration. The tree should still build fine from +within Visual Studio. But source files may be missing and IntelliSense may not +have the proper build configuration. + +To account for this, you'll want to periodically regenerate the Visual Studio +project files. You can do this within Visual Studio by building the +``Build Targets :: visual-studio`` project or by running +``mach build-backend -b VisualStudio`` from the command line. + +Currently, regeneration rewrites the original project files. **If you've made +any customizations to the solution or projects, they will likely get +overwritten.** We would like to improve this user experience in the +future. + +Moving Project Files Around +=========================== + +The produced Visual Studio solution and project files should be portable. +If you want to move them to a non-default directory, they should continue +to work from wherever they are. If they don't, please file a bug. + +Invoking mach through Visual Studio +=================================== + +It's possible to build the tree via Visual Studio. There is some light magic +involved here. + +Alongside the Visual Studio project files is a batch script named ``mach.bat``. +This batch script sets the environment variables present in your *MozillaBuild* +development environment at the time of Visual Studio project generation +and invokes *mach* inside an msys shell with the arguments specified to the +batch script. This script essentially allows you to invoke mach commands +inside the MozillaBuild environment without having to load MozillaBuild. + +While projects currently only utilize the ``mach build`` command, the batch +script does not limit it's use: any mach command can be invoked. Developers +may abuse this fact to add custom projects and commands that invoke other +mach commands. diff --git a/build/dumbmake-dependencies b/build/dumbmake-dependencies new file mode 100644 index 000000000..d99f56757 --- /dev/null +++ b/build/dumbmake-dependencies @@ -0,0 +1,71 @@ +toolkit/library + dom + ipc + security/sandbox + ipc + netwerk/build + netwerk + storage/build + storage + xpcom + chrome + extensions + docshell/build + docshell + uriloader + modules + widget + gfx + toolkit/components/build + toolkit/components + security/manager + security/certverifier + security/build + accessible + dom + content + layout + editor + parser + js/src + mfbt + js/xpconnect + js/xpconnect/loader + view + caps + xpfe/appshell + xpfe/components + js + toolkit + rdf/build + embedding + hal + image/build + image + intl/build + intl + media + profile + services + startupcache + devtools/server + devtools/shared +browser/app + browser/base + browser/components + devtools/client + browser/locales + browser/modules + browser/themes + toolkit + toolkit/components + toolkit/components/jsdownloads + toolkit/content + toolkit/crashreporter + toolkit/forgetaboutsite + toolkit/identity + toolkit/modules + toolkit/mozapps/extensions + toolkit/profile + toolkit/themes + toolkit/webapps diff --git a/build/gecko_templates.mozbuild b/build/gecko_templates.mozbuild new file mode 100644 index 000000000..8eab9d327 --- /dev/null +++ b/build/gecko_templates.mozbuild @@ -0,0 +1,162 @@ +# -*- Mode: python; 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/. + +@template +def GeckoBinary(linkage='dependent', msvcrt='dynamic', mozglue=None): + '''Template for Gecko-related binaries. + + This template is meant to be used in other templates. + + `linkage` indicates the wanted xpcom linkage type. Valid values are + 'dependent', 'standalone' or None. 'dependent' is the default. It is + used for e.g. XPCOM components and executables with direct dependencies + on libxul. Most executables should use the 'standalone' linkage, which + uses the standalone XPCOM glue to load libxul. None means no XPCOM glue + or libxul linkage at all. + + `msvcrt` indicates which Microsoft Visual Studio CRT, for Windows build, + ought to be linked: 'static' or 'dynamic'. + + `mozglue` indicates whether to link against the mozglue library, and if + so, what linkage to apply. Valid values are None (mozglue not linked), + 'program' (mozglue linked to an executable program), or 'library' (mozglue + linked to a shared library). + ''' + if msvcrt == 'dynamic' or CONFIG['OS_ARCH'] != 'WINNT' or CONFIG['MOZ_ASAN']: + xpcomglue = 'xpcomglue' + elif msvcrt == 'static': + USE_STATIC_LIBS = True + xpcomglue = 'xpcomglue_staticruntime' + if not CONFIG['GNU_CC']: + mozglue = None + if linkage == 'dependent': + USE_LIBS += [ + 'mozalloc_staticruntime', + ] + else: + error('msvcrt must be "dynamic" or "static"') + + if linkage == 'dependent': + USE_LIBS += [ + 'nspr', + '%s_s' % xpcomglue, + 'xul', + ] + elif linkage == 'standalone': + DEFINES['XPCOM_GLUE'] = True + + USE_LIBS += [ + xpcomglue, + ] + elif linkage != None: + error('`linkage` must be "dependent", "standalone" or None') + + if mozglue: + LDFLAGS += CONFIG['MOZ_GLUE_WRAP_LDFLAGS'] + if mozglue == 'program': + USE_LIBS += ['mozglue'] + DEFINES['MOZ_HAS_MOZGLUE'] = True + if CONFIG['MOZ_GLUE_IN_PROGRAM']: + if CONFIG['GNU_CC']: + LDFLAGS += ['-rdynamic'] + if CONFIG['MOZ_MEMORY']: + USE_LIBS += ['memory'] + elif mozglue == 'library': + LIBRARY_DEFINES['MOZ_HAS_MOZGLUE'] = True + if not CONFIG['MOZ_GLUE_IN_PROGRAM']: + USE_LIBS += ['mozglue'] + else: + error('`mozglue` must be "program" or "library"') + + if not CONFIG['JS_STANDALONE']: + USE_LIBS += [ + 'fallible', + ] + + +@template +def GeckoProgram(name, linkage='standalone', **kwargs): + '''Template for program executables related to Gecko. + + `name` identifies the executable base name. + + See the documentation for `GeckoBinary` for other possible arguments, + with the notable difference that the default for `linkage` is 'standalone'. + ''' + Program(name) + + kwargs.setdefault('mozglue', 'program') + + GeckoBinary(linkage=linkage, **kwargs) + + +@template +def GeckoSimplePrograms(names, **kwargs): + '''Template for simple program executables related to Gecko. + + `names` identifies the executable base names for each executable. + + See the documentation for `GeckoBinary` for other possible arguments. + ''' + SimplePrograms(names) + + kwargs.setdefault('mozglue', 'program') + + GeckoBinary(**kwargs) + + +@template +def GeckoCppUnitTests(names, **kwargs): + '''Template for C++ unit tests related to Gecko. + + `names` identifies the executable base names for each executable. + + See the documentation for `GeckoBinary` for other possible arguments. + ''' + CppUnitTests(names) + + kwargs.setdefault('mozglue', 'program') + + GeckoBinary(**kwargs) + + +@template +def GeckoSharedLibrary(name, **kwargs): + '''Template for shared libraries related to Gecko. + + `name` identifies the library base name. + See the documentation for `GeckoBinary` for other possible arguments. + ''' + SharedLibrary(name) + + kwargs.setdefault('mozglue', 'library') + + GeckoBinary(**kwargs) + + +@template +def GeckoFramework(name, **kwargs): + '''Template for OSX frameworks related to Gecko. + + `name` identifies the library base name. + See the documentation for `GeckoBinary` for other possible arguments. + ''' + Framework(name) + + kwargs.setdefault('mozglue', 'library') + + GeckoBinary(**kwargs) + + +@template +def XPCOMBinaryComponent(name): + '''Template defining an XPCOM binary component for Gecko. + + `name` is the name of the component. + ''' + GeckoSharedLibrary(name) + + IS_COMPONENT = True diff --git a/build/gen_test_packages_manifest.py b/build/gen_test_packages_manifest.py new file mode 100644 index 000000000..1e2a7c3bb --- /dev/null +++ b/build/gen_test_packages_manifest.py @@ -0,0 +1,84 @@ +#!/usr/bin/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/. + +import json + +from argparse import ArgumentParser + +ALL_HARNESSES = [ + 'common', # Harnesses without a specific package will look here. + 'mochitest', + 'reftest', + 'xpcshell', + 'cppunittest', + 'jittest', + 'mozbase', + 'web-platform', + 'talos', + 'gtest', +] + +PACKAGE_SPECIFIED_HARNESSES = [ + 'cppunittest', + 'mochitest', + 'reftest', + 'xpcshell', + 'web-platform', + 'talos', +] + +# These packages are not present for every build configuration. +OPTIONAL_PACKAGES = [ + 'gtest', +] + + +def parse_args(): + parser = ArgumentParser(description='Generate a test_packages.json file to tell automation which harnesses require which test packages.') + parser.add_argument("--common", required=True, + action="store", dest="tests_common", + help="Name of the \"common\" archive, a package to be used by all harnesses.") + parser.add_argument("--jsshell", required=True, + action="store", dest="jsshell", + help="Name of the jsshell zip.") + for harness in PACKAGE_SPECIFIED_HARNESSES: + parser.add_argument("--%s" % harness, required=True, + action="store", dest=harness, + help="Name of the %s zip." % harness) + for harness in OPTIONAL_PACKAGES: + parser.add_argument("--%s" % harness, required=False, + action="store", dest=harness, + help="Name of the %s zip." % harness) + parser.add_argument("--dest-file", required=True, + action="store", dest="destfile", + help="Path to the output file to be written.") + return parser.parse_args() + + +def generate_package_data(args): + # Generate a dictionary mapping test harness names (exactly as they're known to + # mozharness and testsuite-targets.mk, ideally) to the set of archive names that + # harness depends on to run. + # mozharness will use this file to determine what test zips to download, + # which will be an optimization once parts of the main zip are split to harness + # specific zips. + tests_common = args.tests_common + jsshell = args.jsshell + + harness_requirements = dict([(k, [tests_common]) for k in ALL_HARNESSES]) + harness_requirements['jittest'].append(jsshell) + for harness in PACKAGE_SPECIFIED_HARNESSES + OPTIONAL_PACKAGES: + pkg_name = getattr(args, harness, None) + if pkg_name is None: + continue + harness_requirements[harness].append(pkg_name) + return harness_requirements + +if __name__ == '__main__': + args = parse_args() + packages_data = generate_package_data(args) + with open(args.destfile, 'w') as of: + json.dump(packages_data, of, indent=4) diff --git a/build/genrc.sh b/build/genrc.sh new file mode 100755 index 000000000..c0c619462 --- /dev/null +++ b/build/genrc.sh @@ -0,0 +1,13 @@ +#!/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/. + +DATATYPE="$1" +INFILE="$2" + +echo "${DATATYPE} RCDATA" +sed 's/"/""/g' ${INFILE} | awk 'BEGIN { printf("BEGIN\n") } { printf("\"%s\\r\\n\",\n", $0) } END { printf("\"\\0\"\nEND\n") }' + +exit 0 diff --git a/build/gyp.mozbuild b/build/gyp.mozbuild new file mode 100644 index 000000000..ff04f6aac --- /dev/null +++ b/build/gyp.mozbuild @@ -0,0 +1,136 @@ +# -*- Mode: python; 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/. + +gyp_vars = { + 'lsan': 0, + 'asan': 0, + 'build_with_mozilla': 1, + 'build_with_chromium': 0, + 'use_official_google_api_keys': 0, + 'have_clock_monotonic': 1 if CONFIG['HAVE_CLOCK_MONOTONIC'] else 0, + 'have_ethtool_cmd_speed_hi': 1 if CONFIG['MOZ_WEBRTC_HAVE_ETHTOOL_SPEED_HI'] else 0, + 'include_alsa_audio': 1 if CONFIG['MOZ_ALSA'] else 0, + 'include_pulse_audio': 1 if CONFIG['MOZ_PULSEAUDIO'] else 0, + # basic stuff for everything + 'include_internal_video_render': 0, + 'clang_use_chrome_plugins': 0, + 'enable_protobuf': 0, + 'include_tests': 0, + 'enable_android_opensl': 1, + 'enable_android_opensl_output': 0, + # use_system_lib* still seems to be in use in trunk/build + 'use_system_libjpeg': 0, + 'use_system_libvpx': 0, + 'build_json': 0, + 'build_libjpeg': 0, + 'build_libyuv': 0, + 'build_libvpx': 0, + 'build_ssl': 0, + 'build_json': 0, + 'build_icu': 0, + 'build_opus': 0, + 'libyuv_dir': '/media/libyuv', + 'yuv_disable_avx2': 0 if CONFIG['HAVE_X86_AVX2'] else 1, + # don't use openssl + 'use_openssl': 0, + + 'use_x11': 1 if CONFIG['MOZ_X11'] else 0, + 'use_glib': 1 if CONFIG['GLIB_LIBS'] else 0, + + # turn off mandatory use of NEON and instead use NEON detection + 'arm_neon': 0, + 'arm_neon_optional': 1, + + 'moz_widget_toolkit_gonk': 0, + 'moz_webrtc_omx': 0, + 'moz_webrtc_mediacodec': 0, + + # Turn off multi monitor screen share + 'multi_monitor_screenshare%' : 0, + + # (for vp8) chromium sets to 0 also + 'use_temporal_layers': 0, + + # Creates AEC internal sample dump files in current directory + 'aec_debug_dump': 1, + + # Enable and force use of hardware AEC + 'hardware_aec_ns': 1 if CONFIG['MOZ_WEBRTC_HARDWARE_AEC_NS'] else 0, + + # codec enable/disables: + 'include_g711': 1, + 'include_opus': 1, + 'include_g722': 1, + 'include_ilbc': 0, + # We turn on ISAC because the AGC uses parts of it, and depend on the + # linker to throw away uneeded bits. + 'include_isac': 1, + 'include_pcm16b': 1, +} + +os = CONFIG['OS_TARGET'] + +if os == 'WINNT': + gyp_vars.update( + MSVS_VERSION=CONFIG['_MSVS_VERSION'], + MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_BUILD'] else 32, + ) +elif os == 'Android': + if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': + gyp_vars['build_with_gonk'] = 1 + gyp_vars['moz_widget_toolkit_gonk'] = 1 + gyp_vars['opus_complexity'] = 1 + if int(CONFIG['ANDROID_VERSION']) >= 18: + gyp_vars['moz_webrtc_omx'] = 1 + else: + gyp_vars.update( + gtest_target_type='executable', + moz_webrtc_mediacodec=1, + android_toolchain=CONFIG.get('ANDROID_TOOLCHAIN', ''), + ) + +flavors = { + 'WINNT': 'win', + 'Android': 'linux' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk' else 'android', + 'Linux': 'linux', + 'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios', + 'SunOS': 'solaris', + 'GNU/kFreeBSD': 'freebsd', + 'DragonFly': 'dragonfly', + 'FreeBSD': 'freebsd', + 'NetBSD': 'netbsd', + 'OpenBSD': 'openbsd', +} +gyp_vars['OS'] = flavors.get(os) + +arches = { + 'x86_64': 'x64', + 'x86': 'ia32', + 'aarch64': 'arm64', +} + +gyp_vars['target_arch'] = arches.get(CONFIG['CPU_ARCH'], CONFIG['CPU_ARCH']) + +if CONFIG['ARM_ARCH']: + if int(CONFIG['ARM_ARCH']) < 7: + gyp_vars['armv7'] = 0 + gyp_vars['arm_neon_optional'] = 0 + elif os == 'Android': + gyp_vars['armv7'] = 1 + else: + # CPU detection for ARM works on Android only. armv7 always uses CPU + # detection, so we have to set armv7=0 for non-Android target + gyp_vars['armv7'] = 0 + # For libyuv + gyp_vars['arm_version'] = int(CONFIG['ARM_ARCH']) + +# Don't try to compile ssse3/sse4.1 code if toolchain doesn't support +if CONFIG['INTEL_ARCHITECTURE']: + if not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSSE3'] or not CONFIG['HAVE_TOOLCHAIN_SUPPORT_MSSE4_1']: + gyp_vars['yuv_disable_asm'] = 1 + +if CONFIG['MACOS_SDK_DIR']: + gyp_vars['mac_sdk_path'] = CONFIG['MACOS_SDK_DIR'] diff --git a/build/mach_bootstrap.py b/build/mach_bootstrap.py new file mode 100644 index 000000000..4fbf7de1d --- /dev/null +++ b/build/mach_bootstrap.py @@ -0,0 +1,413 @@ +# 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 errno +import json +import os +import platform +import random +import subprocess +import sys +import uuid +import __builtin__ + +from types import ModuleType + + +STATE_DIR_FIRST_RUN = ''' +mach and the build system store shared state in a common directory on the +filesystem. The following directory will be created: + + {userdir} + +If you would like to use a different directory, hit CTRL+c and set the +MOZBUILD_STATE_PATH environment variable to the directory you would like to +use and re-run mach. For this change to take effect forever, you'll likely +want to export this environment variable from your shell's init scripts. + +Press ENTER/RETURN to continue or CTRL+c to abort. +'''.lstrip() + + +# TODO Bug 794506 Integrate with the in-tree virtualenv configuration. +SEARCH_PATHS = [ + 'python/mach', + 'python/mozboot', + 'python/mozbuild', + 'python/mozlint', + 'python/mozversioncontrol', + 'python/blessings', + 'python/compare-locales', + 'python/configobj', + 'python/futures', + 'python/jsmin', + 'python/psutil', + 'python/pylru', + 'python/which', + 'python/pystache', + 'python/pyyaml/lib', + 'python/requests', + 'python/slugid', + 'python/py', + 'python/pytest', + 'python/redo', + 'python/voluptuous', + 'build', + 'build/pymake', + 'config', + 'dom/bindings', + 'dom/bindings/parser', + 'dom/media/test/external', + 'layout/tools/reftest', + 'other-licenses/ply', + 'taskcluster', + 'testing', + 'testing/firefox-ui/harness', + 'testing/marionette/client', + 'testing/marionette/harness', + 'testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py', + 'testing/marionette/puppeteer/firefox', + 'testing/mozbase/mozcrash', + 'testing/mozbase/mozdebug', + 'testing/mozbase/mozdevice', + 'testing/mozbase/mozfile', + 'testing/mozbase/mozhttpd', + 'testing/mozbase/mozinfo', + 'testing/mozbase/mozinstall', + 'testing/mozbase/mozleak', + 'testing/mozbase/mozlog', + 'testing/mozbase/moznetwork', + 'testing/mozbase/mozprocess', + 'testing/mozbase/mozprofile', + 'testing/mozbase/mozrunner', + 'testing/mozbase/mozsystemmonitor', + 'testing/mozbase/mozscreenshot', + 'testing/mozbase/moztest', + 'testing/mozbase/mozversion', + 'testing/mozbase/manifestparser', + 'testing/taskcluster', + 'testing/tools/autotry', + 'testing/web-platform', + 'testing/web-platform/harness', + 'testing/web-platform/tests/tools/wptserve', + 'testing/xpcshell', + 'xpcom/idl-parser', +] + +# Individual files providing mach commands. +MACH_MODULES = [ + 'addon-sdk/mach_commands.py', + 'build/valgrind/mach_commands.py', + 'devtools/shared/css/generated/mach_commands.py', + 'dom/bindings/mach_commands.py', + 'dom/media/test/external/mach_commands.py', + 'layout/tools/reftest/mach_commands.py', + 'python/mach_commands.py', + 'python/mach/mach/commands/commandinfo.py', + 'python/mach/mach/commands/settings.py', + 'python/compare-locales/mach_commands.py', + 'python/mozboot/mozboot/mach_commands.py', + 'python/mozbuild/mozbuild/mach_commands.py', + 'python/mozbuild/mozbuild/backend/mach_commands.py', + 'python/mozbuild/mozbuild/compilation/codecomplete.py', + 'python/mozbuild/mozbuild/frontend/mach_commands.py', + 'services/common/tests/mach_commands.py', + 'taskcluster/mach_commands.py', + 'testing/firefox-ui/mach_commands.py', + 'testing/mach_commands.py', + 'testing/marionette/mach_commands.py', + 'testing/mochitest/mach_commands.py', + 'testing/mozharness/mach_commands.py', + 'testing/talos/mach_commands.py', + 'testing/web-platform/mach_commands.py', + 'testing/xpcshell/mach_commands.py', + 'tools/docs/mach_commands.py', + 'tools/lint/mach_commands.py', + 'tools/mach_commands.py', + 'tools/power/mach_commands.py', + 'mobile/android/mach_commands.py', +] + + +CATEGORIES = { + 'build': { + 'short': 'Build Commands', + 'long': 'Interact with the build system', + 'priority': 80, + }, + 'post-build': { + 'short': 'Post-build Commands', + 'long': 'Common actions performed after completing a build.', + 'priority': 70, + }, + 'testing': { + 'short': 'Testing', + 'long': 'Run tests.', + 'priority': 60, + }, + 'ci': { + 'short': 'CI', + 'long': 'Taskcluster commands', + 'priority': 59 + }, + 'devenv': { + 'short': 'Development Environment', + 'long': 'Set up and configure your development environment.', + 'priority': 50, + }, + 'build-dev': { + 'short': 'Low-level Build System Interaction', + 'long': 'Interact with specific parts of the build system.', + 'priority': 20, + }, + 'misc': { + 'short': 'Potpourri', + 'long': 'Potent potables and assorted snacks.', + 'priority': 10, + }, + 'disabled': { + 'short': 'Disabled', + 'long': 'The disabled commands are hidden by default. Use -v to display them. These commands are unavailable for your current context, run "mach <command>" to see why.', + 'priority': 0, + } +} + + +# We submit data to telemetry approximately every this many mach invocations +TELEMETRY_SUBMISSION_FREQUENCY = 10 + + +def bootstrap(topsrcdir, mozilla_dir=None): + if mozilla_dir is None: + mozilla_dir = topsrcdir + + # Ensure we are running Python 2.7+. We put this check here so we generate a + # user-friendly error message rather than a cryptic stack trace on module + # import. + if sys.version_info[0] != 2 or sys.version_info[1] < 7: + print('Python 2.7 or above (but not Python 3) is required to run mach.') + print('You are running Python', platform.python_version()) + sys.exit(1) + + # Global build system and mach state is stored in a central directory. By + # default, this is ~/.mozbuild. However, it can be defined via an + # environment variable. We detect first run (by lack of this directory + # existing) and notify the user that it will be created. The logic for + # creation is much simpler for the "advanced" environment variable use + # case. For default behavior, we educate users and give them an opportunity + # to react. We always exit after creating the directory because users don't + # like surprises. + sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS] + import mach.main + from mozboot.util import get_state_dir + + def telemetry_handler(context, data): + # We have not opted-in to telemetry + if 'BUILD_SYSTEM_TELEMETRY' not in os.environ: + return + + telemetry_dir = os.path.join(get_state_dir()[0], 'telemetry') + try: + os.mkdir(telemetry_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + outgoing_dir = os.path.join(telemetry_dir, 'outgoing') + try: + os.mkdir(outgoing_dir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + # Add common metadata to help submit sorted data later on. + data['argv'] = sys.argv + data.setdefault('system', {}).update(dict( + architecture=list(platform.architecture()), + machine=platform.machine(), + python_version=platform.python_version(), + release=platform.release(), + system=platform.system(), + version=platform.version(), + )) + + if platform.system() == 'Linux': + dist = list(platform.linux_distribution()) + data['system']['linux_distribution'] = dist + elif platform.system() == 'Windows': + win32_ver=list((platform.win32_ver())), + data['system']['win32_ver'] = win32_ver + elif platform.system() == 'Darwin': + # mac version is a special Cupertino snowflake + r, v, m = platform.mac_ver() + data['system']['mac_ver'] = [r, list(v), m] + + with open(os.path.join(outgoing_dir, str(uuid.uuid4()) + '.json'), + 'w') as f: + json.dump(data, f, sort_keys=True) + + def should_skip_dispatch(context, handler): + # The user is performing a maintenance command. + if handler.name in ('bootstrap', 'doctor', 'mach-commands', 'mercurial-setup'): + return True + + # We are running in automation. + if 'MOZ_AUTOMATION' in os.environ or 'TASK_ID' in os.environ: + return True + + # The environment is likely a machine invocation. + if sys.stdin.closed or not sys.stdin.isatty(): + return True + + return False + + def post_dispatch_handler(context, handler, args): + """Perform global operations after command dispatch. + + + For now, we will use this to handle build system telemetry. + """ + # Don't do anything when... + if should_skip_dispatch(context, handler): + return + + # We call mach environment in client.mk which would cause the + # data submission below to block the forward progress of make. + if handler.name in ('environment'): + return + + # We have not opted-in to telemetry + if 'BUILD_SYSTEM_TELEMETRY' not in os.environ: + return + + # Every n-th operation + if random.randint(1, TELEMETRY_SUBMISSION_FREQUENCY) != 1: + return + + with open(os.devnull, 'wb') as devnull: + subprocess.Popen([sys.executable, + os.path.join(topsrcdir, 'build', + 'submit_telemetry_data.py'), + get_state_dir()[0]], + stdout=devnull, stderr=devnull) + + def populate_context(context, key=None): + if key is None: + return + if key == 'state_dir': + state_dir, is_environ = get_state_dir() + if is_environ: + if not os.path.exists(state_dir): + print('Creating global state directory from environment variable: %s' + % state_dir) + os.makedirs(state_dir, mode=0o770) + else: + if not os.path.exists(state_dir): + print(STATE_DIR_FIRST_RUN.format(userdir=state_dir)) + try: + sys.stdin.readline() + except KeyboardInterrupt: + sys.exit(1) + + print('\nCreating default state directory: %s' % state_dir) + os.makedirs(state_dir, mode=0o770) + + return state_dir + + if key == 'topdir': + return topsrcdir + + if key == 'telemetry_handler': + return telemetry_handler + + if key == 'post_dispatch_handler': + return post_dispatch_handler + + raise AttributeError(key) + + mach = mach.main.Mach(os.getcwd()) + mach.populate_context_handler = populate_context + + if not mach.settings_paths: + # default global machrc location + mach.settings_paths.append(get_state_dir()[0]) + # always load local repository configuration + mach.settings_paths.append(mozilla_dir) + + for category, meta in CATEGORIES.items(): + mach.define_category(category, meta['short'], meta['long'], + meta['priority']) + + for path in MACH_MODULES: + mach.load_commands_from_file(os.path.join(mozilla_dir, path)) + + return mach + + +# Hook import such that .pyc/.pyo files without a corresponding .py file in +# the source directory are essentially ignored. See further below for details +# and caveats. +# Objdirs outside the source directory are ignored because in most cases, if +# a .pyc/.pyo file exists there, a .py file will be next to it anyways. +class ImportHook(object): + def __init__(self, original_import): + self._original_import = original_import + # Assume the source directory is the parent directory of the one + # containing this file. + self._source_dir = os.path.normcase(os.path.abspath( + os.path.dirname(os.path.dirname(__file__)))) + os.sep + self._modules = set() + + def __call__(self, name, globals=None, locals=None, fromlist=None, + level=-1): + # name might be a relative import. Instead of figuring out what that + # resolves to, which is complex, just rely on the real import. + # Since we don't know the full module name, we can't check sys.modules, + # so we need to keep track of which modules we've already seen to avoid + # to stat() them again when they are imported multiple times. + module = self._original_import(name, globals, locals, fromlist, level) + + # Some tests replace modules in sys.modules with non-module instances. + if not isinstance(module, ModuleType): + return module + + resolved_name = module.__name__ + if resolved_name in self._modules: + return module + self._modules.add(resolved_name) + + # Builtin modules don't have a __file__ attribute. + if not hasattr(module, '__file__'): + return module + + # Note: module.__file__ is not always absolute. + path = os.path.normcase(os.path.abspath(module.__file__)) + # Note: we could avoid normcase and abspath above for non pyc/pyo + # files, but those are actually rare, so it doesn't really matter. + if not path.endswith(('.pyc', '.pyo')): + return module + + # Ignore modules outside our source directory + if not path.startswith(self._source_dir): + return module + + # If there is no .py corresponding to the .pyc/.pyo module we're + # loading, remove the .pyc/.pyo file, and reload the module. + # Since we already loaded the .pyc/.pyo module, if it had side + # effects, they will have happened already, and loading the module + # with the same name, from another directory may have the same side + # effects (or different ones). We assume it's not a problem for the + # python modules under our source directory (either because it + # doesn't happen or because it doesn't matter). + if not os.path.exists(module.__file__[:-1]): + os.remove(module.__file__) + del sys.modules[module.__name__] + module = self(name, globals, locals, fromlist, level) + + return module + + +# Install our hook +__builtin__.__import__ = ImportHook(__builtin__.__import__) diff --git a/build/macosx/build-cctools.sh b/build/macosx/build-cctools.sh new file mode 100755 index 000000000..af0b36221 --- /dev/null +++ b/build/macosx/build-cctools.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +set -e + +if ! git remote -v | grep origin | grep -q cctools-port; then + echo "must be in a cctools-port checkout" + exit 1 +fi + +mkdir build-cctools +cd build-cctools + +CFLAGS='-mcpu=generic -mtune=generic' MACOSX_DEPLOYMENT_TARGET=10.7 ../cctools/configure --target=x86_64-apple-darwin11 +env MACOSX_DEPLOYMENT_TARGET=10.7 make -s -j4 + +if test ! -e ld64/src/ld/ld; then + echo "ld did not get built" + exit 1 +fi + +gtar jcf cctools.tar.bz2 ld64/src/ld/ld --transform 's#ld64/src/ld#cctools/bin#' + +cd ../ + +echo "build from $(git show --pretty=format:%H -s HEAD) complete!" +echo "upload the build-cctools/cctools.tar.bz2 file to tooltool" diff --git a/build/macosx/cross-mozconfig.common b/build/macosx/cross-mozconfig.common new file mode 100644 index 000000000..8e56394d0 --- /dev/null +++ b/build/macosx/cross-mozconfig.common @@ -0,0 +1,47 @@ +# 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/. + +MOZ_AUTOMATION_L10N_CHECK=0 + +if [ "x$IS_NIGHTLY" = "xyes" ]; then + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} +fi +. "$topsrcdir/build/mozconfig.common" + +# ld needs libLTO.so from llvm +mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/clang/lib" + +CROSS_CCTOOLS_PATH=$topsrcdir/cctools +CROSS_SYSROOT=$topsrcdir/MacOSX10.7.sdk +CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/System/Library/PrivateFrameworks +FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT" + +export CC="$topsrcdir/clang/bin/clang $FLAGS" +export CXX="$topsrcdir/clang/bin/clang++ $FLAGS" +export CPP="$topsrcdir/clang/bin/clang $FLAGS -E" +export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config +export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip" +export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10- +export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil +export GENISOIMAGE=$topsrcdir/genisoimage/genisoimage +export DMG_TOOL=$topsrcdir/dmg/dmg + +export HOST_CC="$topsrcdir/clang/bin/clang" +export HOST_CXX="$topsrcdir/clang/bin/clang++" +export HOST_CPP="$topsrcdir/clang/bin/clang -E" +export HOST_CFLAGS="-g" +export HOST_CXXFLAGS="-g" +export HOST_LDFLAGS="-g" + +ac_add_options --target=x86_64-apple-darwin +ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS + +# Enable static analysis checks by default on OSX cross builds. +ac_add_options --enable-clang-plugin + +. "$topsrcdir/build/mozconfig.cache" + +export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token diff --git a/build/macosx/local-mozconfig.common b/build/macosx/local-mozconfig.common new file mode 100644 index 000000000..02a09d2fe --- /dev/null +++ b/build/macosx/local-mozconfig.common @@ -0,0 +1,46 @@ +# 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/. + +if [ "x$IS_NIGHTLY" = "xyes" ]; then + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} +fi +. "$topsrcdir/build/mozconfig.common" + +if [ -d "$topsrcdir/clang" ]; then + # mozilla-central based build + export CC=$topsrcdir/clang/bin/clang + export CXX=$topsrcdir/clang/bin/clang++ + export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config + export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil + # Use an updated linker. + ldflags="-B$topsrcdir/cctools/bin" +elif [ -d "$topsrcdir/../clang" ]; then + # comm-central based build + export CC=$topsrcdir/../clang/bin/clang + export CXX=$topsrcdir/../clang/bin/clang++ + export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config + export DSYMUTIL=$topsrcdir/../clang/bin/llvm-dsymutil + # Use an updated linker. + ldflags="-B$topsrcdir/../cctools/bin" +fi + +# Ensure the updated linker doesn't generate things our older build tools +# don't understand. +ldflags="$ldflags -Wl,-no_data_in_code_info" +export LDFLAGS="$ldflags" + +# If not set use the system default clang +if [ -z "$CC" ]; then + export CC=clang +fi + +# If not set use the system default clang++ +if [ -z "$CXX" ]; then + export CXX=clang++ +fi + +export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token diff --git a/build/macosx/mozconfig.common b/build/macosx/mozconfig.common new file mode 100644 index 000000000..27634b7f3 --- /dev/null +++ b/build/macosx/mozconfig.common @@ -0,0 +1,5 @@ +if test `uname -s` = Linux; then + . $topsrcdir/build/macosx/cross-mozconfig.common +else + . $topsrcdir/build/macosx/local-mozconfig.common +fi diff --git a/build/macosx/permissions/chown_revert.c b/build/macosx/permissions/chown_revert.c new file mode 100644 index 000000000..2cf3e37c3 --- /dev/null +++ b/build/macosx/permissions/chown_revert.c @@ -0,0 +1,20 @@ +/* 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 <unistd.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + if (argc != 2) + return 1; + + uid_t realuser = getuid(); + char uidstring[20]; + snprintf(uidstring, 19, "%i", realuser); + uidstring[19] = '\0'; + + return execl("/usr/sbin/chown", + "/usr/sbin/chown", "-R", "-h", uidstring, argv[1], (char*) 0); +} diff --git a/build/macosx/permissions/chown_root.c b/build/macosx/permissions/chown_root.c new file mode 100644 index 000000000..c9b13a530 --- /dev/null +++ b/build/macosx/permissions/chown_root.c @@ -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/. */ + +#include <unistd.h> + +int main(int argc, char **argv) +{ + if (argc != 2) + return 1; + + return execl("/usr/sbin/chown", + "/usr/sbin/chown", "-R", "-h", "root:admin", argv[1], (char*) 0); +} diff --git a/build/macosx/universal/mozconfig b/build/macosx/universal/mozconfig new file mode 100644 index 000000000..32ab66f2d --- /dev/null +++ b/build/macosx/universal/mozconfig @@ -0,0 +1,11 @@ +# 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/. + +# i386/x86-64 Universal Build mozconfig + +# As used here, arguments in $MOZ_BUILD_PROJECTS are suitable as arguments +# to gcc's -arch parameter. +mk_add_options MOZ_BUILD_PROJECTS="x86_64 i386" + +. $topsrcdir/build/macosx/universal/mozconfig.common diff --git a/build/macosx/universal/mozconfig.common b/build/macosx/universal/mozconfig.common new file mode 100644 index 000000000..bb54bc6c4 --- /dev/null +++ b/build/macosx/universal/mozconfig.common @@ -0,0 +1,55 @@ +# 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/. + +mk_add_options MOZ_UNIFY_BDATE=1 + +DARWIN_VERSION=10 +ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION +ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION +ac_add_app_options i386 --with-unify-dist=../x86_64/dist +ac_add_app_options x86_64 --with-unify-dist=../i386/dist + +if ! test `uname -s` = Linux; then + # Cross-universal builds already do the equivalent of this by setting -isysroot directly + ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk +fi + +. $topsrcdir/build/macosx/mozconfig.common + +# $MOZ_BUILD_APP is only defined when sourced by configure. That's not a +# problem, because the variables it affects only need to be set for +# configure. +if test -n "$MOZ_BUILD_APP" ; then +if test "$MOZ_BUILD_APP" = "i386" -o "$MOZ_BUILD_APP" = "x86_64"; then + TARGET_CPU=$MOZ_BUILD_APP + + # $HOST_CXX is presently unused. $HOST_CC will only be used during a cross + # compile. + HOST_CC=$CC + HOST_CXX=$CXX + + NATIVE_CPU=`$topsrcdir/build/autoconf/config.guess | cut -f1 -d-` + + # It's not strictly necessary to specify -arch during native builds, but it + # makes the merged about:buildconfig easier to follow, and it reduces + # conditionalized differences between builds. + CC="$CC -arch $TARGET_CPU" + CXX="$CXX -arch $TARGET_CPU" + + # These must be set for cross builds, and don't hurt straight builds. + RANLIB="${TOOLCHAIN_PREFIX}ranlib" + AR="${TOOLCHAIN_PREFIX}ar" + AS=$CC + LD=ld + STRIP="${TOOLCHAIN_PREFIX}strip" + OTOOL="${TOOLCHAIN_PREFIX}otool" + + # Each per-CPU build should be entirely oblivious to the fact that a + # universal binary will be produced. The exception is packager.mk, which + # needs to know to look for universal bits when building the .dmg. + UNIVERSAL_BINARY=1 + + export CC CXX HOST_CC HOST_CXX RANLIB AR AS LD STRIP OTOOL +fi +fi diff --git a/build/macosx/universal/unify b/build/macosx/universal/unify new file mode 100755 index 000000000..38dd35414 --- /dev/null +++ b/build/macosx/universal/unify @@ -0,0 +1,1525 @@ +#!/usr/bin/perl +# 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/. + +use strict; +use warnings; + +=pod + +=head1 NAME + +B<unify> - Mac OS X universal binary packager + +=head1 SYNOPSIS + +B<unify> +I<ppc-path> +I<x86-path> +I<universal-path> +[B<--dry-run>] +[B<--only-one> I<action>] +[B<--verbosity> I<level>] +[B<--unify-with-sort> I<regex>] + +=head1 DESCRIPTION + +I<unify> merges any two architecture-specific files or directory trees +into a single file or tree suitable for use on either architecture as a +"fat" or "universal binary." + +Architecture-specific Mach-O files will be merged into fat Mach-O files +using L<lipo(1)>. Non-Mach-O files in the architecture-specific trees +are compared to ensure that they are equivalent before copying. Symbolic +links are permitted in the architecture-specific trees and will cause +identical links to be created in the merged tree, provided that the source +links have identical targets. Directories are processed recursively. + +If the architecture-specific source trees contain zip archives (including +jar files) that are not identical according to a byte-for-byte check, they +are still assumed to be equivalent if both archives contain exactly the +same members with identical checksums and sizes. + +Behavior when one architecture-specific tree contains files that the other +does not is controlled by the B<--only-one> option. + +If Mach-O files cannot be merged using L<lipo(1)>, zip archives are not +equivalent, regular files are not identical, or any other error occurs, +B<unify> will fail with an exit status of 1. Diagnostic messages are +typically printed to stderr; this behavior can be controlled with the +B<--verbosity> option. + +=head1 OPTIONS + +=over 5 + +=item I<ppc-path> + +=item I<x86-path> + +The paths to directory trees containing PowerPC and x86 builds, +respectively. I<ppc-path> and I<x86-path> are permitted to contain files +that are already "fat," and only the appropriate architecture's images will +be used. + +I<ppc-path> and I<x86-path> are also permitted to both be files, in which +case B<unify> operates solely on those files, and produces an appropriate +merged file at I<target-path>. + +=item I<target-path> + +The path to the merged file or directory tree. This path will be created, +and it must not exist prior to running B<unify>. + +=item B<--dry-run> + +When specified, the commands that would be executed are printed, without +actually executing them. Note that B<--dry-run> and the equivalent +B<--verbosity> level during "wet" runs may print equivalent commands when +no commands are in fact executed: certain operations are handled internally +within B<unify>, and an approximation of a command that performs a similar +task is printed. + +=item B<--only-one> I<action> + +Controls handling of files that are only present in one of the two source +trees. I<action> may be: + skip - These files are skipped. + copy - These files are copied from the tree in which they exist. + fail - When this condition occurs, it is treated as an error. + +The default I<action> is copy. + +=item B<--verbosity> I<level> + +Adjusts the level of loudness of B<unify>. The possible values for +I<level> are: + 0 - B<unify> never prints anything. + (Other programs that B<unify> calls may still print messages.) + 1 - Fatal error messages are printed to stderr. + 2 - Nonfatal warnings are printed to stderr. + 3 - Commands are printed to stdout as they are executed. + +The default I<level> is 2. + +=item B<--unify-with-sort> I<regex> + +Allows merging files matching I<regex> that differ only by the ordering +of the lines contained within them. The unified file will have its contents +sorted. This option may be given multiple times to specify multiple +regexes for matching files. + +=back + +=head1 EXAMPLES + +=over 5 + +=item Create a universal .app bundle from two architecture-specific .app +bundles: + +unify --only-one copy ppc/dist/firefox/Firefox.app + x86/dist/firefox/Firefox.app universal/Firefox.app + --verbosity 3 + +=item Merge two identical architecture-specific trees: + +unify --only-one fail /usr/local /nfs/x86/usr/local + /tmp/usrlocal.fat + +=back + +=head1 REQUIREMENTS + +The only esoteric requirement of B<unify> is that the L<lipo(1)> command +be available. It is present on Mac OS X systems at least as early as +10.3.9, and probably earlier. Mac OS X 10.4 ("Tiger") or later are +recommended. + +=head1 LICENSE + +MPL 2. + +=head1 AUTHOR + +The software was initially written by Mark Mentovai; copyright 2006 +Google Inc. + +=head1 SEE ALSO + +L<cmp(1)>, L<ditto(1)>, L<lipo(1)> + +=cut + +use Archive::Zip(':ERROR_CODES'); +use Errno; +use Fcntl; +use File::Compare; +use File::Copy; +use Getopt::Long; + +my (%gConfig, $gDryRun, $gOnlyOne, $gVerbosity, @gSortMatches); + +sub argumentEscape(@); +sub command(@); +sub compareZipArchives($$); +sub complain($$@); +sub copyIfIdentical($$$); +sub slurp($); +sub get_sorted($); +sub compare_sorted($$); +sub copyIfIdenticalWhenSorted($$$); +sub createUniqueFile($$); +sub makeUniversal($$$); +sub makeUniversalDirectory($$$); +sub makeUniversalInternal($$$$); +sub makeUniversalFile($$$); +sub usage(); +sub readZipCRCs($); + +{ + package FileAttrCache; + + sub new($$); + + sub isFat($); + sub isMachO($); + sub isZip($); + sub lIsDir($); + sub lIsExecutable($); + sub lIsRegularFile($); + sub lIsSymLink($); + sub lstat($); + sub lstatMode($); + sub lstatType($); + sub magic($); + sub magic2($); + sub path($); + sub stat($); + sub statSize($); +} + +%gConfig = ( + 'cmd_lipo' => 'lipo', + 'cmd_rm' => 'rm', +); + +$gDryRun = 0; +$gOnlyOne = 'copy'; +$gVerbosity = 2; +@gSortMatches = (); + +Getopt::Long::Configure('pass_through'); +GetOptions('dry-run' => \$gDryRun, + 'only-one=s' => \$gOnlyOne, + 'verbosity=i' => \$gVerbosity, + 'unify-with-sort=s' => \@gSortMatches, + 'config=s' => \%gConfig); # "hidden" option not in usage() + +if (scalar(@ARGV) != 3 || $gVerbosity < 0 || $gVerbosity > 3 || + ($gOnlyOne ne 'skip' && $gOnlyOne ne 'copy' && $gOnlyOne ne 'fail')) { + usage(); + exit(1); +} + +if (!makeUniversal($ARGV[0],$ARGV[1],$ARGV[2])) { + # makeUniversal or something it called will have printed an error. + exit(1); +} + +exit(0); + +# argumentEscape(@arguments) +# +# Takes a list of @arguments and makes them shell-safe. +sub argumentEscape(@) { + my (@arguments); + @arguments = @_; + + my ($argument, @argumentsOut); + foreach $argument (@arguments) { + $argument =~ s%([^A-Za-z0-9_\-/.=+,])%\\$1%g; + push(@argumentsOut, $argument); + } + + return @argumentsOut; +} + +# command(@arguments) +# +# Runs the specified command by calling system(@arguments). If $gDryRun +# is true, the command is printed but not executed, and 0 is returned. +# if $gVerbosity is greater than 1, the command is printed before being +# executed. When the command is executed, the system() return value will +# be returned. stdout and stderr are left connected for command output. +sub command(@) { + my (@arguments); + @arguments = @_; + if ($gVerbosity >= 3 || $gDryRun) { + print(join(' ', argumentEscape(@arguments))."\n"); + } + if ($gDryRun) { + return 0; + } + return system(@arguments); +} + +# compareZipArchives($zip1, $zip2) +# +# Given two pathnames to zip archives, determines whether or not they are +# functionally identical. Returns true if they are, false if they differ in +# some substantial way, and undef if an error occurs. If the zip files +# differ, diagnostic messages are printed indicating how they differ. +# +# Zip files will differ if any of the members are different as defined by +# readZipCRCs, which consider CRCs, sizes, and file types as stored in the +# file header. Timestamps are not considered. Zip files also differ if one +# file contains members that the other one does not. $gOnlyOne has no +# effect on this behavior. +sub compareZipArchives($$) { + my ($zip1, $zip2); + ($zip1, $zip2) = @_; + + my ($CRCHash1, $CRCHash2); + if (!defined($CRCHash1 = readZipCRCs($zip1))) { + # readZipCRCs printed an error. + return undef; + } + if (!defined($CRCHash2 = readZipCRCs($zip2))) { + # readZipCRCs printed an error. + return undef; + } + + my (@diffCRCs, @onlyInZip1); + @diffCRCs = (); + @onlyInZip1 = (); + + my ($memberName); + foreach $memberName (keys(%$CRCHash1)) { + if (!exists($$CRCHash2{$memberName})) { + # The member is present in $zip1 but not $zip2. + push(@onlyInZip1, $memberName); + } + elsif ($$CRCHash1{$memberName} ne $$CRCHash2{$memberName}) { + # The member is present in both archives but its CRC or some other + # other critical attribute isn't identical. + push(@diffCRCs, $memberName); + } + delete($$CRCHash2{$memberName}); + } + + # If any members remain in %CRCHash2, it's because they're not present + # in $zip1. + my (@onlyInZip2); + @onlyInZip2 = keys(%$CRCHash2); + + if (scalar(@onlyInZip1) + scalar(@onlyInZip2) + scalar(@diffCRCs)) { + complain(1, 'compareZipArchives: zip archives differ:', + $zip1, + $zip2); + if (scalar(@onlyInZip1)) { + complain(1, 'compareZipArchives: members only in former:', + @onlyInZip1); + } + if (scalar(@onlyInZip2)) { + complain(1, 'compareZipArchives: members only in latter:', + @onlyInZip2); + } + if (scalar(@diffCRCs)) { + complain(1, 'compareZipArchives: members differ:', + @diffCRCs); + } + return 0; + } + + return 1; +} + +# complain($severity, $message, @list) +# +# Prints $message to stderr if $gVerbosity allows it for severity level +# $severity. @list is a list of words that will be shell-escaped and printed +# after $message, one per line, intended to be used, for example, to list +# arguments to a call that failed. +# +# Expected severity levels are 1 for hard errors and 2 for non-fatal warnings. +# +# Always returns false as a convenience, so callers can return complain's +# return value when it is used to signal errors. +sub complain($$@) { + my ($severity, $message, @list); + ($severity, $message, @list) = @_; + + if ($gVerbosity >= $severity) { + print STDERR ($0.': '.$message."\n"); + + my ($item); + while ($item = shift(@list)) { + print STDERR (' '.(argumentEscape($item))[0]. + (scalar(@list)?',':'')."\n"); + } + } + + return 0; +} + +# copyIfIdentical($source1, $source2, $target) +# +# $source1 and $source2 are FileAttrCache objects that are compared, and if +# identical, copied to path string $target. The comparison is initially +# done as a byte-for-byte comparison, but if the files differ and appear to +# be zip archives, compareZipArchives is called to determine whether +# files that are not byte-for-byte identical are equivalent archives. +# +# Returns true on success, false for files that are not identical or +# equivalent archives, and undef if an error occurs. +# +# One of $source1 and $source2 is permitted to be undef. In this event, +# whichever source is defined is copied directly to $target without performing +# any comparisons. This enables the $gOnlyOne = 'copy' mode, which is +# driven by makeUniversalDirectory and makeUniversalInternal. +sub copyIfIdentical($$$) { + my ($source1, $source2, $target); + ($source1, $source2, $target) = @_; + + if (!defined($source1)) { + # If there's only one source file, make it the first file. Order + # isn't important here, and this makes it possible to use + # defined($source2) as the switch, and to always copy from $source1. + $source1 = $source2; + $source2 = undef; + } + + if (defined($source2)) { + # Only do the comparisons if there are two source files. If there's + # only one source file, skip the comparisons and go straight to the + # copy operation. + if ($gVerbosity >= 3 || $gDryRun) { + print('cmp -s '. + join(' ',argumentEscape($source1->path(), $source2->path()))."\n"); + } + my ($comparison); + if (!defined($comparison = compare($source1->path(), $source2->path())) || + $comparison == -1) { + return complain(1, 'copyIfIdentical: compare: '.$!.' while comparing:', + $source1->path(), + $source2->path()); + } + elsif ($comparison != 0) { + my ($zip1, $zip2); + if (defined($zip1 = $source1->isZip()) && + defined($zip2 = $source2->isZip()) && + $zip1 && $zip2) { + my ($zipComparison); + if (!defined($zipComparison = compareZipArchives($source1->path(), + $source2->path)) || + !$zipComparison) { + # An error occurred or the zip files aren't sufficiently identical. + # compareZipArchives will have printed an error message. + return 0; + } + # The zip files were compared successfully, and they both contain + # all of the same members, and all of their members' CRCs are + # identical. For the purposes of this script, the zip files can be + # treated as identical, so reset $comparison. + $comparison = 0; + } + } + if ($comparison != 0) { + return complain(1, 'copyIfIdentical: files differ:', + $source1->path(), + $source2->path()); + } + } + + if ($gVerbosity >= 3 || $gDryRun) { + print('cp '. + join(' ',argumentEscape($source1->path(), $target))."\n"); + } + + if (!$gDryRun) { + my ($isExecutable); + + # Set the execute bits (as allowed by the umask) on the new file if any + # execute bit is set on either old file. + $isExecutable = $source1->lIsExecutable() || + (defined($source2) && $source2->lIsExecutable()); + + if (!createUniqueFile($target, $isExecutable ? 0777 : 0666)) { + # createUniqueFile printed an error. + return 0; + } + + if (!copy($source1->path(), $target)) { + complain(1, 'copyIfIdentical: copy: '.$!.' while copying', + $source1->path(), + $target); + unlink($target); + return 0; + } + } + + return 1; +} + +# slurp($file) +# +# Read the contents of $file into an array and return it. +# Returns undef on error. +sub slurp($) { + my $file = $_[0]; + open FILE, $file or return undef; + my @lines = <FILE>; + close FILE; + return @lines; +} + +# get_sorted($file) +# Get the sorted lines of a file as a list, normalizing a newline on the last line if necessary. +sub get_sorted($) { + my ($file) = @_; + my @lines = slurp($file); + my $lastline = $lines[-1]; + if (!($lastline =~ /\n/)) { + $lines[-1] = $lastline . "\n"; + } + return sort(@lines); +} + +# compare_sorted($file1, $file2) +# +# Read the contents of both files into arrays, sort the arrays, +# and then compare the two arrays for equality. +# +# Returns 0 if the sorted array contents are equal, or 1 if not. +# Returns undef on error. +sub compare_sorted($$) { + my ($file1, $file2) = @_; + my @lines1 = get_sorted($file1); + my @lines2 = get_sorted($file2); + + return undef if !@lines1 || !@lines2; + return 1 unless scalar @lines1 == scalar @lines2; + + for (my $i = 0; $i < scalar @lines1; $i++) { + return 1 if $lines1[$i] ne $lines2[$i]; + } + return 0; +} + +# copyIfIdenticalWhenSorted($source1, $source2, $target) +# +# $source1 and $source2 are FileAttrCache objects that are compared, and if +# identical, copied to path string $target. The comparison is done by +# sorting the individual lines within the two files and comparing the results. +# +# Returns true on success, false for files that are not equivalent, +# and undef if an error occurs. +sub copyIfIdenticalWhenSorted($$$) { + my ($source1, $source2, $target); + ($source1, $source2, $target) = @_; + + if ($gVerbosity >= 3 || $gDryRun) { + print('cmp -s '. + join(' ',argumentEscape($source1->path(), $source2->path()))."\n"); + } + my ($comparison); + if (!defined($comparison = compare_sorted($source1->path(), + $source2->path())) || + $comparison == -1) { + return complain(1, 'copyIfIdenticalWhenSorted: compare: '.$! + .' while comparing:', + $source1->path(), + $source2->path()); + } + if ($comparison != 0) { + return complain(1, 'copyIfIdenticalWhenSorted: files differ:', + $source1->path(), + $source2->path()); + } + + if ($gVerbosity >= 3 || $gDryRun) { + print('cp '. + join(' ',argumentEscape($source1->path(), $target))."\n"); + } + + if (!$gDryRun) { + my ($isExecutable); + + # Set the execute bits (as allowed by the umask) on the new file if any + # execute bit is set on either old file. + $isExecutable = $source1->lIsExecutable() || + (defined($source2) && $source2->lIsExecutable()); + + if (!createUniqueFile($target, $isExecutable ? 0777 : 0666)) { + # createUniqueFile printed an error. + return 0; + } + + if (!copy($source1->path(), $target)) { + complain(1, 'copyIfIdenticalWhenSorted: copy: '.$! + .' while copying', + $source1->path(), + $target); + unlink($target); + return 0; + } + } + + return 1; +} + +# createUniqueFile($path, $mode) +# +# Creates a new plain empty file at pathname $path, provided it does not +# yet exist. $mode is used as the file mode. The actual file's mode will +# be modified by the effective umask. Returns false if the file could +# not be created, setting $! to the error. An error message is printed +# in the event of failure. +sub createUniqueFile($$) { + my ($path, $mode); + ($path, $mode) = @_; + + my ($fh); + if (!sysopen($fh, $path, O_WRONLY | O_CREAT | O_EXCL, $mode)) { + return complain(1, 'createUniqueFile: open: '.$!.' for:', + $path); + } + close($fh); + + return 1; +} + +# makeUniversal($pathPPC, $pathX86, $pathTarget) +# +# The top-level call. $pathPPC, $pathX86, and $pathTarget are strings +# identifying the ppc and x86 files or directories to merge and the location +# to merge them to. Returns false on failure and true on success. +sub makeUniversal($$$) { + my ($pathTarget, $pathPPC, $pathX86); + ($pathPPC, $pathX86, $pathTarget) = @_; + + my ($filePPC, $fileX86); + $filePPC = FileAttrCache->new($pathPPC); + $fileX86 = FileAttrCache->new($pathX86); + + return makeUniversalInternal(1, $filePPC, $fileX86, $pathTarget); +} + +# makeUniversalDirectory($dirPPC, $dirX86, $dirTarget) +# +# This is part of the heart of recursion. $dirPPC and $dirX86 are +# FileAttrCache objects designating the source ppc and x86 directories to +# merge into a universal directory at $dirTarget, a string. For each file +# in $dirPPC and $dirX86, makeUniversalInternal is called. +# makeUniversalInternal will call back into makeUniversalDirectory for +# directories, thus completing the recursion. If a failure is encountered +# in ths function or in makeUniversalInternal or anything that it calls, +# false is returned, otherwise, true is returned. +# +# If there are files present in one source directory but not both, the +# value of $gOnlyOne controls the behavior. If $gOnlyOne is 'copy', the +# single source file is copied into $pathTarget. If it is 'skip', it is +# skipped. If it is 'fail', such files will trigger makeUniversalDirectory +# to fail. +# +# If either source directory is undef, it is treated as having no files. +# This facilitates deep recursion when entire directories are only present +# in one source when $gOnlyOne = 'copy'. +sub makeUniversalDirectory($$$) { + my ($dirPPC, $dirX86, $dirTarget); + ($dirPPC, $dirX86, $dirTarget) = @_; + + my ($dh, @filesPPC, @filesX86); + + @filesPPC = (); + if (defined($dirPPC)) { + if (!opendir($dh, $dirPPC->path())) { + return complain(1, 'makeUniversalDirectory: opendir ppc: '.$!.' for:', + $dirPPC->path()); + } + @filesPPC = readdir($dh); + closedir($dh); + } + + @filesX86 = (); + if (defined($dirX86)) { + if (!opendir($dh, $dirX86->path())) { + return complain(1, 'makeUniversalDirectory: opendir x86: '.$!.' for:', + $dirX86->path()); + } + @filesX86 = readdir($dh); + closedir($dh); + } + + my (%common, $file, %onlyPPC, %onlyX86); + + %onlyPPC = (); + foreach $file (@filesPPC) { + if ($file eq '.' || $file eq '..') { + next; + } + $onlyPPC{$file}=1; + } + + %common = (); + %onlyX86 = (); + foreach $file (@filesX86) { + if ($file eq '.' || $file eq '..') { + next; + } + if ($onlyPPC{$file}) { + delete $onlyPPC{$file}; + $common{$file}=1; + } + else { + $onlyX86{$file}=1; + } + } + + # First, handle files common to both. + foreach $file (sort(keys(%common))) { + if (!makeUniversalInternal(0, + FileAttrCache->new($dirPPC->path().'/'.$file), + FileAttrCache->new($dirX86->path().'/'.$file), + $dirTarget.'/'.$file)) { + # makeUniversalInternal will have printed an error. + return 0; + } + } + + # Handle files found only in a single directory here. There are three + # options, dictated by $gOnlyOne: fail if files are only present in + # one directory, skip any files only present in one directory, or copy + # these files straight over to the target directory. In any event, + # a message will be printed indicating that the file trees don't match + # exactly. + if (keys(%onlyPPC)) { + complain(($gOnlyOne eq 'fail' ? 1 : 2), + ($gOnlyOne ne 'fail' ? 'warning: ' : ''). + 'makeUniversalDirectory: only in ppc '. + (argumentEscape($dirPPC->path()))[0].':', + argumentEscape(keys(%onlyPPC))); + } + + if (keys(%onlyX86)) { + complain(($gOnlyOne eq 'fail' ? 1 : 2), + ($gOnlyOne ne 'fail' ? 'warning: ' : ''). + 'makeUniversalDirectory: only in x86 '. + (argumentEscape($dirX86->path()))[0].':', + argumentEscape(keys(%onlyX86))); + } + + if ($gOnlyOne eq 'fail' && (keys(%onlyPPC) || keys(%onlyX86))) { + # Error message(s) printed above. + return 0; + } + + if ($gOnlyOne eq 'copy') { + foreach $file (sort(keys(%onlyPPC))) { + if (!makeUniversalInternal(0, + FileAttrCache->new($dirPPC->path().'/'.$file), + undef, + $dirTarget.'/'.$file)) { + # makeUniversalInternal will have printed an error. + return 0; + } + } + + foreach $file (sort(keys(%onlyX86))) { + if (!makeUniversalInternal(0, + undef, + FileAttrCache->new($dirX86->path().'/'.$file), + $dirTarget.'/'.$file)) { + # makeUniversalInternal will have printed an error. + return 0; + } + } + } + + return 1; +} + +# makeUniversalFile($sourcePPC, $sourceX86, $targetPath) +# +# Creates a universal file at pathname $targetPath based on a ppc image at +# $sourcePPC and an x86 image at $sourceX86. $sourcePPC and $sourceX86 are +# both FileAttrCache objects. Returns true on success and false on failure. +# On failure, diagnostics will be printed to stderr. +# +# The source files may be either thin Mach-O images of the appropriate +# architecture, or fat Mach-O files that contain images of the appropriate +# architecture. +# +# This function wraps the lipo utility, see lipo(1). +sub makeUniversalFile($$$) { + my ($sourcePPC, $sourceX86, $targetPath, @tempThinFiles, $thinPPC, $thinX86); + ($sourcePPC, $sourceX86, $targetPath) = @_; + $thinPPC = $sourcePPC; + $thinX86 = $sourceX86; + + @tempThinFiles = (); + + # The source files might already be fat. They should be thinned out to only + # contain a single architecture. + + my ($isFatPPC, $isFatX86); + + if(!defined($isFatPPC = $sourcePPC->isFat())) { + # isFat printed its own error + return 0; + } + elsif($isFatPPC) { + $thinPPC = FileAttrCache->new($targetPath.'.ppc'); + push(@tempThinFiles, $thinPPC->path()); + if (command($gConfig{'cmd_lipo'}, '-thin', 'ppc', + $sourcePPC->path(), '-output', $thinPPC->path()) != 0) { + unlink(@tempThinFiles); + return complain(1, 'lipo thin ppc failed for:', + $sourcePPC->path(), + $thinPPC->path()); + } + } + + if(!defined($isFatX86 = $sourceX86->isFat())) { + # isFat printed its own error + unlink(@tempThinFiles); + return 0; + } + elsif($isFatX86) { + $thinX86 = FileAttrCache->new($targetPath.'.x86'); + push(@tempThinFiles, $thinX86->path()); + if (command($gConfig{'cmd_lipo'}, '-thin', 'i386', + $sourceX86->path(), '-output', $thinX86->path()) != 0) { + unlink(@tempThinFiles); + return complain(1, 'lipo thin x86 failed for:', + $sourceX86->path(), + $thinX86->path()); + } + } + + # The image for each architecture in the fat file will be aligned on + # a specific boundary, default 4096 bytes, see lipo(1) -segalign. + # Since there's no tail-padding, the fat file will consume the least + # space on disk if the image that comes last exceeds the segment size + # by the smallest amount. + # + # This saves an average of 1kB per fat file over the naive approach of + # always putting one architecture first: average savings is 2kB per + # file, but the naive approach would have gotten it right half of the + # time. + + my ($sizePPC, $sizeX86, $thinPPCForStat, $thinX86ForStat); + + if (!$gDryRun) { + $thinPPCForStat = $thinPPC; + $thinX86ForStat = $thinX86; + } + else { + # Normally, fat source files will have been converted into temporary + # thin files. During a dry run, that doesn't happen, so fake it up + # a little bit by always using the source file, fat or thin, for the + # stat. + $thinPPCForStat = $sourcePPC; + $thinX86ForStat = $sourceX86; + } + + if (!defined($sizePPC = $thinPPCForStat->statSize())) { + unlink(@tempThinFiles); + return complain(1, 'stat ppc: '.$!.' for:', + $thinPPCForStat->path()); + } + if (!defined($sizeX86 = $thinX86ForStat->statSize())) { + unlink(@tempThinFiles); + return complain(1, 'stat x86: '.$!.' for:', + $thinX86ForStat->path()); + } + + $sizePPC = $sizePPC % 4096; + $sizeX86 = $sizeX86 % 4096; + + my (@thinFiles); + + if ($sizePPC == 0) { + # PPC image ends on an alignment boundary, there will be no padding before + # starting the x86 image. + @thinFiles = ($thinPPC->path(), $thinX86->path()); + } + elsif ($sizeX86 == 0 || $sizeX86 > $sizePPC) { + # x86 image ends on an alignment boundary, there will be no padding before + # starting the PPC image, or the x86 image exceeds its alignment boundary + # by more than the PPC image, so there will be less padding if the x86 + # comes first. + @thinFiles = ($thinX86->path(), $thinPPC->path()); + } + else { + # PPC image exceeds its alignment boundary by more than the x86 image, so + # there will be less padding if the PPC comes first. + @thinFiles = ($thinPPC->path(), $thinX86->path()); + } + + my ($isExecutable); + $isExecutable = $sourcePPC->lIsExecutable() || + $sourceX86->lIsExecutable(); + + if (!$gDryRun) { + # Ensure that the file does not yet exist. + + # Set the execute bits (as allowed by the umask) on the new file if any + # execute bit is set on either old file. Yes, it is possible to have + # proper Mach-O files without x-bits: think object files (.o) and static + # archives (.a). + if (!createUniqueFile($targetPath, $isExecutable ? 0777 : 0666)) { + # createUniqueFile printed an error. + unlink(@tempThinFiles); + return 0; + } + } + + # Create the fat file. + if (command($gConfig{'cmd_lipo'}, '-create', @thinFiles, + '-output', $targetPath) != 0) { + unlink(@tempThinFiles, $targetPath); + return complain(1, 'lipo create fat failed for:', + @thinFiles, + $targetPath); + } + + unlink(@tempThinFiles); + + if (!$gDryRun) { + # lipo seems to think that it's free to set its own file modes that + # ignore the umask, which is bogus when the rest of this script + # respects the umask. + if (!chmod(($isExecutable ? 0777 : 0666) & ~umask(), $targetPath)) { + complain(1, 'makeUniversalFile: chmod: '.$!.' for', + $targetPath); + unlink($targetPath); + return 0; + } + } + + return 1; +} + +# makeUniversalInternal($isToplevel, $filePPC, $fileX86, $fileTargetPath) +# +# Given FileAttrCache objects $filePPC and $fileX86, compares filetypes +# and performs the appropriate action to produce a universal file at +# path string $fileTargetPath. $isToplevel should be true if this is +# the recursive base and false otherwise; this controls cleanup behavior +# (cleanup is only performed at the base, because cleanup itself is +# recursive). +# +# This handles regular files by determining whether they are Mach-O files +# and calling makeUniversalFile if so and copyIfIdentical otherwise. Symbolic +# links are handled directly in this function by ensuring that the source link +# targets are identical and creating a new link with the same target +# at $fileTargetPath. Directories are handled by calling +# makeUniversalDirectory. +# +# One of $filePPC and $fileX86 is permitted to be undef. In that case, +# the defined source file is copied directly to the target if a regular +# file, and symlinked appropriately if a symbolic link. This facilitates +# use of $gOnlyOne = 'copy', although no $gOnlyOne checks are made in this +# function, they are all handled in makeUniversalDirectory. +# +# Returns true on success. Returns false on failure, including failures +# in other functions called. +sub makeUniversalInternal($$$$) { + my ($filePPC, $fileTargetPath, $fileX86, $isToplevel); + ($isToplevel, $filePPC, $fileX86, $fileTargetPath) = @_; + + my ($typePPC, $typeX86); + if (defined($filePPC) && !defined($typePPC = $filePPC->lstatType())) { + return complain(1, 'makeUniversal: lstat ppc: '.$!.' for:', + $filePPC->path()); + } + if (defined($fileX86) && !defined($typeX86 = $fileX86->lstatType())) { + return complain(1, 'makeUniversal: lstat x86: '.$!.' for:', + $fileX86->path()); + } + + if (defined($filePPC) && defined($fileX86) && $typePPC != $typeX86) { + return complain(1, 'makeUniversal: incompatible types:', + $filePPC->path(), + $fileX86->path()); + } + + # $aSourceFile will contain a FileAttrCache object that will return + # the correct type data. It's used because it's possible for one of + # the two source files to be undefined (indicating a straight copy). + my ($aSourceFile); + if (defined($filePPC)) { + $aSourceFile = $filePPC; + } + else { + $aSourceFile = $fileX86; + } + + if ($aSourceFile->lIsDir()) { + if ($gVerbosity >= 3 || $gDryRun) { + print('mkdir '.(argumentEscape($fileTargetPath))[0]."\n"); + } + if (!$gDryRun && !mkdir($fileTargetPath)) { + return complain(1, 'makeUniversal: mkdir: '.$!.' for:', + $fileTargetPath); + } + + my ($rv); + + if (!($rv = makeUniversalDirectory($filePPC, $fileX86, $fileTargetPath))) { + # makeUniversalDirectory printed an error. + if ($isToplevel) { + command($gConfig{'cmd_rm'},'-rf','--',$fileTargetPath); + } + } + else { + # Touch the directory when leaving it. If unify is being run on an + # .app bundle, the .app might show up without an icon because the + # system might have found the .app before it was completely built. + # Touching it dirties it in LaunchServices' mind. + if ($gVerbosity >= 3) { + print('touch '.(argumentEscape($fileTargetPath))[0]."\n"); + } + utime(undef, undef, $fileTargetPath); + } + + return $rv; + } + elsif ($aSourceFile->lIsSymLink()) { + my ($linkPPC, $linkX86); + if (defined($filePPC) && !defined($linkPPC=readlink($filePPC->path()))) { + return complain(1, 'makeUniversal: readlink ppc: '.$!.' for:', + $filePPC->path()); + } + if (defined($fileX86) && !defined($linkX86=readlink($fileX86->path()))) { + return complain(1, 'makeUniversal: readlink x86: '.$!.' for:', + $fileX86->path()); + } + if (defined($filePPC) && defined($fileX86) && $linkPPC ne $linkX86) { + return complain(1, 'makeUniversal: symbolic links differ:', + $filePPC->path(), + $fileX86->path()); + } + + # $aLink here serves the same purpose as $aSourceFile in the enclosing + # block: it refers to the target of the symbolic link, whether there + # is one valid source or two. + my ($aLink); + if (defined($linkPPC)) { + $aLink = $linkPPC; + } + else { + $aLink = $linkX86; + } + + if ($gVerbosity >= 3 || $gDryRun) { + print('ln -s '. + join(' ',argumentEscape($aLink, $fileTargetPath))."\n"); + } + if (!$gDryRun && !symlink($aLink, $fileTargetPath)) { + return complain(1, 'makeUniversal: symlink: '.$!.' for:', + $aLink, + $fileTargetPath); + } + + return 1; + } + elsif($aSourceFile->lIsRegularFile()) { + my ($machPPC, $machX86, $fileName); + if (!defined($filePPC) || !defined($fileX86)) { + # One of the source files isn't present. The right thing to do is + # to just copy what does exist straight over, so skip Mach-O checks. + $machPPC = 0; + $machX86 = 0; + if (defined($filePPC)) { + $fileName = $filePPC; + } elsif (defined($fileX86)) { + $fileName = $fileX86; + } else { + complain(1, "The file must exist in at least one directory"); + exit(1); + } + } + else { + # both files exist, pick the name of one. + $fileName = $fileX86; + if (!defined($machPPC=$filePPC->isMachO())) { + return complain(1, 'makeUniversal: isFileMachO ppc failed for:', + $filePPC->path()); + } + if (!defined($machX86=$fileX86->isMachO())) { + return complain(1, 'makeUniversal: isFileMachO x86 failed for:', + $fileX86->path()); + } + } + + if ($machPPC != $machX86) { + return complain(1, 'makeUniversal: variant Mach-O attributes:', + $filePPC->path(), + $fileX86->path()); + } + + if ($machPPC) { + # makeUniversalFile will print an error if it fails. + return makeUniversalFile($filePPC, $fileX86, $fileTargetPath); + } + + if (grep { $fileName->path() =~ m/$_/; } @gSortMatches) { + # Regular files, but should be compared with sorting first. + # copyIfIdenticalWhenSorted will print an error if it fails. + return copyIfIdenticalWhenSorted($filePPC, $fileX86, $fileTargetPath); + } + + # Regular file. copyIfIdentical will print an error if it fails. + return copyIfIdentical($filePPC, $fileX86, $fileTargetPath); + } + + # Special file, don't know how to handle. + return complain(1, 'makeUniversal: cannot handle special file:', + $filePPC->path(), + $fileX86->path()); +} + +# usage() +# +# Give the user a hand. +sub usage() { + print STDERR ( +"usage: unify <ppc-path> <x86-path> <universal-path>\n". +" [--dry-run] (print what would be done)\n". +" [--only-one <action>] (skip, copy, fail; default=copy)\n". +" [--verbosity <level>] (0, 1, 2, 3; default=2)\n"); + return; +} + +# readZipCRCs($zipFile) +# +# $zipFile is the pathname to a zip file whose directory will be read. +# A reference to a hash is returned, with the member pathnames from the +# zip file as keys, and reasonably unique identifiers as values. The +# format of the values is not specified exactly, but does include the +# member CRCs and sizes and differentiates between files and directories. +# It specifically does not distinguish between modification times. On +# failure, prints a message and returns undef. +sub readZipCRCs($) { + my ($zipFile); + ($zipFile) = @_; + + my ($ze, $zip); + $zip = Archive::Zip->new(); + + if (($ze = $zip->read($zipFile)) != AZ_OK) { + complain(1, 'readZipCRCs: read error '.$ze.' for:', + $zipFile); + return undef; + } + + my ($member, %memberCRCs, @memberList); + %memberCRCs = (); + @memberList = $zip->members(); + + foreach $member (@memberList) { + # Take a few of the attributes that identify the file and stuff them into + # the members hash. Directories will show up with size 0 and crc32 0, + # so isDirectory() is used to distinguish them from empty files. + $memberCRCs{$member->fileName()} = join(',', $member->isDirectory() ? 1 : 0, + $member->uncompressedSize(), + $member->crc32String()); + } + + return {%memberCRCs}; +} + +{ + # FileAttrCache allows various attributes about a file to be cached + # so that if they are needed again after first use, no system calls + # will be made and the program won't need to hit the disk. + + package FileAttrCache; + + # from /usr/include/mach-o/loader.h + use constant MH_MAGIC => 0xfeedface; + use constant MH_CIGAM => 0xcefaedfe; + use constant MH_MAGIC_64 => 0xfeedfacf; + use constant MH_CIGAM_64 => 0xcffaedfe; + + use Fcntl(':DEFAULT', ':mode'); + + # FileAttrCache->new($path) + # + # Creates a new FileAttrCache object for the file at path $path and + # returns it. The cache is not primed at creation time, values are + # fetched lazily as they are needed. + sub new($$) { + my ($class, $path, $proto, $this); + ($proto, $path) = @_; + if (!($class = ref($proto))) { + $class = $proto; + } + $this = { + 'path' => $path, + 'lstat' => undef, + 'lstatErrno' => 0, + 'lstatInit' => 0, + 'magic' => undef, + 'magic2' => undef, + 'magicErrno' => 0, + 'magicErrMsg' => undef, + 'magicInit' => 0, + 'stat' => undef, + 'statErrno' => 0, + 'statInit' => 0, + }; + bless($this, $class); + return($this); + } + + # $FileAttrCache->isFat() + # + # Returns true if the file is a fat Mach-O file, false if it's not, and + # undef if an error occurs. See /usr/include/mach-o/fat.h. + sub isFat($) { + my ($magic, $magic2, $this); + ($this) = @_; + + # magic() caches, there's no separate cache because isFat() doesn't hit + # the disk other than by calling magic(). + + if (!defined($magic = $this->magic())) { + return undef; + } + $magic2 = $this->magic2(); + + # We have to sanity check the second four bytes, because Java class + # files use the same magic number as Mach-O fat binaries. + # This logic is adapted from file(1), which says that Mach-O uses + # these bytes to count the number of architectures within, while + # Java uses it for a version number. Conveniently, there are only + # 18 labelled Mach-O architectures, and Java's first released + # class format used the version 43.0. + if ($magic == 0xcafebabe && $magic2 < 20) { + return 1; + } + + return 0; + } + + # $FileAttrCache->isMachO() + # + # Returns true if the file is a Mach-O image (including a fat file), false + # if it's not, and undef if an error occurs. See + # /usr/include/mach-o/loader.h and /usr/include/mach-o/fat.h. + sub isMachO($) { + my ($magic, $this); + ($this) = @_; + + # magic() caches, there's no separate cache because isMachO() doesn't hit + # the disk other than by calling magic(). + + if (!defined($magic = $this->magic())) { + return undef; + } + + # Accept Mach-O fat files or Mach-O thin files of either endianness. + if ($magic == MH_MAGIC || + $magic == MH_CIGAM || + $magic == MH_MAGIC_64 || + $magic == MH_CIGAM_64 || + $this->isFat()) { + return 1; + } + + return 0; + } + + # $FileAttrCache->isZip() + # + # Returns true if the file is a zip file, false if it's not, and undef if + # an error occurs. See http://www.pkware.com/business_and_developers/developer/popups/appnote.txt . + sub isZip($) { + my ($magic, $this); + ($this) = @_; + + # magic() caches, there's no separate cache because isFat() doesn't hit + # the disk other than by calling magic(). + + if (!defined($magic = $this->magic())) { + return undef; + } + + if ($magic == 0x504b0304) { + return 1; + } + + return 0; + } + + # $FileAttrCache->lIsExecutable() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is has any, + # execute bit set, false if none are set, or undef if an error occurs. + # On error, $! is set to lstat's errno. + sub lIsExecutable($) { + my ($mode, $this); + ($this) = @_; + + if (!defined($mode = $this->lstatMode())) { + return undef; + } + + return $mode & (S_IXUSR | S_IXGRP | S_IXOTH); + } + + # $FileAttrCache->lIsDir() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is a directory, + # false if it isn't, or undef if an error occurs. Because lstat is used, + # this will return false even if the file is a symlink pointing to a + # directory. On error, $! is set to lstat's errno. + sub lIsDir($) { + my ($type, $this); + ($this) = @_; + + if (!defined($type = $this->lstatType())) { + return undef; + } + + return S_ISDIR($type); + } + + # $FileAttrCache->lIsRegularFile() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is a regular, + # file, false if it isn't, or undef if an error occurs. Because lstat is + # used, this will return false even if the file is a symlink pointing to a + # regular file. On error, $! is set to lstat's errno. + sub lIsRegularFile($) { + my ($type, $this); + ($this) = @_; + + if (!defined($type = $this->lstatType())) { + return undef; + } + + return S_ISREG($type); + } + + # $FileAttrCache->lIsSymLink() + # + # Wraps $FileAttrCache->lstat(), returning true if the file is a symbolic, + # link, false if it isn't, or undef if an error occurs. On error, $! is + # set to lstat's errno. + sub lIsSymLink($) { + my ($type, $this); + ($this) = @_; + + if (!defined($type = $this->lstatType())) { + return undef; + } + + return S_ISLNK($type); + } + + # $FileAttrCache->lstat() + # + # Wraps the lstat system call, providing a cache to speed up multiple + # lstat calls for the same file. See lstat(2) and lstat in perlfunc(1). + sub lstat($) { + my (@stat, $this); + ($this) = @_; + + # Use the cached lstat result. + if ($$this{'lstatInit'}) { + if (defined($$this{'lstatErrno'})) { + $! = $$this{'lstatErrno'}; + } + return @{$$this{'lstat'}}; + } + $$this{'lstatInit'} = 1; + + if (!(@stat = CORE::lstat($$this{'path'}))) { + $$this{'lstatErrno'} = $!; + } + + $$this{'lstat'} = [@stat]; + return @stat; + } + + # $FileAttrCache->lstatMode() + # + # Wraps $FileAttrCache->lstat(), returning the mode bits from the st_mode + # field, or undef if an error occurs. On error, $! is set to lstat's + # errno. + sub lstatMode($) { + my (@stat, $this); + ($this) = @_; + + if (!(@stat = $this->lstat())) { + return undef; + } + + return S_IMODE($stat[2]); + } + + # $FileAttrCache->lstatType() + # + # Wraps $FileAttrCache->lstat(), returning the type bits from the st_mode + # field, or undef if an error occurs. On error, $! is set to lstat's + # errno. + sub lstatType($) { + my (@stat, $this); + ($this) = @_; + + if (!(@stat = $this->lstat())) { + return undef; + } + + return S_IFMT($stat[2]); + } + + # $FileAttrCache->magic() + # + # Returns the "magic number" for the file by reading its first four bytes + # as a big-endian unsigned 32-bit integer and returning the result. If an + # error occurs, returns undef and prints diagnostic messages to stderr. If + # the file is shorter than 32 bits, returns -1. A cache is provided to + # speed multiple magic calls for the same file. + sub magic($) { + my ($this); + ($this) = @_; + + # Use the cached magic result. + if ($$this{'magicInit'}) { + if (defined($$this{'magicErrno'})) { + if (defined($$this{'magicErrMsg'})) { + main::complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:', + $$this{'path'}); + } + $! = $$this{'magicErrno'}; + } + return $$this{'magic'}; + } + + $$this{'magicInit'} = 1; + + my ($fh); + if (!sysopen($fh, $$this{'path'}, O_RDONLY)) { + $$this{'magicErrno'} = $!; + $$this{'magicErrMsg'} = 'open "'.$$this{'path'}.'": '.$!; + main::complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:', + $$this{'path'}); + return undef; + } + + $! = 0; + my ($bytes, $magic, $bytes2, $magic2); + if (!defined($bytes = sysread($fh, $magic, 4))) { + $$this{'magicErrno'} = $!; + $$this{'magicErrMsg'} = 'read "'.$$this{'path'}.'": '.$!; + main::complain(1, 'FileAttrCache::magic: '.$$this{'magicErrMsg'}.' for:', + $$this{'path'}); + close($fh); + return undef; + } + else { + $bytes2 = sysread($fh, $magic2, 4); + } + + close($fh); + + if ($bytes != 4) { + # The file is too short, didn't read a magic number. This isn't really + # an error. Return an unlikely value. + $$this{'magic'} = -1; + $$this{'magic2'} = -1; + return -1; + } + if ($bytes2 != 4) { + # File is too short to read a second 4 bytes. + $magic2 = -1; + } + + $$this{'magic'} = unpack('N', $magic); + $$this{'magic2'} = unpack('N', $magic2); + return $$this{'magic'}; + } + + # $FileAttrCache->magic2() + # + # Returns the second four bytes of the file as a 32-bit little endian number. + # See magic(), above for more info. + sub magic2($) { + my ($this); + ($this) = @_; + + # we do the actual work (and cache it) in magic(). + if (!$$this{'magicInit'}) { + my $magic = $$this->magic(); + } + + return $$this{'magic2'}; + } + + # $FileAttrCache->path() + # + # Returns the file's pathname. + sub path($) { + my ($this); + ($this) = @_; + return $$this{'path'}; + } + + # $FileAttrCache->stat() + # + # Wraps the stat system call, providing a cache to speed up multiple + # stat calls for the same file. If lstat() has already been called and + # the file is not a symbolic link, the cached lstat() result will be used. + # See stat(2) and lstat in perlfunc(1). + sub stat($) { + my (@stat, $this); + ($this) = @_; + + # Use the cached stat result. + if ($$this{'statInit'}) { + if (defined($$this{'statErrno'})) { + $! = $$this{'statErrno'}; + } + return @{$$this{'stat'}}; + } + + $$this{'statInit'} = 1; + + # If lstat has already been called, and the file isn't a symbolic link, + # use the cached lstat result. + if ($$this{'lstatInit'} && !$$this{'lstatErrno'} && + !S_ISLNK(${$$this{'lstat'}}[2])) { + $$this{'stat'} = $$this{'lstat'}; + return @{$$this{'stat'}}; + } + + if (!(@stat = CORE::stat($$this{'path'}))) { + $$this{'statErrno'} = $!; + } + + $$this{'stat'} = [@stat]; + return @stat; + } + + # $FileAttrCache->statSize() + # + # Wraps $FileAttrCache->stat(), returning the st_size field, or undef + # undef if an error occurs. On error, $! is set to stat's errno. + sub statSize($) { + my (@stat, $this); + ($this) = @_; + + if (!(@stat = $this->lstat())) { + return undef; + } + + return $stat[7]; + } +} diff --git a/build/mobile/b2gautomation.py b/build/mobile/b2gautomation.py new file mode 100644 index 000000000..d49a5f1ac --- /dev/null +++ b/build/mobile/b2gautomation.py @@ -0,0 +1,455 @@ +# 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 datetime +import mozcrash +import threading +import os +import posixpath +import Queue +import re +import shutil +import signal +import tempfile +import time +import traceback +import zipfile + +from automation import Automation +from mozlog import get_default_logger +from mozprocess import ProcessHandlerMixin + + +class StdOutProc(ProcessHandlerMixin): + """Process handler for b2g which puts all output in a Queue. + """ + + def __init__(self, cmd, queue, **kwargs): + self.queue = queue + kwargs.setdefault('processOutputLine', []).append(self.handle_output) + ProcessHandlerMixin.__init__(self, cmd, **kwargs) + + def handle_output(self, line): + self.queue.put_nowait(line) + + +class B2GRemoteAutomation(Automation): + _devicemanager = None + + def __init__(self, deviceManager, appName='', remoteLog=None, + marionette=None): + self._devicemanager = deviceManager + self._appName = appName + self._remoteProfile = None + self._remoteLog = remoteLog + self.marionette = marionette + self._is_emulator = False + self.test_script = None + self.test_script_args = None + + # Default our product to b2g + self._product = "b2g" + self.lastTestSeen = "b2gautomation.py" + # Default log finish to mochitest standard + self.logFinish = 'INFO SimpleTest FINISHED' + Automation.__init__(self) + + def setEmulator(self, is_emulator): + self._is_emulator = is_emulator + + def setDeviceManager(self, deviceManager): + self._devicemanager = deviceManager + + def setAppName(self, appName): + self._appName = appName + + def setRemoteProfile(self, remoteProfile): + self._remoteProfile = remoteProfile + + def setProduct(self, product): + self._product = product + + def setRemoteLog(self, logfile): + self._remoteLog = logfile + + def getExtensionIDFromRDF(self, rdfSource): + """ + Retrieves the extension id from an install.rdf file (or string). + """ + from xml.dom.minidom import parse, parseString, Node + + if isinstance(rdfSource, file): + document = parse(rdfSource) + else: + document = parseString(rdfSource) + + # Find the <em:id> element. There can be multiple <em:id> tags + # within <em:targetApplication> tags, so we have to check this way. + for rdfChild in document.documentElement.childNodes: + if rdfChild.nodeType == Node.ELEMENT_NODE and rdfChild.tagName == "Description": + for descChild in rdfChild.childNodes: + if descChild.nodeType == Node.ELEMENT_NODE and descChild.tagName == "em:id": + return descChild.childNodes[0].data + return None + + def installExtension(self, extensionSource, profileDir, extensionID=None): + # Bug 827504 - installing special-powers extension separately causes problems in B2G + if extensionID != "special-powers@mozilla.org": + if not os.path.isdir(profileDir): + self.log.info("INFO | automation.py | Cannot install extension, invalid profileDir at: %s", profileDir) + return + + installRDFFilename = "install.rdf" + + extensionsRootDir = os.path.join(profileDir, "extensions", "staged") + if not os.path.isdir(extensionsRootDir): + os.makedirs(extensionsRootDir) + + if os.path.isfile(extensionSource): + reader = zipfile.ZipFile(extensionSource, "r") + + for filename in reader.namelist(): + # Sanity check the zip file. + if os.path.isabs(filename): + self.log.info("INFO | automation.py | Cannot install extension, bad files in xpi") + return + + # We may need to dig the extensionID out of the zip file... + if extensionID is None and filename == installRDFFilename: + extensionID = self.getExtensionIDFromRDF(reader.read(filename)) + + # We must know the extensionID now. + if extensionID is None: + self.log.info("INFO | automation.py | Cannot install extension, missing extensionID") + return + + # Make the extension directory. + extensionDir = os.path.join(extensionsRootDir, extensionID) + os.mkdir(extensionDir) + + # Extract all files. + reader.extractall(extensionDir) + + elif os.path.isdir(extensionSource): + if extensionID is None: + filename = os.path.join(extensionSource, installRDFFilename) + if os.path.isfile(filename): + with open(filename, "r") as installRDF: + extensionID = self.getExtensionIDFromRDF(installRDF) + + if extensionID is None: + self.log.info("INFO | automation.py | Cannot install extension, missing extensionID") + return + + # Copy extension tree into its own directory. + # "destination directory must not already exist". + shutil.copytree(extensionSource, os.path.join(extensionsRootDir, extensionID)) + + else: + self.log.info("INFO | automation.py | Cannot install extension, invalid extensionSource at: %s", extensionSource) + + # Set up what we need for the remote environment + def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False): + # Because we are running remote, we don't want to mimic the local env + # so no copying of os.environ + if env is None: + env = {} + + if crashreporter: + env['MOZ_CRASHREPORTER'] = '1' + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + + # We always hide the results table in B2G; it's much slower if we don't. + env['MOZ_HIDE_RESULTS_TABLE'] = '1' + return env + + def waitForNet(self): + active = False + time_out = 0 + while not active and time_out < 40: + data = self._devicemanager._runCmd(['shell', '/system/bin/netcfg']).stdout.readlines() + data.pop(0) + for line in data: + if (re.search(r'UP\s+(?:[0-9]{1,3}\.){3}[0-9]{1,3}', line)): + active = True + break + time_out += 1 + time.sleep(1) + return active + + def checkForCrashes(self, directory, symbolsPath): + crashed = False + remote_dump_dir = self._remoteProfile + '/minidumps' + print "checking for crashes in '%s'" % remote_dump_dir + if self._devicemanager.dirExists(remote_dump_dir): + local_dump_dir = tempfile.mkdtemp() + self._devicemanager.getDirectory(remote_dump_dir, local_dump_dir) + try: + logger = get_default_logger() + if logger is not None: + crashed = mozcrash.log_crashes(logger, local_dump_dir, symbolsPath, test=self.lastTestSeen) + else: + crashed = mozcrash.check_for_crashes(local_dump_dir, symbolsPath, test_name=self.lastTestSeen) + except: + traceback.print_exc() + finally: + shutil.rmtree(local_dump_dir) + self._devicemanager.removeDir(remote_dump_dir) + return crashed + + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + # if remote profile is specified, use that instead + if (self._remoteProfile): + profileDir = self._remoteProfile + + cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs) + + return app, args + + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, + debuggerInfo, symbolsPath, outputHandler=None): + """ Wait for tests to finish (as evidenced by a signature string + in logcat), or for a given amount of time to elapse with no + output. + """ + timeout = timeout or 120 + while True: + lines = proc.getStdoutLines(timeout) + if lines: + currentlog = '\n'.join(lines) + + if outputHandler: + for line in lines: + outputHandler(line) + else: + print(currentlog) + + # Match the test filepath from the last TEST-START line found in the new + # log content. These lines are in the form: + # ... INFO TEST-START | /filepath/we/wish/to/capture.html\n + testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", currentlog) + if testStartFilenames: + self.lastTestSeen = testStartFilenames[-1] + if (outputHandler and outputHandler.suite_finished) or ( + hasattr(self, 'logFinish') and self.logFinish in currentlog): + return 0 + else: + self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed " + "out after %d seconds with no output", + self.lastTestSeen, int(timeout)) + self._devicemanager.killProcess('/system/b2g/b2g', sig=signal.SIGABRT) + + timeout = 10 # seconds + starttime = datetime.datetime.now() + while datetime.datetime.now() - starttime < datetime.timedelta(seconds=timeout): + if not self._devicemanager.processExist('/system/b2g/b2g'): + break + time.sleep(1) + else: + print "timed out after %d seconds waiting for b2g process to exit" % timeout + return 1 + + self.checkForCrashes(None, symbolsPath) + return 1 + + def getDeviceStatus(self, serial=None): + # Get the current status of the device. If we know the device + # serial number, we look for that, otherwise we use the (presumably + # only) device shown in 'adb devices'. + serial = serial or self._devicemanager._deviceSerial + status = 'unknown' + + for line in self._devicemanager._runCmd(['devices']).stdout.readlines(): + result = re.match('(.*?)\t(.*)', line) + if result: + thisSerial = result.group(1) + if not serial or thisSerial == serial: + serial = thisSerial + status = result.group(2) + + return (serial, status) + + def restartB2G(self): + # TODO hangs in subprocess.Popen without this delay + time.sleep(5) + self._devicemanager._checkCmd(['shell', 'stop', 'b2g']) + # Wait for a bit to make sure B2G has completely shut down. + time.sleep(10) + self._devicemanager._checkCmd(['shell', 'start', 'b2g']) + if self._is_emulator: + self.marionette.emulator.wait_for_port(self.marionette.port) + + def rebootDevice(self): + # find device's current status and serial number + serial, status = self.getDeviceStatus() + + # reboot! + self._devicemanager._runCmd(['shell', '/system/bin/reboot']) + + # The above command can return while adb still thinks the device is + # connected, so wait a little bit for it to disconnect from adb. + time.sleep(10) + + # wait for device to come back to previous status + print 'waiting for device to come back online after reboot' + start = time.time() + rserial, rstatus = self.getDeviceStatus(serial) + while rstatus != 'device': + if time.time() - start > 120: + # device hasn't come back online in 2 minutes, something's wrong + raise Exception("Device %s (status: %s) not back online after reboot" % (serial, rstatus)) + time.sleep(5) + rserial, rstatus = self.getDeviceStatus(serial) + print 'device:', serial, 'status:', rstatus + + def Process(self, cmd, stdout=None, stderr=None, env=None, cwd=None): + # On a desktop or fennec run, the Process method invokes a gecko + # process in which to the tests. For B2G, we simply + # reboot the device (which was configured with a test profile + # already), wait for B2G to start up, and then navigate to the + # test url using Marionette. There doesn't seem to be any way + # to pass env variables into the B2G process, but this doesn't + # seem to matter. + + # reboot device so it starts up with the mochitest profile + # XXX: We could potentially use 'stop b2g' + 'start b2g' to achieve + # a similar effect; will see which is more stable while attempting + # to bring up the continuous integration. + if not self._is_emulator: + self.rebootDevice() + time.sleep(5) + #wait for wlan to come up + if not self.waitForNet(): + raise Exception("network did not come up, please configure the network" + + " prior to running before running the automation framework") + + # stop b2g + self._devicemanager._runCmd(['shell', 'stop', 'b2g']) + time.sleep(5) + + # For some reason user.js in the profile doesn't get picked up. + # Manually copy it over to prefs.js. See bug 1009730 for more details. + self._devicemanager.moveTree(posixpath.join(self._remoteProfile, 'user.js'), + posixpath.join(self._remoteProfile, 'prefs.js')) + + # relaunch b2g inside b2g instance + instance = self.B2GInstance(self._devicemanager, env=env) + + time.sleep(5) + + # Set up port forwarding again for Marionette, since any that + # existed previously got wiped out by the reboot. + if not self._is_emulator: + self._devicemanager._checkCmd(['forward', + 'tcp:%s' % self.marionette.port, + 'tcp:%s' % self.marionette.port]) + + if self._is_emulator: + self.marionette.emulator.wait_for_port(self.marionette.port) + else: + time.sleep(5) + + # start a marionette session + session = self.marionette.start_session() + if 'b2g' not in session: + raise Exception("bad session value %s returned by start_session" % session) + + with self.marionette.using_context(self.marionette.CONTEXT_CHROME): + self.marionette.execute_script(""" + let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer"; + Components.utils.import("resource://gre/modules/Services.jsm"); + Services.prefs.setBoolPref(SECURITY_PREF, true); + + if (!testUtils.hasOwnProperty("specialPowersObserver")) { + let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"] + .getService(Components.interfaces.mozIJSSubScriptLoader); + loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.jsm", + testUtils); + testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver(); + testUtils.specialPowersObserver.init(); + } + """) + + # run the script that starts the tests + if self.test_script: + if os.path.isfile(self.test_script): + script = open(self.test_script, 'r') + self.marionette.execute_script(script.read(), script_args=self.test_script_args) + script.close() + elif isinstance(self.test_script, basestring): + self.marionette.execute_script(self.test_script, script_args=self.test_script_args) + else: + # assumes the tests are started on startup automatically + pass + + return instance + + # be careful here as this inner class doesn't have access to outer class members + class B2GInstance(object): + """Represents a B2G instance running on a device, and exposes + some process-like methods/properties that are expected by the + automation. + """ + + def __init__(self, dm, env=None): + self.dm = dm + self.env = env or {} + self.stdout_proc = None + self.queue = Queue.Queue() + + # Launch b2g in a separate thread, and dump all output lines + # into a queue. The lines in this queue are + # retrieved and returned by accessing the stdout property of + # this class. + cmd = [self.dm._adbPath] + if self.dm._deviceSerial: + cmd.extend(['-s', self.dm._deviceSerial]) + cmd.append('shell') + for k, v in self.env.iteritems(): + cmd.append("%s=%s" % (k, v)) + cmd.append('/system/bin/b2g.sh') + proc = threading.Thread(target=self._save_stdout_proc, args=(cmd, self.queue)) + proc.daemon = True + proc.start() + + def _save_stdout_proc(self, cmd, queue): + self.stdout_proc = StdOutProc(cmd, queue) + self.stdout_proc.run() + if hasattr(self.stdout_proc, 'processOutput'): + self.stdout_proc.processOutput() + self.stdout_proc.wait() + self.stdout_proc = None + + @property + def pid(self): + # a dummy value to make the automation happy + return 0 + + def getStdoutLines(self, timeout): + # Return any lines in the queue used by the + # b2g process handler. + lines = [] + # get all of the lines that are currently available + while True: + try: + lines.append(self.queue.get_nowait()) + except Queue.Empty: + break + + # wait 'timeout' for any additional lines + if not lines: + try: + lines.append(self.queue.get(True, timeout)) + except Queue.Empty: + pass + return lines + + def wait(self, timeout=None): + # this should never happen + raise Exception("'wait' called on B2GInstance") + + def kill(self): + # this should never happen + raise Exception("'kill' called on B2GInstance") + diff --git a/build/mobile/remoteautomation.py b/build/mobile/remoteautomation.py new file mode 100644 index 000000000..7b2fad6cb --- /dev/null +++ b/build/mobile/remoteautomation.py @@ -0,0 +1,432 @@ +# 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 datetime +import glob +import time +import re +import os +import posixpath +import tempfile +import shutil +import subprocess +import sys + +from automation import Automation +from mozdevice import DMError, DeviceManager +from mozlog import get_default_logger +import mozcrash + +# signatures for logcat messages that we don't care about much +fennecLogcatFilters = [ "The character encoding of the HTML document was not declared", + "Use of Mutation Events is deprecated. Use MutationObserver instead.", + "Unexpected value from nativeGetEnabledTags: 0" ] + +class RemoteAutomation(Automation): + _devicemanager = None + + # Part of a hack for Robocop: "am COMMAND" is handled specially if COMMAND + # is in this set. See usages below. + _specialAmCommands = ('instrument', 'start') + + def __init__(self, deviceManager, appName = '', remoteLog = None, + processArgs=None): + self._devicemanager = deviceManager + self._appName = appName + self._remoteProfile = None + self._remoteLog = remoteLog + self._processArgs = processArgs or {}; + + # Default our product to fennec + self._product = "fennec" + self.lastTestSeen = "remoteautomation.py" + Automation.__init__(self) + + def setDeviceManager(self, deviceManager): + self._devicemanager = deviceManager + + def setAppName(self, appName): + self._appName = appName + + def setRemoteProfile(self, remoteProfile): + self._remoteProfile = remoteProfile + + def setProduct(self, product): + self._product = product + + def setRemoteLog(self, logfile): + self._remoteLog = logfile + + # Set up what we need for the remote environment + def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, dmdPath=None, lsanPath=None): + # Because we are running remote, we don't want to mimic the local env + # so no copying of os.environ + if env is None: + env = {} + + if dmdPath: + env['MOZ_REPLACE_MALLOC_LIB'] = os.path.join(dmdPath, 'libdmd.so') + + # Except for the mochitest results table hiding option, which isn't + # passed to runtestsremote.py as an actual option, but through the + # MOZ_HIDE_RESULTS_TABLE environment variable. + if 'MOZ_HIDE_RESULTS_TABLE' in os.environ: + env['MOZ_HIDE_RESULTS_TABLE'] = os.environ['MOZ_HIDE_RESULTS_TABLE'] + + if crashreporter and not debugger: + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + env['MOZ_CRASHREPORTER'] = '1' + else: + env['MOZ_CRASHREPORTER_DISABLE'] = '1' + + # Crash on non-local network connections by default. + # MOZ_DISABLE_NONLOCAL_CONNECTIONS can be set to "0" to temporarily + # enable non-local connections for the purposes of local testing. + # Don't override the user's choice here. See bug 1049688. + env.setdefault('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1') + + # Send an env var noting that we are in automation. Passing any + # value except the empty string will declare the value to exist. + # + # This may be used to disabled network connections during testing, e.g. + # Switchboard & telemetry uploads. + env.setdefault('MOZ_IN_AUTOMATION', '1') + + # Set WebRTC logging in case it is not set yet. + # On Android, environment variables cannot contain ',' so the + # standard WebRTC setting for NSPR_LOG_MODULES is not available. + # env.setdefault('NSPR_LOG_MODULES', 'signaling:5,mtransport:5,datachannel:5,jsep:5,MediaPipelineFactory:5') + env.setdefault('R_LOG_LEVEL', '6') + env.setdefault('R_LOG_DESTINATION', 'stderr') + env.setdefault('R_LOG_VERBOSE', '1') + + return env + + def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None): + """ Wait for tests to finish. + If maxTime seconds elapse or no output is detected for timeout + seconds, kill the process and fail the test. + """ + # maxTime is used to override the default timeout, we should honor that + status = proc.wait(timeout = maxTime, noOutputTimeout = timeout) + self.lastTestSeen = proc.getLastTestSeen + + topActivity = self._devicemanager.getTopActivity() + if topActivity == proc.procName: + proc.kill(True) + if status == 1: + if maxTime: + print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \ + "allowed maximum time of %s seconds" % (self.lastTestSeen, maxTime) + else: + print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \ + "allowed maximum time" % (self.lastTestSeen) + if status == 2: + print "TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output" \ + % (self.lastTestSeen, int(timeout)) + + return status + + def deleteANRs(self): + # empty ANR traces.txt file; usually need root permissions + # we make it empty and writable so we can test the ANR reporter later + traces = "/data/anr/traces.txt" + try: + self._devicemanager.shellCheckOutput(['echo', '', '>', traces], root=True, + timeout=DeviceManager.short_timeout) + self._devicemanager.shellCheckOutput(['chmod', '666', traces], root=True, + timeout=DeviceManager.short_timeout) + except DMError: + print "Error deleting %s" % traces + pass + + def checkForANRs(self): + traces = "/data/anr/traces.txt" + if self._devicemanager.fileExists(traces): + try: + t = self._devicemanager.pullFile(traces) + if t: + stripped = t.strip() + if len(stripped) > 0: + print "Contents of %s:" % traces + print t + # Once reported, delete traces + self.deleteANRs() + except DMError: + print "Error pulling %s" % traces + except IOError: + print "Error pulling %s" % traces + else: + print "%s not found" % traces + + def deleteTombstones(self): + # delete any existing tombstone files from device + remoteDir = "/data/tombstones" + try: + self._devicemanager.shellCheckOutput(['rm', '-r', remoteDir], root=True, + timeout=DeviceManager.short_timeout) + except DMError: + # This may just indicate that the tombstone directory is missing + pass + + def checkForTombstones(self): + # pull any tombstones from device and move to MOZ_UPLOAD_DIR + remoteDir = "/data/tombstones" + blobberUploadDir = os.environ.get('MOZ_UPLOAD_DIR', None) + if blobberUploadDir: + if not os.path.exists(blobberUploadDir): + os.mkdir(blobberUploadDir) + if self._devicemanager.dirExists(remoteDir): + # copy tombstone files from device to local blobber upload directory + try: + self._devicemanager.shellCheckOutput(['chmod', '777', remoteDir], root=True, + timeout=DeviceManager.short_timeout) + self._devicemanager.shellCheckOutput(['chmod', '666', os.path.join(remoteDir, '*')], root=True, + timeout=DeviceManager.short_timeout) + self._devicemanager.getDirectory(remoteDir, blobberUploadDir, False) + except DMError: + # This may just indicate that no tombstone files are present + pass + self.deleteTombstones() + # add a .txt file extension to each tombstone file name, so + # that blobber will upload it + for f in glob.glob(os.path.join(blobberUploadDir, "tombstone_??")): + # add a unique integer to the file name, in case there are + # multiple tombstones generated with the same name, for + # instance, after multiple robocop tests + for i in xrange(1, sys.maxint): + newname = "%s.%d.txt" % (f, i) + if not os.path.exists(newname): + os.rename(f, newname) + break + else: + print "%s does not exist; tombstone check skipped" % remoteDir + else: + print "MOZ_UPLOAD_DIR not defined; tombstone check skipped" + + def checkForCrashes(self, directory, symbolsPath): + self.checkForANRs() + self.checkForTombstones() + + logcat = self._devicemanager.getLogcat(filterOutRegexps=fennecLogcatFilters) + + javaException = mozcrash.check_for_java_exception(logcat, test_name=self.lastTestSeen) + if javaException: + return True + + # If crash reporting is disabled (MOZ_CRASHREPORTER!=1), we can't say + # anything. + if not self.CRASHREPORTER: + return False + + try: + dumpDir = tempfile.mkdtemp() + remoteCrashDir = posixpath.join(self._remoteProfile, 'minidumps') + if not self._devicemanager.dirExists(remoteCrashDir): + # If crash reporting is enabled (MOZ_CRASHREPORTER=1), the + # minidumps directory is automatically created when Fennec + # (first) starts, so its lack of presence is a hint that + # something went wrong. + print "Automation Error: No crash directory (%s) found on remote device" % remoteCrashDir + # Whilst no crash was found, the run should still display as a failure + return True + self._devicemanager.getDirectory(remoteCrashDir, dumpDir) + + logger = get_default_logger() + if logger is not None: + crashed = mozcrash.log_crashes(logger, dumpDir, symbolsPath, test=self.lastTestSeen) + else: + crashed = Automation.checkForCrashes(self, dumpDir, symbolsPath) + + finally: + try: + shutil.rmtree(dumpDir) + except: + print "WARNING: unable to remove directory: %s" % dumpDir + return crashed + + def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs): + # If remote profile is specified, use that instead + if (self._remoteProfile): + profileDir = self._remoteProfile + + # Hack for robocop, if app & testURL == None and extraArgs contains the rest of the stuff, lets + # assume extraArgs is all we need + if app == "am" and extraArgs[0] in RemoteAutomation._specialAmCommands: + return app, extraArgs + + cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs) + # Remove -foreground if it exists, if it doesn't this just returns + try: + args.remove('-foreground') + except: + pass +#TODO: figure out which platform require NO_EM_RESTART +# return app, ['--environ:NO_EM_RESTART=1'] + args + return app, args + + def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = None): + if stdout == None or stdout == -1 or stdout == subprocess.PIPE: + stdout = self._remoteLog + + return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd, self._appName, + **self._processArgs) + + # be careful here as this inner class doesn't have access to outer class members + class RProcess(object): + # device manager process + dm = None + def __init__(self, dm, cmd, stdout=None, stderr=None, env=None, cwd=None, app=None, + messageLogger=None): + self.dm = dm + self.stdoutlen = 0 + self.lastTestSeen = "remoteautomation.py" + self.proc = dm.launchProcess(cmd, stdout, cwd, env, True) + self.messageLogger = messageLogger + + if (self.proc is None): + if cmd[0] == 'am': + self.proc = stdout + else: + raise Exception("unable to launch process") + self.procName = cmd[0].split('/')[-1] + if cmd[0] == 'am' and cmd[1] in RemoteAutomation._specialAmCommands: + self.procName = app + + # Setting timeout at 1 hour since on a remote device this takes much longer. + # Temporarily increased to 90 minutes because no more chunks can be created. + self.timeout = 5400 + # The benefit of the following sleep is unclear; it was formerly 15 seconds + time.sleep(1) + + # Used to buffer log messages until we meet a line break + self.logBuffer = "" + + @property + def pid(self): + pid = self.dm.processExist(self.procName) + # HACK: we should probably be more sophisticated about monitoring + # running processes for the remote case, but for now we'll assume + # that this method can be called when nothing exists and it is not + # an error + if pid is None: + return 0 + return pid + + def read_stdout(self): + """ + Fetch the full remote log file using devicemanager, process them and + return whether there were any new log entries since the last call. + """ + if not self.dm.fileExists(self.proc): + return False + try: + newLogContent = self.dm.pullFile(self.proc, self.stdoutlen) + except DMError: + # we currently don't retry properly in the pullFile + # function in dmSUT, so an error here is not necessarily + # the end of the world + return False + if not newLogContent: + return False + + self.stdoutlen += len(newLogContent) + + if self.messageLogger is None: + testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", newLogContent) + if testStartFilenames: + self.lastTestSeen = testStartFilenames[-1] + print newLogContent + return True + + self.logBuffer += newLogContent + lines = self.logBuffer.split('\n') + lines = [l for l in lines if l] + + if lines: + if self.logBuffer.endswith('\n'): + # all lines are complete; no need to buffer + self.logBuffer = "" + else: + # keep the last (unfinished) line in the buffer + self.logBuffer = lines[-1] + del lines[-1] + + if not lines: + return False + + for line in lines: + # This passes the line to the logger (to be logged or buffered) + parsed_messages = self.messageLogger.write(line) + for message in parsed_messages: + if isinstance(message, dict) and message.get('action') == 'test_start': + self.lastTestSeen = message['test'] + return True + + @property + def getLastTestSeen(self): + return self.lastTestSeen + + # Wait for the remote process to end (or for its activity to go to background). + # While waiting, periodically retrieve the process output and print it. + # If the process is still running after *timeout* seconds, return 1; + # If the process is still running but no output is received in *noOutputTimeout* + # seconds, return 2; + # Else, once the process exits/goes to background, return 0. + def wait(self, timeout = None, noOutputTimeout = None): + timer = 0 + noOutputTimer = 0 + interval = 10 + if timeout == None: + timeout = self.timeout + status = 0 + top = self.procName + slowLog = False + while (top == self.procName): + # Get log updates on each interval, but if it is taking + # too long, only do it every 60 seconds + if (not slowLog) or (timer % 60 == 0): + startRead = datetime.datetime.now() + hasOutput = self.read_stdout() + if (datetime.datetime.now() - startRead) > datetime.timedelta(seconds=5): + slowLog = True + if hasOutput: + noOutputTimer = 0 + time.sleep(interval) + timer += interval + noOutputTimer += interval + if (timer > timeout): + status = 1 + break + if (noOutputTimeout and noOutputTimer > noOutputTimeout): + status = 2 + break + top = self.dm.getTopActivity() + # Flush anything added to stdout during the sleep + self.read_stdout() + return status + + def kill(self, stagedShutdown = False): + if stagedShutdown: + # Trigger an ANR report with "kill -3" (SIGQUIT) + self.dm.killProcess(self.procName, 3) + time.sleep(3) + # Trigger a breakpad dump with "kill -6" (SIGABRT) + self.dm.killProcess(self.procName, 6) + # Wait for process to end + retries = 0 + while retries < 3: + pid = self.dm.processExist(self.procName) + if pid and pid > 0: + print "%s still alive after SIGABRT: waiting..." % self.procName + time.sleep(5) + else: + return + retries += 1 + self.dm.killProcess(self.procName, 9) + pid = self.dm.processExist(self.procName) + if pid and pid > 0: + self.dm.killProcess(self.procName) + else: + self.dm.killProcess(self.procName) diff --git a/build/moz-automation.mk b/build/moz-automation.mk new file mode 100644 index 000000000..e7fa16fcb --- /dev/null +++ b/build/moz-automation.mk @@ -0,0 +1,122 @@ +# +# 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/. + +ifneq (,$(filter automation/%,$(MAKECMDGOALS))) +ifeq (4.0,$(firstword $(sort 4.0 $(MAKE_VERSION)))) +MAKEFLAGS += --output-sync=target +else +.NOTPARALLEL: +endif +endif + +ifndef JS_STANDALONE +include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk +include $(topsrcdir)/toolkit/mozapps/installer/upload-files.mk + +# Clear out DIST_FILES if it was set by upload-files.mk (for Android builds) +DIST_FILES = +endif + +# Helper variables to convert from MOZ_AUTOMATION_* variables to the +# corresponding the make target +tier_MOZ_AUTOMATION_BUILD_SYMBOLS = buildsymbols +tier_MOZ_AUTOMATION_L10N_CHECK = l10n-check +tier_MOZ_AUTOMATION_PRETTY_L10N_CHECK = pretty-l10n-check +tier_MOZ_AUTOMATION_INSTALLER = installer +tier_MOZ_AUTOMATION_PRETTY_INSTALLER = pretty-installer +tier_MOZ_AUTOMATION_PACKAGE = package +tier_MOZ_AUTOMATION_PRETTY_PACKAGE = pretty-package +tier_MOZ_AUTOMATION_PACKAGE_TESTS = package-tests +tier_MOZ_AUTOMATION_PRETTY_PACKAGE_TESTS = pretty-package-tests +tier_MOZ_AUTOMATION_UPDATE_PACKAGING = update-packaging +tier_MOZ_AUTOMATION_PRETTY_UPDATE_PACKAGING = pretty-update-packaging +tier_MOZ_AUTOMATION_UPLOAD_SYMBOLS = uploadsymbols +tier_MOZ_AUTOMATION_UPLOAD = upload +tier_MOZ_AUTOMATION_SDK = sdk + +# Automation build steps. Everything in MOZ_AUTOMATION_TIERS also gets used in +# TIERS for mach display. As such, the MOZ_AUTOMATION_TIERS are roughly sorted +# here in the order that they will be executed (since mach doesn't know of the +# dependencies between them). +moz_automation_symbols = \ + MOZ_AUTOMATION_PACKAGE_TESTS \ + MOZ_AUTOMATION_PRETTY_PACKAGE_TESTS \ + MOZ_AUTOMATION_BUILD_SYMBOLS \ + MOZ_AUTOMATION_UPLOAD_SYMBOLS \ + MOZ_AUTOMATION_PACKAGE \ + MOZ_AUTOMATION_PRETTY_PACKAGE \ + MOZ_AUTOMATION_INSTALLER \ + MOZ_AUTOMATION_PRETTY_INSTALLER \ + MOZ_AUTOMATION_UPDATE_PACKAGING \ + MOZ_AUTOMATION_PRETTY_UPDATE_PACKAGING \ + MOZ_AUTOMATION_L10N_CHECK \ + MOZ_AUTOMATION_PRETTY_L10N_CHECK \ + MOZ_AUTOMATION_UPLOAD \ + MOZ_AUTOMATION_SDK \ + $(NULL) +MOZ_AUTOMATION_TIERS := $(foreach sym,$(moz_automation_symbols),$(if $(filter 1,$($(sym))),$(tier_$(sym)))) + +# Dependencies between automation build steps +automation/uploadsymbols: automation/buildsymbols + +automation/update-packaging: automation/package +automation/update-packaging: automation/installer +automation/pretty-update-packaging: automation/pretty-package +automation/pretty-update-packaging: automation/pretty-installer + +automation/l10n-check: automation/package +automation/l10n-check: automation/installer +automation/pretty-l10n-check: automation/pretty-package +automation/pretty-l10n-check: automation/pretty-installer + +automation/upload: automation/installer +automation/upload: automation/package +automation/upload: automation/package-tests +automation/upload: automation/buildsymbols +automation/upload: automation/update-packaging +automation/upload: automation/sdk + +# automation/{pretty-}package should depend on build (which is implicit due to +# the way client.mk invokes automation/build), but buildsymbols changes the +# binaries/libs, and that's what we package/test. +automation/pretty-package: automation/buildsymbols + +# The installer, sdk and packager all run stage-package, and may conflict +# with each other. +automation/installer: automation/package +automation/sdk: automation/installer automation/package + +# The 'pretty' versions of targets run before the regular ones to avoid +# conflicts in writing to the same files. +automation/installer: automation/pretty-installer +automation/package: automation/pretty-package +automation/package-tests: automation/pretty-package-tests +automation/l10n-check: automation/pretty-l10n-check +automation/update-packaging: automation/pretty-update-packaging + +automation/build: $(addprefix automation/,$(MOZ_AUTOMATION_TIERS)) + @echo Automation steps completed. + +# Note: We have to force -j1 here, at least until bug 1036563 is fixed. +AUTOMATION_EXTRA_CMDLINE-l10n-check = -j1 +AUTOMATION_EXTRA_CMDLINE-pretty-l10n-check = -j1 + +# The commands only run if the corresponding MOZ_AUTOMATION_* variable is +# enabled. This means, for example, if we enable MOZ_AUTOMATION_UPLOAD, then +# 'buildsymbols' will only run if MOZ_AUTOMATION_BUILD_SYMBOLS is also set. +# However, the target automation/buildsymbols will still be executed in this +# case because it is a prerequisite of automation/upload. +define automation_commands +@+$(MAKE) $1 $(AUTOMATION_EXTRA_CMDLINE-$1) +$(call BUILDSTATUS,TIER_FINISH $1) +endef + +# The tier start message is in a separate target so make doesn't buffer it +# until the step completes with output syncing enabled. +automation-start/%: + $(if $(filter $*,$(MOZ_AUTOMATION_TIERS)),$(call BUILDSTATUS,TIER_START $*)) + +automation/%: automation-start/% + $(if $(filter $*,$(MOZ_AUTOMATION_TIERS)),$(call automation_commands,$*)) diff --git a/build/moz.build b/build/moz.build new file mode 100644 index 000000000..345ba9be0 --- /dev/null +++ b/build/moz.build @@ -0,0 +1,105 @@ +# -*- Mode: python; 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/. + +with Files('**'): + BUG_COMPONENT = ('Core', 'Build Config') + +# This cannot be named "build" because of bug 922191. +SPHINX_TREES['buildsystem'] = 'docs' + +if CONFIG['OS_ARCH'] == 'WINNT': + DIRS += ['win32'] +else: + DIRS += ['unix'] + +if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android': + DIRS += ['annotationProcessors'] + +for var in ('GRE_MILESTONE', 'MOZ_APP_VERSION', 'MOZ_APP_BASENAME', + 'MOZ_APP_VENDOR', 'MOZ_APP_ID', 'MAR_CHANNEL_ID', + 'ACCEPTED_MAR_CHANNEL_IDS', 'MOZ_APP_REMOTINGNAME'): + DEFINES[var] = CONFIG[var] + +if CONFIG['MOZ_APP_DISPLAYNAME'] != CONFIG['MOZ_APP_BASENAME']: + DEFINES['MOZ_APP_DISPLAYNAME'] = CONFIG['MOZ_APP_DISPLAYNAME'] + +if CONFIG['MOZ_BUILD_APP'] == 'browser': + DEFINES['MOZ_BUILD_APP_IS_BROWSER'] = True + +if CONFIG['MOZ_APP_PROFILE']: + DEFINES['MOZ_APP_PROFILE'] = CONFIG['MOZ_APP_PROFILE'] + +for var in ('MOZ_CRASHREPORTER', 'MOZ_PROFILE_MIGRATOR', + 'MOZ_APP_STATIC_INI'): + if CONFIG[var]: + DEFINES[var] = True + +if CONFIG['MOZ_BUILD_APP'] == 'browser': + PYTHON_UNIT_TESTS += [ + 'compare-mozconfig/compare-mozconfigs-wrapper.py', + ] + +if CONFIG['ENABLE_TESTS'] or CONFIG['MOZ_DMD']: + FINAL_TARGET_FILES += ['/tools/rb/fix_stack_using_bpsyms.py'] + if CONFIG['OS_ARCH'] == 'Darwin': + FINAL_TARGET_FILES += ['/tools/rb/fix_macosx_stack.py'] + if CONFIG['OS_ARCH'] == 'Linux': + FINAL_TARGET_FILES += ['/tools/rb/fix_linux_stack.py'] + +if CONFIG['MOZ_DMD']: + FINAL_TARGET_FILES += ['/memory/replace/dmd/dmd.py'] + +# Put a useful .gdbinit in the bin directory, to be picked up automatically +# by GDB when we debug executables there. +FINAL_TARGET_FILES += ['/.gdbinit'] +FINAL_TARGET_PP_FILES += ['.gdbinit_python.in'] +OBJDIR_FILES += ['!/dist/bin/.gdbinit_python'] + +# Install the clang-cl runtime library for ASAN next to the binaries we produce. +if CONFIG['MOZ_ASAN'] and CONFIG['CLANG_CL']: + FINAL_TARGET_FILES += ['%' + CONFIG['MOZ_CLANG_RT_ASAN_LIB_PATH']] + +if CONFIG['MOZ_APP_BASENAME']: + FINAL_TARGET_PP_FILES += ['application.ini'] + if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' and CONFIG['MOZ_UPDATER']: + FINAL_TARGET_PP_FILES += ['update-settings.ini'] + + if CONFIG['MOZ_APP_STATIC_INI']: + GENERATED_FILES += ['application.ini.h'] + appini = GENERATED_FILES['application.ini.h'] + appini.script = 'appini_header.py' + appini.inputs = ['!/dist/bin/application.ini'] + +DEFINES['TOPOBJDIR'] = TOPOBJDIR + +# NOTE: Keep .gdbinit in the topsrcdir for people who run gdb from the topsrcdir. +OBJDIR_FILES += ['/.gdbinit'] + +# Put a .lldbinit in the bin directory and the objdir, to be picked up +# automatically by LLDB when we debug executables using either of those two +# directories as the current working directory. The .lldbinit file will +# load $(topsrcdir)/.lldbinit, which is where the actual debugging commands are. +DEFINES['topsrcdir'] = TOPSRCDIR +FINAL_TARGET_PP_FILES += ['.lldbinit.in'] +OBJDIR_FILES += ['!/dist/bin/.lldbinit'] + +# Put the .ycm_extra_conf.py file at the root of the objdir. It is used by +# the vim plugin YouCompleteMe. +OBJDIR_FILES += ['/.ycm_extra_conf.py'] + +if CONFIG['MOZ_VALGRIND']: + OBJDIR_FILES._valgrind += [ + 'valgrind/cross-architecture.sup', + 'valgrind/i386-redhat-linux-gnu.sup', + 'valgrind/x86_64-redhat-linux-gnu.sup', + ] + +if CONFIG['MOZ_ARTIFACT_BUILDS']: + # Ensure a pre-built interfaces.xpt installed to the objdir by the artifact + # code is included by the top-level chrome.manifest. + EXTRA_COMPONENTS += [ + 'prebuilt-interfaces.manifest', + ] diff --git a/build/moz.configure/android-ndk.configure b/build/moz.configure/android-ndk.configure new file mode 100644 index 000000000..6a0b30529 --- /dev/null +++ b/build/moz.configure/android-ndk.configure @@ -0,0 +1,150 @@ +# -*- Mode: python; 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/. + + +js_option('--with-android-ndk', nargs=1, + help='location where the Android NDK can be found') + +js_option('--with-android-toolchain', nargs=1, + help='location of the Android toolchain') + +js_option('--with-android-gnu-compiler-version', nargs=1, + help='GNU compiler version to use') + +min_android_version = dependable(lambda: '9') + +js_option('--with-android-version', + nargs=1, + help='android platform version', + default=min_android_version) + +@depends('--with-android-version', min_android_version) +@imports(_from='__builtin__', _import='ValueError') +def android_version(value, min_version): + if not value: + # Someone has passed --without-android-version. + die('--with-android-version cannot be disabled.') + + try: + version = int(value[0]) + except ValueError: + die('--with-android-version expects an integer value') + + if version < int(min_version): + die('--with-android-version must be at least %s (got %s)', + min_version, value[0]) + + return version + +add_old_configure_assignment('android_version', android_version) + +@depends('--with-android-ndk', build_project) +def ndk(value, build_project): + if build_project == 'mobile/android' and not value: + die('You must specify --with-android-ndk=/path/to/ndk when ' + 'building mobile/android') + if value: + return value[0] + +set_config('ANDROID_NDK', ndk) +add_old_configure_assignment('android_ndk', ndk) + +@depends(target, android_version, ndk) +@checking('for android platform directory') +@imports(_from='os.path', _import='isdir') +def android_platform(target, android_version, ndk): + if target.os != 'Android': + return + + if 'mips' in target.cpu: + target_dir_name = 'mips' + elif 'aarch64' == target.cpu: + target_dir_name = 'arm64' + else: + target_dir_name = target.cpu + + # Not all Android releases have their own platform release. We use + # the next lower platform version in these cases. + if android_version in (11, 10): + platform_version = 9 + elif android_version in (20, 22): + platform_version = android_version - 1 + else: + platform_version = android_version + + platform_dir = os.path.join(ndk, + 'platforms', + 'android-%s' % platform_version, + 'arch-%s' % target_dir_name) + + if not isdir(platform_dir): + die("Android platform directory not found. With the current " + "configuration, it should be in %s" % platform_dir) + + return platform_dir + +add_old_configure_assignment('android_platform', android_platform) + +@depends(android_platform) +def extra_toolchain_flags(platform_dir): + if not platform_dir: + return [] + return ['-idirafter', + os.path.join(platform_dir, 'usr', 'include')] + +@depends(target, host, ndk, '--with-android-toolchain', + '--with-android-gnu-compiler-version') +@checking('for the Android toolchain directory', lambda x: x or 'not found') +@imports(_from='os.path', _import='isdir') +@imports(_from='mozbuild.shellutil', _import='quote') +def android_toolchain(target, host, ndk, toolchain, gnu_compiler_version): + if not ndk: + return + if toolchain: + return toolchain[0] + else: + if target.cpu == 'arm' and target.endianness == 'little': + target_base = 'arm-linux-androideabi' + elif target.cpu == 'x86': + target_base = 'x86' + elif target.cpu == 'mips32' and target.endianness == 'little': + target_base = 'mipsel-linux-android' + elif target.cpu == 'aarch64' and target.endianness == 'little': + target_base = 'aarch64-linux-android' + else: + die('Target cpu is not supported.') + + toolchain_format = '%s/toolchains/%s-%s/prebuilt/%s-%s' + + for version in gnu_compiler_version or ['4.9', '4.8', '4.7']: + toolchain = toolchain_format % (ndk, target_base, version, + host.kernel.lower(), host.cpu) + log.debug('Trying %s' % quote(toolchain)) + if not isdir(toolchain) and host.cpu == 'x86_64': + toolchain = toolchain_format % (ndk, target_base, version, + host.kernel.lower(), 'x86') + log.debug('Trying %s' % quote(toolchain)) + if isdir(toolchain): + return toolchain + else: + if gnu_compiler_version: + die('Your --with-android-gnu-compiler-version may be wrong') + die('You have to specify --with-android-toolchain=' + '/path/to/ndk/toolchain.') + +set_config('ANDROID_TOOLCHAIN', android_toolchain) + +@depends(target, android_toolchain) +def android_toolchain_prefix(target, toolchain): + if toolchain: + if target.cpu == 'x86': + # Ideally, the --target should just have the right x86 variant + # in the first place. + return '%s/bin/i686-linux-android-' % toolchain + return '%s/bin/%s-' % (toolchain, target.toolchain) + +imply_option('--with-toolchain-prefix', android_toolchain_prefix, + reason='--with-android-ndk') diff --git a/build/moz.configure/checks.configure b/build/moz.configure/checks.configure new file mode 100644 index 000000000..8c2dbc0cc --- /dev/null +++ b/build/moz.configure/checks.configure @@ -0,0 +1,144 @@ +# -*- Mode: python; 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/. + +# Templates implementing some generic checks. +# ============================================================== + +# Declare some exceptions. This is cumbersome, but since we shouldn't need a +# lot of them, let's stack them all here. When adding a new one, put it in the +# _declare_exceptions template, and add it to the return statement. Then +# destructure in the assignment below the function declaration. +@template +@imports(_from='__builtin__', _import='Exception') +def _declare_exceptions(): + class FatalCheckError(Exception): + '''An exception to throw from a function decorated with @checking. + It will result in calling die() with the given message. + Debugging messages emitted from the decorated function will also be + printed out.''' + return (FatalCheckError,) + +(FatalCheckError,) = _declare_exceptions() + +del _declare_exceptions + +# Helper to display "checking" messages +# @checking('for foo') +# def foo(): +# return 'foo' +# is equivalent to: +# def foo(): +# log.info('checking for foo... ') +# ret = foo +# log.info(ret) +# return ret +# This can be combined with e.g. @depends: +# @depends(some_option) +# @checking('for something') +# def check(value): +# ... +# An optional callback can be given, that will be used to format the returned +# value when displaying it. +@template +def checking(what, callback=None): + def decorator(func): + def wrapped(*args, **kwargs): + log.info('checking %s... ', what) + with log.queue_debug(): + error, ret = None, None + try: + ret = func(*args, **kwargs) + except FatalCheckError as e: + error = e.message + display_ret = callback(ret) if callback else ret + if display_ret is True: + log.info('yes') + elif display_ret is False or display_ret is None: + log.info('no') + else: + log.info(display_ret) + if error is not None: + die(error) + return ret + return wrapped + return decorator + + +# Template to check for programs in $PATH. +# - `var` is the name of the variable that will be set with `set_config` when +# the program is found. +# - `progs` is a list (or tuple) of program names that will be searched for. +# It can also be a reference to a @depends function that returns such a +# list. If the list is empty and there is no input, the check is skipped. +# - `what` is a human readable description of what is being looked for. It +# defaults to the lowercase version of `var`. +# - `input` is a string reference to an existing option or a reference to a +# @depends function resolving to explicit input for the program check. +# The default is to create an option for the environment variable `var`. +# This argument allows to use a different kind of option (possibly using a +# configure flag), or doing some pre-processing with a @depends function. +# - `allow_missing` indicates whether not finding the program is an error. +# - `paths` is a list of paths or @depends function returning a list of paths +# that will cause the given path(s) to be searched rather than $PATH. Input +# paths may either be individual paths or delimited by os.pathsep, to allow +# passing $PATH (for example) as an element. +# +# The simplest form is: +# check_prog('PROG', ('a', 'b')) +# This will look for 'a' or 'b' in $PATH, and set_config PROG to the one +# it can find. If PROG is already set from the environment or command line, +# use that value instead. +@template +@imports(_from='mozbuild.shellutil', _import='quote') +def check_prog(var, progs, what=None, input=None, allow_missing=False, + paths=None): + if input: + # Wrap input with type checking and normalization. + @depends(input) + def input(value): + if not value: + return + if isinstance(value, str): + return (value,) + if isinstance(value, (tuple, list)) and len(value) == 1: + return value + configure_error('input must resolve to a tuple or a list with a ' + 'single element, or a string') + else: + option(env=var, nargs=1, + help='Path to %s' % (what or 'the %s program' % var.lower())) + input = var + what = what or var.lower() + + # Trick to make a @depends function out of an immediate value. + progs = dependable(progs) + paths = dependable(paths) + + @depends_if(input, progs, paths) + @checking('for %s' % what, lambda x: quote(x) if x else 'not found') + def check(value, progs, paths): + if progs is None: + progs = () + + if not isinstance(progs, (tuple, list)): + configure_error('progs must resolve to a list or tuple!') + + for prog in value or progs: + log.debug('%s: Trying %s', var.lower(), quote(prog)) + result = find_program(prog, paths) + if result: + return result + + if not allow_missing or value: + raise FatalCheckError('Cannot find %s' % what) + + @depends_if(check, progs) + def normalized_for_config(value, progs): + return ':' if value is None else value + + set_config(var, normalized_for_config) + + return check diff --git a/build/moz.configure/compile-checks.configure b/build/moz.configure/compile-checks.configure new file mode 100644 index 000000000..9f0ebcd61 --- /dev/null +++ b/build/moz.configure/compile-checks.configure @@ -0,0 +1,152 @@ +# -*- 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/. + + +# Generates a test program and attempts to compile it. In case of failure, the +# resulting check will return None. If the test program succeeds, it will return +# the output of the test program. +# - `includes` are the includes (as file names) that will appear at the top of +# the generated test program. +# - `body` is the code that will appear in the main function of the generated +# test program. `return 0;` is appended to the function body automatically. +# - `language` is the language selection, so that the appropriate compiler is +# used. +# - `flags` are the flags to be passed to the compiler, in addition to `-c`. +# - `check_msg` is the message to be printed to accompany compiling the test +# program. +@template +def try_compile(includes=None, body='', language='C++', flags=None, check_msg=None, + when=None): + compiler = { + 'C': c_compiler, + 'C++': cxx_compiler, + }[language] + + return compiler.try_compile(includes, body, flags, check_msg, when=when) + + +# Checks for the presence of the given header on the target system by compiling +# a test program including that header. The return value of the template is a +# check function returning True if the header is present, and None if it is not. +# The value of this check function is also used to set a variable (with set_define) +# corresponding to the checked header. For instance, HAVE_MALLOC_H will be set in +# defines if check_header if called with 'malloc.h' as input and malloc.h is +# present on the target. +# - `header` is the header, as a file name, to check for. +# - `language` is the language selection, so that the appropriate compiler is +# used. +# - `flags` are the flags to be passed to the compiler, in addition to `-c`. +# - `includes` are additional includes, as file names, to appear before the +# header checked for. +# - `when` is a depends function that if present will make performing the check +# conditional on the value of that function. +@template +def check_header(header, language='C++', flags=None, includes=None, when=None): + when = when or always + + if includes: + includes = includes[:] + else: + includes = [] + includes.append(header) + + have_header = try_compile(includes=includes, language=language, flags=flags, + check_msg='for %s' % header, when=when) + header_var = 'HAVE_%s' % (header.upper() + .replace('-', '_') + .replace('/', '_') + .replace('.', '_')) + set_define(header_var, have_header) + return have_header + +# A convenience wrapper for check_header for checking multiple headers. +# returns an array of the resulting checks in order corresponding to the +# provided headers. +# - `headers` are the headers to be checked. +# - `kwargs` are keyword arguments passed verbatim to check_header. +@template +def check_headers(*headers, **kwargs): + checks = [] + for header in headers: + checks.append(check_header(header, **kwargs)) + return checks + + +@depends(c_compiler) +def warnings_cflags(c_compiler): + return [] + +@depends(cxx_compiler) +def warnings_cxxflags(cxx_compiler): + return [] + + +# Tests whether GCC or clang support the given warning flag, and if it is, +# add it to the list of warning flags for the build. +# - `warning` is the warning flag (e.g. -Wfoo) +# - `compiler` (optional) is the compiler to test against (c_compiler or +# cxx_compiler, from toolchain.configure). When omitted, both compilers +# are tested. +# - `when` (optional) is a @depends function or option name conditioning +# when the warning flag is wanted. +# - `check`, when not set, skips checking whether the flag is supported and +# adds it to the list of warning flags unconditionally. This is only meant +# for add_gcc_warning(). +@template +def check_and_add_gcc_warning(warning, compiler=None, when=None, check=True): + if compiler: + compilers = (compiler,) + else: + compilers = (c_compiler, cxx_compiler) + + when = when or always + + for c in compilers: + assert c in (c_compiler, cxx_compiler) + lang, warnings_flags = { + c_compiler: ('C', warnings_cflags), + cxx_compiler: ('C++', warnings_cxxflags), + }[c] + + # GCC and clang will fail if given an unknown warning option like + # -Wfoobar. But later versions won't fail if given an unknown negated + # warning option like -Wno-foobar. So when we are checking for support + # of a negated warning option, we actually test the positive form, but + # add the negated form to the flags variable. + if (warning.startswith('-Wno-') and + not warning.startswith('-Wno-error=')): + flags = ['-Werror', '-W' + warning[5:]] + elif warning.startswith('-Werror='): + flags = [warning] + else: + flags = ['-Werror', warning] + + @depends(c, when) + def result(c, when): + if when and c.type in ('clang', 'gcc'): + return True + + if check: + result = c.try_compile( + flags=flags, when=result, + check_msg='whether the %s compiler supports %s' % (lang, + warning)) + + @depends(result, warnings_flags) + def maybe_add_flag(result, warnings_flags): + if result: + warnings_flags.append(warning) + +# Add the given warning to the list of warning flags for the build. +# - `warning` is the warning flag (e.g. -Wfoo) +# - `compiler` (optional) is the compiler to add the flag for (c_compiler or +# cxx_compiler, from toolchain.configure). When omitted, the warning flag +# is added for both compilers. +# - `when` (optional) is a @depends function or option name conditioning +# when the warning flag is wanted. +@template +def add_gcc_warning(warning, compiler=None, when=None): + check_and_add_gcc_warning(warning, compiler, when, check=False) diff --git a/build/moz.configure/compilers-util.configure b/build/moz.configure/compilers-util.configure new file mode 100644 index 000000000..32271a010 --- /dev/null +++ b/build/moz.configure/compilers-util.configure @@ -0,0 +1,62 @@ +# -*- Mode: python; 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/. + +@template +@imports('textwrap') +@imports(_from='mozbuild.configure', _import='SandboxDependsFunction') +def compiler_class(compiler): + class Compiler(SandboxDependsFunction): + # Generates a test program and attempts to compile it. In case of + # failure, the resulting check will return None. If the test program + # succeeds, it will return the output of the test program. + # - `includes` are the includes (as file names) that will appear at the + # top of the generated test program. + # - `body` is the code that will appear in the main function of the + # generated test program. `return 0;` is appended to the function + # body automatically. + # - `flags` are the flags to be passed to the compiler, in addition to + # `-c`. + # - `check_msg` is the message to be printed to accompany compiling the + # test program. + def try_compile(self, includes=None, body='', flags=None, + check_msg=None, when=None, onerror=lambda: None): + includes = includes or [] + source_lines = ['#include <%s>' % f for f in includes] + source = '\n'.join(source_lines) + '\n' + source += textwrap.dedent('''\ + int + main(void) + { + %s + ; + return 0; + } + ''' % body) + + if check_msg: + def checking_fn(fn): + return checking(check_msg)(fn) + else: + def checking_fn(fn): + return fn + + @depends_when(self, dependable(flags), extra_toolchain_flags, when=when) + @checking_fn + def func(compiler, flags, extra_flags): + flags = flags or [] + flags += extra_flags or [] + flags.append('-c') + + if try_invoke_compiler( + compiler.wrapper + [compiler.compiler] + compiler.flags, + compiler.language, source, flags, + onerror=onerror) is not None: + return True + + return func + + compiler.__class__ = Compiler + return compiler diff --git a/build/moz.configure/headers.configure b/build/moz.configure/headers.configure new file mode 100644 index 000000000..52ffa2f89 --- /dev/null +++ b/build/moz.configure/headers.configure @@ -0,0 +1,93 @@ +# -*- 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/. + +# Check for headers defining standard int types. +check_header('stdint.h') +have_inttypes = check_header('inttypes.h') + +# Assume we have ansi C header files available. +set_define('STDC_HEADERS', True) + +set_config('HAVE_INTTYPES_H', have_inttypes) + +building_linux = depends(target)(lambda target: target.kernel == 'Linux') + +have_malloc = check_header('malloc.h') + +check_header('alloca.h') + +add_old_configure_assignment('HAVE_MALLOC_H', have_malloc) + +check_headers( + 'sys/byteorder.h', + 'getopt.h', + 'unistd.h', + 'nl_types.h', + 'cpuid.h', + when=non_msvc_compiler, +) + +# These are all the places some variant of statfs can be hiding. +check_headers( + 'sys/statvfs.h', + 'sys/statfs.h', + 'sys/vfs.h', + 'sys/mount.h', + when=non_msvc_compiler, +) + +# Quota support +check_header('sys/quota.h', + when=non_msvc_compiler) +check_header('linux/quota.h', + includes=['sys/socket.h'], + when=building_linux) + +# SCTP support - needs various network include headers +check_headers( + 'linux/if_addr.h', + 'linux/rtnetlink.h', + includes=['sys/socket.h'], + when=building_linux, +) + +check_header('sys/queue.h', + when=non_msvc_compiler) + +check_headers( + 'sys/types.h', + 'netinet/in.h', + 'byteswap.h', + when=non_msvc_compiler, +) + +# TODO: Move these checks to file specific to --enable-project=js. +have_perf_event_h = check_header('linux/perf_event.h', + when=building_linux) + +js_option('--with-linux-headers', + help='location where the Linux kernel headers can be found', + nargs=1) + +passed_linux_header_flags = depends_if('--with-linux-headers')(lambda v: ['-I%s' % v[0]]) + +@depends_when(try_compile(includes=['asm/unistd.h'], + body='return sizeof(__NR_perf_event_open);', + flags=passed_linux_header_flags, + check_msg='for perf_event_open system call'), + when=have_perf_event_h) +def have_perf_event_open(have_perf_event_open): + if have_perf_event_open: + return True + +set_config('HAVE_LINUX_PERF_EVENT_H', have_perf_event_open) + +@depends(passed_linux_header_flags, have_perf_event_open) +def linux_headers_includes(passed_linux_header_flags, have_perf_event_open): + if have_perf_event_open and passed_linux_header_flags: + return passed_linux_header_flags[0] + +set_config('LINUX_HEADERS_INCLUDES', linux_headers_includes) diff --git a/build/moz.configure/init.configure b/build/moz.configure/init.configure new file mode 100644 index 000000000..2123bebc9 --- /dev/null +++ b/build/moz.configure/init.configure @@ -0,0 +1,780 @@ +# -*- Mode: python; 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('util.configure') +include('checks.configure') + +option(env='DIST', nargs=1, help='DIST directory') + +# Do not allow objdir == srcdir builds. +# ============================================================== +@depends('--help', 'DIST') +@imports(_from='os.path', _import='exists') +def check_build_environment(help, dist): + topobjdir = os.path.realpath(os.path.abspath('.')) + topsrcdir = os.path.realpath(os.path.abspath( + os.path.join(os.path.dirname(__file__), '..', '..'))) + + if dist: + dist = normsep(dist[0]) + else: + dist = os.path.join(topobjdir, 'dist') + + result = namespace( + topsrcdir=topsrcdir, + topobjdir=topobjdir, + dist=dist, + ) + + if help: + return result + + if topsrcdir == topobjdir: + die(' ***\n' + ' * Building directly in the main source directory is not allowed.\n' + ' *\n' + ' * To build, you must run configure from a separate directory\n' + ' * (referred to as an object directory).\n' + ' *\n' + ' * If you are building with a mozconfig, you will need to change your\n' + ' * mozconfig to point to a different object directory.\n' + ' ***' + ) + + # Check for a couple representative files in the source tree + conflict_files = [ + '* %s' % f for f in ('Makefile', 'config/autoconf.mk') + if exists(os.path.join(topsrcdir, f)) + ] + if conflict_files: + die(' ***\n' + ' * Your source tree contains these files:\n' + ' %s\n' + ' * This indicates that you previously built in the source tree.\n' + ' * A source tree build can confuse the separate objdir build.\n' + ' *\n' + ' * To clean up the source tree:\n' + ' * 1. cd %s\n' + ' * 2. gmake distclean\n' + ' ***' + % ('\n '.join(conflict_files), topsrcdir) + ) + + return result + +set_config('TOPSRCDIR', delayed_getattr(check_build_environment, 'topsrcdir')) +set_config('TOPOBJDIR', delayed_getattr(check_build_environment, 'topobjdir')) +set_config('MOZ_BUILD_ROOT', delayed_getattr(check_build_environment, + 'topobjdir')) +set_config('DIST', delayed_getattr(check_build_environment, 'dist')) + + +option(env='MOZ_AUTOMATION', help='Enable options for automated builds') +set_config('MOZ_AUTOMATION', depends_if('MOZ_AUTOMATION')(lambda x: True)) + + +option(env='OLD_CONFIGURE', nargs=1, help='Path to the old configure script') + +option(env='MOZ_CURRENT_PROJECT', nargs=1, help='Current build project') +option(env='MOZCONFIG', nargs=1, help='Mozconfig location') + +# Read user mozconfig +# ============================================================== +# Note: the dependency on --help is only there to always read the mozconfig, +# even when --help is passed. Without this dependency, the function wouldn't +# be called when --help is passed, and the mozconfig wouldn't be read. +@depends('MOZ_CURRENT_PROJECT', 'MOZCONFIG', 'OLD_CONFIGURE', + check_build_environment, '--help') +@imports(_from='mozbuild.mozconfig', _import='MozconfigLoader') +def mozconfig(current_project, mozconfig, old_configure, build_env, help): + if not old_configure: + die('The OLD_CONFIGURE environment variable must be set') + + # Don't read the mozconfig for the js configure (yay backwards + # compatibility) + # While the long term goal is that js and top-level use the same configure + # and the same overall setup, including the possibility to use mozconfigs, + # figuring out what we want to do wrt mozconfig vs. command line and + # environment variable is not a clear-cut case, and it's more important to + # fix the immediate problem mozconfig causes to js developers by + # "temporarily" returning to the previous behavior of not loading the + # mozconfig for the js configure. + # Separately to the immediate problem for js developers, there is also the + # need to not load a mozconfig when running js configure as a subconfigure. + # Unfortunately, there is no direct way to tell whether the running + # configure is the js configure. The indirect way is to look at the + # OLD_CONFIGURE path, which points to js/src/old-configure. + # I expect we'll have figured things out for mozconfigs well before + # old-configure dies. + if os.path.dirname(os.path.abspath(old_configure[0])).endswith('/js/src'): + return {'path': None} + + loader = MozconfigLoader(build_env.topsrcdir) + current_project = current_project[0] if current_project else None + mozconfig = mozconfig[0] if mozconfig else None + mozconfig = loader.find_mozconfig(env={'MOZCONFIG': mozconfig}) + mozconfig = loader.read_mozconfig(mozconfig, moz_build_app=current_project) + + return mozconfig + +set_config('MOZCONFIG', depends(mozconfig)(lambda m: m['path'])) + + +option(env='PYTHON', nargs=1, help='Python interpreter') + +# Setup python virtualenv +# ============================================================== +@depends('PYTHON', check_build_environment, mozconfig, '--help') +@imports('os') +@imports('sys') +@imports('subprocess') +@imports(_from='mozbuild.configure.util', _import='LineIO') +@imports(_from='mozbuild.virtualenv', _import='VirtualenvManager') +@imports(_from='mozbuild.virtualenv', _import='verify_python_version') +@imports('distutils.sysconfig') +def virtualenv_python(env_python, build_env, mozconfig, help): + if help: + return + + python = env_python[0] if env_python else None + + # Ideally we'd rely on the mozconfig injection from mozconfig_options, + # but we'd rather avoid the verbosity when we need to reexecute with + # a different python. + if mozconfig['path']: + if 'PYTHON' in mozconfig['env']['added']: + python = mozconfig['env']['added']['PYTHON'] + elif 'PYTHON' in mozconfig['env']['modified']: + python = mozconfig['env']['modified']['PYTHON'][1] + elif 'PYTHON' in mozconfig['vars']['added']: + python = mozconfig['vars']['added']['PYTHON'] + elif 'PYTHON' in mozconfig['vars']['modified']: + python = mozconfig['vars']['modified']['PYTHON'][1] + + with LineIO(lambda l: log.error(l)) as out: + verify_python_version(out) + topsrcdir, topobjdir = build_env.topsrcdir, build_env.topobjdir + if topobjdir.endswith('/js/src'): + topobjdir = topobjdir[:-7] + + with LineIO(lambda l: log.info(l)) as out: + manager = VirtualenvManager( + topsrcdir, topobjdir, + os.path.join(topobjdir, '_virtualenv'), out, + os.path.join(topsrcdir, 'build', 'virtualenv_packages.txt')) + + if python: + # If we're not in the virtualenv, we need the which module for + # find_program. + if normsep(sys.executable) != normsep(manager.python_path): + sys.path.append(os.path.join(topsrcdir, 'python', 'which')) + found_python = find_program(python) + if not found_python: + die('The PYTHON environment variable does not contain ' + 'a valid path. Cannot find %s', python) + python = found_python + else: + python = sys.executable + + if not manager.up_to_date(python): + log.info('Creating Python environment') + manager.build(python) + + python = normsep(manager.python_path) + + if python != normsep(sys.executable): + log.info('Reexecuting in the virtualenv') + if env_python: + del os.environ['PYTHON'] + # One would prefer to use os.execl, but that's completely borked on + # Windows. + sys.exit(subprocess.call([python] + sys.argv)) + + # We are now in the virtualenv + if not distutils.sysconfig.get_python_lib(): + die('Could not determine python site packages directory') + + return python + +set_config('PYTHON', virtualenv_python) +add_old_configure_assignment('PYTHON', virtualenv_python) + +# Inject mozconfig options +# ============================================================== +# All options defined above this point can't be injected in mozconfig_options +# below, so collect them. +@template +def early_options(): + @dependable + @imports('__sandbox__') + def early_options(): + return set( + option.env + for option in __sandbox__._options.itervalues() + if option.env + ) + return early_options + +early_options = early_options() + +@depends(mozconfig, '--help') +# This gives access to the sandbox. Don't copy this blindly. +@imports('__sandbox__') +@imports('os') +def mozconfig_options(mozconfig, help): + if mozconfig['path']: + helper = __sandbox__._helper + log.info('Adding configure options from %s' % mozconfig['path']) + for arg in mozconfig['configure_args']: + log.info(' %s' % arg) + # We could be using imply_option() here, but it has other + # contraints that don't really apply to the command-line + # emulation that mozconfig provides. + helper.add(arg, origin='mozconfig', args=helper._args) + + def add(key, value): + if key.isupper(): + arg = '%s=%s' % (key, value) + log.info(' %s' % arg) + helper.add(arg, origin='mozconfig', args=helper._args) + + for key, value in mozconfig['env']['added'].iteritems(): + add(key, value) + os.environ[key] = value + for key, (_, value) in mozconfig['env']['modified'].iteritems(): + add(key, value) + os.environ[key] = value + for key, value in mozconfig['vars']['added'].iteritems(): + # mozconfig_loader adds _IS_SET variables that are irrelevant + if not key.endswith('_IS_SET'): + add(key, value) + for key, (_, value) in mozconfig['vars']['modified'].iteritems(): + add(key, value) + + +# Mozilla-Build +# ============================================================== +option(env='MOZILLABUILD', nargs=1, + help='Path to Mozilla Build (Windows-only)') + +option(env='CONFIG_SHELL', nargs=1, help='Path to a POSIX shell') + +# It feels dirty replicating this from python/mozbuild/mozbuild/mozconfig.py, +# but the end goal being that the configure script would go away... +@depends('CONFIG_SHELL', 'MOZILLABUILD') +@checking('for a shell') +@imports('sys') +def shell(value, mozillabuild): + if value: + return find_program(value[0]) + shell = 'sh' + if mozillabuild: + shell = mozillabuild[0] + '/msys/bin/sh' + if sys.platform == 'win32': + shell = shell + '.exe' + return find_program(shell) + + +# Host and target systems +# ============================================================== +option('--host', nargs=1, help='Define the system type performing the build') + +option('--target', nargs=1, + help='Define the system type where the resulting executables will be ' + 'used') + +@imports(_from='mozbuild.configure.constants', _import='CPU') +@imports(_from='mozbuild.configure.constants', _import='CPU_bitness') +@imports(_from='mozbuild.configure.constants', _import='Endianness') +@imports(_from='mozbuild.configure.constants', _import='Kernel') +@imports(_from='mozbuild.configure.constants', _import='OS') +def split_triplet(triplet): + # The standard triplet is defined as + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM + # There is also a quartet form: + # CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM + # But we can consider the "KERNEL-OPERATING_SYSTEM" as one. + cpu, manufacturer, os = triplet.split('-', 2) + + # Autoconf uses config.sub to validate and canonicalize those triplets, + # but the granularity of its results has never been satisfying to our + # use, so we've had our own, different, canonicalization. We've also + # historically not been very consistent with how we use the canonicalized + # values. Hopefully, this will help us make things better. + # The tests are inherited from our decades-old autoconf-based configure, + # which can probably be improved/cleaned up because they are based on a + # mix of uname and config.guess output, while we now only use the latter, + # which presumably has a cleaner and leaner output. Let's refine later. + os = os.replace('/', '_') + if 'android' in os: + canonical_os = 'Android' + canonical_kernel = 'Linux' + elif os.startswith('linux'): + canonical_os = 'GNU' + canonical_kernel = 'Linux' + elif os.startswith('kfreebsd') and os.endswith('-gnu'): + canonical_os = 'GNU' + canonical_kernel = 'kFreeBSD' + elif os.startswith('gnu'): + canonical_os = canonical_kernel = 'GNU' + elif os.startswith('mingw'): + canonical_os = canonical_kernel = 'WINNT' + elif os.startswith('darwin'): + canonical_kernel = 'Darwin' + canonical_os = 'OSX' + elif os.startswith('ios'): + canonical_kernel = 'Darwin' + canonical_os = 'iOS' + elif os.startswith('dragonfly'): + canonical_os = canonical_kernel = 'DragonFly' + elif os.startswith('freebsd'): + canonical_os = canonical_kernel = 'FreeBSD' + elif os.startswith('netbsd'): + canonical_os = canonical_kernel = 'NetBSD' + elif os.startswith('openbsd'): + canonical_os = canonical_kernel = 'OpenBSD' + else: + die('Unknown OS: %s' % os) + + # The CPU granularity is probably not enough. Moving more things from + # old-configure will tell us if we need more + if cpu.endswith('86') or (cpu.startswith('i') and '86' in cpu): + canonical_cpu = 'x86' + endianness = 'little' + elif cpu in ('x86_64', 'ia64'): + canonical_cpu = cpu + endianness = 'little' + elif cpu in ('s390', 's390x'): + canonical_cpu = cpu + endianness = 'big' + elif cpu in ('powerpc64', 'ppc64', 'powerpc64le', 'ppc64le'): + canonical_cpu = 'ppc64' + endianness = 'little' if 'le' in cpu else 'big' + elif cpu in ('powerpc', 'ppc', 'rs6000') or cpu.startswith('powerpc'): + canonical_cpu = 'ppc' + endianness = 'big' + elif cpu in ('Alpha', 'alpha', 'ALPHA'): + canonical_cpu = 'Alpha' + endianness = 'little' + elif cpu.startswith('hppa') or cpu == 'parisc': + canonical_cpu = 'hppa' + endianness = 'big' + elif cpu.startswith('sparc64'): + canonical_cpu = 'sparc64' + endianness = 'big' + elif cpu.startswith('sparc') or cpu == 'sun4u': + canonical_cpu = 'sparc' + endianness = 'big' + elif cpu.startswith('arm'): + canonical_cpu = 'arm' + endianness = 'big' if cpu.startswith(('armeb', 'armbe')) else 'little' + elif cpu in ('mips', 'mipsel'): + canonical_cpu = 'mips32' + endianness = 'little' if 'el' in cpu else 'big' + elif cpu in ('mips64', 'mips64el'): + canonical_cpu = 'mips64' + endianness = 'little' if 'el' in cpu else 'big' + elif cpu.startswith('aarch64'): + canonical_cpu = 'aarch64' + endianness = 'little' + else: + die('Unknown CPU type: %s' % cpu) + + return namespace( + alias=triplet, + cpu=CPU(canonical_cpu), + bitness=CPU_bitness[canonical_cpu], + kernel=Kernel(canonical_kernel), + os=OS(canonical_os), + endianness=Endianness(endianness), + raw_cpu=cpu, + raw_os=os, + # Toolchains, most notably for cross compilation may use cpu-os + # prefixes. + toolchain='%s-%s' % (cpu, os), + ) + + +@imports('subprocess') +def config_sub(shell, triplet): + config_sub = os.path.join(os.path.dirname(__file__), '..', + 'autoconf', 'config.sub') + return subprocess.check_output([shell, config_sub, triplet]).strip() + + +@depends('--host', shell) +@checking('for host system type', lambda h: h.alias) +@imports('subprocess') +def host(value, shell): + if not value: + config_guess = os.path.join(os.path.dirname(__file__), '..', + 'autoconf', 'config.guess') + host = subprocess.check_output([shell, config_guess]).strip() + else: + host = value[0] + + return split_triplet(config_sub(shell, host)) + + +@depends('--target', host, shell) +@checking('for target system type', lambda t: t.alias) +def target(value, host, shell): + if not value: + return host + return split_triplet(config_sub(shell, value[0])) + + +@depends(host, target) +@checking('whether cross compiling') +def cross_compiling(host, target): + return host != target + +set_config('CROSS_COMPILE', cross_compiling) +set_define('CROSS_COMPILE', cross_compiling) +add_old_configure_assignment('CROSS_COMPILE', cross_compiling) + + +@depends(target) +def have_64_bit(target): + if target.bitness == 64: + return True + +set_config('HAVE_64BIT_BUILD', have_64_bit) +set_define('HAVE_64BIT_BUILD', have_64_bit) +add_old_configure_assignment('HAVE_64BIT_BUILD', have_64_bit) + + +# Autoconf needs these set +@depends(host) +def host_for_old_configure(host): + return '--host=%s' % host.alias + +add_old_configure_arg(host_for_old_configure) + +@depends(host, target) +def target_for_old_configure(host, target): + target_alias = target.alias + # old-configure does plenty of tests against $target and $target_os + # and expects darwin for iOS, so make it happy. + if target.os == 'iOS': + target_alias = target_alias.replace('-ios', '-darwin') + return '--target=%s' % target_alias + +add_old_configure_arg(target_for_old_configure) + + +# These variables are for compatibility with the current moz.builds and +# old-configure. Eventually, we'll want to canonicalize better. +@depends(target) +def target_variables(target): + if target.kernel == 'kFreeBSD': + os_target = 'GNU/kFreeBSD' + os_arch = 'GNU_kFreeBSD' + elif target.kernel == 'Darwin' or (target.kernel == 'Linux' and + target.os == 'GNU'): + os_target = target.kernel + os_arch = target.kernel + else: + os_target = target.os + os_arch = target.kernel + + if target.kernel == 'Darwin' and target.cpu == 'x86': + os_test = 'i386' + else: + os_test = target.raw_cpu + + return namespace( + OS_TARGET=os_target, + OS_ARCH=os_arch, + OS_TEST=os_test, + INTEL_ARCHITECTURE=target.cpu in ('x86', 'x86_64') or None, + ) + +set_config('OS_TARGET', delayed_getattr(target_variables, 'OS_TARGET')) +add_old_configure_assignment('OS_TARGET', + delayed_getattr(target_variables, 'OS_TARGET')) +set_config('OS_ARCH', delayed_getattr(target_variables, 'OS_ARCH')) +add_old_configure_assignment('OS_ARCH', + delayed_getattr(target_variables, 'OS_ARCH')) +set_config('OS_TEST', delayed_getattr(target_variables, 'OS_TEST')) +add_old_configure_assignment('OS_TEST', + delayed_getattr(target_variables, 'OS_TEST')) +set_config('CPU_ARCH', delayed_getattr(target, 'cpu')) +add_old_configure_assignment('CPU_ARCH', delayed_getattr(target, 'cpu')) +set_config('INTEL_ARCHITECTURE', delayed_getattr(target_variables, + 'INTEL_ARCHITECTURE')) +set_config('TARGET_CPU', delayed_getattr(target, 'raw_cpu')) +set_config('TARGET_OS', delayed_getattr(target, 'raw_os')) + + +@depends(host) +def host_variables(host): + if host.kernel == 'kFreeBSD': + os_arch = 'GNU_kFreeBSD' + else: + os_arch = host.kernel + return namespace( + HOST_OS_ARCH=os_arch, + ) + +set_config('HOST_OS_ARCH', delayed_getattr(host_variables, 'HOST_OS_ARCH')) +add_old_configure_assignment('HOST_OS_ARCH', + delayed_getattr(host_variables, 'HOST_OS_ARCH')) + +@depends(target) +def target_is_windows(target): + if target.kernel == 'WINNT': + return True + +set_define('_WINDOWS', target_is_windows) +set_define('WIN32', target_is_windows) +set_define('XP_WIN', target_is_windows) +set_define('XP_WIN32', target_is_windows) + +@depends(target) +def target_is_unix(target): + if target.kernel != 'WINNT': + return True + +set_define('XP_UNIX', target_is_unix) + +@depends(target) +def target_is_darwin(target): + if target.kernel == 'Darwin': + return True + +set_define('XP_DARWIN', target_is_darwin) + +@depends(target) +def target_is_ios(target): + if target.kernel == 'Darwin' and target.os == 'iOS': + return True + +set_define('XP_IOS', target_is_ios) + +@depends(target) +def target_is_osx(target): + if target.kernel == 'Darwin' and target.os == 'OSX': + return True + +set_define('XP_MACOSX', target_is_osx) + +@depends(target) +def target_is_linux(target): + if target.kernel == 'Linux': + return True + +set_define('XP_LINUX', target_is_linux) + +# The application/project to build +# ============================================================== +option('--enable-application', nargs=1, env='MOZ_BUILD_APP', + help='Application to build. Same as --enable-project.') + +@depends('--enable-application', '--help') +def application(app, help): + if app: + return app + +imply_option('--enable-project', application) + + +@depends(check_build_environment, '--help') +def default_project(build_env, help): + if build_env.topobjdir.endswith('/js/src'): + return 'js' + return 'browser' + +option('--enable-project', nargs=1, default=default_project, + help='Project to build') + +option('--with-external-source-dir', env='EXTERNAL_SOURCE_DIR', nargs=1, + help='External directory containing additional build files') + +@depends('--enable-project', '--with-external-source-dir', + check_build_environment, '--help') +@imports(_from='os.path', _import='exists') +def include_project_configure(project, external_source_dir, build_env, help): + if not project: + die('--enable-project is required.') + + base_dir = build_env.topsrcdir + if external_source_dir: + base_dir = os.path.join(base_dir, external_source_dir[0]) + + path = os.path.join(base_dir, project[0], 'moz.configure') + if not exists(path): + die('Cannot find project %s', project[0]) + return path + +@depends('--with-external-source-dir') +def external_source_dir(value): + if value: + return value[0] + +set_config('EXTERNAL_SOURCE_DIR', external_source_dir) +add_old_configure_assignment('EXTERNAL_SOURCE_DIR', external_source_dir) + + +@depends(include_project_configure, check_build_environment, '--help') +def build_project(include_project_configure, build_env, help): + ret = os.path.dirname(os.path.relpath(include_project_configure, + build_env.topsrcdir)) + return ret + +set_config('MOZ_BUILD_APP', build_project) +set_define('MOZ_BUILD_APP', build_project) +add_old_configure_assignment('MOZ_BUILD_APP', build_project) + + +# set RELEASE_OR_BETA and NIGHTLY_BUILD variables depending on the cycle we're in +# The logic works like this: +# - if we have "a1" in GRE_MILESTONE, we're building Nightly (define NIGHTLY_BUILD) +# - otherwise, if we have "a" in GRE_MILESTONE, we're building Nightly or Aurora +# - otherwise, we're building Release/Beta (define RELEASE_OR_BETA) +@depends(check_build_environment, '--help') +@imports(_from='__builtin__', _import='open') +def milestone(build_env, _): + milestone_path = os.path.join(build_env.topsrcdir, + 'config', + 'milestone.txt') + with open(milestone_path, 'r') as fh: + milestone = fh.read().splitlines()[-1] + + is_nightly = is_release_or_beta = None + + if 'a1' in milestone: + is_nightly = True + elif 'a' not in milestone: + is_release_or_beta = True + + return namespace(version=milestone, + is_nightly=is_nightly, + is_release_or_beta=is_release_or_beta) + +set_config('GRE_MILESTONE', delayed_getattr(milestone, 'version')) +set_config('NIGHTLY_BUILD', delayed_getattr(milestone, 'is_nightly')) +set_define('NIGHTLY_BUILD', delayed_getattr(milestone, 'is_nightly')) +add_old_configure_assignment('NIGHTLY_BUILD', + delayed_getattr(milestone, 'is_nightly')) +set_config('RELEASE_OR_BETA', delayed_getattr(milestone, 'is_release_or_beta')) +set_define('RELEASE_OR_BETA', delayed_getattr(milestone, 'is_release_or_beta')) +add_old_configure_assignment('RELEASE_OR_BETA', + delayed_getattr(milestone, 'is_release_or_beta')) + +# The app update channel is 'default' when not supplied. The value is used in +# the application's confvars.sh (and is made available to a project specific +# moz.configure). +option('--enable-update-channel', + nargs=1, + help='Select application update channel', + default='default') + +@depends('--enable-update-channel') +def update_channel(channel): + if channel[0] == '': + return 'default' + return channel[0].lower() + +set_config('MOZ_UPDATE_CHANNEL', update_channel) +set_define('MOZ_UPDATE_CHANNEL', update_channel) +add_old_configure_assignment('MOZ_UPDATE_CHANNEL', update_channel) + + +# A template providing a shorthand for setting a variable. The created +# option will only be settable with imply_option. +# It is expected that a project-specific moz.configure will call imply_option +# to set a value other than the default. +# If required, the set_as_define and set_for_old_configure arguments +# will additionally cause the variable to be set using set_define and +# add_old_configure_assignment. util.configure would be an appropriate place for +# this, but it uses add_old_configure_assignment, which is defined in this file. +@template +def project_flag(env=None, set_for_old_configure=False, + set_as_define=False, **kwargs): + + if not env: + configure_error("A project_flag must be passed a variable name to set.") + + opt = option(env=env, possible_origins=('implied',), **kwargs) + + @depends(opt.option) + def option_implementation(value): + if value: + if len(value): + return value + return bool(value) + + set_config(env, option_implementation) + if set_as_define: + set_define(env, option_implementation) + if set_for_old_configure: + add_old_configure_assignment(env, option_implementation) + +# milestone.is_nightly corresponds to cases NIGHTLY_BUILD is set. +@depends(milestone, '--help') +def enabled_in_nightly(milestone, _): + return milestone.is_nightly + +# Set the MOZ_CONFIGURE_OPTIONS variable with all the options that +# were passed somehow (environment, command line, mozconfig) +@depends(mozconfig_options) +@imports(_from='mozbuild.shellutil', _import='quote') +@imports('__sandbox__') +def all_configure_options(_): + result = [] + previous = None + for option in __sandbox__._options.itervalues(): + # __sandbox__._options contains items for both option.name and + # option.env. But it's also an OrderedDict, meaning both are + # consecutive. + # Also ignore OLD_CONFIGURE and MOZCONFIG because they're not + # interesting. + if option == previous or option.env in ('OLD_CONFIGURE', 'MOZCONFIG'): + continue + previous = option + value = __sandbox__._value_for(option) + # We only want options that were explicitly given on the command + # line, the environment, or mozconfig, and that differ from the + # defaults. + if (value is not None and value.origin not in ('default', 'implied') and + value != option.default): + result.append(__sandbox__._raw_options[option]) + # We however always include options that are sent to old configure + # because we don't know their actual defaults. (Keep the conditions + # separate for ease of understanding and ease of removal) + elif (option.help == 'Help missing for old configure options' and + option in __sandbox__._raw_options): + result.append(__sandbox__._raw_options[option]) + + return quote(*result) + +set_config('MOZ_CONFIGURE_OPTIONS', all_configure_options) + + +# This is temporary until js/src/configure and configure are merged. +# Use instead of option() in js/moz.configure and more generally, for +# options that are shared between configure and js/src/configure. +@template +def js_option(*args, **kwargs): + opt = option(*args, **kwargs) + + @depends(opt.option, build_project) + def js_option(value, build_project): + if build_project != 'js': + return value.format(opt.option) + + add_old_configure_arg(js_option) + + +# Bug 1278542: This function is a workaround to resolve +# |android_ndk_include|'s dependency on 'gonkdir.' The +# actual implementation is located in b2g/moz.configure. +# Remove this function as soon as 'android_ndk_include' +# depends on 'target.' +@depends('--help') +def gonkdir(_): + return None diff --git a/build/moz.configure/java.configure b/build/moz.configure/java.configure new file mode 100644 index 000000000..459bd44cf --- /dev/null +++ b/build/moz.configure/java.configure @@ -0,0 +1,62 @@ +# -*- Mode: python; 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/. + + +# Java detection +# ======================================================== +option('--with-java-bin-path', nargs=1, + help='Location of Java binaries (java, javac, jar)') + +@depends('--with-java-bin-path') +@imports(_from='os', _import='environ') +def java_search_paths(path): + if path: + # Look for javac and jar in the specified path. + return path + # With no path specified, look for javac and jar in $JAVA_HOME (if set) + # and $PATH. + if 'JAVA_HOME' in environ: + return [os.path.join(environ['JAVA_HOME'], 'bin'), + environ.get('PATH', '')] + return [environ.get('PATH')] + +# Finds the given java tool, failing with a custom error message if we can't +# find it. +@template +def check_java_tool(tool): + check = check_prog(tool.upper(), (tool,), paths=java_search_paths, + allow_missing=True) + + @depends(check) + def require_tool(result): + if result is None: + die("The program %s was not found. Set $JAVA_HOME to your Java " + "SDK directory or use '--with-java-bin-path={java-bin-dir}'" + % tool) + return result + + return require_tool + +check_java_tool('java') +check_java_tool('javah') +check_java_tool('jar') +check_java_tool('jarsigner') +check_java_tool('keytool') +javac = check_java_tool('javac') + +@depends(javac) +@checking('for javac version') +@imports('subprocess') +def javac_version(javac): + try: + output = subprocess.check_output([javac, '-version'], + stderr=subprocess.STDOUT).rstrip() + version = Version(output.split(' ')[-1]) + if version < '1.7': + die('javac 1.7 or higher is required (found %s)' % version) + return version + except subprocess.CalledProcessError as e: + die('Failed to get javac version: %s', e.output) diff --git a/build/moz.configure/keyfiles.configure b/build/moz.configure/keyfiles.configure new file mode 100644 index 000000000..5a71fd4b2 --- /dev/null +++ b/build/moz.configure/keyfiles.configure @@ -0,0 +1,68 @@ +# -*- Mode: python; 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/. + + +@template +def keyfile(desc, help=None, callback=lambda x: x): + help = help or ('Use the secret key contained in the given keyfile ' + 'for %s requests' % desc) + name = desc.lower().replace(' ', '-') + no_key = callback('no-%s-key' % name) + + option('--with-%s-keyfile' % name, nargs=1, help=help) + + @depends('--with-%s-keyfile' % name) + @checking('for the %s key' % desc, lambda x: x and x is not no_key) + @imports(_from='__builtin__', _import='open') + @imports(_from='__builtin__', _import='IOError') + def keyfile(value): + if value: + try: + with open(value[0]) as fh: + result = fh.read().strip() + if result: + return callback(result) + raise FatalCheckError("'%s' is empty." % value[0]) + except IOError as e: + raise FatalCheckError("'%s': %s." % (value[0], e.strerror)) + return no_key + + return keyfile + + +@template +def simple_keyfile(desc): + value = keyfile(desc) + set_config('MOZ_%s_KEY' % desc.upper().replace(' ', '_'), value) + # Only really required for MOZ_ADJUST_SDK_KEY currently still used in + # old-configure. + add_old_configure_assignment('MOZ_%s_KEY' % desc.upper().replace(' ', '_'), + value) + + +@template +def id_and_secret_keyfile(desc): + def id_and_secret(value): + if value.startswith('no-') and value.endswith('-key'): + id = value[:-3] + 'clientid' + secret = value + elif ' ' in value: + id, secret = value.split(' ', 1) + else: + raise FatalCheckError('%s key file has an invalid format.' % desc) + return namespace( + id=id, + secret=secret, + ) + + content = keyfile(desc, help='Use the client id and secret key contained ' + 'in the given keyfile for %s requests' % desc, + callback=id_and_secret) + + + name = desc.upper().replace(' ', '_') + set_config('MOZ_%s_CLIENTID' % name, delayed_getattr(content, 'id')) + set_config('MOZ_%s_KEY' % name, delayed_getattr(content, 'secret')) diff --git a/build/moz.configure/memory.configure b/build/moz.configure/memory.configure new file mode 100644 index 000000000..3beed2fb2 --- /dev/null +++ b/build/moz.configure/memory.configure @@ -0,0 +1,98 @@ +# -*- Mode: python; 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/. + + +option(env='MOZ_JEMALLOC4', help='Enable jemalloc 4') +imply_option('--enable-jemalloc', depends_if('MOZ_JEMALLOC4')(lambda x: '4')) + + +option('--enable-jemalloc', nargs='?', choices=('4', 'moz'), env='MOZ_MEMORY', + help='Replace memory allocator with jemalloc') + +@depends('--enable-jemalloc', target, build_project, c_compiler) +def jemalloc(value, target, build_project, c_compiler): + if value.origin != 'default': + return bool(value) or None + + if build_project == 'js': + return True + + if target.kernel == 'Darwin' and target.cpu == 'x86_64': + # Don't enable by default on 32-bits OSX. See bug 702250. + return True + + if target.kernel == 'WINNT' and c_compiler.type in ('msvc', 'clang-cl'): + return True + + if target.kernel == 'Linux': + return True + +@depends('--enable-jemalloc') +def jemalloc4(jemalloc): + if len(jemalloc) and jemalloc[0] == '4': + return True + + +set_config('MOZ_MEMORY', jemalloc) +set_define('MOZ_MEMORY', jemalloc) +add_old_configure_assignment('MOZ_MEMORY', jemalloc) + +set_config('MOZ_JEMALLOC4', jemalloc4) +set_define('MOZ_JEMALLOC4', jemalloc4) +add_old_configure_assignment('MOZ_JEMALLOC4', jemalloc4) + + +# Because --enable-jemalloc doesn't use a default because of the dependency +# on the target, we can't use a js_option for it to propagate to js/src +# through the old-configure. +@depends(jemalloc, jemalloc4) +def jemalloc_for_old_configure(jemalloc, jemalloc4): + if jemalloc: + return '--enable-jemalloc=4' if jemalloc4 else '--enable-jemalloc' + return '--disable-jemalloc' + +add_old_configure_arg(jemalloc_for_old_configure) + + +@depends(jemalloc, jemalloc4, target) +def jemalloc_os_define(jemalloc, jemalloc4, target): + if jemalloc and not jemalloc4: + if target.kernel == 'WINNT': + return 'MOZ_MEMORY_WINDOWS' + if target.kernel == 'Linux': + return 'MOZ_MEMORY_LINUX' + if target.kernel == 'Darwin': + return 'MOZ_MEMORY_DARWIN' + if target.kernel in ('kFreeBSD', 'FreeBSD', 'NetBSD'): + return 'MOZ_MEMORY_BSD' + die('--enable-jemalloc is not supported on %s', target.kernel) + +set_define(jemalloc_os_define, '1') + +@depends(jemalloc, jemalloc4, target) +def jemalloc_os_define_android(jemalloc, jemalloc4, target): + if jemalloc and not jemalloc4 and target.os == 'Android': + return 'MOZ_MEMORY_ANDROID' + +set_define(jemalloc_os_define_android, '1') + + +option('--enable-replace-malloc', + help='Enable ability to dynamically replace the malloc implementation') + +@depends('--enable-replace-malloc', jemalloc, milestone, build_project) +def replace_malloc(value, jemalloc, milestone, build_project): + # Enable on central for the debugging opportunities it adds. + if value and not jemalloc: + die('--enable-replace-malloc requires --enable-jemalloc') + if value.origin != 'default': + return bool(value) or None + if milestone.is_nightly and jemalloc and build_project != 'js': + return True + +set_config('MOZ_REPLACE_MALLOC', replace_malloc) +set_define('MOZ_REPLACE_MALLOC', replace_malloc) +add_old_configure_assignment('MOZ_REPLACE_MALLOC', replace_malloc) diff --git a/build/moz.configure/old.configure b/build/moz.configure/old.configure new file mode 100644 index 000000000..b32c3f7b7 --- /dev/null +++ b/build/moz.configure/old.configure @@ -0,0 +1,425 @@ +# -*- Mode: python; 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/. + +@imports('codecs') +@imports('sys') +def encoded_open(path, mode): + encoding = 'mbcs' if sys.platform == 'win32' else 'utf-8' + return codecs.open(path, mode, encoding) + + +option(env='AUTOCONF', nargs=1, help='Path to autoconf 2.13') + +@depends(mozconfig, 'AUTOCONF') +@checking('for autoconf') +@imports(_from='os.path', _import='exists') +@imports('re') +def autoconf(mozconfig, autoconf): + mozconfig_autoconf = None + if mozconfig['path']: + make_extra = mozconfig['make_extra'] + if make_extra: + for assignment in make_extra: + m = re.match('(?:export\s+)?AUTOCONF\s*:?=\s*(.+)$', + assignment) + if m: + mozconfig_autoconf = m.group(1) + + autoconf = autoconf[0] if autoconf else None + + for ac in (mozconfig_autoconf, autoconf, 'autoconf-2.13', 'autoconf2.13', + 'autoconf213'): + if ac: + autoconf = find_program(ac) + if autoconf: + break + else: + fink = find_program('fink') + if fink: + autoconf = os.path.normpath(os.path.join( + fink, '..', '..', 'lib', 'autoconf2.13', 'bin', 'autoconf')) + else: + brew = find_program('brew') + if brew: + autoconf = os.path.normpath(os.path.join( + brew, '..', '..', 'Cellar', 'autoconf213', '2.13', 'bin', + 'autoconf213')) + + if not autoconf: + die('Could not find autoconf 2.13') + + if not exists(autoconf): + die('Could not find autoconf 2.13 at %s', autoconf) + + return autoconf + +set_config('AUTOCONF', autoconf) + + +@depends('OLD_CONFIGURE', mozconfig, autoconf, check_build_environment, shell, + old_configure_assignments, build_project) +@imports(_from='__builtin__', _import='open') +@imports(_from='__builtin__', _import='print') +@imports('glob') +@imports('itertools') +@imports('subprocess') +# Import getmtime without overwriting the sandbox os.path. +@imports(_from='os.path', _import='getmtime') +@imports(_from='os.path', _import='exists') +@imports(_from='mozbuild.shellutil', _import='quote') +def prepare_configure(old_configure, mozconfig, autoconf, build_env, shell, + old_configure_assignments, build_project): + # os.path.abspath in the sandbox will ensure forward slashes on Windows, + # which is actually necessary because this path actually ends up literally + # as $0, and backslashes there breaks autoconf's detection of the source + # directory. + old_configure = os.path.abspath(old_configure[0]) + if build_project == 'js': + old_configure_dir = os.path.dirname(old_configure) + if not old_configure_dir.endswith('/js/src'): + old_configure = os.path.join(old_configure_dir, 'js', 'src', + os.path.basename(old_configure)) + + refresh = True + if exists(old_configure): + mtime = getmtime(old_configure) + aclocal = os.path.join(build_env.topsrcdir, 'build', 'autoconf', + '*.m4') + for input in itertools.chain( + (old_configure + '.in', + os.path.join(os.path.dirname(old_configure), 'aclocal.m4')), + glob.iglob(aclocal), + ): + if getmtime(input) > mtime: + break + else: + refresh = False + + if refresh: + log.info('Refreshing %s with %s', old_configure, autoconf) + script = subprocess.check_output([ + shell, autoconf, + '--localdir=%s' % os.path.dirname(old_configure), + old_configure + '.in']) + + # Make old-configure append to config.log, where we put our own log. + # This could be done with a m4 macro, but it's way easier this way + script = script.replace('>./config.log', '>>./config.log') + + with open(old_configure, 'wb') as fh: + fh.write(script) + + cmd = [shell, old_configure] + with encoded_open('old-configure.vars', 'w') as out: + log.debug('Injecting the following to old-configure:') + def inject(command): + print(command, file=out) + log.debug('| %s', command) + + if mozconfig['path']: + for key, value in mozconfig['vars']['added'].items(): + inject("%s=%s" % (key, quote(value))) + for key, (old, value) in mozconfig['vars']['modified'].items(): + inject("%s=%s" % (key, quote(value))) + for t in ('env', 'vars'): + for key in mozconfig[t]['removed'].keys(): + inject("unset %s" % key) + + # Autoconf is special, because it might be passed from + # mozconfig['make_extra'], which we don't pass automatically above. + inject('export AUTOCONF=%s' % quote(autoconf)) + + for assignment in old_configure_assignments: + inject(assignment) + + return cmd + + +@template +def old_configure_options(*options): + for opt in options: + option(opt, nargs='*', help='Help missing for old configure options') + + @dependable + def all_options(): + return list(options) + + return depends(prepare_configure, extra_old_configure_args, all_options, + *options) + + +@old_configure_options( + '--cache-file', + '--datadir', + '--enable-accessibility', + '--enable-address-sanitizer', + '--enable-alsa', + '--enable-android-omx', + '--enable-b2g-bt', + '--enable-b2g-camera', + '--enable-b2g-ril', + '--enable-bundled-fonts', + '--enable-clang-plugin', + '--enable-content-sandbox', + '--enable-cookies', + '--enable-cpp-rtti', + '--enable-crashreporter', + '--enable-dbus', + '--enable-debug-js-modules', + '--enable-directshow', + '--enable-dtrace', + '--enable-dump-painting', + '--enable-elf-hack', + '--enable-extensions', + '--enable-faststripe', + '--enable-feeds', + '--enable-gamepad', + '--enable-gconf', + '--enable-gczeal', + '--enable-gio', + '--enable-gnomeui', + '--enable-gold', + '--enable-hardware-aec-ns', + '--enable-icf', + '--enable-install-strip', + '--enable-ion', + '--enable-ios-target', + '--enable-jitspew', + '--enable-libjpeg-turbo', + '--enable-libproxy', + '--enable-llvm-hacks', + '--enable-logrefcnt', + '--enable-maintenance-service', + '--enable-memory-sanitizer', + '--enable-mobile-optimize', + '--enable-mozril-geoloc', + '--enable-necko-wifi', + '--enable-negotiateauth', + '--enable-nfc', + '--enable-nspr-build', + '--enable-official-branding', + '--enable-omx-plugin', + '--enable-oom-breakpoint', + '--enable-optimize', + '--enable-parental-controls', + '--enable-pie', + '--enable-png-arm-neon-support', + '--enable-posix-nspr-emulation', + '--enable-pref-extensions', + '--enable-pulseaudio', + '--enable-raw', + '--enable-readline', + '--enable-reflow-perf', + '--enable-release', + '--enable-require-all-d3dc-versions', + '--enable-safe-browsing', + '--enable-sandbox', + '--enable-signmar', + '--enable-simulator', + '--enable-small-chunk-size', + '--enable-startup-notification', + '--enable-startupcache', + '--enable-stdcxx-compat', + '--enable-strip', + '--enable-synth-pico', + '--enable-system-cairo', + '--enable-system-extension-dirs', + '--enable-system-pixman', + '--enable-system-sqlite', + '--enable-tasktracer', + '--enable-thread-sanitizer', + '--enable-trace-logging', + '--enable-ui-locale', + '--enable-universalchardet', + '--enable-updater', + '--enable-url-classifier', + '--enable-valgrind', + '--enable-verify-mar', + '--enable-webrtc', + '--enable-xul', + '--enable-zipwriter', + '--includedir', + '--libdir', + '--no-create', + '--prefix', + '--with-android-cxx-stl', + '--with-android-distribution-directory', + '--with-android-max-sdk', + '--with-android-min-sdk', + '--with-android-sdk', + '--with-app-basename', + '--with-app-name', + '--with-arch', + '--with-branding', + '--with-crashreporter-enable-percent', + '--with-cross-lib', + '--with-debug-label', + '--with-default-mozilla-five-home', + '--with-distribution-id', + '--with-doc-include-dirs', + '--with-doc-input-dirs', + '--with-doc-output-dir', + '--with-float-abi', + '--with-fpu', + '--with-intl-api', + '--with-ios-sdk', + '--with-jitreport-granularity', + '--with-macbundlename-prefix', + '--with-macos-private-frameworks', + '--with-macos-sdk', + '--with-nspr-cflags', + '--with-nspr-exec-prefix', + '--with-nspr-libs', + '--with-nspr-prefix', + '--with-nss-exec-prefix', + '--with-nss-prefix', + '--with-pthreads', + '--with-qemu-exe', + '--with-sixgill', + '--with-soft-float', + '--with-system-bz2', + '--with-system-icu', + '--with-system-jpeg', + '--with-system-libevent', + '--with-system-libvpx', + '--with-system-nspr', + '--with-system-nss', + '--with-system-png', + '--with-system-zlib', + '--with-thumb', + '--with-thumb-interwork', + '--with-unify-dist', + '--with-user-appdir', + '--x-includes', + '--x-libraries', + + # Below are the configure flags used by comm-central. + '--enable-ldap', + '--enable-mapi', + '--enable-calendar', + '--enable-incomplete-external-linkage', +) +@imports(_from='__builtin__', _import='compile') +@imports(_from='__builtin__', _import='open') +@imports('logging') +@imports('os') +@imports('subprocess') +@imports('sys') +@imports(_from='mozbuild.shellutil', _import='quote') +def old_configure(prepare_configure, extra_old_configure_args, all_options, + *options): + cmd = prepare_configure + + # old-configure only supports the options listed in @old_configure_options + # so we don't need to pass it every single option we've been passed. Only + # the ones that are not supported by python configure need to. + cmd += [ + value.format(name) + for name, value in zip(all_options, options) + if value.origin != 'default' + ] + + # We also pass it the options from js/moz.configure so that it can pass + # them down to js/src/configure. Note this list is empty when running + # js/src/configure, in which case we don't need to pass those options + # to old-configure since old-configure doesn't handle them anyways. + if extra_old_configure_args: + cmd += extra_old_configure_args + + # For debugging purpose, in case it's not what we'd expect. + log.debug('Running %s', quote(*cmd)) + + # Our logging goes to config.log, the same file old.configure uses. + # We can't share the handle on the file, so close it. We assume nothing + # beyond this point is going to be interesting to log to config.log from + # our end, so we don't make the effort to recreate a logging.FileHandler. + logger = logging.getLogger('moz.configure') + for handler in logger.handlers: + if isinstance(handler, logging.FileHandler): + handler.close() + logger.removeHandler(handler) + + log_size = os.path.getsize('config.log') + + ret = subprocess.call(cmd) + if ret: + with log.queue_debug(): + with encoded_open('config.log', 'r') as fh: + fh.seek(log_size) + for line in fh: + log.debug(line.rstrip()) + log.error('old-configure failed') + sys.exit(ret) + + raw_config = {} + with encoded_open('config.data', 'r') as fh: + code = compile(fh.read(), 'config.data', 'exec') + # Every variation of the exec() function I tried led to: + # SyntaxError: unqualified exec is not allowed in function 'main' it + # contains a nested function with free variables + exec code in raw_config + + # Ensure all the flags known to old-configure appear in the + # @old_configure_options above. + all_options = set(all_options) + for flag in raw_config['flags']: + if flag not in all_options: + die('Missing option in `@old_configure_options` in %s: %s', + __file__, flag) + + # If the code execution above fails, we want to keep the file around for + # debugging. + os.remove('config.data') + return raw_config + + +# set_config is only available in the global namespace, not directly in +# @depends functions, but we do need to enumerate the result of +# old_configure, so we cheat. +@imports('__sandbox__') +def set_old_configure_config(name, value): + __sandbox__.set_config_impl(name, value) + +# Same as set_old_configure_config, but for set_define. +@imports('__sandbox__') +def set_old_configure_define(name, value): + __sandbox__.set_define_impl(name, value) + + +@depends(old_configure) +@imports('types') +def post_old_configure(raw_config): + for k, v in raw_config['substs']: + set_old_configure_config( + k[1:-1], v[1:-1] if isinstance(v, types.StringTypes) else v) + + for k, v in dict(raw_config['defines']).iteritems(): + set_old_configure_define(k[1:-1], v[1:-1]) + + set_old_configure_config('non_global_defines', + raw_config['non_global_defines']) + + +# Assuming no other option is declared after this function, handle the +# env options that were injected by mozconfig_options by creating dummy +# Option instances and having the sandbox's CommandLineHelper handle +# them. We only do so for options that haven't been declared so far, +# which should be a proxy for the options that old-configure handles +# and that we don't know anything about. +@depends('--help') +@imports('__sandbox__') +@imports(_from='mozbuild.configure.options', _import='Option') +def remaining_mozconfig_options(_): + helper = __sandbox__._helper + for arg in helper: + if helper._origins[arg] != 'mozconfig': + continue + name = arg.split('=', 1)[0] + if name.isupper() and name not in __sandbox__._options: + option = Option(env=name, nargs='*', help=name) + helper.handle(option) + +# Please do not add anything after remaining_mozconfig_options() diff --git a/build/moz.configure/pkg.configure b/build/moz.configure/pkg.configure new file mode 100644 index 000000000..991f401ae --- /dev/null +++ b/build/moz.configure/pkg.configure @@ -0,0 +1,97 @@ +# -*- Mode: python; 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/. + +@depends('--enable-compile-environment') +def pkg_config(compile_env): + if compile_env: + return ('pkg-config',) + +pkg_config = check_prog('PKG_CONFIG', pkg_config, allow_missing=True) + +@depends_if(pkg_config) +@checking('for pkg-config version') +@imports('subprocess') +def pkg_config_version(pkg_config): + return Version(check_cmd_output(pkg_config, '--version').rstrip()) + +# Locates the given module using pkg-config. +# - `var` determines the name of variables to set when the package is found. +# <var>_CFLAGS and <var>_LIBS are set with corresponding values. +# - `package_desc` package name and version requirement string, list of +# strings describing packages to locate, or depends function that will +# resolve to such a string or list of strings. +# - `when` a depends function that will determine whether to perform +# any checks (default is to always perform checks). +# - `allow_missing` If set, failure to fulfill the package description +# will not result in an error or logged message, and any error message +# will be returned to the caller. +# Returns `True` when the package description is fulfilled. +@template +def pkg_check_modules(var, package_desc, when=always, + allow_missing=False): + if isinstance(package_desc, (tuple, list)): + package_desc = ' '.join(package_desc) + package_desc = dependable(package_desc) + + @depends(when, '--enable-compile-environment') + def when_and_compile_environment(when, compile_environment): + return when and compile_environment + + @depends_when(pkg_config, pkg_config_version, + when=when_and_compile_environment) + def check_pkg_config(pkg_config, version): + min_version = '0.9.0' + if pkg_config is None: + die("*** The pkg-config script could not be found. Make sure it is\n" + "*** in your path, or set the PKG_CONFIG environment variable\n" + "*** to the full path to pkg-config.") + if version < min_version: + die("*** Your version of pkg-config is too old. You need version %s or newer.", + min_version) + + @depends_when(pkg_config, package_desc, when=when_and_compile_environment) + @imports('subprocess') + @imports('sys') + @imports(_from='mozbuild.configure.util', _import='LineIO') + def package(pkg_config, package_desc): + # package_desc may start as a depends function, so we can't use + # @checking here. + log.info("checking for %s... " % package_desc) + with log.queue_debug(): + try: + subprocess.check_output([pkg_config, '--errors-to-stdout', + '--print-errors', package_desc]) + log.info("yes") + return True + except subprocess.CalledProcessError as e: + log.info("no") + log_writer = log.warning if allow_missing else log.error + with LineIO(lambda l: log_writer(l)) as o: + o.write(e.output) + if not allow_missing: + sys.exit(1) + + @depends_when(pkg_config, package_desc, when=package) + @checking('%s_CFLAGS' % var, callback=lambda t: ' '.join(t)) + def pkg_cflags(pkg_config, package_desc): + flags = check_cmd_output(pkg_config, '--cflags', package_desc) + return tuple(flags.split()) + + @depends_when(pkg_config, package_desc, when=package) + @checking('%s_LIBS' % var, callback=lambda t: ' '.join(t)) + def pkg_libs(pkg_config, package_desc): + libs = check_cmd_output(pkg_config, '--libs', package_desc) + # Remove evil flags like -Wl,--export-dynamic + return tuple(libs.replace('-Wl,--export-dynamic', '').split()) + + @depends_when(pkg_cflags, pkg_libs, when=package) + def pkg_info(cflags, libs): + return namespace(cflags=cflags, libs=libs) + + set_config('%s_CFLAGS' % var, pkg_cflags) + set_config('%s_LIBS' % var, pkg_libs) + + return pkg_info diff --git a/build/moz.configure/rust.configure b/build/moz.configure/rust.configure new file mode 100644 index 000000000..261768f64 --- /dev/null +++ b/build/moz.configure/rust.configure @@ -0,0 +1,166 @@ +# -*- Mode: python; 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/. + +option('--enable-rust', help='Include Rust language sources') + +@depends('--enable-rust') +def rust_compiler_names(value): + if value: + return ['rustc'] + +@depends('--enable-rust') +def cargo_binary_names(value): + if value: + return ['cargo'] + +rustc = check_prog('RUSTC', rust_compiler_names, allow_missing=True) +cargo = check_prog('CARGO', cargo_binary_names, allow_missing=True) + +@depends_if(rustc) +@checking('rustc version', lambda info: info.version) +def rustc_info(rustc): + out = check_cmd_output(rustc, '--version', '--verbose').splitlines() + info = dict((s.strip() for s in line.split(':', 1)) for line in out[1:]) + return namespace( + version=Version(info.get('release', '0')), + commit=info.get('commit-hash', 'unknown'), + ) + +@depends_if(cargo) +@checking('cargo support for --frozen') +@imports('subprocess') +@imports('os') +def cargo_supports_frozen(cargo): + try: + lines = subprocess.check_output( + [cargo, 'help', 'build'] + ).splitlines() + supported = any(' --frozen' in l for l in lines) + if 'MOZ_AUTOMATION' in os.environ and not supported: + die('cargo in automation must support --frozen') + return supported + except subprocess.CalledProcessError as e: + die('Failed to call cargo: %s', e.message) + +set_config('MOZ_CARGO_SUPPORTS_FROZEN', cargo_supports_frozen) + +@depends('--enable-rust', rustc, rustc_info) +@imports(_from='textwrap', _import='dedent') +def rust_compiler(value, rustc, rustc_info): + if value: + if not rustc: + die(dedent('''\ + Rust compiler not found. + To compile rust language sources, you must have 'rustc' in your path. + See https//www.rust-lang.org/ for more information. + ''')) + version = rustc_info.version + min_version = Version('1.10') + if version < min_version: + die(dedent('''\ + Rust compiler {} is too old. + To compile Rust language sources please install at least + version {} of the 'rustc' toolchain and make sure it is + first in your path. + You can verify this by typing 'rustc --version'. + '''.format(version, min_version))) + return True + +set_config('MOZ_RUST', rust_compiler) + +@depends(rust_compiler, rustc, target, cross_compiling) +@imports('os') +@imports('subprocess') +@imports(_from='mozbuild.configure.util', _import='LineIO') +@imports(_from='mozbuild.shellutil', _import='quote') +@imports(_from='tempfile', _import='mkstemp') +def rust_target(rust_compiler, rustc, target, cross_compiling): + if rust_compiler: + # Rust's --target options are similar to, but not exactly the same + # as, the autoconf-derived targets we use. An example would be that + # Rust uses distinct target triples for targetting the GNU C++ ABI + # and the MSVC C++ ABI on Win32, whereas autoconf has a single + # triple and relies on the user to ensure that everything is + # compiled for the appropriate ABI. We need to perform appropriate + # munging to get the correct option to rustc. + # + # The canonical list of targets supported can be derived from: + # + # https://github.com/rust-lang/rust/tree/master/mk/cfg + + # Avoid having to write out os+kernel for all the platforms where + # they don't differ. + os_or_kernel = target.kernel if target.kernel == 'Linux' and target.os != 'Android' else target.os + rustc_target = { + # DragonFly + ('x86_64', 'DragonFly'): 'x86_64-unknown-dragonfly', + # FreeBSD + ('x86', 'FreeBSD'): 'i686-unknown-freebsd', + ('x86_64', 'FreeBSD'): 'x86_64-unknown-freebsd', + # NetBSD + ('x86_64', 'NetBSD'): 'x86_64-unknown-netbsd', + # OpenBSD + ('x86_64', 'OpenBSD'): 'x86_64-unknown-openbsd', + # Linux + ('x86', 'Linux'): 'i586-unknown-linux-gnu', + # Linux + ('x86_64', 'Linux'): 'x86_64-unknown-linux-gnu', + # OS X and iOS + ('x86', 'OSX'): 'i686-apple-darwin', + ('x86', 'iOS'): 'i386-apple-ios', + ('x86_64', 'OSX'): 'x86_64-apple-darwin', + # Android + ('x86', 'Android'): 'i686-linux-android', + ('arm', 'Android'): 'armv7-linux-androideabi', + # Windows + # XXX better detection of CXX needed here, to figure out whether + # we need i686-pc-windows-gnu instead, since mingw32 builds work. + ('x86', 'WINNT'): 'i686-pc-windows-msvc', + ('x86_64', 'WINNT'): 'x86_64-pc-windows-msvc', + }.get((target.cpu, os_or_kernel), None) + + if rustc_target is None: + die("Don't know how to translate {} for rustc".format(target.alias)) + + # Check to see whether our rustc has a reasonably functional stdlib + # for our chosen target. + target_arg = '--target=' + rustc_target + in_fd, in_path = mkstemp(prefix='conftest', suffix='.rs') + out_fd, out_path = mkstemp(prefix='conftest', suffix='.rlib') + os.close(out_fd) + try: + source = 'pub extern fn hello() { println!("Hello world"); }' + log.debug('Creating `%s` with content:', in_path) + with LineIO(lambda l: log.debug('| %s', l)) as out: + out.write(source) + + os.write(in_fd, source) + os.close(in_fd) + + cmd = [ + rustc, + '--crate-type', 'staticlib', + target_arg, + '-o', out_path, + in_path, + ] + def failed(): + die('Cannot compile for {} with {}'.format(target.alias, rustc)) + check_cmd_output(*cmd, onerror=failed) + if not os.path.exists(out_path) or os.path.getsize(out_path) == 0: + failed() + finally: + os.remove(in_path) + os.remove(out_path) + # This target is usable. + return rustc_target + +set_config('RUST_TARGET', rust_target) + +# Until we remove all the other Rust checks in old-configure. +add_old_configure_assignment('MOZ_RUST', rust_compiler) +add_old_configure_assignment('RUSTC', rustc) +add_old_configure_assignment('RUST_TARGET', rust_target) diff --git a/build/moz.configure/toolchain.configure b/build/moz.configure/toolchain.configure new file mode 100644 index 000000000..8b2416152 --- /dev/null +++ b/build/moz.configure/toolchain.configure @@ -0,0 +1,910 @@ +# -*- Mode: python; 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/. + +# PGO +# ============================================================== +option(env='MOZ_PGO', help='Build with profile guided optimizations') + +set_config('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x))) +add_old_configure_assignment('MOZ_PGO', depends('MOZ_PGO')(lambda x: bool(x))) + +# yasm detection +# ============================================================== +yasm = check_prog('YASM', ['yasm'], allow_missing=True) + +@depends_if(yasm) +@checking('yasm version') +def yasm_version(yasm): + version = check_cmd_output( + yasm, '--version', + onerror=lambda: die('Failed to get yasm version.') + ).splitlines()[0].split()[1] + return Version(version) + +# Until we move all the yasm consumers out of old-configure. +# bug 1257904 +add_old_configure_assignment('_YASM_MAJOR_VERSION', + delayed_getattr(yasm_version, 'major')) +add_old_configure_assignment('_YASM_MINOR_VERSION', + delayed_getattr(yasm_version, 'minor')) + +@depends(yasm, target) +def yasm_asflags(yasm, target): + if yasm: + asflags = { + ('OSX', 'x86'): '-f macho32', + ('OSX', 'x86_64'): '-f macho64', + ('WINNT', 'x86'): '-f win32', + ('WINNT', 'x86_64'): '-f x64', + }.get((target.os, target.cpu), None) + if asflags is None: + # We're assuming every x86 platform we support that's + # not Windows or Mac is ELF. + if target.cpu == 'x86': + asflags = '-f elf32' + elif target.cpu == 'x86_64': + asflags = '-f elf64' + if asflags: + asflags += ' -rnasm -pnasm' + return asflags + +set_config('YASM_ASFLAGS', yasm_asflags) + +@depends(yasm_asflags) +def have_yasm(value): + if value: + return True + +set_config('HAVE_YASM', have_yasm) +# Until the YASM variable is not necessary in old-configure. +add_old_configure_assignment('YASM', have_yasm) + +# Android NDK +# ============================================================== + +@depends('--disable-compile-environment', build_project, gonkdir, '--help') +def compiling_android(compile_env, build_project, gonkdir, _): + return compile_env and (gonkdir or build_project in ('mobile/android', 'js')) + +include('android-ndk.configure', when=compiling_android) + +# MacOS deployment target version +# ============================================================== +# This needs to happen before any compilation test is done. + +option('--enable-macos-target', env='MACOSX_DEPLOYMENT_TARGET', nargs=1, + default='10.7', help='Set the minimum MacOS version needed at runtime') + +@depends('--enable-macos-target', target) +@imports(_from='os', _import='environ') +def macos_target(value, target): + if value and target.os == 'OSX': + # Ensure every compiler process we spawn uses this value. + environ['MACOSX_DEPLOYMENT_TARGET'] = value[0] + return value[0] + if value and value.origin != 'default': + die('--enable-macos-target cannot be used when targeting %s', + target.os) + + +set_config('MACOSX_DEPLOYMENT_TARGET', macos_target) +add_old_configure_assignment('MACOSX_DEPLOYMENT_TARGET', macos_target) + + +# Compiler wrappers +# ============================================================== +# Normally, we'd use js_option and automatically have those variables +# propagated to js/src, but things are complicated by possible additional +# wrappers in CC/CXX, and by other subconfigures that do not handle those +# options and do need CC/CXX altered. +option('--with-compiler-wrapper', env='COMPILER_WRAPPER', nargs=1, + help='Enable compiling with wrappers such as distcc and ccache') + +option('--with-ccache', env='CCACHE', nargs='?', + help='Enable compiling with ccache') + +@depends_if('--with-ccache') +def ccache(value): + if len(value): + return value + # If --with-ccache was given without an explicit value, we default to + # 'ccache'. + return 'ccache' + +ccache = check_prog('CCACHE', progs=(), input=ccache) + +@depends_if(ccache) +def using_ccache(ccache): + return True + +set_config('MOZ_USING_CCACHE', using_ccache) + +@depends('--with-compiler-wrapper', ccache) +@imports(_from='mozbuild.shellutil', _import='split', _as='shell_split') +def compiler_wrapper(wrapper, ccache): + if wrapper: + raw_wrapper = wrapper[0] + wrapper = shell_split(raw_wrapper) + wrapper_program = find_program(wrapper[0]) + if not wrapper_program: + die('Cannot find `%s` from the given compiler wrapper `%s`', + wrapper[0], raw_wrapper) + wrapper[0] = wrapper_program + + if ccache: + if wrapper: + return tuple([ccache] + wrapper) + else: + return (ccache,) + elif wrapper: + return tuple(wrapper) + +add_old_configure_assignment('COMPILER_WRAPPER', compiler_wrapper) + +@depends_if(compiler_wrapper) +def using_compiler_wrapper(compiler_wrapper): + return True + +set_config('MOZ_USING_COMPILER_WRAPPER', using_compiler_wrapper) + + +# Cross-compilation related things. +# ============================================================== +js_option('--with-toolchain-prefix', env='TOOLCHAIN_PREFIX', nargs=1, + help='Prefix for the target toolchain') + +@depends('--with-toolchain-prefix', target, host, cross_compiling) +def toolchain_prefix(value, target, host, cross_compiling): + if value: + return tuple(value) + if cross_compiling: + return ('%s-' % target.toolchain, '%s-' % target.alias) + +@depends(toolchain_prefix, target) +def first_toolchain_prefix(toolchain_prefix, target): + # Pass TOOLCHAIN_PREFIX down to the build system if it was given from the + # command line/environment (in which case there's only one value in the tuple), + # or when cross-compiling for Android. + if toolchain_prefix and (target.os == 'Android' or len(toolchain_prefix) == 1): + return toolchain_prefix[0] + +set_config('TOOLCHAIN_PREFIX', first_toolchain_prefix) +add_old_configure_assignment('TOOLCHAIN_PREFIX', first_toolchain_prefix) + + +# Compilers +# ============================================================== +include('compilers-util.configure') + +def try_preprocess(compiler, language, source): + return try_invoke_compiler(compiler, language, source, ['-E']) + +@imports(_from='mozbuild.configure.constants', _import='CompilerType') +@imports(_from='mozbuild.configure.constants', + _import='CPU_preprocessor_checks') +@imports(_from='mozbuild.configure.constants', + _import='kernel_preprocessor_checks') +@imports(_from='textwrap', _import='dedent') +def get_compiler_info(compiler, language): + '''Returns information about the given `compiler` (command line in the + form of a list or tuple), in the given `language`. + + The returned information includes: + - the compiler type (msvc, clang-cl, clang or gcc) + - the compiler version + - the compiler supported language + - the compiler supported language version + ''' + # Note: MSVC doesn't expose __STDC_VERSION__. It does expose __STDC__, + # but only when given the -Za option, which disables compiler + # extensions. + # Note: We'd normally do a version check for clang, but versions of clang + # in Xcode have a completely different versioning scheme despite exposing + # the version with the same defines. + # So instead, we make things such that the version is missing when the + # clang used is below the minimum supported version (currently clang 3.6). + # We then only include the version information when the C++ compiler + # matches the feature check, so that an unsupported version of clang would + # have no version number. + check = dedent('''\ + #if defined(_MSC_VER) + #if defined(__clang__) + %COMPILER "clang-cl" + %VERSION _MSC_FULL_VER + #else + %COMPILER "msvc" + %VERSION _MSC_FULL_VER + #endif + #elif defined(__clang__) + %COMPILER "clang" + # if !__cplusplus || __has_feature(cxx_alignof) + %VERSION __clang_major__.__clang_minor__.__clang_patchlevel__ + # endif + #elif defined(__GNUC__) + %COMPILER "gcc" + %VERSION __GNUC__.__GNUC_MINOR__.__GNUC_PATCHLEVEL__ + #endif + + #if __cplusplus + %cplusplus __cplusplus + #elif __STDC_VERSION__ + %STDC_VERSION __STDC_VERSION__ + #elif __STDC__ + %STDC_VERSION 198900L + #endif + ''') + + # While we're doing some preprocessing, we might as well do some more + # preprocessor-based tests at the same time, to check the toolchain + # matches what we want. + for name, preprocessor_checks in ( + ('CPU', CPU_preprocessor_checks), + ('KERNEL', kernel_preprocessor_checks), + ): + for n, (value, condition) in enumerate(preprocessor_checks.iteritems()): + check += dedent('''\ + #%(if)s %(condition)s + %%%(name)s "%(value)s" + ''' % { + 'if': 'elif' if n else 'if', + 'condition': condition, + 'name': name, + 'value': value, + }) + check += '#endif\n' + + # Also check for endianness. The advantage of living in modern times is + # that all the modern compilers we support now have __BYTE_ORDER__ defined + # by the preprocessor, except MSVC, which only supports little endian. + check += dedent('''\ + #if _MSC_VER || __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ + %ENDIANNESS "little" + #elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ + %ENDIANNESS "big" + #endif + ''') + + result = try_preprocess(compiler, language, check) + + if not result: + raise FatalCheckError( + 'Unknown compiler or compiler not supported.') + + # Metadata emitted by preprocessors such as GCC with LANG=ja_JP.utf-8 may + # have non-ASCII characters. Treat the output as bytearray. + data = {} + for line in result.splitlines(): + if line.startswith(b'%'): + k, _, v = line.partition(' ') + k = k.lstrip('%') + data[k] = v.replace(' ', '').lstrip('"').rstrip('"') + log.debug('%s = %s', k, data[k]) + + try: + type = CompilerType(data['COMPILER']) + except: + raise FatalCheckError( + 'Unknown compiler or compiler not supported.') + + cplusplus = int(data.get('cplusplus', '0L').rstrip('L')) + stdc_version = int(data.get('STDC_VERSION', '0L').rstrip('L')) + + version = data.get('VERSION') + if version and type in ('msvc', 'clang-cl'): + msc_ver = version + version = msc_ver[0:2] + if len(msc_ver) > 2: + version += '.' + msc_ver[2:4] + if len(msc_ver) > 4: + version += '.' + msc_ver[4:] + + if version: + version = Version(version) + + return namespace( + type=type, + version=version, + cpu=data.get('CPU'), + kernel=data.get('KERNEL'), + endianness=data.get('ENDIANNESS'), + language='C++' if cplusplus else 'C', + language_version=cplusplus if cplusplus else stdc_version, + ) + + +@imports(_from='mozbuild.shellutil', _import='quote') +def check_compiler(compiler, language, target): + info = get_compiler_info(compiler, language) + + flags = [] + + def append_flag(flag): + if flag not in flags: + if info.type == 'clang-cl': + flags.append('-Xclang') + flags.append(flag) + + # Check language standards + # -------------------------------------------------------------------- + if language != info.language: + raise FatalCheckError( + '`%s` is not a %s compiler.' % (quote(*compiler), language)) + + # Note: We do a strict version check because there sometimes are backwards + # incompatible changes in the standard, and not all code that compiles as + # C99 compiles as e.g. C11 (as of writing, this is true of libnestegg, for + # example) + if info.language == 'C' and info.language_version != 199901: + if info.type in ('clang-cl', 'clang', 'gcc'): + append_flag('-std=gnu99') + + # Note: MSVC, while supporting C++11, still reports 199711L for __cplusplus. + # Note: this is a strict version check because we used to always add + # -std=gnu++11. + if info.language == 'C++': + if info.type in ('clang', 'gcc') and info.language_version != 201103: + append_flag('-std=gnu++11') + # MSVC 2015 headers include C++14 features, but don't guard them + # with appropriate checks. + if info.type == 'clang-cl' and info.language_version != 201402: + append_flag('-std=c++14') + + # We force clang-cl to emulate Visual C++ 2015 Update 3 with fallback to + # cl.exe. + if info.type == 'clang-cl' and info.version != '19.00.24213': + # Those flags are direct clang-cl flags that don't need -Xclang, add + # them directly. + flags.append('-fms-compatibility-version=19.00.24213') + flags.append('-fallback') + + # Check compiler target + # -------------------------------------------------------------------- + if not info.cpu or info.cpu != target.cpu: + if info.type == 'clang': + append_flag('--target=%s' % target.toolchain) + elif info.type == 'gcc': + same_arch_different_bits = ( + ('x86', 'x86_64'), + ('ppc', 'ppc64'), + ('sparc', 'sparc64'), + ) + if (target.cpu, info.cpu) in same_arch_different_bits: + append_flag('-m32') + elif (info.cpu, target.cpu) in same_arch_different_bits: + append_flag('-m64') + + if not info.kernel or info.kernel != target.kernel: + if info.type == 'clang': + append_flag('--target=%s' % target.toolchain) + + if not info.endianness or info.endianness != target.endianness: + if info.type == 'clang': + append_flag('--target=%s' % target.toolchain) + + return namespace( + type=info.type, + version=info.version, + target_cpu=info.cpu, + target_kernel=info.kernel, + target_endianness=info.endianness, + flags=flags, + ) + + +@imports(_from='collections', _import='defaultdict') +@imports(_from='__builtin__', _import='sorted') +def get_vc_paths(base): + vc = defaultdict(lambda: defaultdict(dict)) + subkey = r'Microsoft\VisualStudio\VC\*\*\*\Compiler' + for v, h, t, p in get_registry_values(base + '\\' + subkey): + vc[v][h][t] = p + if not vc: + return + version, data = sorted(vc.iteritems(), key=lambda x: Version(x[0]))[-1] + return data + + +@depends(host) +@imports('platform') +def vc_compiler_path(host): + if host.kernel != 'WINNT': + return + vc_host = { + 'x86': 'x86', + 'AMD64': 'x64', + }.get(platform.machine()) + if vc_host is None: + return + vc_target = { + 'x86': 'x86', + 'x86_64': 'x64', + 'arm': 'arm', + }.get(host.cpu) + if vc_target is None: + return + + base_key = r'HKEY_LOCAL_MACHINE\SOFTWARE' + data = get_vc_paths(base_key) + if not data: + data = get_vc_paths(base_key + r'\Wow6432Node') + if not data: + return + + path = data.get(vc_host, {}).get(vc_target) + if not path and vc_host == 'x64': + vc_host = 'x86' + path = data.get(vc_host, {}).get(vc_target) + if not path: + return + path = os.path.dirname(path) + if vc_host != vc_target: + other_path = data.get(vc_host, {}).get(vc_host) + if other_path: + return (path, os.path.dirname(other_path)) + return (path,) + + +@depends(vc_compiler_path) +@imports('os') +def toolchain_search_path(vc_compiler_path): + if vc_compiler_path: + result = [os.environ.get('PATH')] + result.extend(vc_compiler_path) + # We're going to alter PATH for good in windows.configure, but we also + # need to do it for the valid_compiler() check below. + os.environ['PATH'] = os.pathsep.join(result) + return result + + +@template +def default_c_compilers(host_or_target): + '''Template defining the set of default C compilers for the host and + target platforms. + `host_or_target` is either `host` or `target` (the @depends functions + from init.configure. + ''' + assert host_or_target in (host, target) + + @depends(host_or_target, target, toolchain_prefix) + def default_c_compilers(host_or_target, target, toolchain_prefix): + gcc = ('gcc',) + if toolchain_prefix and host_or_target is target: + gcc = tuple('%sgcc' % p for p in toolchain_prefix) + gcc + + if host_or_target.kernel == 'WINNT': + return ('cl', 'clang-cl') + gcc + ('clang',) + if host_or_target.kernel == 'Darwin': + return ('clang',) + return gcc + ('clang',) + + return default_c_compilers + + +@template +def default_cxx_compilers(c_compiler): + '''Template defining the set of default C++ compilers for the host and + target platforms. + `c_compiler` is the @depends function returning a Compiler instance for + the desired platform. + + Because the build system expects the C and C++ compilers to be from the + same compiler suite, we derive the default C++ compilers from the C + compiler that was found if none was provided. + ''' + + @depends(c_compiler) + def default_cxx_compilers(c_compiler): + dir = os.path.dirname(c_compiler.compiler) + file = os.path.basename(c_compiler.compiler) + + if c_compiler.type == 'gcc': + return (os.path.join(dir, file.replace('gcc', 'g++')),) + + if c_compiler.type == 'clang': + return (os.path.join(dir, file.replace('clang', 'clang++')),) + + return (c_compiler.compiler,) + + return default_cxx_compilers + + +@template +def compiler(language, host_or_target, c_compiler=None, other_compiler=None, + other_c_compiler=None): + '''Template handling the generic base checks for the compiler for the + given `language` on the given platform (`host_or_target`). + `host_or_target` is either `host` or `target` (the @depends functions + from init.configure. + When the language is 'C++', `c_compiler` is the result of the `compiler` + template for the language 'C' for the same `host_or_target`. + When `host_or_target` is `host`, `other_compiler` is the result of the + `compiler` template for the same `language` for `target`. + When `host_or_target` is `host` and the language is 'C++', + `other_c_compiler` is the result of the `compiler` template for the + language 'C' for `target`. + ''' + assert host_or_target in (host, target) + assert language in ('C', 'C++') + assert language == 'C' or c_compiler + assert host_or_target == target or other_compiler + assert language == 'C' or host_or_target == target or other_c_compiler + + host_or_target_str = { + host: 'host', + target: 'target', + }[host_or_target] + + var = { + ('C', target): 'CC', + ('C++', target): 'CXX', + ('C', host): 'HOST_CC', + ('C++', host): 'HOST_CXX', + }[language, host_or_target] + + default_compilers = { + 'C': lambda: default_c_compilers(host_or_target), + 'C++': lambda: default_cxx_compilers(c_compiler), + }[language]() + + what='the %s %s compiler' % (host_or_target_str, language) + + option(env=var, nargs=1, help='Path to %s' % what) + + # Handle the compiler given by the user through one of the CC/CXX/HOST_CC/ + # HOST_CXX variables. + @depends_if(var) + @imports(_from='itertools', _import='takewhile') + @imports(_from='mozbuild.shellutil', _import='split', _as='shell_split') + def provided_compiler(cmd): + # Historically, the compiler variables have contained more than the + # path to the compiler itself. So for backwards compatibility, try to + # find what is what in there, assuming the first dash-prefixed item is + # a compiler option, the item before that is the compiler, and anything + # before that is a compiler wrapper. + cmd = shell_split(cmd[0]) + + without_flags = list(takewhile(lambda x: not x.startswith('-'), cmd)) + + return namespace( + wrapper=without_flags[:-1], + compiler=without_flags[-1], + flags=cmd[len(without_flags):], + ) + + # Derive the host compiler from the corresponding target compiler when no + # explicit compiler was given and we're not cross compiling. For the C++ + # compiler, though, prefer to derive from the host C compiler when it + # doesn't match the target C compiler. + # As a special case, since clang supports all kinds of targets in the same + # executable, when cross compiling with clang, default to the same compiler + # as the target compiler, resetting flags. + if host_or_target == host: + args = (c_compiler, other_c_compiler) if other_c_compiler else () + + @depends(provided_compiler, other_compiler, cross_compiling, *args) + def provided_compiler(value, other_compiler, cross_compiling, *args): + if value: + return value + c_compiler, other_c_compiler = args if args else (None, None) + if not cross_compiling and c_compiler == other_c_compiler: + return other_compiler + if cross_compiling and other_compiler.type == 'clang': + return namespace(**{ + k: [] if k == 'flags' else v + for k, v in other_compiler.__dict__.iteritems() + }) + + # Normally, we'd use `var` instead of `_var`, but the interaction with + # old-configure complicates things, and for now, we a) can't take the plain + # result from check_prog as CC/CXX/HOST_CC/HOST_CXX and b) have to let + # old-configure AC_SUBST it (because it's autoconf doing it, not us) + compiler = check_prog('_%s' % var, what=what, progs=default_compilers, + input=delayed_getattr(provided_compiler, 'compiler'), + paths=toolchain_search_path) + + @depends(compiler, provided_compiler, compiler_wrapper, host_or_target) + @checking('whether %s can be used' % what, lambda x: bool(x)) + @imports(_from='mozbuild.shellutil', _import='quote') + def valid_compiler(compiler, provided_compiler, compiler_wrapper, + host_or_target): + wrapper = list(compiler_wrapper or ()) + if provided_compiler: + provided_wrapper = list(provided_compiler.wrapper) + # When doing a subconfigure, the compiler is set by old-configure + # and it contains the wrappers from --with-compiler-wrapper and + # --with-ccache. + if provided_wrapper[:len(wrapper)] == wrapper: + provided_wrapper = provided_wrapper[len(wrapper):] + wrapper.extend(provided_wrapper) + flags = provided_compiler.flags + else: + flags = [] + + # Ideally, we'd always use the absolute path, but unfortunately, on + # Windows, the compiler is very often in a directory containing spaces. + # Unfortunately, due to the way autoconf does its compiler tests with + # eval, that doesn't work out. So in that case, check that the + # compiler can still be found in $PATH, and use the file name instead + # of the full path. + if quote(compiler) != compiler: + full_path = os.path.abspath(compiler) + compiler = os.path.basename(compiler) + found_compiler = find_program(compiler) + if not found_compiler: + die('%s is not in your $PATH' + % quote(os.path.dirname(full_path))) + if os.path.normcase(find_program(compiler)) != os.path.normcase( + full_path): + die('Found `%s` before `%s` in your $PATH. ' + 'Please reorder your $PATH.', + quote(os.path.dirname(found_compiler)), + quote(os.path.dirname(full_path))) + + info = check_compiler(wrapper + [compiler] + flags, language, + host_or_target) + + # Check that the additional flags we got are enough to not require any + # more flags. + if info.flags: + flags += info.flags + info = check_compiler(wrapper + [compiler] + flags, language, + host_or_target) + + if not info.target_cpu or info.target_cpu != host_or_target.cpu: + raise FatalCheckError( + '%s %s compiler target CPU (%s) does not match --%s CPU (%s)' + % (host_or_target_str.capitalize(), language, + info.target_cpu or 'unknown', host_or_target_str, + host_or_target.raw_cpu)) + + if not info.target_kernel or (info.target_kernel != + host_or_target.kernel): + raise FatalCheckError( + '%s %s compiler target kernel (%s) does not match --%s kernel (%s)' + % (host_or_target_str.capitalize(), language, + info.target_kernel or 'unknown', host_or_target_str, + host_or_target.kernel)) + + if not info.target_endianness or (info.target_endianness != + host_or_target.endianness): + raise FatalCheckError( + '%s %s compiler target endianness (%s) does not match --%s ' + 'endianness (%s)' + % (host_or_target_str.capitalize(), language, + info.target_endianness or 'unknown', host_or_target_str, + host_or_target.endianness)) + + if info.flags: + raise FatalCheckError( + 'Unknown compiler or compiler not supported.') + + # Compiler version checks + # =================================================== + # Check the compiler version here instead of in `compiler_version` so + # that the `checking` message doesn't pretend the compiler can be used + # to then bail out one line later. + if info.type == 'gcc' and info.version < '4.8.0': + raise FatalCheckError( + 'Only GCC 4.8 or newer is supported (found version %s).' + % info.version) + + # If you want to bump the version check here search for + # __cpp_static_assert above, and see the associated comment. + if info.type == 'clang' and not info.version: + raise FatalCheckError( + 'Only clang/llvm 3.6 or newer is supported.') + + if info.type == 'msvc': + if info.version < '19.00.24213': + raise FatalCheckError( + 'This version (%s) of the MSVC compiler is not ' + 'supported.\n' + 'You must install Visual C++ 2015 Update 3 or newer in ' + 'order to build.\n' + 'See https://developer.mozilla.org/en/' + 'Windows_Build_Prerequisites' % info.version) + + return namespace( + wrapper=wrapper, + compiler=compiler, + flags=flags, + type=info.type, + version=info.version, + language=language, + ) + + @depends(valid_compiler) + @checking('%s version' % what) + def compiler_version(compiler): + return compiler.version + + if language == 'C++': + @depends(valid_compiler, c_compiler) + def valid_compiler(compiler, c_compiler): + if compiler.type != c_compiler.type: + die('The %s C compiler is %s, while the %s C++ compiler is ' + '%s. Need to use the same compiler suite.', + host_or_target_str, c_compiler.type, + host_or_target_str, compiler.type) + + if compiler.version != c_compiler.version: + die('The %s C compiler is version %s, while the %s C++ ' + 'compiler is version %s. Need to use the same compiler ' + 'version.', + host_or_target_str, c_compiler.version, + host_or_target_str, compiler.version) + return compiler + + # Set CC/CXX/HOST_CC/HOST_CXX for old-configure, which needs the wrapper + # and the flags that were part of the user input for those variables to + # be provided. + add_old_configure_assignment(var, depends_if(valid_compiler)( + lambda x: list(x.wrapper) + [x.compiler] + list(x.flags))) + + # Set CC_TYPE/CC_VERSION/HOST_CC_TYPE/HOST_CC_VERSION to allow + # old-configure to do some of its still existing checks. + if language == 'C': + set_config( + '%s_TYPE' % var, delayed_getattr(valid_compiler, 'type')) + add_old_configure_assignment( + '%s_TYPE' % var, delayed_getattr(valid_compiler, 'type')) + add_old_configure_assignment( + '%s_VERSION' % var, delayed_getattr(valid_compiler, 'version')) + + valid_compiler = compiler_class(valid_compiler) + + def compiler_error(): + raise FatalCheckError('Failed compiling a simple %s source with %s' + % (language, what)) + + valid_compiler.try_compile(check_msg='%s works' % what, + onerror=compiler_error) + + + # Set CPP/CXXCPP for both the build system and old-configure. We don't + # need to check this works for preprocessing, because we already relied + # on $CC -E/$CXX -E doing preprocessing work to validate the compiler + # in the first place. + if host_or_target == target: + pp_var = { + 'C': 'CPP', + 'C++': 'CXXCPP', + }[language] + + preprocessor = depends_if(valid_compiler)( + lambda x: list(x.wrapper) + [x.compiler, '-E'] + list(x.flags)) + + set_config(pp_var, preprocessor) + add_old_configure_assignment(pp_var, preprocessor) + + return valid_compiler + + +c_compiler = compiler('C', target) +cxx_compiler = compiler('C++', target, c_compiler=c_compiler) +host_c_compiler = compiler('C', host, other_compiler=c_compiler) +host_cxx_compiler = compiler('C++', host, c_compiler=host_c_compiler, + other_compiler=cxx_compiler, + other_c_compiler=c_compiler) + +# Generic compiler-based conditions. +non_msvc_compiler = depends(c_compiler)(lambda info: info.type != 'msvc') +building_with_gcc = depends(c_compiler)(lambda info: info.type == 'gcc') + +include('compile-checks.configure') + +@depends(have_64_bit, + try_compile(body='static_assert(sizeof(void *) == 8, "")', + check_msg='for 64-bit OS')) +def check_have_64_bit(have_64_bit, compiler_have_64_bit): + if have_64_bit != compiler_have_64_bit: + configure_error('The target compiler does not agree with configure ' + 'about the target bitness.') + + +@depends(c_compiler) +def default_debug_flags(compiler_info): + # Debug info is ON by default. + if compiler_info.type in ('msvc', 'clang-cl'): + return '-Zi' + return '-g' + +option(env='MOZ_DEBUG_FLAGS', + nargs=1, + help='Debug compiler flags') + +imply_option('--enable-debug-symbols', + depends_if('--enable-debug')(lambda v: v)) + +js_option('--enable-debug-symbols', + nargs='?', + default=True, + help='Enable debug symbols using the given compiler flags') + +set_config('MOZ_DEBUG_SYMBOLS', + depends_if('--enable-debug-symbols')(lambda _: True)) + +@depends('MOZ_DEBUG_FLAGS', '--enable-debug-symbols', default_debug_flags) +def debug_flags(env_debug_flags, enable_debug_flags, default_debug_flags): + # If MOZ_DEBUG_FLAGS is set, and --enable-debug-symbols is set to a value, + # --enable-debug-symbols takes precedence. Note, the value of + # --enable-debug-symbols may be implied by --enable-debug. + if len(enable_debug_flags): + return enable_debug_flags[0] + if env_debug_flags: + return env_debug_flags[0] + return default_debug_flags + +set_config('MOZ_DEBUG_FLAGS', debug_flags) +add_old_configure_assignment('MOZ_DEBUG_FLAGS', debug_flags) + +# Some standard library headers (notably bionic on Android) declare standard +# functions (e.g. getchar()) and also #define macros for those standard +# functions. libc++ deals with this by doing something like the following +# (explanatory comments added): +# +# #ifdef FUNC +# // Capture the definition of FUNC. +# inline _LIBCPP_INLINE_VISIBILITY int __libcpp_FUNC(...) { return FUNC(...); } +# #undef FUNC +# // Use a real inline definition. +# inline _LIBCPP_INLINE_VISIBILITY int FUNC(...) { return _libcpp_FUNC(...); } +# #endif +# +# _LIBCPP_INLINE_VISIBILITY is typically defined as: +# +# __attribute__((__visibility__("hidden"), __always_inline__)) +# +# Unfortunately, this interacts badly with our system header wrappers, as the: +# +# #pragma GCC visibility push(default) +# +# that they do prior to including the actual system header is treated by the +# compiler as an explicit declaration of visibility on every function declared +# in the header. Therefore, when the libc++ code above is encountered, it is +# as though the compiler has effectively seen: +# +# int FUNC(...) __attribute__((__visibility__("default"))); +# int FUNC(...) __attribute__((__visibility__("hidden"))); +# +# and the compiler complains about the mismatched visibility declarations. +# +# However, libc++ will only define _LIBCPP_INLINE_VISIBILITY if there is no +# existing definition. We can therefore define it to the empty string (since +# we are properly managing visibility ourselves) and avoid this whole mess. +# Note that we don't need to do this with gcc, as libc++ detects gcc and +# effectively does the same thing we are doing here. +@depends(c_compiler, target) +def libcxx_inline_visibility(c_compiler, target): + if c_compiler.type == 'clang' and target.os == 'Android': + return '' + +set_define('_LIBCPP_INLINE_VISIBILITY', libcxx_inline_visibility) +set_define('_LIBCPP_INLINE_VISIBILITY_EXCEPT_GCC49', libcxx_inline_visibility) + +@depends(c_compiler, target, check_build_environment) +def visibility_flags(c_compiler, target, env): + if target.os != 'WINNT': + if target.kernel == 'Darwin': + return ('-fvisibility=hidden', '-fvisibility-inlines-hidden') + return ('-I%s/system_wrappers' % os.path.join(env.dist), + '-include', + '%s/config/gcc_hidden.h' % env.topsrcdir) + +@depends(target, visibility_flags) +def wrap_system_includes(target, visibility_flags): + if visibility_flags and target.kernel != 'Darwin': + return True + +set_define('HAVE_VISIBILITY_HIDDEN_ATTRIBUTE', + depends(visibility_flags)(lambda v: bool(v) or None)) +set_define('HAVE_VISIBILITY_ATTRIBUTE', + depends(visibility_flags)(lambda v: bool(v) or None)) +set_config('WRAP_SYSTEM_INCLUDES', wrap_system_includes) +set_config('VISIBILITY_FLAGS', visibility_flags) + +include('windows.configure') +include('rust.configure') diff --git a/build/moz.configure/util.configure b/build/moz.configure/util.configure new file mode 100644 index 000000000..33ed17de6 --- /dev/null +++ b/build/moz.configure/util.configure @@ -0,0 +1,440 @@ +# -*- Mode: python; 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/. + +@imports('sys') +def die(*args): + 'Print an error and terminate configure.' + log.error(*args) + sys.exit(1) + + +@imports(_from='mozbuild.configure', _import='ConfigureError') +def configure_error(message): + '''Raise a programming error and terminate configure. + Primarily for use in moz.configure templates to sanity check + their inputs from moz.configure usage.''' + raise ConfigureError(message) + +# A wrapper to obtain a process' output that returns the output generated +# by running the given command if it exits normally, and streams that +# output to log.debug and calls die or the given error callback if it +# does not. +@imports('subprocess') +@imports('sys') +@imports(_from='mozbuild.configure.util', _import='LineIO') +@imports(_from='mozbuild.shellutil', _import='quote') +def check_cmd_output(*args, **kwargs): + onerror = kwargs.pop('onerror', None) + + with log.queue_debug(): + log.debug('Executing: `%s`', quote(*args)) + proc = subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + stdout, stderr = proc.communicate() + retcode = proc.wait() + if retcode == 0: + return stdout + + log.debug('The command returned non-zero exit status %d.', + retcode) + for out, desc in ((stdout, 'output'), (stderr, 'error output')): + if out: + log.debug('Its %s was:', desc) + with LineIO(lambda l: log.debug('| %s', l)) as o: + o.write(out) + if onerror: + return onerror() + die('Command `%s` failed with exit status %d.' % + (quote(*args), retcode)) + +@imports('os') +def is_absolute_or_relative(path): + if os.altsep and os.altsep in path: + return True + return os.sep in path + + +@imports(_import='mozpack.path', _as='mozpath') +def normsep(path): + return mozpath.normsep(path) + + +@imports('ctypes') +@imports(_from='ctypes', _import='wintypes') +@imports(_from='mozbuild.configure.constants', _import='WindowsBinaryType') +def windows_binary_type(path): + """Obtain the type of a binary on Windows. + + Returns WindowsBinaryType constant. + """ + GetBinaryTypeW = ctypes.windll.kernel32.GetBinaryTypeW + GetBinaryTypeW.argtypes = [wintypes.LPWSTR, wintypes.POINTER(wintypes.DWORD)] + GetBinaryTypeW.restype = wintypes.BOOL + + bin_type = wintypes.DWORD() + res = GetBinaryTypeW(path, ctypes.byref(bin_type)) + if not res: + die('could not obtain binary type of %s' % path) + + if bin_type.value == 0: + return WindowsBinaryType('win32') + elif bin_type.value == 6: + return WindowsBinaryType('win64') + # If we see another binary type, something is likely horribly wrong. + else: + die('unsupported binary type on %s: %s' % (path, bin_type)) + + +@imports('ctypes') +@imports(_from='ctypes', _import='wintypes') +def get_GetShortPathNameW(): + GetShortPathNameW = ctypes.windll.kernel32.GetShortPathNameW + GetShortPathNameW.argtypes = [wintypes.LPCWSTR, wintypes.LPWSTR, + wintypes.DWORD] + GetShortPathNameW.restype = wintypes.DWORD + return GetShortPathNameW + + +@template +@imports('ctypes') +@imports('platform') +@imports(_from='mozbuild.shellutil', _import='quote') +def normalize_path(): + # Until the build system can properly handle programs that need quoting, + # transform those paths into their short version on Windows (e.g. + # c:\PROGRA~1...). + if platform.system() == 'Windows': + GetShortPathNameW = get_GetShortPathNameW() + + def normalize_path(path): + path = normsep(path) + if quote(path) == path: + return path + size = 0 + while True: + out = ctypes.create_unicode_buffer(size) + needed = GetShortPathNameW(path, out, size) + if size >= needed: + if ' ' in out.value: + die("GetShortPathName returned a long path name. " + "Are 8dot3 filenames disabled?") + return normsep(out.value) + size = needed + + else: + def normalize_path(path): + return normsep(path) + + return normalize_path + +normalize_path = normalize_path() + + +# Locates the given program using which, or returns the given path if it +# exists. +# The `paths` parameter may be passed to search the given paths instead of +# $PATH. +@imports(_from='which', _import='which') +@imports(_from='which', _import='WhichError') +@imports('itertools') +@imports(_from='os', _import='pathsep') +def find_program(file, paths=None): + try: + if is_absolute_or_relative(file): + return normalize_path(which(os.path.basename(file), + [os.path.dirname(file)])) + if paths: + if not isinstance(paths, (list, tuple)): + die("Paths provided to find_program must be a list of strings, " + "not %r", paths) + paths = list(itertools.chain( + *(p.split(pathsep) for p in paths if p))) + return normalize_path(which(file, path=paths)) + except WhichError: + return None + + +@imports('os') +@imports('subprocess') +@imports(_from='mozbuild.configure.util', _import='LineIO') +@imports(_from='tempfile', _import='mkstemp') +def try_invoke_compiler(compiler, language, source, flags=None, onerror=None): + flags = flags or [] + + if not isinstance(flags, (list, tuple)): + die("Flags provided to try_compile must be a list of strings, " + "not %r", paths) + + suffix = { + 'C': '.c', + 'C++': '.cpp', + }[language] + + fd, path = mkstemp(prefix='conftest.', suffix=suffix) + try: + source = source.encode('ascii', 'replace') + + log.debug('Creating `%s` with content:', path) + with LineIO(lambda l: log.debug('| %s', l)) as out: + out.write(source) + + os.write(fd, source) + os.close(fd) + cmd = compiler + list(flags) + [path] + kwargs = {'onerror': onerror} + return check_cmd_output(*cmd, **kwargs) + finally: + os.remove(path) + + +def unique_list(l): + result = [] + for i in l: + if l not in result: + result.append(i) + return result + + +# Get values out of the Windows registry. This function can only be called on +# Windows. +# The `pattern` argument is a string starting with HKEY_ and giving the full +# "path" of the registry key to get the value for, with backslash separators. +# The string can contains wildcards ('*'). +# The result of this functions is an enumerator yielding tuples for each +# match. Each of these tuples contains the key name matching wildcards +# followed by the value. +# +# Examples: +# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\' +# r'Windows Kits\Installed Roots\KitsRoot*') +# yields e.g.: +# ('KitsRoot81', r'C:\Program Files (x86)\Windows Kits\8.1\') +# ('KitsRoot10', r'C:\Program Files (x86)\Windows Kits\10\') +# +# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\' +# r'Windows Kits\Installed Roots\KitsRoot8.1') +# yields e.g.: +# (r'C:\Program Files (x86)\Windows Kits\8.1\',) +# +# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\' +# r'Windows Kits\*\KitsRoot*') +# yields e.g.: +# ('Installed Roots', 'KitsRoot81', +# r'C:\Program Files (x86)\Windows Kits\8.1\') +# ('Installed Roots', 'KitsRoot10', +# r'C:\Program Files (x86)\Windows Kits\10\') +# +# get_registry_values(r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\' +# r'VisualStudio\VC\*\x86\*\Compiler') +# yields e.g.: +# ('19.0', 'arm', r'C:\...\amd64_arm\cl.exe') +# ('19.0', 'x64', r'C:\...\amd64\cl.exe') +# ('19.0', 'x86', r'C:\...\amd64_x86\cl.exe') +@imports(_import='_winreg', _as='winreg') +@imports(_from='__builtin__', _import='WindowsError') +@imports(_from='fnmatch', _import='fnmatch') +def get_registry_values(pattern): + def enum_helper(func, key): + i = 0 + while True: + try: + yield func(key, i) + except WindowsError: + break + i += 1 + + def get_keys(key, pattern): + try: + s = winreg.OpenKey(key, '\\'.join(pattern[:-1])) + except WindowsError: + return + for k in enum_helper(winreg.EnumKey, s): + if fnmatch(k, pattern[-1]): + try: + yield k, winreg.OpenKey(s, k) + except WindowsError: + pass + + def get_values(key, pattern): + try: + s = winreg.OpenKey(key, '\\'.join(pattern[:-1])) + except WindowsError: + return + for k, v, t in enum_helper(winreg.EnumValue, s): + if fnmatch(k, pattern[-1]): + yield k, v + + def split_pattern(pattern): + subpattern = [] + for p in pattern: + subpattern.append(p) + if '*' in p: + yield subpattern + subpattern = [] + if subpattern: + yield subpattern + + pattern = pattern.split('\\') + assert pattern[0].startswith('HKEY_') + keys = [(getattr(winreg, pattern[0]),)] + pattern = list(split_pattern(pattern[1:])) + for i, p in enumerate(pattern): + next_keys = [] + for base_key in keys: + matches = base_key[:-1] + base_key = base_key[-1] + if i == len(pattern) - 1: + want_name = '*' in p[-1] + for name, value in get_values(base_key, p): + yield matches + ((name, value) if want_name else (value,)) + else: + for name, k in get_keys(base_key, p): + next_keys.append(matches + (name, k)) + keys = next_keys + + +@imports(_from='mozbuild.configure.util', _import='Version', _as='_Version') +def Version(v): + 'A version number that can be compared usefully.' + return _Version(v) + +# Denotes a deprecated option. Combines option() and @depends: +# @deprecated_option('--option') +# def option(value): +# ... +# @deprecated_option() takes the same arguments as option(), except `help`. +# The function may handle the option like a typical @depends function would, +# but it is recommended it emits a deprecation error message suggesting an +# alternative option to use if there is one. +@template +def deprecated_option(*args, **kwargs): + assert 'help' not in kwargs + kwargs['help'] = 'Deprecated' + opt = option(*args, **kwargs) + + def decorator(func): + @depends(opt.option) + def deprecated(value): + if value.origin != 'default': + return func(value) + return deprecated + + return decorator + + +# from mozbuild.util import ReadOnlyNamespace as namespace +@imports(_from='mozbuild.util', _import='ReadOnlyNamespace') +def namespace(**kwargs): + return ReadOnlyNamespace(**kwargs) + + +# Turn an object into an object that can be used as an argument to @depends. +# The given object can be a literal value, a function that takes no argument, +# or, for convenience, a @depends function. +@template +@imports(_from='inspect', _import='isfunction') +@imports(_from='mozbuild.configure', _import='SandboxDependsFunction') +def dependable(obj): + if isinstance(obj, SandboxDependsFunction): + return obj + if isfunction(obj): + return depends(when=True)(obj) + return depends(when=True)(lambda: obj) + + +always = dependable(True) +never = dependable(False) + + +# Some @depends function return namespaces, and one could want to use one +# specific attribute from such a namespace as a "value" given to functions +# such as `set_config`. But those functions do not take immediate values. +# The `delayed_getattr` function allows access to attributes from the result +# of a @depends function in a non-immediate manner. +# @depends('--option') +# def option(value) +# return namespace(foo=value) +# set_config('FOO', delayed_getattr(option, 'foo') +@template +def delayed_getattr(func, key): + @depends(func) + def result(value): + # The @depends function we're being passed may have returned + # None, or an object that simply doesn't have the wanted key. + # In that case, just return None. + return getattr(value, key, None) + + return result + + +# Like @depends, but the decorated function is only called if one of the +# arguments it would be called with has a positive value (bool(value) is True) +@template +def depends_if(*args): + def decorator(func): + @depends(*args) + def wrapper(*args): + if any(arg for arg in args): + return func(*args) + return wrapper + return decorator + +# Like @depends_if, but a distinguished value passed as a keyword argument +# "when" is truth tested instead of every argument. This value is not passed +# to the function if it is called. +@template +def depends_when(*args, **kwargs): + if not len(kwargs) == 1 and kwargs.get('when'): + die('depends_when requires a single keyword argument, "when"') + when = kwargs['when'] + if not when: + return depends(*args) + + def decorator(fn): + @depends(when, *args) + def wrapper(val, *args): + if val: + return fn(*args) + return wrapper + return decorator + + +# Hacks related to old-configure +# ============================== + +@dependable +def old_configure_assignments(): + return [] + +@dependable +def extra_old_configure_args(): + return [] + +@template +def add_old_configure_assignment(var, value): + var = dependable(var) + value = dependable(value) + + @depends(old_configure_assignments, var, value) + @imports(_from='mozbuild.shellutil', _import='quote') + def add_assignment(assignments, var, value): + if var is None or value is None: + return + if value is True: + assignments.append('%s=1' % var) + elif value is False: + assignments.append('%s=' % var) + else: + if isinstance(value, (list, tuple)): + value = quote(*value) + assignments.append('%s=%s' % (var, quote(str(value)))) + +@template +def add_old_configure_arg(arg): + @depends(extra_old_configure_args, arg) + def add_arg(args, arg): + if arg: + args.append(arg) diff --git a/build/moz.configure/warnings.configure b/build/moz.configure/warnings.configure new file mode 100644 index 000000000..273a41bd9 --- /dev/null +++ b/build/moz.configure/warnings.configure @@ -0,0 +1,111 @@ +# -*- 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/. + +js_option('--enable-warnings-as-errors', env='MOZ_ENABLE_WARNINGS_AS_ERRORS', + default=depends('MOZ_AUTOMATION', '--help')(lambda x, _: bool(x)), + help='Enable treating warnings as errors') + +add_old_configure_assignment( + 'MOZ_ENABLE_WARNINGS_AS_ERRORS', + depends('--enable-warnings-as-errors')(lambda x: bool(x))) + + +# GCC/Clang warnings: +# https://gcc.gnu.org/onlinedocs/gcc-4.7.2/gcc/Warning-Options.html + +# lots of useful warnings +add_gcc_warning('-Wall') + +# catches C++ version forward-compat issues +add_gcc_warning('-Wc++11-compat', cxx_compiler) + +# catches bugs, e.g. "if (c); foo();", few false positives +add_gcc_warning('-Wempty-body') + +# catches return types with qualifiers like const +add_gcc_warning('-Wignored-qualifiers') + +# function declaration hides virtual function from base class +add_gcc_warning('-Woverloaded-virtual', cxx_compiler) + +# catches pointer arithmetic using NULL or sizeof(void) +add_gcc_warning('-Wpointer-arith') + +# catches comparing signed/unsigned ints +add_gcc_warning('-Wsign-compare') + +# catches overflow bugs, few false positives +add_gcc_warning('-Wtype-limits') + +# catches some dead code +add_gcc_warning('-Wunreachable-code') + +# catches treating string literals as non-const +add_gcc_warning('-Wwrite-strings', cxx_compiler) + +# turned on by -Wall, but we use offsetof on non-POD types frequently +add_gcc_warning('-Wno-invalid-offsetof', cxx_compiler) + +# catches objects passed by value to variadic functions. +check_and_add_gcc_warning('-Wclass-varargs') + +# catches issues around loops +check_and_add_gcc_warning('-Wloop-analysis') + +# catches C++ version forward-compat issues +check_and_add_gcc_warning('-Wc++11-compat-pedantic', cxx_compiler) +check_and_add_gcc_warning('-Wc++14-compat', cxx_compiler) +check_and_add_gcc_warning('-Wc++14-compat-pedantic', cxx_compiler) +check_and_add_gcc_warning('-Wc++1z-compat', cxx_compiler) + +# catches unintentional switch case fallthroughs +check_and_add_gcc_warning('-Wimplicit-fallthrough', cxx_compiler) + +# catches expressions used as a null pointer constant +# XXX: at the time of writing, the version of clang used on the OS X test +# machines has a bug that causes it to reject some valid files if both +# -Wnon-literal-null-conversion and -Wsometimes-uninitialized are +# specified. We work around this by instead using +# -Werror=non-literal-null-conversion, but we only do that when +# --enable-warnings-as-errors is specified so that no unexpected fatal +# warnings are produced. +check_and_add_gcc_warning('-Werror=non-literal-null-conversion', + when='--enable-warnings-as-errors') + +# catches string literals used in boolean expressions +check_and_add_gcc_warning('-Wstring-conversion') + +# catches inconsistent use of mutexes +check_and_add_gcc_warning('-Wthread-safety') + +# we inline 'new' and 'delete' in mozalloc +check_and_add_gcc_warning('-Wno-inline-new-delete', cxx_compiler) + +# Prevent the following GCC warnings from being treated as errors: +# too many false positives +check_and_add_gcc_warning('-Wno-error=maybe-uninitialized') + +# we don't want our builds held hostage when a platform-specific API +# becomes deprecated. +check_and_add_gcc_warning('-Wno-error=deprecated-declarations') + +# false positives depending on optimization +check_and_add_gcc_warning('-Wno-error=array-bounds') + +# can't get rid of those PGO warnings +check_and_add_gcc_warning('-Wno-error=coverage-mismatch', when='MOZ_PGO') + +# false positives during PGO +check_and_add_gcc_warning('-Wno-error=free-nonheap-object', when='MOZ_PGO') + +# We use mix of both POSIX and Win32 printf format across the tree, so format +# warnings are useless on mingw. +check_and_add_gcc_warning('-Wno-format', + when=depends(target)(lambda t: t.kernel == 'WINNT')) + +# Please keep these last in this file +add_old_configure_assignment('_WARNINGS_CFLAGS', warnings_cflags) +add_old_configure_assignment('_WARNINGS_CXXFLAGS', warnings_cxxflags) diff --git a/build/moz.configure/windows.configure b/build/moz.configure/windows.configure new file mode 100644 index 000000000..b9a3898a1 --- /dev/null +++ b/build/moz.configure/windows.configure @@ -0,0 +1,431 @@ +# -*- 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/. + +option('--with-windows-version', nargs=1, default='603', + help='Windows SDK version to target. Win 8.1 (603) is currently' + 'the minimum supported version.') + +@depends(target) +def is_windows(target): + return target.kernel == 'WINNT' + + +@template +def depends_win(*args): + return depends_when(*args, when=is_windows) + + +@depends_win('--with-windows-version') +@imports(_from='__builtin__', _import='ValueError') +def valid_windows_version(value): + if not value: + die('Cannot build with --without-windows-version') + try: + version = int(value[0], 16) + if version in (0x603,): + return version + except ValueError: + pass + + die('Invalid value for --with-windows-version (%s)', value[0]) + + +option(env='WINDOWSSDKDIR', nargs=1, + help='Directory containing the Windows SDK') + +@depends_win('WINDOWSSDKDIR', host) +def windows_sdk_dir(value, host): + if value: + return value + if host.kernel != 'WINNT': + return () + + return tuple(x[1] for x in get_registry_values( + r'HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows Kits\Installed Roots' + r'\KitsRoot*')) + +# The Windows SDK 8.1 and 10 have different layouts. The former has +# $SDK/include/$subdir, while the latter has $SDK/include/$version/$subdir. +# The vcvars* scripts don't actually care about the version, they just take +# the last alphanumerically. +# The $SDK/lib directories always have version subdirectories, but while the +# versions match the one in $SDK/include for SDK 10, it's "winv6.3" for SDK +# 8.1. +@imports('os') +@imports('re') +@imports(_from='__builtin__', _import='sorted') +@imports(_from='__builtin__', _import='WindowsError') +def get_sdk_dirs(sdk, subdir): + def get_dirs_containing(sdk, stem, subdir): + base = os.path.join(sdk, stem) + try: + subdirs = [d for d in os.listdir(base) + if os.path.isdir(os.path.join(base, d))] + except WindowsError: + subdirs = [] + if not subdirs: + return () + if subdir in subdirs: + return (base,) + # At this point, either we have an incomplete or invalid SDK directory, + # or we exclusively have version numbers in subdirs. + return tuple(os.path.join(base, s) for s in subdirs + if os.path.isdir(os.path.join(base, s, subdir))) + + def categorize(dirs): + return {os.path.basename(d): d for d in dirs} + + include_dirs = categorize(get_dirs_containing(sdk, 'include', subdir)) + lib_dirs = categorize(get_dirs_containing(sdk, 'lib', subdir)) + + if 'include' in include_dirs: + include_dirs['winv6.3'] = include_dirs['include'] + del include_dirs['include'] + + valid_versions = sorted(set(include_dirs) & set(lib_dirs), reverse=True) + if valid_versions: + return namespace( + path=sdk, + lib=lib_dirs[valid_versions[0]], + include=include_dirs[valid_versions[0]], + ) + + +@imports(_from='mozbuild.shellutil', _import='quote') +def valid_windows_sdk_dir_result(value): + if value: + return '0x%04x in %s' % (value.version, quote(value.path)) + +@depends_win(c_compiler, windows_sdk_dir, valid_windows_version, + 'WINDOWSSDKDIR') +@checking('for Windows SDK', valid_windows_sdk_dir_result) +@imports(_from='__builtin__', _import='sorted') +@imports(_from='textwrap', _import='dedent') +def valid_windows_sdk_dir(compiler, windows_sdk_dir, target_version, + windows_sdk_dir_env): + if windows_sdk_dir_env: + windows_sdk_dir_env = windows_sdk_dir_env[0] + sdks = {} + for d in windows_sdk_dir: + sdk = get_sdk_dirs(d, 'um') + if sdk: + check = dedent('''\ + #include <winsdkver.h> + WINVER_MAXVER + ''') + um_dir = os.path.join(sdk.include, 'um') + shared_dir = os.path.join(sdk.include, 'shared') + result = try_preprocess(compiler.wrapper + [compiler.compiler] + + compiler.flags + + ['-I', um_dir, '-I', shared_dir], 'C', + check) + if result: + maxver = result.splitlines()[-1] + try: + maxver = int(maxver, 0) + except: + pass + else: + sdks[d] = maxver, sdk + continue + if d == windows_sdk_dir_env: + raise FatalCheckError( + 'Error while checking the version of the SDK in ' + 'WINDOWSSDKDIR (%s). Please verify it contains a valid and ' + 'complete SDK installation.' % windows_sdk_dir_env) + + valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) + if valid_sdks: + biggest_version, sdk = sdks[valid_sdks[0]] + if not valid_sdks or biggest_version < target_version: + if windows_sdk_dir_env: + raise FatalCheckError( + 'You are targeting Windows version 0x%04x, but your SDK only ' + 'supports up to version 0x%04x. Install and use an updated SDK, ' + 'or target a lower version using --with-windows-version. ' + 'Alternatively, try running the Windows SDK Configuration Tool ' + 'and selecting a newer SDK. See ' + 'https://developer.mozilla.org/En/Windows_SDK_versions for ' + 'details on fixing this.' % (target_version, biggest_version)) + + raise FatalCheckError( + 'Cannot find a Windows SDK for version >= 0x%04x.' % target_version) + + return namespace( + path=sdk.path, + include=sdk.include, + lib=sdk.lib, + version=biggest_version, + ) + + +add_old_configure_assignment( + 'WINDOWSSDKDIR', + delayed_getattr(valid_windows_sdk_dir, 'path')) +add_old_configure_assignment( + 'MOZ_WINSDK_MAXVER', + depends(valid_windows_sdk_dir)( + lambda x: '0x%04X0000' % x.version if x else None)) + + +@imports(_from='mozbuild.shellutil', _import='quote') +def valid_ucrt_sdk_dir_result(value): + if value: + return '%s in %s' % (value.version, quote(value.path)) + +@depends_win(windows_sdk_dir, 'WINDOWSSDKDIR') +@checking('for Universal CRT SDK', valid_ucrt_sdk_dir_result) +@imports('os') +@imports(_from='__builtin__', _import='sorted') +@imports(_import='mozpack.path', _as='mozpath') +def valid_ucrt_sdk_dir(windows_sdk_dir, windows_sdk_dir_env): + if windows_sdk_dir_env: + windows_sdk_dir_env = windows_sdk_dir_env[0] + sdks = {} + for d in windows_sdk_dir: + sdk = get_sdk_dirs(d, 'ucrt') + if sdk: + version = os.path.basename(sdk.include) + # We're supposed to always find a version in the directory, because + # the 8.1 SDK, which doesn't have a version in the directory, doesn't + # contain the Universal CRT SDK. When the main SDK is 8.1, there + # is, however, supposed to be a reduced install of the SDK 10 + # with the UCRT. + if version != 'include': + sdks[d] = Version(version), sdk + continue + if d == windows_sdk_dir_env: + # When WINDOWSSDKDIR is set in the environment and we can't find the + # Universal CRT SDK, chances are this is a start-shell-msvc*.bat + # setup, where INCLUDE and LIB already contain the UCRT paths. + ucrt_includes = [ + p for p in os.environ.get('INCLUDE', '').split(os.pathsep) + if os.path.basename(p).lower() == 'ucrt' + ] + ucrt_libs = [ + p for p in os.environ.get('LIB', '').split(os.pathsep) + if os.path.basename(os.path.dirname(p)).lower() == 'ucrt' + ] + if ucrt_includes and ucrt_libs: + # Pick the first of each, since they are the ones that the + # compiler would look first. Assume they contain the SDK files. + include = os.path.dirname(ucrt_includes[0]) + lib = os.path.dirname(os.path.dirname(ucrt_libs[0])) + path = os.path.dirname(os.path.dirname(include)) + version = os.path.basename(include) + if version != 'include' and mozpath.basedir(lib, [path]): + sdks[d] = Version(version), namespace( + path=path, + include=include, + lib=lib, + ) + continue + raise FatalCheckError( + 'The SDK in WINDOWSSDKDIR (%s) does not contain the Universal ' + 'CRT.' % windows_sdk_dir_env) + + valid_sdks = sorted(sdks, key=lambda x: sdks[x][0], reverse=True) + if not valid_sdks: + raise FatalCheckError('Cannot find the Universal CRT SDK. ' + 'Please install it.') + + version, sdk = sdks[valid_sdks[0]] + + return namespace( + path=sdk.path, + include=sdk.include, + lib=sdk.lib, + version=version, + ) + + +@depends_win(c_compiler) +@imports('os') +def vc_path(c_compiler): + if c_compiler.type != 'msvc': + return + # Normally, we'd start from c_compiler.compiler, but for now, it's not the + # ideal full path to the compiler. At least, we're guaranteed find_program + # will get us the one we found in toolchain.configure. + cl = find_program(c_compiler.compiler) + result = os.path.dirname(cl) + while True: + next, p = os.path.split(result) + if next == result: + die('Cannot determine the Visual C++ directory the compiler (%s) ' + 'is in' % cl) + result = next + if p.lower() == 'bin': + break + return result + + +@depends_win(vc_path) +@checking('for the Debug Interface Access SDK', lambda x: x or 'not found') +@imports(_from='os.path', _import='isdir') +def dia_sdk_dir(vc_path): + if vc_path: + path = os.path.join(os.path.dirname(vc_path), 'DIA SDK') + if isdir(path): + return path + + +@depends_win(vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir) +@imports('os') +def include_path(vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir): + if not vc_path: + return + atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'include') + if not os.path.isdir(atlmfc_dir): + die('Cannot find the ATL/MFC headers in the Visual C++ directory (%s). ' + 'Please install them.' % vc_path) + + winrt_dir = os.path.join(windows_sdk_dir.include, 'winrt') + if not os.path.isdir(winrt_dir): + die('Cannot find the WinRT headers in the Windows SDK directory (%s). ' + 'Please install them.' % windows_sdk_dir.path) + + includes = [] + include_env = os.environ.get('INCLUDE') + if include_env: + includes.append(include_env) + includes.extend(( + os.path.join(vc_path, 'include'), + atlmfc_dir, + os.path.join(windows_sdk_dir.include, 'shared'), + os.path.join(windows_sdk_dir.include, 'um'), + winrt_dir, + os.path.join(ucrt_sdk_dir.include, 'ucrt'), + )) + if dia_sdk_dir: + includes.append(os.path.join(dia_sdk_dir, 'include')) + # Set in the environment for old-configure + includes = os.pathsep.join(includes) + os.environ['INCLUDE'] = includes + return includes + +set_config('INCLUDE', include_path) + + +@depends_win(target, vc_path, valid_windows_sdk_dir, valid_ucrt_sdk_dir, dia_sdk_dir) +@imports('os') +def lib_path(target, vc_path, windows_sdk_dir, ucrt_sdk_dir, dia_sdk_dir): + if not vc_path: + return + vc_target = { + 'x86': '', + 'x86_64': 'amd64', + 'arm': 'arm', + }.get(target.cpu) + if vc_target is None: + return + # As vc_target can be '', and os.path.join will happily use the empty + # string, leading to a string ending with a backslash, that Make will + # interpret as a "string continues on next line" indicator, use variable + # args. + vc_target = (vc_target,) if vc_target else () + sdk_target = { + 'x86': 'x86', + 'x86_64': 'x64', + 'arm': 'arm', + }.get(target.cpu) + + atlmfc_dir = os.path.join(vc_path, 'atlmfc', 'lib', *vc_target) + if not os.path.isdir(atlmfc_dir): + die('Cannot find the ATL/MFC libraries in the Visual C++ directory (%s). ' + 'Please install them.' % vc_path) + + + libs = [] + lib_env = os.environ.get('LIB') + if lib_env: + libs.append(lib_env) + libs.extend(( + os.path.join(vc_path, 'lib', *vc_target), + atlmfc_dir, + os.path.join(windows_sdk_dir.lib, 'um', sdk_target), + os.path.join(ucrt_sdk_dir.lib, 'ucrt', sdk_target), + )) + if dia_sdk_dir: + libs.append(os.path.join(dia_sdk_dir, 'lib', *vc_target)) + # Set in the environment for old-configure + libs = os.pathsep.join(libs) + os.environ['LIB'] = libs + return libs + +set_config('LIB', lib_path) + + +option(env='MT', nargs=1, help='Path to the Microsoft Manifest Tool') + +@depends_win(valid_windows_sdk_dir) +@imports(_from='os', _import='environ') +@imports('platform') +def sdk_bin_path(valid_windows_sdk_dir): + if not valid_windows_sdk_dir: + return + + vc_host = { + 'x86': 'x86', + 'AMD64': 'x64', + }.get(platform.machine()) + + result = [ + environ['PATH'], + os.path.join(valid_windows_sdk_dir.path, 'bin', vc_host) + ] + if vc_host == 'x64': + result.append( + os.path.join(valid_windows_sdk_dir.path, 'bin', 'x86')) + return result + + +mt = check_prog('MT', depends_win()(lambda: ('mt.exe',)), input='MT', + paths=sdk_bin_path) + + +# Check that MT is not something unexpected like "magnetic tape manipulation +# utility". +@depends_win(mt) +@checking('whether MT is really Microsoft Manifest Tool', lambda x: bool(x)) +@imports('subprocess') +def valid_mt(path): + try: + out = subprocess.check_output([path]).splitlines() + out = '\n'.join(l for l in out + if 'Microsoft (R) Manifest Tool' in l) + if out: + return path + except subprocess.CalledProcessError: + pass + raise FatalCheckError('%s is not Microsoft Manifest Tool') + + +set_config('MSMANIFEST_TOOL', depends(valid_mt)(lambda x: bool(x))) + + +# Ultimately, this will move to toolchain.configure and be turned into a +# cross-platform check. +option(env='LD', nargs=1, help='Path to the linker') + +link = check_prog('LINK', depends_win()(lambda: ('link.exe',)), input='LD', + paths=vc_compiler_path) + +add_old_configure_assignment('LD', depends_win(link)(lambda x: x)) + + +# Normally, we'd just have CC, etc. set to absolute paths, but the build system +# doesn't currently handle properly the case where the paths contain spaces. +# Additionally, there's the issue described in toolchain.configure, in +# valid_compiler(). +@depends_win(sdk_bin_path) +@imports('os') +def alter_path(sdk_bin_path): + path = os.pathsep.join(sdk_bin_path) + os.environ['PATH'] = path + return path + +set_config('PATH', alter_path) diff --git a/build/mozconfig.automation b/build/mozconfig.automation new file mode 100644 index 000000000..057a4a0b5 --- /dev/null +++ b/build/mozconfig.automation @@ -0,0 +1,33 @@ +# 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/. + +# Common mozconfig for automation builds. +# +# We export MOZ_AUTOMATION_* variables here to trigger various steps in +# automation builds. For example, if MOZ_AUTOMATION_PACKAGE is set, then the +# package step will run. This file contains the default settings, which can be +# overridden by setting them earlier in the appropriate mozconfig. + +mk_add_options "export MOZ_AUTOMATION_BUILD_SYMBOLS=${MOZ_AUTOMATION_BUILD_SYMBOLS-1}" +mk_add_options "export MOZ_AUTOMATION_L10N_CHECK=${MOZ_AUTOMATION_L10N_CHECK-1}" +mk_add_options "export MOZ_AUTOMATION_PACKAGE=${MOZ_AUTOMATION_PACKAGE-1}" +mk_add_options "export MOZ_AUTOMATION_PACKAGE_TESTS=${MOZ_AUTOMATION_PACKAGE_TESTS-1}" +mk_add_options "export MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-0}" +mk_add_options "export MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-0}" +mk_add_options "export MOZ_AUTOMATION_UPLOAD=${MOZ_AUTOMATION_UPLOAD-1}" +mk_add_options "export MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-0}" +mk_add_options "export MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-0}" + +# If we are also building with MOZ_PKG_PRETTYNAMES, set the corresponding +# stages. +if test "$MOZ_AUTOMATION_PRETTY" = "1"; then + mk_add_options "export MOZ_AUTOMATION_PRETTY_PACKAGE=${MOZ_AUTOMATION_PACKAGE-1}" + mk_add_options "export MOZ_AUTOMATION_PRETTY_PACKAGE_TESTS=${MOZ_AUTOMATION_PACKAGE_TESTS-1}" + mk_add_options "export MOZ_AUTOMATION_PRETTY_L10N_CHECK=${MOZ_AUTOMATION_L10N_CHECK-1}" + mk_add_options "export MOZ_AUTOMATION_PRETTY_INSTALLER=${MOZ_AUTOMATION_INSTALLER-0}" + + # Note that we always build the update packaging with pretty names even if + # we don't build it without, so this is set to 1. + mk_add_options "export MOZ_AUTOMATION_PRETTY_UPDATE_PACKAGING=1" +fi diff --git a/build/mozconfig.cache b/build/mozconfig.cache new file mode 100644 index 000000000..be740e293 --- /dev/null +++ b/build/mozconfig.cache @@ -0,0 +1,135 @@ +# 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/. + +# Setup for build cache + +# Avoid duplication if the file happens to be included twice. +if test -z "$bucket" -a -z "$NO_CACHE"; then + +# buildbot (or builders that use buildprops.json): +if [ -f $topsrcdir/../buildprops.json ]; then +read branch platform master <<EOF +$(python2.7 -c 'import json; p = json.loads(open("'"$topsrcdir"'/../buildprops.json").read())["properties"]; print p["branch"], p["platform"], p["master"]' 2> /dev/null) +EOF + +bucket= +if test -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET" -a -z "$MOZ_PGO"; then + case "${branch}" in + try) + case "${master}" in + *scl1.mozilla.com*|*.scl3.mozilla.com*) + bucket=mozilla-releng-s3-cache-us-west-1-try + ;; + *use1.mozilla.com*) + bucket=mozilla-releng-s3-cache-us-east-1-try + ;; + *usw2.mozilla.com*) + bucket=mozilla-releng-s3-cache-us-west-2-try + ;; + esac + ;; + autoland|mozilla-inbound) + case "${master}" in + *use1.mozilla.com*) + bucket=mozilla-releng-s3-cache-us-east-1-prod + ;; + *usw2.mozilla.com*) + bucket=mozilla-releng-s3-cache-us-west-2-prod + ;; + esac + ;; + esac +fi + +# builds without buildprops (eg: taskcluster or non-buildbot) and without ccache env config and without sccache disabled: +elif test -z "$CCACHE_DIR" -a -z "$SCCACHE_DISABLE" -a -z "$no_sccache" -a -z "$MOZ_PGO_IS_SET" -a -z "$MOZ_PGO"; then + + # prevent rerun if az is set, or wget is not available + if test -z "$availability_zone" -a -x "$(command -v wget)"; then + # timeout after 1 second, and don't retry (failure indicates instance is not in ec2 or network issue) + # availability_zone is of the form <region><letter> where region is e.g. us-west-2, and az is us-west-2a + availability_zone=$(wget -T 1 -t 1 -q -O - http://169.254.169.254/latest/meta-data/placement/availability-zone || true) + if test -z "$availability_zone" -o "$availability_zone" = "not-ec2"; then + availability_zone=not-ec2 + else + # region is az with last letter trimmed + region=${availability_zone%?} + # set S3 bucket according to tree (level) + case "${GECKO_HEAD_REPOSITORY}" in + *hg.mozilla.org/try*) + bucket=taskcluster-level-1-sccache-${region} + ;; + *hg.mozilla.org/integration/autoland*|*hg.mozilla.org/integration/mozilla-inbound*) + bucket=taskcluster-level-3-sccache-${region} + ;; + esac + + # set a dummy master + case "${region}" in + eu-central-1) + master=dummy.euc1.mozilla.com + ;; + us-east-1) + master=dummy.use1.mozilla.com + ;; + us-west-1) + master=dummy.usw1.mozilla.com + ;; + us-west-2) + master=dummy.usw2.mozilla.com + ;; + esac + fi + fi +fi + +# if platform hasn't been determined from buildprops, and we're on windows, +# it must be set to prevent adding ac_add_options --with-ccache below +if test -z "$platform"; then + # set platform based on the SYSTEMROOT env var + case "${SYSTEMROOT}" in + *Windows) + platform=windows + ;; + esac +fi + +if test -z "$bucket"; then + case "$platform" in + win*) : ;; + *) + ac_add_options --with-ccache + esac +else + if ! test -e $topsrcdir/sccache/sccache.py; then + echo "Sccache missing in the tooltool manifest" >&2 + exit 1 + fi + mk_add_options "export SCCACHE_BUCKET=$bucket" + case "$master" in + *us[ew][12].mozilla.com*|*euc1.mozilla.com*) + mk_add_options "export SCCACHE_NAMESERVER=169.254.169.253" + ;; + esac + ac_add_options "--with-compiler-wrapper=python2.7 $topsrcdir/sccache/sccache.py" + mk_add_options MOZ_PREFLIGHT_ALL+=build/sccache.mk + mk_add_options MOZ_POSTFLIGHT_ALL+=build/sccache.mk + mk_add_options "UPLOAD_EXTRA_FILES+=sccache.log.gz" + case "$platform" in + win*) + # sccache supports a special flag to create depfiles. + export _DEPEND_CFLAGS='-deps$(MDDEPDIR)/$(@F).pp' + # Windows builds have a default wrapper that needs to be overridden + mk_add_options "export CC_WRAPPER=" + mk_add_options "export CXX_WRAPPER=" + # For now, sccache doesn't support separate PDBs so force debug info to be + # in object files. + mk_add_options "export COMPILE_PDB_FLAG=" + mk_add_options "export HOST_PDB_FLAG=" + mk_add_options "export MOZ_DEBUG_FLAGS=-Z7" + ;; + esac +fi + +fi diff --git a/build/mozconfig.clang-cl b/build/mozconfig.clang-cl new file mode 100644 index 000000000..5fa1007bd --- /dev/null +++ b/build/mozconfig.clang-cl @@ -0,0 +1,7 @@ +CLANG_DIR=`cd "$topsrcdir/clang/bin" ; pwd` +export PATH="${CLANG_DIR}:${PATH}" + +mk_export_correct_style PATH + +export CC=clang-cl +export CXX=clang-cl diff --git a/build/mozconfig.common b/build/mozconfig.common new file mode 100644 index 000000000..3d2d0b289 --- /dev/null +++ b/build/mozconfig.common @@ -0,0 +1,26 @@ +# 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/. + +# Common mozconfig for official builds. +# +# Add options to this file that will be inherited by all in-tree mozconfigs. +# This is useful for eg try builds with nondefault options that apply to all +# architectures, though note that if you want to override options set in +# another mozconfig file, you'll need to use mozconfig.common.override instead +# of this file. + +mk_add_options AUTOCLOBBER=1 + +ac_add_options --enable-crashreporter + +ac_add_options --enable-release + +# Disable checking that add-ons are signed by the trusted root +MOZ_ADDON_SIGNING=${MOZ_ADDON_SIGNING-0} +# Disable enforcing that add-ons are signed by the trusted root +MOZ_REQUIRE_SIGNING=${MOZ_REQUIRE_SIGNING-0} + +ac_add_options --enable-js-shell + +. "$topsrcdir/build/mozconfig.automation" diff --git a/build/mozconfig.common.override b/build/mozconfig.common.override new file mode 100644 index 000000000..7285aa9dd --- /dev/null +++ b/build/mozconfig.common.override @@ -0,0 +1,11 @@ +# 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/. + +# Common mozconfig for all users +# +# Add options to this file that will be inherited by all in-tree mozconfigs. +# This file is included at the *end* of the mozconfigs, and so may be used +# to override anything done previously. +# +# The common expected usage is for try builds with nondefault options. diff --git a/build/mozconfig.rust b/build/mozconfig.rust new file mode 100644 index 000000000..65177a6bd --- /dev/null +++ b/build/mozconfig.rust @@ -0,0 +1,10 @@ +# Options to enable rust in automation builds. + +# Tell configure to use the tooltool rustc. +# Assume this is compiled with --enable-rpath so we don't +# have to set LD_LIBRARY_PATH. +RUSTC="$topsrcdir/rustc/bin/rustc" +CARGO="$topsrcdir/cargo/bin/cargo" + +# Enable rust in the build. +ac_add_options --enable-rust diff --git a/build/mozconfig.vs-common b/build/mozconfig.vs-common new file mode 100644 index 000000000..ca5df2f3a --- /dev/null +++ b/build/mozconfig.vs-common @@ -0,0 +1,4 @@ +# Pymake needs Windows-style paths. Use cmd.exe to hack around this.
+mk_export_correct_style() {
+ mk_add_options "export $1=$(cmd.exe //c echo %$1%)"
+}
diff --git a/build/mozconfig.win-common b/build/mozconfig.win-common new file mode 100644 index 000000000..6e25b7ce4 --- /dev/null +++ b/build/mozconfig.win-common @@ -0,0 +1,16 @@ +# 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/. + +if [ "x$IS_NIGHTLY" = "xyes" ]; then + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} +fi + +# Some builds (eg: Mulet) don't want the installer, so only set this if it +# hasn't already been set. +MOZ_AUTOMATION_INSTALLER=${MOZ_AUTOMATION_INSTALLER-1} + +export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=c:/builds/crash-stats-api.token diff --git a/build/package/mac_osx/make-diskimage b/build/package/mac_osx/make-diskimage new file mode 100755 index 000000000..c214ceb59 --- /dev/null +++ b/build/package/mac_osx/make-diskimage @@ -0,0 +1,47 @@ +#!/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/. + +# Create a read-only disk image of the contents of a folder +# +# Usage: make-diskimage <image_file> +# <src_folder> +# <volume_name> +# <eula_resource_file> +# <.dsstore_file> +# <background_image_file> +# +# tip: use '-null-' for <eula-resource-file> if you only want to +# provide <.dsstore_file> and <background_image_file> + +DMG_PATH=$1 +SRC_FOLDER=$2 +VOLUME_NAME=$3 + +# optional arguments +EULA_RSRC=$4 +DMG_DSSTORE=$5 +DMG_BKGND_IMG=$6 + +EXTRA_ARGS= + +if test -n "$EULA_RSRC" && test "$EULA_RSRC" != "-null-" ; then + EXTRA_ARGS="--resource $EULA_RSRC" +fi + +if test -n "$DMG_DSSTORE" ; then + EXTRA_ARGS="$EXTRA_ARGS --copy $DMG_DSSTORE:/.DS_Store" +fi + +if test -n "$DMG_BKGND_IMG" ; then + EXTRA_ARGS="$EXTRA_ARGS --mkdir /.background --copy $DMG_BKGND_IMG:/.background" +fi + +echo `dirname $0`/pkg-dmg --target "$DMG_PATH" --source "$SRC_FOLDER" \ + --volname "$VOLUME_NAME" $EXTRA_ARGS + +`dirname $0`/pkg-dmg --target "$DMG_PATH" --source "$SRC_FOLDER" \ + --volname "$VOLUME_NAME" $EXTRA_ARGS + +exit $? diff --git a/build/package/mac_osx/mozilla-background.jpg b/build/package/mac_osx/mozilla-background.jpg Binary files differnew file mode 100644 index 000000000..adb4df036 --- /dev/null +++ b/build/package/mac_osx/mozilla-background.jpg diff --git a/build/package/mac_osx/mozilla.dsstore b/build/package/mac_osx/mozilla.dsstore Binary files differnew file mode 100644 index 000000000..520eb08d6 --- /dev/null +++ b/build/package/mac_osx/mozilla.dsstore diff --git a/build/package/mac_osx/unpack-diskimage b/build/package/mac_osx/unpack-diskimage new file mode 100755 index 000000000..3ba977805 --- /dev/null +++ b/build/package/mac_osx/unpack-diskimage @@ -0,0 +1,54 @@ +#!/bin/bash +# 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/. + +# Unpack a disk image to a specified target folder +# +# Usage: unpack-diskimage <image_file> +# <mountpoint> +# <target_path> + +DMG_PATH=$1 +MOUNTPOINT=$2 +TARGETPATH=$3 + +# How long to wait before giving up waiting for the mount to finish (seconds) +TIMEOUT=90 + +# If mnt already exists, then the previous run may not have cleaned up +# properly. We should try to umount and remove the mnt directory. +if [ -d $MOUNTPOINT ]; then + echo "mnt already exists, trying to clean up" + hdiutil detach $MOUNTPOINT -force + rm -rdfv $MOUNTPOINT +fi + +# Install an on-exit handler that will unmount and remove the '$MOUNTPOINT' directory +trap "{ if [ -d $MOUNTPOINT ]; then hdiutil detach $MOUNTPOINT -force; rm -rdfv $MOUNTPOINT; fi; }" EXIT + +mkdir -p $MOUNTPOINT + +hdiutil attach -verbose -noautoopen -mountpoint $MOUNTPOINT "$DMG_PATH" +# Wait for files to show up +# hdiutil uses a helper process, diskimages-helper, which isn't always done its +# work by the time hdiutil exits. So we wait until something shows up in the +# mnt directory. Due to the async nature of diskimages-helper, the best thing +# we can do is to make sure the glob() rsync is making can find files. +i=0 +while [ "$(echo $MOUNTPOINT/*)" == "$MOUNTPOINT/*" ]; do + if [ $i -gt $TIMEOUT ]; then + echo "No files found, exiting" + exit 1 + fi + sleep 1 + i=$(expr $i + 1) +done +# Now we can copy everything out of the $MOUNTPOINT directory into the target directory +rsync -av $MOUNTPOINT/* $MOUNTPOINT/.DS_Store $MOUNTPOINT/.background $MOUNTPOINT/.VolumeIcon.icns $TARGETPATH/. +hdiutil detach $MOUNTPOINT +rm -rdf $MOUNTPOINT +# diskimage-helper prints messages to stdout asynchronously as well, sleep +# for a bit to ensure they don't disturb following commands in a script that +# might parse stdout messages +sleep 5 diff --git a/build/pgo/blueprint/LICENSE b/build/pgo/blueprint/LICENSE new file mode 100644 index 000000000..d7474100a --- /dev/null +++ b/build/pgo/blueprint/LICENSE @@ -0,0 +1,314 @@ +Blueprint CSS Framework License +---------------------------------------------------------------- + +Copyright (c) 2007-2008 Olav Bjorkoy (olav at bjorkoy.com) + +The Blueprint CSS Framework is available for use in all personal or +commercial projects, under both the (modified) MIT and the GPL license. You +may choose the one that fits your project. + + +The (modified) MIT License +---------------------------------------------------------------- + +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, sub-license, 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 every other copyright notice found in this +software, and all the attributions in every file, 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 NON-INFRINGEMENT. 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. + + +The GPL License +---------------------------------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES.
\ No newline at end of file diff --git a/build/pgo/blueprint/elements.html b/build/pgo/blueprint/elements.html new file mode 100644 index 000000000..51d79fae4 --- /dev/null +++ b/build/pgo/blueprint/elements.html @@ -0,0 +1,250 @@ +<!-- 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/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint HTML Elements Tests</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> + +</head> +<body> + + <div class="container showgrid"> + <h2>Tests for common HTML elements</h2> + <hr> + + <h5>PARAGRAPHS <span class="alt">&</span> BOXES</h5> + + <div class="span-8"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor <sub>sub text</sub> ut labore et <sup>sup text</sup> magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + + <div class="span-8"> + <p class="small">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + <p class="large">Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + + <div class="span-8 last"> + + <div class="box"> + <p class="last">Aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + <blockquote> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</p> + </blockquote> + + </div> + <hr> + + <h5>LISTS</h5> + + <div class="span-8"> + <ul> + <li>Unordered list test</li> + <li>Another list element. Lorem ipsum dolor sit amet, consectetur adipisicing elit.</li> + <li>Yet another element in the list</li> + <li>Some long text. Lorem ipsum dolor sit amet, consectetur adipisicing elit. Lorem ipsum dolor sit amet, consectetur adipisicing elit.</li> + </ul> + <ol> + <li>Ordered list test</li> + <li>Another list element</li> + <li>Yet another element in the list</li> + </ol> + </div> + + <div class="span-8"> + <ol> + <li>Ordered list</li> + <li>Here's a nested unordered list + <ul> + <li>Nested Unordered list</li> + <li>Nested ordered list + <ol> + <li>The first</li> + <li>And the second</li> + </ol> + </li> + </ul> + </li> + <li>Ordered List item</li> + <li>Nested Ordered list + <ol> + <li>Some point</li> + <li>Nested Unordered list + <ul> + <li>The first</li> + <li>And the second</li> + </ul> + </li> + </ol> + </li> + </ol> + </div> + + <div class="span-8 last"> + <dl> + <dt>definition list dt</dt> + <dd>definition list dd</dd> + <dt>definition list dt</dt> + <dd>definition list dd</dd> + <dt>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dt> + <dd>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dd> + <dt>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dt> + <dd>Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</dd> + </dl> + </div> + <hr> + + <h5>HEADINGS</h5> + + <div class="span-8"> + <h1>H1: Lorem ipsum dolor sit amet</h1> + <h2>H2: Lorem ipsum dolor sit amet, consectetur elit</h2> + <h3>H3: Lorem ipsum dolor sit amet, consectetur adipisicing elit</h3> + <h4>H4: Lorem ipsum dolor sit amet, consectetur adipisicing elit adipis</h4> + <h5>H5: Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</h5> + <h6>H6: Lorem ipsum dolor sit amet, consectetur adipisicing elit adipisicing elit adipisicing elit</h6> + </div> + + <div class="span-8"> + <h1>Heading 1</h1><hr> + <h2>Heading 2</h2><hr> + <h3>Heading 3</h3><hr> + <h4>Heading 4</h4><hr> + <h5>Heading 5</h5><hr> + <h6>Heading 6</h6> + </div> + + <div class="span-8 last"> + <h1>Heading 1</h1> + <h2>Heading 2</h2> + <h3>Heading 3</h3> + <h4>Heading 4</h4> + <h5>Heading 5</h5> + <h6>Heading 6</h6> + </div> + <hr> + + <h5>MISC ELEMENTS</h5> + + <div class="span-8"> + <p> + <strong><strong></strong><br> + <del><del> deleted</del><br> + <dfn><dfn> dfn</dfn><br> + <em><em> emphasis</em> + </p> + <p> + <a><a> anchor</a><br> + <a href="http://www.google.com"><a> a + href</a> + </p> + <p> + <abbr title="extended abbr text should show when mouse over"><abbr> abbr - extended text when mouseover.</abbr><br> + <acronym title="extended acronym text should show when mouse over"><acronym> acronym - extended text when mouseover.</acronym> + </p> + <address> + <address><br> + Donald Duck<br> + Box 555<br> + Disneyland + </address> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore dolore.</p> + </div> + + <div class="span-8"> + <table summary="This is the summary text for this table." border="0" cellspacing="0" cellpadding="0"> + <caption><em>A standard test table with a caption, tr, td elements</em></caption> + <tr> + <th class="span-4">Table Header One</th> + <th class="span-4 last">Table Header Two</th> + </tr> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + <tr> + <td colspan="2">TD colspan 2</td> + </tr> + </table> + + <table summary="This is the summary text for this table." border="0" cellspacing="0" cellpadding="0"> + <caption><em>A test table with a thead, tfoot, and tbody elements</em></caption> + <thead> + <tr> + <th class="span-4">Table Header One</th> + <th class="span-4 last">Table Header Two</th> + </tr> + </thead> + <tfoot> + <tr> + <td colspan="2">tfoot footer</td> + </tr> + </tfoot> + <tbody> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + </tbody> + <tbody> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + <tr> + <td>TD One</td> + <td>TD Two</td> + </tr> + </tbody> + </table> + </div> + + <div class="span-8 last"> + +<pre><pre> +pre space1 +pre space1 +pre space2 +pre space2 +pre tab +pre tab</pre> + +<code><code> +Not indented + indent1 + indent1 + indent2 + indent3</code> + + <tt><tt> + This tt text should be monospaced + and + wrap as if + one line of text + even though the code has newlines, spaces, and tabs. + It should be the same size as <p> text. + </tt> + </div> + <hr> + + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="top"></a></p> + + </div> +</body> +</html> diff --git a/build/pgo/blueprint/fancytype-screen.css b/build/pgo/blueprint/fancytype-screen.css new file mode 100644 index 000000000..0d3feb77f --- /dev/null +++ b/build/pgo/blueprint/fancytype-screen.css @@ -0,0 +1,75 @@ +/* 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/. */ + +/* -------------------------------------------------------------- + + fancy-type.css + * Lots of pretty advanced classes for manipulating text. + + See the Readme file in this folder for additional instructions. + +-------------------------------------------------------------- */ + +/* Indentation instead of line shifts for sibling paragraphs. */ + p + p { text-indent:2em; margin-top:-1.5em; } + form p + p { text-indent: 0; } /* Don't want this in forms. */ + + +/* For great looking type, use this code instead of asdf: + <span class="alt">asdf</span> + Best used on prepositions and ampersands. */ + +.alt { + color: #666; + font-family: "Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua", Georgia, serif; + font-style: italic; + font-weight: normal; +} + + +/* For great looking quote marks in titles, replace "asdf" with: + <span class="dquo">“</span>asdf” + (That is, when the title starts with a quote mark). + (You may have to change this value depending on your font size). */ + +.dquo { margin-left: -.5em; } + + +/* Reduced size type with incremental leading + (http://www.markboulton.co.uk/journal/comments/incremental_leading/) + + This could be used for side notes. For smaller type, you don't necessarily want to + follow the 1.5x vertical rhythm -- the line-height is too much. + + Using this class, it reduces your font size and line-height so that for + every four lines of normal sized type, there is five lines of the sidenote. eg: + + New type size in em's: + 10px (wanted side note size) / 12px (existing base size) = 0.8333 (new type size in ems) + + New line-height value: + 12px x 1.5 = 18px (old line-height) + 18px x 4 = 72px + 72px / 5 = 14.4px (new line height) + 14.4px / 10px = 1.44 (new line height in em's) */ + +p.incr, .incr p { + font-size: 10px; + line-height: 1.44em; + margin-bottom: 1.5em; +} + + +/* Surround uppercase words and abbreviations with this class. + Based on work by Jørgen Arnor Gårdsø Lom [http://twistedintellect.com/] */ + +.caps { + font-variant: small-caps; + letter-spacing: 1px; + text-transform: lowercase; + font-size:1.2em; + line-height:1%; + font-weight:bold; + padding:0 2px; +} diff --git a/build/pgo/blueprint/forms.html b/build/pgo/blueprint/forms.html new file mode 100644 index 000000000..8310ba4d4 --- /dev/null +++ b/build/pgo/blueprint/forms.html @@ -0,0 +1,104 @@ +<!-- 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/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint Forms Tests</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> +</head> +<body> + + <div class="container showgrid"> + <h1>Forms</h1> + <hr> + + <div class="span-12"> + + <form id="dummy" action="" method="post"> + + <fieldset> + <legend>Simple sample form</legend> + + <p><label for="dummy0">Text input (title)</label><br> + <input type="text" class="title" name="dummy0" id="dummy0" value="Field with class .title"></p> + + <p><label for="dummy1">Another field</label><br> + <input type="text" class="text" id="dummy1" name="dummy1" value="Field with class .text"></p> + + <p><label for="dummy2">Textarea</label><br> + <textarea name="dummy2" id="dummy2" rows="5" cols="20"></textarea></p> + + <p><input type="submit" value="Submit"> + <input type="reset" value="Reset"></p> + + </fieldset> + </form> + + </div> + <div class="span-12 last"> + + <div class="error"> + This is a <div> with the class <strong>.error</strong>. <a href="#">Link</a>. + </div> + <div class="notice"> + This is a <div> with the class <strong>.notice</strong>. <a href="#">Link</a>. + </div> + <div class="success"> + This is a <div> with the class <strong>.success</strong>. <a href="#">Link</a>. + </div> + + <fieldset> + <legend>Select, checkboxes, lists</legend> + + <p><label for="dummy3">Select field</label><br> + <select id="dummy3" name="dummy3"> + <option value="1">Ottawa</option> + <option value="2">Calgary</option> + <option value="3">Moosejaw</option> + </select></p> + + <p><label for="dummy4">Select with groups</label><br> + <select id="dummy4" name="dummy4"> + <option>Favorite pet</option> + <optgroup label="mammals"> + <option>dog</option> + <option>cat</option> + <option>rabbit</option> + <option>horse</option> + </optgroup> + <optgroup label="reptiles"> + <option>iguana</option> + <option>snake</option> + </optgroup> + </select></p> + + <p><label>Radio buttons</label><br> + <input type="radio" name="example"> Radio one<br> + <input type="radio" name="example"> Radio two<br> + <input type="radio" name="example"> Radio three<br></p> + + <p><label>Checkboxes</label><br> + <input type="checkbox"> Check one<br> + <input type="checkbox"> Check two<br> + <input type="checkbox"> Check three<br></p> + + </fieldset> + + </div> + <hr> + + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="top"></a></p> + + </div> +</body> +</html> diff --git a/build/pgo/blueprint/grid.html b/build/pgo/blueprint/grid.html new file mode 100644 index 000000000..e851a6313 --- /dev/null +++ b/build/pgo/blueprint/grid.html @@ -0,0 +1,210 @@ +<!-- 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/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint Grid Tests</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> +</head> +<body> + + <div class="container showgrid"> + <h1>Blueprint Tests: grid.css</h1> + + + <div class="span-8"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-8"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-8 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + + + <div class="span-6 append-1"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 append-2"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 append-3 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + + + <div class="span-6 prepend-1"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 prepend-2"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <div class="span-6 prepend-3 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p> + </div> + <hr> + + <div class="span-12 border"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + <div class="span-12 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + <hr> + + <div class="span-1 prepend-1"><p>1</p></div> + <div class="span-1 prepend-2"><p>2</p></div> + <div class="span-1 prepend-3"><p>3</p></div> + <div class="span-1 prepend-4"><p>4</p></div> + <div class="span-1 prepend-5"><p>5</p></div> + <div class="span-1 prepend-3 last"><p>3</p></div> + + <div class="span-1 append-1"><p>1</p></div> + <div class="span-1 append-2"><p>2</p></div> + <div class="span-1 append-3"><p>3</p></div> + <div class="span-1 append-4"><p>4</p></div> + <div class="span-1 append-5"><p>5</p></div> + <div class="span-1 append-3 last"><p>3</p></div> + + <div class="span-1 border"><p>1</p></div> + <div class="span-1 border"><p>2</p></div> + <div class="span-1 border"><p>3</p></div> + <div class="span-1 border"><p>4</p></div> + <div class="span-1 border"><p>5</p></div> + <div class="span-1 border"><p>6</p></div> + <div class="span-1 border"><p>7</p></div> + <div class="span-1 border"><p>8</p></div> + <div class="span-1 border"><p>9</p></div> + <div class="span-1 border"><p>10</p></div> + <div class="span-1 border"><p>11</p></div> + <div class="span-1 border"><p>12</p></div> + <div class="span-1 border"><p>13</p></div> + <div class="span-1 border"><p>14</p></div> + <div class="span-1 border"><p>15</p></div> + <div class="span-1 border"><p>16</p></div> + <div class="span-1 border"><p>17</p></div> + <div class="span-1 border"><p>18</p></div> + <div class="span-1 border"><p>19</p></div> + <div class="span-1 border"><p>20</p></div> + <div class="span-1 border"><p>21</p></div> + <div class="span-1 border"><p>22</p></div> + <div class="span-1 border"><p>23</p></div> + <div class="span-1 last"><p>24</p></div> + + + <div class="span-4"><p>1</p></div> + <div class="span-4"><p>2</p></div> + <div class="span-4"><p>3</p></div> + <div class="span-4"><p>4</p></div> + <div class="span-4"><p>5</p></div> + <div class="span-4 last"><p>6</p></div> + + + <div class="prepend-23 span-1 last"><p>24</p></div> + + + <div class="prepend-1 span-1"><p>2</p></div> + <div class="prepend-20 span-1 append-1 last"><p>23</p></div> + <hr> + + <div class="span-24"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p> + </div> + + + <div class="span-12"> + <div class="span-6"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod.</p> + </div> + + <div class="span-6 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + + <div class="span-12 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + </div> + + <div class="span-12 last"> + <div class="span-6"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod.</p> + </div> + + <div class="span-6 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + + <div class="span-12 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + </div> + + + <div class="span-14 prepend-5 append-5 last"> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p> + </div> + <hr> + + <div class="span-12"> + <h5>TESTING .PUSH-1 TO .PUSH-5</h5> + + <div class="span-2"><img src="test-small.jpg" class="push-1"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-2"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-3"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-4"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + <div class="span-2"><img src="test-small.jpg" class="push-5"></div> + <div class="span-10 last"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + + </div> + + <div class="span-12 last"> + <h5>TESTING .PULL-1 TO .PULL-5</h5> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-1"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-2"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-3"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-4"></div> + + <div class="span-10"><p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.</p></div> + <div class="span-2 last"><img src="test-small.jpg" class="top pull-5"></div> + + </div> + + + + + + <div class="span-24"> + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="bottom"></a></p> + </div> + + </div> +</body> +</html> diff --git a/build/pgo/blueprint/grid.png b/build/pgo/blueprint/grid.png Binary files differnew file mode 100644 index 000000000..129d4a29f --- /dev/null +++ b/build/pgo/blueprint/grid.png diff --git a/build/pgo/blueprint/print.css b/build/pgo/blueprint/print.css new file mode 100644 index 000000000..661861457 --- /dev/null +++ b/build/pgo/blueprint/print.css @@ -0,0 +1,29 @@ +/* ----------------------------------------------------------------------- + + Blueprint CSS Framework 0.7.1 + http://blueprintcss.googlecode.com + + * Copyright (c) 2007-2008. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* print.css */ +body {line-height:1.5;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;color:#000;background:none;font-size:10pt;} +.container {background:none;} +hr {background:#ccc;color:#ccc;width:100%;height:2px;margin:2em 0;padding:0;border:none;} +hr.space {background:#fff;color:#fff;} +h1, h2, h3, h4, h5, h6 {font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;} +code {font:.9em "Courier New", Monaco, Courier, monospace;} +img {float:left;margin:1.5em 1.5em 1.5em 0;} +a img {border:none;} +p img.top {margin-top:0;} +blockquote {margin:1.5em;padding:1em;font-style:italic;font-size:.9em;} +.small {font-size:.9em;} +.large {font-size:1.1em;} +.quiet {color:#999;} +.hide {display:none;} +a:link, a:visited {background:transparent;font-weight:700;text-decoration:underline;} +a:link:after, a:visited:after {content:" (" attr(href) ") ";font-size:90%;}
\ No newline at end of file diff --git a/build/pgo/blueprint/sample.html b/build/pgo/blueprint/sample.html new file mode 100644 index 000000000..d2c4dfeb8 --- /dev/null +++ b/build/pgo/blueprint/sample.html @@ -0,0 +1,91 @@ +<!-- 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/. --> + +<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" + "http://www.w3.org/TR/html4/strict.dtd"> + +<html lang="en"> +<head> + <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> + <title>Blueprint Sample Page</title> + + <!-- Framework CSS --> + <link rel="stylesheet" href="screen.css" type="text/css" media="screen, projection"> + <link rel="stylesheet" href="print.css" type="text/css" media="print"> + <!--[if IE]><link rel="stylesheet" href="ie.css" type="text/css" media="screen, projection"><![endif]--> + + <!-- Import fancy-type plugin for the sample page. --> + <link rel="stylesheet" href="fancytype-screen.css" type="text/css" media="screen, projection"> +</head> + +<body> + + <div class="container"> + <h1>A simple sample page</h1> + <hr> + <h2 class="alt">This sample page demonstrates a tiny fraction of what you get with Blueprint.</h2> + <hr> + + <div class="span-7 colborder"> + <h6>Here's a box</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p> + </div> + + <div class="span-8 colborder"> + <h6>And another box</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat laboris nisi ut aliquip.</p> + </div> + + <div class="span-7 last"> + <h6>This box is aligned with the sidebar</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip.</p> + </div> + <hr> + <hr class="space"> + + <div class="span-15 prepend-1 colborder"> + <p><img src="test.jpg" class="top pull-1" alt="test">Lorem ipsum dolor sit amet, <em>consectetuer adipiscing elit</em>. Nunc congue ipsum vestibulum libero. Aenean vitae justo. Nam eget tellus. Etiam convallis, est eu lobortis mattis, lectus tellus tempus felis, a ultricies erat ipsum at metus.</p> + <p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit. <a href="#">Morbi et risus</a>. Aliquam nisl. Nulla facilisi. Cras accumsan vestibulum ante. Vestibulum sed tortor. Praesent <span class="caps">SMALL CAPS</span> tempus fringilla elit. Ut elit diam, sagittis in, nonummy in, gravida non, nunc. Ut orci. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Nam egestas, orci eu imperdiet malesuada, nisl purus fringilla odio, quis commodo est orci vitae justo. Aliquam placerat odio tincidunt nulla. Cras in libero. Aenean rutrum, magna non tristique posuere, erat odio eleifend nisl, non convallis est tortor blandit ligula. Nulla id augue.</p> + <p>Nullam mattis, odio ut tempus facilisis, metus nisl facilisis metus, auctor consectetuer felis ligula nec mauris. Vestibulum odio erat, fermentum at, commodo vitae, ultrices et, urna. Mauris vulputate, mi pulvinar sagittis condimentum, sem nulla aliquam velit, sed imperdiet mi purus eu magna. Nulla varius metus ut eros. Aenean aliquet magna eget orci. Class aptent taciti sociosqu ad litora.</p> + <p>Vivamus euismod. Cum sociis natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Suspendisse vel nibh ut turpis dictum sagittis. Aliquam vel velit a elit auctor sollicitudin. Nam vel dui vel neque lacinia pretium. Quisque nunc erat, venenatis id, volutpat ut, scelerisque sed, diam. Mauris ante. Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Donec mattis. Morbi dignissim sollicitudin libero. Nulla lorem.</p> + <blockquote> + <p>Integer cursus ornare mauris. Praesent nisl arcu, imperdiet eu, ornare id, scelerisque ut, nunc. Praesent sagittis erat sed velit tempus imperdiet. Ut tristique, ante in interdum hendrerit, erat enim faucibus felis, quis rutrum mauris lorem quis sem. Vestibulum ligula nisi, mattis nec, posuere et, blandit eu, ligula. Nam suscipit placerat odio. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos hymenaeos. Pellentesque tortor libero, venenatis vitae, rhoncus eu, placerat ut, mi. Nulla nulla.</p> + </blockquote> + <p>Maecenas vel metus quis magna pharetra fermentum. <em>Integer sit amet tortor</em>. Maecenas porttitor, pede sed gravida auctor, nulla augue aliquet elit, at pretium urna orci ut metus. Aliquam in dolor. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Sed aliquam, tellus id ornare posuere, quam nunc accumsan turpis, at convallis tellus orci et nisl. Phasellus congue neque a lorem.</p> + + <hr> + <div class="span-7 colborder"> + <h6>This is a nested column</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> + </div> + <div class="span-7 last"> + <h6>This is another nested column</h6> + <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.</p> + </div> + + </div> + <div class="span-7 last"> + + <h3>A <span class="alt">Simple</span> Sidebar</h3> + + <p>Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue.</p> + <p>Mauris a lectus. Aliquam erat volutpat. Phasellus ultrices mi a sapien. Nunc rutrum egestas lorem. Duis ac sem sagittis elit tincidunt gravida. Mauris a lectus. Aliquam erat volutpat. Phasellus ultrices mi a sapien. Nunc rutrum egestas lorem. Duis ac sem sagittis elit tincidunt gravida.</p> + <p class="quiet">Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue.</p> + + <h5>Incremental leading</h5> + <p class="incr">Vestibulum ante ipsum primis in faucibus orci luctus vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. sed aliquet vehicula, lectus tellus.</p> + <p class="incr">Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. ipsum primis in faucibus orci luctus et ultrices posuere cubilia Curae; Cras ornare mattis nunc. Mauris venenatis, pede sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue. sed aliquet vehicula, lectus tellus pulvinar neque, non cursus sem nisi vel augue.</p> + + </div> + + <hr> + <h2 class="alt">You may pick and choose amongst these and many more features, so be bold.</h2> + <hr> + + <p><a href="http://validator.w3.org/check?uri=referer"> + <img src="valid.png" alt="Valid HTML 4.01 Strict" height="31" width="88" class="top"></a></p> + </div> + +</body> +</html> diff --git a/build/pgo/blueprint/screen.css b/build/pgo/blueprint/screen.css new file mode 100644 index 000000000..c631ead6a --- /dev/null +++ b/build/pgo/blueprint/screen.css @@ -0,0 +1,226 @@ +/* ----------------------------------------------------------------------- + + Blueprint CSS Framework 0.7.1 + http://blueprintcss.googlecode.com + + * Copyright (c) 2007-2008. See LICENSE for more info. + * See README for instructions on how to use Blueprint. + * For credits and origins, see AUTHORS. + * This is a compressed file. See the sources in the 'src' directory. + +----------------------------------------------------------------------- */ + +/* reset.css */ +html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} +body {line-height:1.5;} +table {border-collapse:separate;border-spacing:0;} +caption, th, td {text-align:left;font-weight:normal;} +table, td, th {vertical-align:middle;} +blockquote:before, blockquote:after, q:before, q:after {content:"";} +blockquote, q {quotes:"" "";} +a img {border:none;} + +/* typography.css */ +body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;} +h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} +h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} +h2 {font-size:2em;margin-bottom:0.75em;} +h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} +h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;height:1.25em;} +h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} +h6 {font-size:1em;font-weight:bold;} +h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} +p {margin:0 0 1.5em;} +p img {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;} +p img.right {float:right;margin:1.5em 0 1.5em 1.5em;} +a:focus, a:hover {color:#000;} +a {color:#009;text-decoration:underline;} +blockquote {margin:1.5em;color:#666;font-style:italic;} +strong {font-weight:bold;} +em, dfn {font-style:italic;} +dfn {font-weight:bold;} +sup, sub {line-height:0;} +abbr, acronym {border-bottom:1px dotted #666;} +address {margin:0 0 1.5em;font-style:italic;} +del {color:#666;} +pre, code {margin:1.5em 0;white-space:pre;} +pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} +li ul, li ol {margin:0 1.5em;} +ul, ol {margin:0 1.5em 1.5em 1.5em;} +ul {list-style-type:disc;} +ol {list-style-type:decimal;} +dl {margin:0 0 1.5em 0;} +dl dt {font-weight:bold;} +dd {margin-left:1.5em;} +table {margin-bottom:1.4em;width:100%;} +th {font-weight:bold;background:#C3D9FF;} +th, td {padding:4px 10px 4px 5px;} +tr.even td {background:#E5ECF9;} +tfoot {font-style:italic;} +caption {background:#eee;} +.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} +.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} +.hide {display:none;} +.quiet {color:#666;} +.loud {color:#000;} +.highlight {background:#ff0;} +.added {background:#060;color:#fff;} +.removed {background:#900;color:#fff;} +.first {margin-left:0;padding-left:0;} +.last {margin-right:0;padding-right:0;} +.top {margin-top:0;padding-top:0;} +.bottom {margin-bottom:0;padding-bottom:0;} + +/* grid.css */ +.container {width:950px;margin:0 auto;} +.showgrid {background:url(grid.png);} +body {margin:1.5em 0;} +div.span-1, div.span-2, div.span-3, div.span-4, div.span-5, div.span-6, div.span-7, div.span-8, div.span-9, div.span-10, div.span-11, div.span-12, div.span-13, div.span-14, div.span-15, div.span-16, div.span-17, div.span-18, div.span-19, div.span-20, div.span-21, div.span-22, div.span-23, div.span-24 {float:left;margin-right:10px;} +div.last {margin-right:0;} +.span-1 {width:30px;} +.span-2 {width:70px;} +.span-3 {width:110px;} +.span-4 {width:150px;} +.span-5 {width:190px;} +.span-6 {width:230px;} +.span-7 {width:270px;} +.span-8 {width:310px;} +.span-9 {width:350px;} +.span-10 {width:390px;} +.span-11 {width:430px;} +.span-12 {width:470px;} +.span-13 {width:510px;} +.span-14 {width:550px;} +.span-15 {width:590px;} +.span-16 {width:630px;} +.span-17 {width:670px;} +.span-18 {width:710px;} +.span-19 {width:750px;} +.span-20 {width:790px;} +.span-21 {width:830px;} +.span-22 {width:870px;} +.span-23 {width:910px;} +.span-24, div.span-24 {width:950px;margin:0;} +.append-1 {padding-right:40px;} +.append-2 {padding-right:80px;} +.append-3 {padding-right:120px;} +.append-4 {padding-right:160px;} +.append-5 {padding-right:200px;} +.append-6 {padding-right:240px;} +.append-7 {padding-right:280px;} +.append-8 {padding-right:320px;} +.append-9 {padding-right:360px;} +.append-10 {padding-right:400px;} +.append-11 {padding-right:440px;} +.append-12 {padding-right:480px;} +.append-13 {padding-right:520px;} +.append-14 {padding-right:560px;} +.append-15 {padding-right:600px;} +.append-16 {padding-right:640px;} +.append-17 {padding-right:680px;} +.append-18 {padding-right:720px;} +.append-19 {padding-right:760px;} +.append-20 {padding-right:800px;} +.append-21 {padding-right:840px;} +.append-22 {padding-right:880px;} +.append-23 {padding-right:920px;} +.prepend-1 {padding-left:40px;} +.prepend-2 {padding-left:80px;} +.prepend-3 {padding-left:120px;} +.prepend-4 {padding-left:160px;} +.prepend-5 {padding-left:200px;} +.prepend-6 {padding-left:240px;} +.prepend-7 {padding-left:280px;} +.prepend-8 {padding-left:320px;} +.prepend-9 {padding-left:360px;} +.prepend-10 {padding-left:400px;} +.prepend-11 {padding-left:440px;} +.prepend-12 {padding-left:480px;} +.prepend-13 {padding-left:520px;} +.prepend-14 {padding-left:560px;} +.prepend-15 {padding-left:600px;} +.prepend-16 {padding-left:640px;} +.prepend-17 {padding-left:680px;} +.prepend-18 {padding-left:720px;} +.prepend-19 {padding-left:760px;} +.prepend-20 {padding-left:800px;} +.prepend-21 {padding-left:840px;} +.prepend-22 {padding-left:880px;} +.prepend-23 {padding-left:920px;} +div.border {padding-right:4px;margin-right:5px;border-right:1px solid #eee;} +div.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;} +.pull-1 {margin-left:-40px;} +.pull-2 {margin-left:-80px;} +.pull-3 {margin-left:-120px;} +.pull-4 {margin-left:-160px;} +.pull-5 {margin-left:-200px;} +.pull-6 {margin-left:-240px;} +.pull-7 {margin-left:-280px;} +.pull-8 {margin-left:-320px;} +.pull-9 {margin-left:-360px;} +.pull-10 {margin-left:-400px;} +.pull-11 {margin-left:-440px;} +.pull-12 {margin-left:-480px;} +.pull-13 {margin-left:-520px;} +.pull-14 {margin-left:-560px;} +.pull-15 {margin-left:-600px;} +.pull-16 {margin-left:-640px;} +.pull-17 {margin-left:-680px;} +.pull-18 {margin-left:-720px;} +.pull-19 {margin-left:-760px;} +.pull-20 {margin-left:-800px;} +.pull-21 {margin-left:-840px;} +.pull-22 {margin-left:-880px;} +.pull-23 {margin-left:-920px;} +.pull-24 {margin-left:-960px;} +.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} +.push-1 {margin:0 -40px 1.5em 40px;} +.push-2 {margin:0 -80px 1.5em 80px;} +.push-3 {margin:0 -120px 1.5em 120px;} +.push-4 {margin:0 -160px 1.5em 160px;} +.push-5 {margin:0 -200px 1.5em 200px;} +.push-6 {margin:0 -240px 1.5em 240px;} +.push-7 {margin:0 -280px 1.5em 280px;} +.push-8 {margin:0 -320px 1.5em 320px;} +.push-9 {margin:0 -360px 1.5em 360px;} +.push-10 {margin:0 -400px 1.5em 400px;} +.push-11 {margin:0 -440px 1.5em 440px;} +.push-12 {margin:0 -480px 1.5em 480px;} +.push-13 {margin:0 -520px 1.5em 520px;} +.push-14 {margin:0 -560px 1.5em 560px;} +.push-15 {margin:0 -600px 1.5em 600px;} +.push-16 {margin:0 -640px 1.5em 640px;} +.push-17 {margin:0 -680px 1.5em 680px;} +.push-18 {margin:0 -720px 1.5em 720px;} +.push-19 {margin:0 -760px 1.5em 760px;} +.push-20 {margin:0 -800px 1.5em 800px;} +.push-21 {margin:0 -840px 1.5em 840px;} +.push-22 {margin:0 -880px 1.5em 880px;} +.push-23 {margin:0 -920px 1.5em 920px;} +.push-24 {margin:0 -960px 1.5em 960px;} +.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:right;position:relative;} +.box {padding:1.5em;margin-bottom:1.5em;background:#E5ECF9;} +hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:.1em;margin:0 0 1.45em;border:none;} +hr.space {background:#fff;color:#fff;} +.clearfix:after, .container:after {content:".";display:block;height:0;clear:both;visibility:hidden;} +.clearfix, .container {display:inline-block;} +* html .clearfix, * html .container {height:1%;} +.clearfix, .container {display:block;} +.clear {clear:both;} + +/* forms.css */ +label {font-weight:bold;} +fieldset {padding:1.4em;margin:0 0 1.5em 0;border:1px solid #ccc;} +legend {font-weight:bold;font-size:1.2em;} +input.text, input.title, textarea, select {margin:0.5em 0;border:1px solid #bbb;} +input.text:focus, input.title:focus, textarea:focus, select:focus {border:1px solid #666;} +input.text, input.title {width:300px;padding:5px;} +input.title {font-size:1.5em;} +textarea {width:390px;height:250px;padding:5px;} +.error, .notice, .success {padding:.8em;margin-bottom:1em;border:2px solid #ddd;} +.error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;} +.notice {background:#FFF6BF;color:#514721;border-color:#FFD324;} +.success {background:#E6EFC2;color:#264409;border-color:#C6D880;} +.error a {color:#8a1f11;} +.notice a {color:#514721;} +.success a {color:#264409;}
\ No newline at end of file diff --git a/build/pgo/blueprint/test-small.jpg b/build/pgo/blueprint/test-small.jpg Binary files differnew file mode 100644 index 000000000..aa599d99a --- /dev/null +++ b/build/pgo/blueprint/test-small.jpg diff --git a/build/pgo/blueprint/test.jpg b/build/pgo/blueprint/test.jpg Binary files differnew file mode 100644 index 000000000..0107be273 --- /dev/null +++ b/build/pgo/blueprint/test.jpg diff --git a/build/pgo/blueprint/valid.png b/build/pgo/blueprint/valid.png Binary files differnew file mode 100644 index 000000000..dd20e497e --- /dev/null +++ b/build/pgo/blueprint/valid.png diff --git a/build/pgo/certs/README b/build/pgo/certs/README new file mode 100644 index 000000000..ba2b346f9 --- /dev/null +++ b/build/pgo/certs/README @@ -0,0 +1,9 @@ +The certificate authority and server certificates here are generated by $topsrcdir/build/pgo/genpgocert.py. + +You can generate a new CA cert by running: +$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-ca + +You can generate new server certificates by running: +$objdir/_virtualenv/bin/python $topsrcdir/build/pgo/genpgocert.py --gen-server + +These will place the new files in this directory where you can commit them. diff --git a/build/pgo/certs/alternateroot.ca b/build/pgo/certs/alternateroot.ca new file mode 100644 index 000000000..058b9f56d --- /dev/null +++ b/build/pgo/certs/alternateroot.ca @@ -0,0 +1,18 @@ +-----BEGIN CERTIFICATE----- +MIIC2jCCAcKgAwIBAgIBATANBgkqhkiG9w0BAQsFADAmMSQwIgYDVQQDExtBbHRl
+cm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkwHhcNMTQwOTI1MjEyMTU0WhcNMjQwOTI1
+MjEyMTU0WjAmMSQwIgYDVQQDExtBbHRlcm5hdGUgVHJ1c3RlZCBBdXRob3JpdHkw
+ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDBT+BwAhO52IWgSIdZZifU
+9LHOs3IR/+8DCC0WP5d/OuyKlZ6Rqd0tsd3i7durhQyjHSbLf2lJStcnFjcVEbEn
+NI76RuvlN8xLLn5eV+2Ayr4cZYKztudwRmw+DV/iYAiMSy0hs7m3ssfX7qpoi1aN
+RjUanwU0VTCPQhF1bEKAC2du+C5Z8e92zN5t87w7bYr7lt+m8197XliXEu+0s9Rg
+nGwGaZ296BIRz6NOoJYTa43n06LU1I1+Z4d6lPdzUFrSR0GBaMhUSurUBtOin3yW
+iMhg1VHX/KwqGc4als5GyCVXy8HGrA/0zQPOhetxrlhEVAdK/xBt7CZvByj1Rcc7
+AgMBAAGjEzARMA8GA1UdEwQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADggEBAJq/
+hogSRqzPWTwX4wTn/DVSNdWwFLv53qep9YrSMJ8ZsfbfK9Es4VP4dBLRQAVMJ0Z5
+mW1I6d/n0KayTanuUBvemYdxPi/qQNSs8UJcllqdhqWzmzAg6a0LxrMnEeKzPBPD
+6q8PwQ7tYP+B4sBN9tnnsnyPgti9ZiNZn5FwXZliHXseQ7FE9/SqHlLw5LXW3YtK
+juti6RmuV6fq3j+D4oeC5vb1mKgIyoTqGN6ze57v8RHi+pQ8Q+kmoUn/L3Z2YmFe
+4SKN/4WoyXr8TdejpThGOCGCAd3565s5gOx5QfSQX11P8NZKO8hcN0tme3VzmGpH
+K0Z/6MTmdpNaTwQ6odk= +-----END CERTIFICATE----- diff --git a/build/pgo/certs/cert8.db b/build/pgo/certs/cert8.db Binary files differnew file mode 100644 index 000000000..1e65b619d --- /dev/null +++ b/build/pgo/certs/cert8.db diff --git a/build/pgo/certs/evintermediate.ca b/build/pgo/certs/evintermediate.ca new file mode 100644 index 000000000..17a048ad8 --- /dev/null +++ b/build/pgo/certs/evintermediate.ca @@ -0,0 +1,34 @@ +-----BEGIN CERTIFICATE----- +MIIF9zCCBN+gAwIBAgIBAzANBgkqhkiG9w0BAQUFADCB4TELMAkGA1UEBhMCVVMx
+CzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MSMwIQYDVQQKExpN
+b3ppbGxhIC0gRVYgZGVidWcgdGVzdCBDQTEdMBsGA1UECxMUU2VjdXJpdHkgRW5n
+aW5lZXJpbmcxJjAkBgNVBAMTHUVWIFRlc3RpbmcgKHVudHJ1c3R3b3J0aHkpIENB
+MRMwEQYDVQQpEwpldi10ZXN0LWNhMSwwKgYJKoZIhvcNAQkBFh1jaGFybGF0YW5A
+dGVzdGluZy5leGFtcGxlLmNvbTAeFw0xMzAyMTQxNzU5MDlaFw0yMzAyMTIxNzU5
+MDlaMIHRMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50
+YWluIFZpZXcxIzAhBgNVBAoTGk1vemlsbGEgLSBFViBkZWJ1ZyB0ZXN0IENBMR0w
+GwYDVQQLExRTZWN1cml0eSBFbmdpbmVlcmluZzEWMBQGA1UEAxMNaW50ZXJtZWRp
+YXRlMzETMBEGA1UEKRMKZXYtdGVzdC1jYTEsMCoGCSqGSIb3DQEJARYdY2hhcmxh
+dGFuQHRlc3RpbmcuZXhhbXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
+ggEKAoIBAQDAfzrlJdawr7v8m7lslODk5FTqCiBO7tPxnWhAOEL5g05knLTZTc5J
+3ywmGoW6ae6RwPlWuqRuFd2Ea+yCawyjkUoLOpFH/xziDzvaS6LXNdJoxQqWk/LX
+8YYQVFfmxh8E11fz74IoCzX++mY1byaNONf3bLU2HU8vnVvENr1gy9Bzpm8wUuKm
+HkBYuG0SVzaeym2H/mo5PJICPVhPa+YxfEVS8EIFCigXGH7xrz/bPXnpfgsSJTnN
+4amBNkORfjf7H9x6IWkJGEkIvkVoYKT4iQ9q6/C4YDjWa9p5lA4F/qxnJefezH/I
+6hcqEODSaDsY+I6vsN8ks8r8MTTnd7BjAgMBAAGjggHGMIIBwjAdBgNVHQ4EFgQU
+fluXMAT0ZS21pV13vv46m8k7nRkwggEYBgNVHSMEggEPMIIBC4AUyJg651hwk+3B
+V0rQvQZv9n2bWPahgeekgeQwgeExCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEW
+MBQGA1UEBxMNTW91bnRhaW4gVmlldzEjMCEGA1UEChMaTW96aWxsYSAtIEVWIGRl
+YnVnIHRlc3QgQ0ExHTAbBgNVBAsTFFNlY3VyaXR5IEVuZ2luZWVyaW5nMSYwJAYD
+VQQDEx1FViBUZXN0aW5nICh1bnRydXN0d29ydGh5KSBDQTETMBEGA1UEKRMKZXYt
+dGVzdC1jYTEsMCoGCSqGSIb3DQEJARYdY2hhcmxhdGFuQHRlc3RpbmcuZXhhbXBs
+ZS5jb22CCQCvxT0iZiZJMjAMBgNVHRMEBTADAQH/MDYGA1UdHwQvMC0wK6ApoCeG
+JWh0dHA6Ly9leGFtcGxlLmNvbS9yb290LWV2LXRlc3Rlci5jcmwwPwYDVR0gBDgw
+NjA0BgRVHSAAMCwwKgYIKwYBBQUHAgEWHmh0dHA6Ly9teXRlc3Rkb21haW4ubG9j
+YWwvY3BzOzANBgkqhkiG9w0BAQUFAAOCAQEAC4grNTV5K8yqiAJ/0f6oIkTMqyJ4
+lyHXvvKXMHTpRZ7Jdy0aq5KTSHswx64ZRN7V2ds+czzDWgxX3rBuZZAgOW1JYva3
+Ps3XRYUiaTW8eeaWjuVRFAp7ytRmSsOGeOtHbez8jDmTqPRQ1mTMsMzpY4bFD8do
+5y0xsbz4DYIeeNnX9+XGB5u2ml8t5L8Cj65wwMAx9HlsjTrfQTMIwpwbNle6GuZ3
+9FzmE2piAND73yCgU5W66K2lZg8N6vHBq0UhPDCF72y8MlHxQOpTr3/jIGr4X7k9
+uyYq0Pw5Y/LKyGbyW5iMFdLzabm1ua8IWAf7DSFMH6L3WlK8mngCfJ1icQ== +-----END CERTIFICATE----- diff --git a/build/pgo/certs/jartests-object.ca b/build/pgo/certs/jartests-object.ca new file mode 100644 index 000000000..fc6d0ffb5 --- /dev/null +++ b/build/pgo/certs/jartests-object.ca @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICTTCCAbagAwIBAgIBADANBgkqhkiG9w0BAQUFADBaMRMwEQYDVQQLEwpVbml0
+IFRlc3RzMRgwFgYDVQQKEw9Nb3ppbGxhIFRlc3RpbmcxKTAnBgNVBAMTIFNpZ25l
+ZCBKQVIgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MB4XDTA5MDExNTE0MDkwM1oXDTM0
+MDExNTE0MDkwM1owWjETMBEGA1UECxMKVW5pdCBUZXN0czEYMBYGA1UEChMPTW96
+aWxsYSBUZXN0aW5nMSkwJwYDVQQDEyBTaWduZWQgSkFSIENlcnRpZmljYXRlIEF1
+dGhvcml0eTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsQd8eUw4WSK7YoKl
+hqe+CjEgI5Rs3TirWtDsfmMtMBmTvRhJpdTeMAFTpWvlOPuXJwkKXMMFLxE8ayNX
+fO5ixCgJ7LrpguOVZ3pY4RvEyE6yh3Hv81Ztblbo120IdcrkyN4KMs5EgeauDllU
+ehhbq9lmnmQxIQs3KPcoMteqAXcCAwEAAaMjMCEwEQYJYIZIAYb4QgEBBAQDAgAH
+MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADgYEAAZHhPT133TcavAKnn37X
+0VE9davrX7t20CLb06KYpgkg7yO0BjIjTnYeJBQgaH652pZVEFT7dbi0JTn4BMXz
+EwOQ2JjzjwNUDHpWAopiCKxAnjwy/kGcZfkKUydwQHKr8m1Faywu1Cyrj0gBHClL
+b2b9ywK4pb545mE6V9pi1zg= +-----END CERTIFICATE----- diff --git a/build/pgo/certs/key3.db b/build/pgo/certs/key3.db Binary files differnew file mode 100644 index 000000000..a45004df8 --- /dev/null +++ b/build/pgo/certs/key3.db diff --git a/build/pgo/certs/mochitest.client b/build/pgo/certs/mochitest.client Binary files differnew file mode 100644 index 000000000..5d72cd2b7 --- /dev/null +++ b/build/pgo/certs/mochitest.client diff --git a/build/pgo/certs/pgoca.ca b/build/pgo/certs/pgoca.ca new file mode 100644 index 000000000..65fb8a589 --- /dev/null +++ b/build/pgo/certs/pgoca.ca @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICXTCCAcagAwIBAgIBATANBgkqhkiG9w0BAQUFADBqMSQwIgYDVQQLExtQcm9m +aWxlIEd1aWRlZCBPcHRpbWl6YXRpb24xGDAWBgNVBAoTD01vemlsbGEgVGVzdGlu +ZzEoMCYGA1UEAxMfVGVtcG9yYXJ5IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0w +ODA1MjIwMDM4MDVaFw0xODA1MjIwMDM4MDVaMGoxJDAiBgNVBAsTG1Byb2ZpbGUg +R3VpZGVkIE9wdGltaXphdGlvbjEYMBYGA1UEChMPTW96aWxsYSBUZXN0aW5nMSgw +JgYDVQQDEx9UZW1wb3JhcnkgQ2VydGlmaWNhdGUgQXV0aG9yaXR5MIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQDg6iipAXGZYmgTcHfx8M2hcLqmqDalcj7sZ1A7 +a3LiCBb+1uHKKy9hUxRUe61aJF4NgMAF5oc+HpXN0hpvkiNHxqqD7R6hrkP3gAJ3 +eczEFKsFUI6AqaCL0+xpyhaaZmmarcHxU+PL2h5zq6VssxfBAsO0DkzWzk6E8vM+ +jrku7QIDAQABoxMwETAPBgNVHRMECDAGAQH/AgEAMA0GCSqGSIb3DQEBBQUAA4GB +ALPbn3Ztg0m8qDt8Vkf5You6HEqIxZe+ffDTrfq/L7ofHk/OXEpL7OWKRHU33pNG +QS8khBG+sO461C51s6u9giW+eq2PaQv2HGASBpDbvPqc/Hf+zupZsdsXzHv6rt0V +lu5B6nOpMse1nhA494i1ARSuBNzLv5mas38YWG8Rr6jR +-----END CERTIFICATE----- diff --git a/build/pgo/certs/pgoca.p12 b/build/pgo/certs/pgoca.p12 Binary files differnew file mode 100644 index 000000000..4867c286b --- /dev/null +++ b/build/pgo/certs/pgoca.p12 diff --git a/build/pgo/certs/secmod.db b/build/pgo/certs/secmod.db Binary files differnew file mode 100644 index 000000000..a3341f767 --- /dev/null +++ b/build/pgo/certs/secmod.db diff --git a/build/pgo/favicon.ico b/build/pgo/favicon.ico Binary files differnew file mode 100644 index 000000000..d44438903 --- /dev/null +++ b/build/pgo/favicon.ico diff --git a/build/pgo/genpgocert.py b/build/pgo/genpgocert.py new file mode 100644 index 000000000..c58bcd40d --- /dev/null +++ b/build/pgo/genpgocert.py @@ -0,0 +1,192 @@ +#!/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 script exists to generate the Certificate Authority and server +# certificates used for SSL testing in Mochitest. The already generated +# certs are located at $topsrcdir/build/pgo/certs/ . + +import mozinfo +import os +import random +import re +import shutil +import subprocess +import sys +import tempfile + +from mozbuild.base import MozbuildObject +from mozfile import NamedTemporaryFile +from mozprofile.permissions import ServerLocations + +dbFiles = [ + re.compile("^cert[0-9]+\.db$"), + re.compile("^key[0-9]+\.db$"), + re.compile("^secmod\.db$") +] + +def unlinkDbFiles(path): + for root, dirs, files in os.walk(path): + for name in files: + for dbFile in dbFiles: + if dbFile.match(name) and os.path.exists(os.path.join(root, name)): + os.unlink(os.path.join(root, name)) + +def dbFilesExist(path): + for root, dirs, files in os.walk(path): + for name in files: + for dbFile in dbFiles: + if dbFile.match(name) and os.path.exists(os.path.join(root, name)): + return True + return False + + +def runUtil(util, args, inputdata = None): + env = os.environ.copy() + if mozinfo.os == "linux": + pathvar = "LD_LIBRARY_PATH" + app_path = os.path.dirname(util) + if pathvar in env: + env[pathvar] = "%s%s%s" % (app_path, os.pathsep, env[pathvar]) + else: + env[pathvar] = app_path + proc = subprocess.Popen([util] + args, env=env, + stdin=subprocess.PIPE if inputdata else None) + proc.communicate(inputdata) + return proc.returncode + + +def createRandomFile(randomFile): + for count in xrange(0, 2048): + randomFile.write(chr(random.randint(0, 255))) + + +def createCertificateAuthority(build, srcDir): + certutil = build.get_binary_path(what="certutil") + pk12util = build.get_binary_path(what="pk12util") + + #TODO: mozfile.TemporaryDirectory + tempDbDir = tempfile.mkdtemp() + with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile: + pgoCAModulePathSrc = os.path.join(srcDir, "pgoca.p12") + pgoCAPathSrc = os.path.join(srcDir, "pgoca.ca") + + pwfile.write("\n") + + # Create temporary certification database for CA generation + status = runUtil(certutil, ["-N", "-d", tempDbDir, "-f", pwfile.name]) + if status: + return status + + createRandomFile(rndfile) + status = runUtil(certutil, ["-S", "-d", tempDbDir, "-s", "CN=Temporary Certificate Authority, O=Mozilla Testing, OU=Profile Guided Optimization", "-t", "C,,", "-x", "-m", "1", "-v", "120", "-n", "pgo temporary ca", "-2", "-f", pwfile.name, "-z", rndfile.name], "Y\n0\nN\n") + if status: + return status + + status = runUtil(certutil, ["-L", "-d", tempDbDir, "-n", "pgo temporary ca", "-a", "-o", pgoCAPathSrc, "-f", pwfile.name]) + if status: + return status + + status = runUtil(pk12util, ["-o", pgoCAModulePathSrc, "-n", "pgo temporary ca", "-d", tempDbDir, "-w", pwfile.name, "-k", pwfile.name]) + if status: + return status + + shutil.rmtree(tempDbDir) + return 0 + + +def createSSLServerCertificate(build, srcDir): + certutil = build.get_binary_path(what="certutil") + pk12util = build.get_binary_path(what="pk12util") + + with NamedTemporaryFile() as pwfile, NamedTemporaryFile() as rndfile: + pgoCAPath = os.path.join(srcDir, "pgoca.p12") + + pwfile.write("\n") + + if not dbFilesExist(srcDir): + # Make sure all DB files from src are really deleted + unlinkDbFiles(srcDir) + + # Create certification database for ssltunnel + status = runUtil(certutil, ["-N", "-d", srcDir, "-f", pwfile.name]) + if status: + return status + + status = runUtil(pk12util, ["-i", pgoCAPath, "-w", pwfile.name, "-d", srcDir, "-k", pwfile.name]) + if status: + return status + + # Generate automatic certificate + locations = ServerLocations(os.path.join(build.topsrcdir, + "build", "pgo", + "server-locations.txt")) + iterator = iter(locations) + + # Skips the first entry, I don't know why: bug 879740 + iterator.next() + + locationsParam = "" + firstLocation = "" + for loc in iterator: + if loc.scheme == "https" and "nocert" not in loc.options: + customCertOption = False + customCertRE = re.compile("^cert=(?:\w+)") + for option in loc.options: + match = customCertRE.match(option) + if match: + customCertOption = True + break + + if not customCertOption: + if len(locationsParam) > 0: + locationsParam += "," + locationsParam += loc.host + + if firstLocation == "": + firstLocation = loc.host + + if not firstLocation: + print "Nothing to generate, no automatic secure hosts specified" + else: + createRandomFile(rndfile) + + runUtil(certutil, ["-D", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name]) + # Ignore the result, the certificate may not be present when new database is being built + + status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "120", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfile.name, "-f", pwfile.name]) + if status: + return status + + return 0 + +if len(sys.argv) == 1: + print "Specify --gen-server or --gen-ca" + sys.exit(1) + +build = MozbuildObject.from_environment() +certdir = os.path.join(build.topsrcdir, "build", "pgo", "certs") +if sys.argv[1] == "--gen-server": + certificateStatus = createSSLServerCertificate(build, certdir) + if certificateStatus: + print "TEST-UNEXPECTED-FAIL | SSL Server Certificate generation" + + sys.exit(certificateStatus) + +if sys.argv[1] == "--gen-ca": + certificateStatus = createCertificateAuthority(build, certdir) + if certificateStatus: + print "TEST-UNEXPECTED-FAIL | Certificate Authority generation" + else: + print "\n\n" + print "===================================================" + print " IMPORTANT:" + print " To use this new certificate authority in tests" + print " run 'make' at testing/mochitest" + print "===================================================" + + sys.exit(certificateStatus) + +print "Invalid option specified" +sys.exit(1) diff --git a/build/pgo/index.html b/build/pgo/index.html new file mode 100644 index 000000000..ccfa00d00 --- /dev/null +++ b/build/pgo/index.html @@ -0,0 +1,64 @@ +<script> +/* 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/. */ + + var list = + [ + "blueprint/sample.html", + "blueprint/forms.html", + "blueprint/grid.html", + "blueprint/elements.html", + "js-input/3d-thingy.html", + "js-input/crypto-otp.html", + "js-input/sunspider/3d-cube.html", + "js-input/sunspider/3d-morph.html", + "js-input/sunspider/3d-raytrace.html", + "js-input/sunspider/access-binary-trees.html", + "js-input/sunspider/access-fannkuch.html", + "js-input/sunspider/access-nbody.html", + "js-input/sunspider/access-nsieve.html", + "js-input/sunspider/bitops-3bit-bits-in-byte.html", + "js-input/sunspider/bitops-bits-in-byte.html", + "js-input/sunspider/bitops-bitwise-and.html", + "js-input/sunspider/bitops-nsieve-bits.html", + "js-input/sunspider/controlflow-recursive.html", + "js-input/sunspider/crypto-aes.html", + "js-input/sunspider/crypto-md5.html", + "js-input/sunspider/crypto-sha1.html", + "js-input/sunspider/date-format-tofte.html", + "js-input/sunspider/date-format-xparb.html", + "js-input/sunspider/math-cordic.html", + "js-input/sunspider/math-partial-sums.html", + "js-input/sunspider/math-spectral-norm.html", + "js-input/sunspider/regexp-dna.html", + "js-input/sunspider/string-base64.html", + "js-input/sunspider/string-fasta.html", + "js-input/sunspider/string-tagcloud.html", + "js-input/sunspider/string-unpack-code.html", + "js-input/sunspider/string-validate-input.html" + ]; + var interval = 1000; + var idx = 0; + var w; + + window.onload = function () { + w = window.open("about:blank"); + window.setTimeout(loadURL, interval); + }; + function loadURL () { + w.close(); + w = window.open(list[idx++]); + if (idx < list.length) { + window.setTimeout(loadURL, interval); + } else { + window.setTimeout(Quitter.quit, interval); + } + } + var i; + + for(i=0; i < list.length;i++) { + document.write(list[i]); + document.write("<br>"); + } + </script> diff --git a/build/pgo/js-input/3d-thingy.html b/build/pgo/js-input/3d-thingy.html new file mode 100644 index 000000000..9e54299df --- /dev/null +++ b/build/pgo/js-input/3d-thingy.html @@ -0,0 +1,390 @@ +<html> +<head> +<title>3d thingy</title> +<style type="text/css"> +div.z2 { position:absolute; z-index:2; } +div.z1 { position:absolute; z-index:1; } +</style> +<script type="text/javascript"> +/************************************************************************** +JavaScript Graphics Library 0.0.1, Updated Source Code at Scriptersoft.com +Copyright (C) 2005 Kurt L. Whicher +November,13,2005 + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +**************************************************************************/ + +//________________________________________ global variables + +var S_piDoubled=Math.PI*2; +var S_deg2Rad=Math.PI/180; + +//_______________________________________________ functions + +function S_matrix() { + return [1,0,0,0, + 0,1,0,0, + 0,0,1,0, + 0,0,0,1]; +} +function S_vec2D(x,y) { this.x=x; this.y=y; } +function S_vec3D(x,y,z) { this.x=x; this.y=y; this.z=z; } +function S_subVec2D(a,b) { + return new S_vec2D(a.x-b.x, a.y-b.y); +} +function S_subVec3D(a,b) { + return new S_vec3D(a.x-b.x, a.y-b.y, a.z-b.z); +} +function S_dotVec3D(a, b) { return a.x*b.x+a.y*b.y+a.z*b.z; } +function S_cross(a,b) { + return new S_vec3D( a.y*b.z-a.z*b.y, a.z*b.x-a.x*b.z, a.x*b.y-a.y*b.x); +} +function S_lengthSquaredVec3D(v) { return S_dotVec3D(v,v); } +function S_lengthVec3D(v) { return Math.sqrt(S_lengthSquaredVec3D(v)); } +function S_normalizeVec3D(v) { + var l=S_lengthVec3D(v), nv=new S_vec3D(0,0,0); + if(l!=0) { nv.x=v.x/l; nv.y=v.y/l; nv.z=v.z/l; } + return nv; +} +function S_rotate(m,ax,a) { // transformation matrix, axis, angle + var i,j,ij=new Array(),v=new Array(),c=Math.cos(a),s=Math.sin(a); + if (ax=="x") ij=[1,2,5,6,9,10,13,14]; + else if (ax=="y") ij=[2,0,6,4,10,8,14,12]; + else if (ax=="z") ij=[0,1,4,5,8,9,12,13]; + for (i=0;i<8;i++) v[i]=m[ij[i]]; + for (i=0,j=1;i<8;i+=2,j+=2) { + m[ij[i]]=v[i]*c-v[j]*s; + m[ij[j]]=v[i]*s+v[j]*c + } +} +function S_checkBrowser() { + if (document.getElementById) return true; else return false; +} +function S_zIndex(e,z) { document.getElementById(e).style.zIndex=z; } +function S_rgbColor(r,g,b) { + var i, c=[r,g,b]; + for(i=0; i<3; i++) { + c[i]=Math.floor(c[i]); + if(c[i]<0) c[i]=0; else if(c[i]>255) c[i]=255; + } + return c; +} +function S_rgbColorString(c) { + return "rgb("+c[0]+","+c[1]+","+c[2]+")"; +} +function S_vertice(x,y,z) { + this.x=x; this.y=y; this.z=z; this.w=1; + this.t=new S_vec3D(x,y,z); // transformed 3d + this.p=new S_vec2D(0,0); // projected 2d +} +function S_face(v0,v1,v2,c) { // 3 vertice faces + this.v=[v0,v1,v2]; this.c=c; this.b=0; // b:brightness + this.d=true; // display: true or false +} +// x coordinate, number of vertices, distance +function S_verticeRing(x,nv,d) { + var i,a,v=new Array(); + for(i=0;i<nv;i++) { + a=S_piDoubled*i/nv; + v[i]=new S_vertice(x,d*Math.sin(a),d*Math.cos(a)); + } + return v; +} +function S_triangleRing(r1,r2,c,clr) { // rows 1 & 2, cols, color + var i,j,tr=new Array(); + for(i=0,j=1;i<c;i++,j=++j%c) { + tr.push(new S_face(r1+i,r2+i,r2+j,clr)); + tr.push(new S_face(r1+i,r2+j,r1+j,clr)); + } + return tr; +} +function S_model(v,f) { + // vertice & face arrays, transformation matrix, display boolean + this.v=v; this.f=f, this.tm=S_matrix(), this.d=true; +} +S_model.prototype.S_rotateX=function(a) { + S_rotate(this.tm,"x",a*=S_deg2Rad); +} +S_model.prototype.S_rotateY=function(a) { + S_rotate(this.tm,"y",a*=S_deg2Rad); +} +S_model.prototype.S_rotateZ=function(a) { + S_rotate(this.tm,"z",a*=S_deg2Rad); +} +S_model.prototype.S_show=function() { this.d=true; } +S_model.prototype.S_hide=function() { this.d=false; } +function S_cube(d,c) { //distance & color + return new S_cone(d,d,Math.cos(Math.PI/4)*d*2,1,4,c); +} +function S_cylinder(w,h,r,c,clr,e) { + return new S_cone(w,w,h,r,c,clr,e); +} +// width, height, "rows", "columns", color, ends +function S_cone(w1,w2,h,r,c,clr,e) { + var i,r1=0,r2=c,v=new Array(),t=new Array(),rxc=r*c; + for(i=0;i<=r;i++) + v=v.concat(S_verticeRing(h*(0.5-i/r),c,w1*i/r+w2*(r-i)/r)); + for(i=0;i<r;i++,r1+=c,r2+=c) + t=t.concat(S_triangleRing(r1,r2,c,clr)); + if (e!="hideEnds") + for(i=1;i<(c-1);i++) { + t.push(new S_face(0,i,i+1,clr)); + t.push(new S_face(rxc,rxc+i+1,rxc+i,clr)); + } + return new S_model(v,t); +} +function S_sphere(d,r,c,clr) { + // distance, "rows">=2, "columns">=3, color paramaters + var v=new Array(),t=new Array(),r_1xc=(r-1)*c,r_2xc=(r-2)*c; + var i,j,tmp,r1=0,r2=c; + for(i=1;i<r;i++) { + tmp=Math.PI*i/r; + v=v.concat(S_verticeRing(d*Math.cos(tmp),c,Math.sin(tmp)*d)); + } + v.push(new S_vertice( d,0,0)); + v.push(new S_vertice(-d,0,0)); + for(i=0;i<(r-2);i++,r1+=c,r2+=c) + t=t.concat(S_triangleRing(r1,r2,c,clr)); + for(i=0,j=1;i<c;i++,j=++j%c) { + t.push(new S_face(r_1xc,i,j,clr)); + t.push(new S_face(r_1xc+1,r_2xc+j,r_2xc+i,clr)); + } + return new S_model(v,t); +} +S_model.prototype.S_scale=function(x) { + this.tm[0]*=x; this.tm[5]*=x; this.tm[10]*=x; +} +S_model.prototype.S_faceColor=function(i,c) { this.f[i].c=c; } +S_model.prototype.S_scaleX=function(s) { this.tm[0]*=s; } +S_model.prototype.S_scaleY=function(s) { this.tm[5]*=s; } +S_model.prototype.S_scaleZ=function(s) { this.tm[10]*=s; } +function S_scene(dv,l,t,w,h,cmra) { // left, top, width, height + this.dv=dv; + this.ps=1; // pixel size + this.l=l; this.t=t; this.w=w; this.h=h; + this.cx=l+w/2; this.cy=t+h/2; // center x, center y + this.dt="paint"; // output type + this.m=new Array(); // model array + this.lght=new S_light(); + this.lc=S_rgbColor(255,255,255); // light color + this.cmra=-cmra; // camera on z axis + this.bfr=S_buffer(h,w); +} +function S_buffer(h,w) { + var i, j, b=new Array(); + for(i=0;i<h;i++) { + b[i]=new Array(); + for(j=0;j<w;j++) b[i][j]=new S_pixel(); + } + return b; +} +function S_pixel() { // display boolean, color + this.d=true; this.c=0; +} +S_pixel.prototype.S_setColor=function(c) { + this.d=true; this.c=c; +} +S_pixel.prototype.S_hide=function() { this.d=false; } +S_scene.prototype.S_pixelSize=function(ps){ this.ps=ps; } +S_scene.prototype.S_widthAndHeight=function(w,h){ this.w=w; this.h=h; } +S_scene.prototype.S_center=function(cx,cy){ this.cx=cx; this.cy=cy; } +S_scene.prototype.S_paint=function(){ this.dt="paint"; } +S_scene.prototype.S_models=function() { + var i; this.m=new Array(); + for(i=0;i<arguments.length;i++) this.m.push(arguments[i]); +} +S_scene.prototype.S_lightColor=function(c){ this.lc=c; } +S_scene.prototype.S_project=function() { + var i, j, v, tm, d, m; + for(i=0;i<this.m.length;i++) { + m=this.m[i]; tm=this.m[i].tm; + for(j=0;j<m.v.length;j++) { + v=m.v[j]; + v.t.x=v.x*tm[0]+v.y*tm[4]+v.z*tm[8]+v.w*tm[12]; + v.t.y=v.x*tm[1]+v.y*tm[5]+v.z*tm[9]+v.w*tm[13]; + v.t.z=v.x*tm[2]+v.y*tm[6]+v.z*tm[10]+v.w*tm[14]; + d=(this.cmra-v.t.z/2); + if (d<0) { + v.p.x=(this.cmra*v.t.x/d)+this.cx; + v.p.y=-(this.cmra*v.t.y/d)+this.cy; + } + } + } +} +S_scene.prototype.S_display=function(disp){ + var i, j, k, s="", ds, c, cnt=0; // ds:div start + this.tr=new Array(); // triangles ready to draw + this.S_project(); + this.S_adjustLight(); + this.S_clearBuffer(); + for(i=0;i<this.m.length;i++) { + this.m[i].S_setupFaces(this.tr,this.lght.t); + for(j=0;j<this.tr.length;j++) { // loop through triangles + c=S_divColor(this.tr[j].c,this.lc,this.tr[j].b); + S_setupBuffer(this,this.tr[j].p,c); + } + } + for(i=0;i<this.h;i++) { + ds=-1; + for(j=0,k=1;j<this.w;j++,k++) { + if((this.bfr[i][j].d==true)&&(ds==-1)) ds=j; + if( (this.bfr[i][j].d==true)&& + ( (k==this.w)|| + (this.bfr[i][k].d==false)|| + (!S_sameColor(this.bfr[i][j].c, this.bfr[i][k].c)) ) ) { + s+=S_divString(S_rgbColorString(this.bfr[i][j].c),this.t+i*this.ps,this.l+ds*this.ps,this.ps,(k-ds)*this.ps); + ds=-1; + cnt++; + } + } + } + S_writeInnerHTML(this.dv,s); + if(disp=="ShowCount") alert(cnt); +} +S_scene.prototype.S_displayAndShowCount=function(){ + this.S_display("ShowCount"); +} +S_model.prototype.S_setupFaces=function(tr,lght) { + var i, j, fn, v, p=new Array(); // vertice & projection arrays + var z=new Array(); + for(i=0;i<this.f.length;i++) { // loop through faces + v=this.f[i].v; + for(j=0;j<3;j++) { p[j]=this.v[v[j]].p; } + for(j=0;j<3;j++) { z[j]=this.v[v[j]].t.z; } + if (((p[1].x-p[0].x)*(p[2].y-p[0].y))<((p[2].x-p[0].x)*(p[1].y-p[0].y))) { + this.f[i].d=true; + fn=S_faceNormal(this.v[v[0]].t, this.v[v[1]].t, this.v[v[2]].t); + this.f[i].b=S_faceIntensity(fn,lght); + tr.push(new S_triangle(fn,this.f[i].b,p.slice(),this.f[i].c,z)); + } else { this.f[i].d=false; } + } +} +// normal, brightness, array of 2D projection coordinates, and z depth +function S_triangle(fn,b,p,c,z) { + this.fn=fn; this.b=b; this.p=p; this.z=z; this.c=c; +} +function S_faceNormal(a,b,c){ + var cr=S_cross(S_subVec3D(b,a), S_subVec3D(b,c)); + return S_normalizeVec3D(cr); +} +function S_faceIntensity(fn,lght) { + var i=S_dotVec3D(fn,lght); return (i>0)?i:0; +} +function S_divColor(c,lc,b) { // c:array of colors + var i, clr=new Array(); + for(i=0;i<3;i++) clr[i]=Math.floor(c[i]+(lc[i]-c[i]+1)*b); + for(i=0;i<3;i++) if (clr[i]>lc[i]) { clr[i]=lc[i]; } + return S_rgbColor(clr[0],clr[1],clr[2]); +} +function S_sameColor(a,b) { + for(var i=0;i<3;i++) { if(a[i]!=b[i]) return false; } + return true; +} +function S_setupBuffer(scn,p,c) { + // temp, counters, min, max, scanline, vertice & slope arrays + var t,i,j,xmin=new Array(),xmax=new Array(),sl; + var v=new Array(), m=new Array(); + p.sort(function(a,b) { return a.y-b.y; } ); + for(i=0;i<3;i++) p[i].y=Math.floor(p[i].y); + v[0]=S_subVec2D(p[1],p[0]); + v[1]=S_subVec2D(p[2],p[0]); + v[2]=S_subVec2D(p[2],p[1]); + for(i=0;i<3;i++) { m[i]=(v[i].y!=0)?v[i].x/v[i].y:0; } + for(i=0,sl=scn.t;i<scn.h;i++,sl++) { + xmin[i]=1000;xmax[i]=0; + if((sl>=p[0].y)&&(sl<=p[2].y)) { + xmin[i]=xmax[i]=Math.floor(p[0].x+m[1]*(sl-p[0].y)); + } + if((sl>=p[0].y)&&(sl<=p[1].y)) { + t=Math.floor(p[0].x+m[0]*(sl-p[0].y)); + if(t<xmin[i]) xmin[i]=Math.floor(t); + else if(t>xmax[i]) xmax[i]=Math.floor(t); + } + if((sl>=p[1].y)&&(sl<=p[2].y)) { + t=Math.floor(p[1].x+m[2]*(sl-p[1].y)); + if(t<xmin[i]) xmin[i]=Math.floor(t); + else if(t>xmax[i]) xmax[i]=Math.floor(t); + } + for(j=0;j<scn.w;j++) + if((j>=(xmin[i]-scn.l))&&(j<=(xmax[i]-scn.l))) { + scn.bfr[i][j].d=true; scn.bfr[i][j].c=c; + } + } +} +function S_light() { + this.x=0; this.y=1; this.z=0; this.w=1; // original coordinates + this.t=new S_vec3D(0,1,0); // transformed coordinates + this.tm=new S_matrix(); +} +S_scene.prototype.S_adjustLight=function() { + var m=this.lght.tm, l=this.lght; + l.t.x=l.x*m[0]+l.y*m[4]+ l.z*m[8]+l.w*m[12]; + l.t.y=l.x*m[1]+l.y*m[5]+ l.z*m[9]+l.w*m[13]; + l.t.z=l.x*m[2]+l.y*m[6]+ l.z*m[10]+l.w*m[14]; + l.t=S_normalizeVec3D(l.t); +} +S_scene.prototype.S_lightRotateX=function(a) { + S_rotate(this.lght.tm,"x",a*=S_deg2Rad); +} +S_scene.prototype.S_lightRotateY=function(a) { + S_rotate(this.lght.tm,"y",a*=S_deg2Rad); +} +S_scene.prototype.S_lightRotateZ=function(a) { + S_rotate(this.lght.tm,"z",a*=S_deg2Rad); +} +S_scene.prototype.S_clearBuffer=function() { + for(var i=0;i<this.h;i++) + for(var j=0;j<this.w;j++) this.bfr[i][j].d=false; +} +function S_divString(b,t,l,h,w) { + var s='<div style="background-color:'+b+';position:absolute;'; + s+='top:'+t+'px;left:'+l+'px;height:'+h+'px;width:'+w; + return s+'px;font-size:0;visibility:visible"></div>'; +} +function S_writeInnerHTML(id,text) { + document.getElementById(id).innerHTML = text; +} +</script> +</head> +<body> +<div class="z1" id="graphicsDiv">Text to be replaced with graphics.</div> +<script type="text/javascript"> +if(S_checkBrowser()) { +var intrvl; +// Create a new scene with parameters for +// div id, left, top, width, height, and camera distance +var scn=new S_scene("graphicsDiv",75,25,100,100,300); +scn.S_pixelSize(3); // set scene pixel size +var c=S_rgbColor(0,0,127); // color +var c2=S_rgbColor(0,127,127); // color +var m=new S_cube(18,c); // model +m.S_faceColor(4,c2); +m.S_faceColor(5,c2); +m.S_scaleX(2.5); // scale model along x axis +scn.S_models(m); // add model(s) to scene +scn.S_lightRotateX(-25); // adjust light +function r(){ // rotation function +m.S_rotateX(11); // rotate model around y axis +m.S_rotateY(5); // rotate model around y axis +m.S_rotateZ(7); // rotate model around z axis +scn.S_display(); // display scene +} // end rotation function +intrvl=setInterval('r();',75); +} +</script> + +</body> +</html> + diff --git a/build/pgo/js-input/crypto-otp.html b/build/pgo/js-input/crypto-otp.html new file mode 100644 index 000000000..f6e1ca295 --- /dev/null +++ b/build/pgo/js-input/crypto-otp.html @@ -0,0 +1,1344 @@ +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" + "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" /> +<title>One-Time Pad Generator</title> +<meta name="description" content="JavaScript One-Time Pad Generator" /> +<meta name="author" content="John Walker" /> +<meta name="keywords" content="one, time, pad, generator, onetime, cryptography, JavaScript" /> +<style type="text/css"> + a:link, a:visited { + background-color: inherit; + color: rgb(0%, 0%, 80%); + text-decoration: none; + } + + a:hover { + background-color: rgb(30%, 30%, 100%); + color: rgb(100%, 100%, 100%); + } + + a:active { + color: rgb(100%, 0%, 0%); + background-color: rgb(30%, 30%, 100%); + } + + a.i:link, a.i:visited, a.i:hover { + background-color: inherit; + color: inherit; + text-decoration: none; + } + + body { + margin-left: 15%; + margin-right: 10%; + background-color: #FFFFFF; + color: #000000; + } + + body.jsgen { + margin-left: 5%; + margin-right: 5%; + } + + dt { + margin-top: 0.5em; + } + + img.button { + border: 0px; + vertical-align: middle; + } + + img.keyicon { + vertical-align: bottom; + } + + p, dd, li { + text-align: justify; + } + + p.centre { + text-align: center; + } + + table.r { + float: right; + } + + table.c { + background-color: #E0E0E0; + color: #000000; + margin-left: auto; + margin-right: auto; + } + + td.c { + text-align: center; + } + + textarea { + background-color: #FFFFD0; + color: #000000; + } +</style> +<script type="text/javascript"> +//<![CDATA[ + + loadTime = (new Date()).getTime(); + +/* + + L'Ecuyer's two-sequence generator with a Bays-Durham shuffle + on the back-end. Schrage's algorithm is used to perform + 64-bit modular arithmetic within the 32-bit constraints of + JavaScript. + + Bays, C. and S. D. Durham. ACM Trans. Math. Software: 2 (1976) + 59-64. + + L'Ecuyer, P. Communications of the ACM: 31 (1968) 742-774. + + Schrage, L. ACM Trans. Math. Software: 5 (1979) 132-138. + +*/ + +function uGen(old, a, q, r, m) { // Schrage's modular multiplication algorithm + var t; + + t = Math.floor(old / q); + t = a * (old - (t * q)) - (t * r); + return Math.round((t < 0) ? (t + m) : t); +} + +function LEnext() { // Return next raw value + var i; + + this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563); + this.gen2 = uGen(this.gen2, 40692, 52774, 3791, 2147483399); + + /* Extract shuffle table index from most significant part + of the previous result. */ + + i = Math.floor(this.state / 67108862); + + // New state is sum of generators modulo one of their moduli + + this.state = Math.round((this.shuffle[i] + this.gen2) % 2147483563); + + // Replace value in shuffle table with generator 1 result + + this.shuffle[i] = this.gen1; + + return this.state; +} + +// Return next random integer between 0 and n inclusive + +function LEnint(n) { + return Math.floor(this.next() / (1 + 2147483562 / (n + 1))); +} + +// Constructor. Called with seed value + +function LEcuyer(s) { + var i; + + this.shuffle = new Array(32); + this.gen1 = this.gen2 = (s & 0x7FFFFFFF); + for (i = 0; i < 19; i++) { + this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563); + } + + // Fill the shuffle table with values + + for (i = 0; i < 32; i++) { + this.gen1 = uGen(this.gen1, 40014, 53668, 12211, 2147483563); + this.shuffle[31 - i] = this.gen1; + } + this.state = this.shuffle[0]; + this.next = LEnext; + this.nextInt = LEnint; +} + +function sepchar() { + if (rsep) { + var seps = "!#$%&()*+,-./:;<=>?@[]^_{|}~"; + return seps.charAt(sepran.nextInt(seps.length - 1)); + } + return "-"; +} + +/* + * md5.jvs 1.0b 27/06/96 + * + * Javascript implementation of the RSA Data Security, Inc. MD5 + * Message-Digest Algorithm. + * + * Copyright (c) 1996 Henri Torgemane. All Rights Reserved. + * + * Permission to use, copy, modify, and distribute this software + * and its documentation for any purposes and without + * fee is hereby granted provided that this copyright notice + * appears in all copies. + * + * Of course, this soft is provided "as is" without express or implied + * warranty of any kind. + + This version contains some trivial reformatting modifications + by John Walker. + + */ + +function array(n) { + for (i = 0; i < n; i++) { + this[i] = 0; + } + this.length = n; +} + +/* Some basic logical functions had to be rewritten because of a bug in + * Javascript.. Just try to compute 0xffffffff >> 4 with it.. + * Of course, these functions are slower than the original would be, but + * at least, they work! + */ + +function integer(n) { + return n % (0xffffffff + 1); +} + +function shr(a, b) { + a = integer(a); + b = integer(b); + if (a - 0x80000000 >= 0) { + a = a % 0x80000000; + a >>= b; + a += 0x40000000 >> (b - 1); + } else { + a >>= b; + } + return a; +} + +function shl1(a) { + a = a % 0x80000000; + if (a & 0x40000000 == 0x40000000) { + a -= 0x40000000; + a *= 2; + a += 0x80000000; + } else { + a *= 2; + } + return a; +} + +function shl(a, b) { + a = integer(a); + b = integer(b); + for (var i = 0; i < b; i++) { + a = shl1(a); + } + return a; +} + +function and(a, b) { + a = integer(a); + b = integer(b); + var t1 = a - 0x80000000; + var t2 = b - 0x80000000; + if (t1 >= 0) { + if (t2 >= 0) { + return ((t1 & t2) + 0x80000000); + } else { + return (t1 & b); + } + } else { + if (t2 >= 0) { + return (a & t2); + } else { + return (a & b); + } + } +} + +function or(a, b) { + a = integer(a); + b = integer(b); + var t1 = a - 0x80000000; + var t2 = b - 0x80000000; + if (t1 >= 0) { + if (t2 >= 0) { + return ((t1 | t2) + 0x80000000); + } else { + return ((t1 | b) + 0x80000000); + } + } else { + if (t2 >= 0) { + return ((a | t2) + 0x80000000); + } else { + return (a | b); + } + } +} + +function xor(a, b) { + a = integer(a); + b = integer(b); + var t1 = a - 0x80000000; + var t2 = b - 0x80000000; + if (t1 >= 0) { + if (t2 >= 0) { + return (t1 ^ t2); + } else { + return ((t1 ^ b) + 0x80000000); + } + } else { + if (t2 >= 0) { + return ((a ^ t2) + 0x80000000); + } else { + return (a ^ b); + } + } +} + +function not(a) { + a = integer(a); + return 0xffffffff - a; +} + +/* Here begin the real algorithm */ + +var state = new array(4); +var count = new array(2); + count[0] = 0; + count[1] = 0; +var buffer = new array(64); +var transformBuffer = new array(16); +var digestBits = new array(16); + +var S11 = 7; +var S12 = 12; +var S13 = 17; +var S14 = 22; +var S21 = 5; +var S22 = 9; +var S23 = 14; +var S24 = 20; +var S31 = 4; +var S32 = 11; +var S33 = 16; +var S34 = 23; +var S41 = 6; +var S42 = 10; +var S43 = 15; +var S44 = 21; + +function F(x, y, z) { + return or(and(x, y), and(not(x), z)); +} + +function G(x, y, z) { + return or(and(x, z), and(y, not(z))); +} + +function H(x, y, z) { + return xor(xor(x, y), z); +} + +function I(x, y, z) { + return xor(y ,or(x , not(z))); +} + +function rotateLeft(a, n) { + return or(shl(a, n), (shr(a, (32 - n)))); +} + +function FF(a, b, c, d, x, s, ac) { + a = a + F(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function GG(a, b, c, d, x, s, ac) { + a = a + G(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function HH(a, b, c, d, x, s, ac) { + a = a + H(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function II(a, b, c, d, x, s, ac) { + a = a + I(b, c, d) + x + ac; + a = rotateLeft(a, s); + a = a + b; + return a; +} + +function transform(buf, offset) { + var a = 0, b = 0, c = 0, d = 0; + var x = transformBuffer; + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + for (i = 0; i < 16; i++) { + x[i] = and(buf[i * 4 + offset], 0xFF); + for (j = 1; j < 4; j++) { + x[i] += shl(and(buf[i * 4 + j + offset] ,0xFF), j * 8); + } + } + + /* Round 1 */ + a = FF( a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + d = FF( d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + c = FF( c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + b = FF( b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + a = FF( a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + d = FF( d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + c = FF( c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + b = FF( b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + a = FF( a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + d = FF( d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + c = FF( c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + b = FF( b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + a = FF( a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + d = FF( d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + c = FF( c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + b = FF( b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + a = GG( a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + d = GG( d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + c = GG( c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + b = GG( b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + a = GG( a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + d = GG( d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + c = GG( c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + b = GG( b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + a = GG( a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + d = GG( d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + c = GG( c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + b = GG( b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + a = GG( a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + d = GG( d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + c = GG( c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + b = GG( b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + a = HH( a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + d = HH( d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + c = HH( c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + b = HH( b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + a = HH( a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + d = HH( d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + c = HH( c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + b = HH( b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + a = HH( a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + d = HH( d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + c = HH( c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + b = HH( b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + a = HH( a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + d = HH( d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + c = HH( c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + b = HH( b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + a = II( a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + d = II( d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + c = II( c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + b = II( b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + a = II( a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + d = II( d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + c = II( c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + b = II( b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + a = II( a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + d = II( d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + c = II( c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + b = II( b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + a = II( a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + d = II( d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + c = II( c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + b = II( b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + +} + +function init() { + count[0] = count[1] = 0; + state[0] = 0x67452301; + state[1] = 0xefcdab89; + state[2] = 0x98badcfe; + state[3] = 0x10325476; + for (i = 0; i < digestBits.length; i++) { + digestBits[i] = 0; + } +} + +function update(b) { + var index, i; + + index = and(shr(count[0],3) , 0x3F); + if (count[0] < 0xFFFFFFFF - 7) { + count[0] += 8; + } else { + count[1]++; + count[0] -= 0xFFFFFFFF + 1; + count[0] += 8; + } + buffer[index] = and(b, 0xff); + if (index >= 63) { + transform(buffer, 0); + } +} + +function finish() { + var bits = new array(8); + var padding; + var i = 0, index = 0, padLen = 0; + + for (i = 0; i < 4; i++) { + bits[i] = and(shr(count[0], (i * 8)), 0xFF); + } + for (i = 0; i < 4; i++) { + bits[i + 4] = and(shr(count[1], (i * 8)), 0xFF); + } + index = and(shr(count[0], 3), 0x3F); + padLen = (index < 56) ? (56 - index) : (120 - index); + padding = new array(64); + padding[0] = 0x80; + for (i = 0; i < padLen; i++) { + update(padding[i]); + } + for (i = 0; i < 8; i++) { + update(bits[i]); + } + + for (i = 0; i < 4; i++) { + for (j = 0; j < 4; j++) { + digestBits[i * 4 + j] = and(shr(state[i], (j * 8)) , 0xFF); + } + } +} + +/* End of the MD5 algorithm */ + +function gen() { + window.status = "Generating..."; + document.getElementById('onetime').pad.value = ""; + + lower = document.getElementById('onetime').textcase.selectedIndex == 0; + upper = document.getElementById('onetime').textcase.selectedIndex == 1; + mixed = document.getElementById('onetime').textcase.selectedIndex == 2; + rsep = document.getElementById('onetime').rsep.checked; + if (!(numeric = document.getElementById('onetime').keytype[0].checked)) { + english = document.getElementById('onetime').keytype[1].checked; + gibberish = document.getElementById('onetime').keytype[3].checked; + } + clockseed = document.getElementById('onetime').seedy[0].checked + makesig = document.getElementById('onetime').dosig.checked; + npass = document.getElementById('onetime').nkeys.value; + pw_length = Math.round(document.getElementById('onetime').klength.value); + sep = document.getElementById('onetime').sep.value; + linelen = document.getElementById('onetime').linelen.value; +// 01234567890123456789012345678901 + charcodes = " " + + "!\"#$%&'()*+,-./0123456789:;<=>?" + + "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_" + + "`abcdefghijklmnopqrstuvwxyz{|}~"; + + if (clockseed) { + var n, j, ran0; + + /* Obtain seed from the clock. To reduce the likelihood + of the seed being guessed, we create the seed by combining + the time of the request with the time the page was loaded, + then use that composite value to seed an auxiliary generator + which is cycled between one and 32 times based on the time + derived initial seed, with the output of the generator fed + back into the seed we use to generate the pad. */ + + seed = Math.round((new Date()).getTime() % Math.pow(2, 31)); + ran0 = new LEcuyer((seed ^ Math.round(loadTime % Math.pow(2, 31))) & 0x7FFFFFFF); + for (j = 0; j < (5 + ((seed >> 3) & 0xF)); j++) { + n = ran0.nextInt(31); + } + while (n-- >= 0) { + seed = ((seed << 11) | (seed >>> (32 - 11))) ^ ran0.next(); + } + seed &= 0x7FFFFFFF; + document.getElementById('onetime').seeder.value = seed; + } else { + var useed, seedNum; + + /* Obtain seed from user specification. If the seed is a + decimal number, use it as-is. If it contains any + non-numeric characters, construct a hash code and + use that as the seed. */ + + useed = document.getElementById('onetime').seeder.value; + seedNum = true; + for (i = 0; i < useed.length; i++) { + if ("0123456789".indexOf(useed.charAt(i)) == -1) { + seedNum = false; + break; + } + } + if (seedNum) { + seed = Math.round(Math.floor(document.getElementById('onetime').seeder.value) % Math.pow(2, 31)); + document.getElementById('onetime').seeder.value = seed; + } else { + var s, t, iso, hex; + + iso = ""; + hex = "0123456789ABCDEF"; + for (i = 32; i < 256; i++) { + if (i < 127 || i >= 160) { + // Why not "s = i.toString(16);"? Doesn't work in Netscape 3.0 + iso += "%" + hex.charAt(i >> 4) + hex.charAt(i & 0xF); + } + } + iso = unescape(iso); + s = 0; + for (i = 0; i < useed.length; i++) { + t = iso.indexOf(useed.charAt(i)); + if (t < 0) { + t = 17; + } + s = 0x7FFFFFFF & (((s << 5) | (s >> (32 - 5))) ^ t); + } + seed = s; + } + } + ran1 = new LEcuyer(seed); + ran2 = new LEcuyer(seed); + if (rsep) { + /* Use a separate random generator for separators + so that results are the same for a given seed + for both choices of separators. */ + sepran = new LEcuyer(seed); + } + + ndig = 1; + j = 10; + while (npass >= j) { + ndig++; + j *= 10; + } + pw_item = pw_length + (sep > 0 ? (pw_length / sep) : 0); + pw_item += ndig + 5; + j = pw_item * 3; + if (j < 132) { + j = 132; + } + npline = Math.floor(linelen / pw_item); + if (npline < 1) { + npline = 0; + } + v = ""; + md5v = ""; + lineno = 0; + if (!numeric) { + letters = "abcdefghijklmnopqrstuvwxyz"; + if (upper) { + letters = letters.toUpperCase(); + } + if (english) { + + // Frequency of English digraphs (from D. Edwards 1/27/66) + + frequency = new Array( + new Array(4, 20, 28, 52, 2, 11, 28, 4, 32, 4, 6, 62, + 23, 167, 2, 14, 0, 83, 76, 127, 7, 25, 8, 1, + 9, 1), /* aa - az */ + + new Array(13, 0, 0, 0, 55, 0, 0, 0, 8, 2, 0, 22, 0, 0, + 11, 0, 0, 15, 4, 2, 13, 0, 0, 0, 15, 0), /* ba - bz */ + + new Array(32, 0, 7, 1, 69, 0, 0, 33, 17, 0, 10, 9, 1, + 0, 50, 3, 0, 10, 0, 28, 11, 0, 0, 0, 3, 0), /* ca - cz */ + + new Array(40, 16, 9, 5, 65, 18, 3, 9, 56, 0, 1, 4, 15, + 6, 16, 4, 0, 21, 18, 53, 19, 5, 15, 0, 3, 0), /* da - dz */ + + new Array(84, 20, 55, 125, 51, 40, 19, 16, 50, 1, 4, + 55, 54, 146, 35, 37, 6, 191, 149, 65, 9, 26, + 21, 12, 5, 0), /* ea - ez */ + + new Array(19, 3, 5, 1, 19, 21, 1, 3, 30, 2, 0, 11, 1, + 0, 51, 0, 0, 26, 8, 47, 6, 3, 3, 0, 2, 0), /* fa - fz */ + + new Array(20, 4, 3, 2, 35, 1, 3, 15, 18, 0, 0, 5, 1, + 4, 21, 1, 1, 20, 9, 21, 9, 0, 5, 0, 1, 0), /* ga - gz */ + + new Array(101, 1, 3, 0, 270, 5, 1, 6, 57, 0, 0, 0, 3, + 2, 44, 1, 0, 3, 10, 18, 6, 0, 5, 0, 3, 0), /* ha - hz */ + + new Array(40, 7, 51, 23, 25, 9, 11, 3, 0, 0, 2, 38, + 25, 202, 56, 12, 1, 46, 79, 117, 1, 22, 0, + 4, 0, 3), /* ia - iz */ + + new Array(3, 0, 0, 0, 5, 0, 0, 0, 1, 0, 0, 0, 0, 0, 4, + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0), /* ja - jz */ + + new Array(1, 0, 0, 0, 11, 0, 0, 0, 13, 0, 0, 0, 0, 2, + 0, 0, 0, 0, 6, 2, 1, 0, 2, 0, 1, 0), /* ka - kz */ + + new Array(44, 2, 5, 12, 62, 7, 5, 2, 42, 1, 1, 53, 2, + 2, 25, 1, 1, 2, 16, 23, 9, 0, 1, 0, 33, 0), /* la - lz */ + + new Array(52, 14, 1, 0, 64, 0, 0, 3, 37, 0, 0, 0, 7, + 1, 17, 18, 1, 2, 12, 3, 8, 0, 1, 0, 2, 0), /* ma - mz */ + + new Array(42, 10, 47, 122, 63, 19, 106, 12, 30, 1, 6, + 6, 9, 7, 54, 7, 1, 7, 44, 124, 6, 1, 15, 0, + 12, 0), /* na - nz */ + + new Array(7, 12, 14, 17, 5, 95, 3, 5, 14, 0, 0, 19, + 41, 134, 13, 23, 0, 91, 23, 42, 55, 16, 28, + 0, 4, 1), /* oa - oz */ + + new Array(19, 1, 0, 0, 37, 0, 0, 4, 8, 0, 0, 15, 1, 0, + 27, 9, 0, 33, 14, 7, 6, 0, 0, 0, 0, 0), /* pa - pz */ + + new Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0), /* qa - qz */ + + new Array(83, 8, 16, 23, 169, 4, 8, 8, 77, 1, 10, 5, + 26, 16, 60, 4, 0, 24, 37, 55, 6, 11, 4, 0, + 28, 0), /* ra - rz */ + + new Array(65, 9, 17, 9, 73, 13, 1, 47, 75, 3, 0, 7, + 11, 12, 56, 17, 6, 9, 48, 116, 35, 1, 28, 0, + 4, 0), /* sa - sz */ + + new Array(57, 22, 3, 1, 76, 5, 2, 330, 126, 1, 0, 14, + 10, 6, 79, 7, 0, 49, 50, 56, 21, 2, 27, 0, + 24, 0), /* ta - tz */ + + new Array(11, 5, 9, 6, 9, 1, 6, 0, 9, 0, 1, 19, 5, 31, + 1, 15, 0, 47, 39, 31, 0, 3, 0, 0, 0, 0), /* ua - uz */ + + new Array(7, 0, 0, 0, 72, 0, 0, 0, 28, 0, 0, 0, 0, 0, + 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0), /* va - vz */ + + new Array(36, 1, 1, 0, 38, 0, 0, 33, 36, 0, 0, 4, 1, + 8, 15, 0, 0, 0, 4, 2, 0, 0, 1, 0, 0, 0), /* wa - wz */ + + new Array(1, 0, 2, 0, 0, 1, 0, 0, 3, 0, 0, 0, 0, 0, 1, + 5, 0, 0, 0, 3, 0, 0, 1, 0, 0, 0), /* xa - xz */ + + new Array(14, 5, 4, 2, 7, 12, 12, 6, 10, 0, 0, 3, 7, + 5, 17, 3, 0, 4, 16, 30, 0, 0, 5, 0, 0, 0), /* ya - yz */ + + new Array(1, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0) /* za - zz */ ); + + // This MUST be equal to the sum of the equivalent rows above. + + row_sums = new Array( + 796, 160, 284, 401, 1276, 262, 199, 539, 777, + 16, 39, 351, 243, 751, 662, 181, 17, 683, + 662, 968, 248, 115, 180, 17, 162, 5 + ); + + // Frequencies of starting characters. + + start_freq = new Array( + 1299, 425, 725, 271, 375, 470, 93, 223, 1009, + 24, 20, 355, 379, 319, 823, 618, 21, 317, + 962, 1991, 271, 104, 516, 6, 16, 14 + ); + + // This MUST be equal to the sum of all elements in the above array. + + total_sum = 11646; + } + if (gibberish) { + gibber = "abcdefghijklmnopqrstuvwxyz" + + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + + "0123456789" + + "!#$%&()*+,-./:;<=>?@[]^_{|}~"; + if (upper) { + /* Convert to upper case, leaving two copies of the + alphabet for two reasons: first, to favour letters + over gnarl, and second, to change only the letter case + when the mode is selected. */ + gibber = gibber.toUpperCase(); + } else if (lower) { + gibber = gibber.toLowerCase(); + } + } + } + for (line = 1; line <= npass; line++) { + password = ""; + if (numeric) { + for (nchars = 0; nchars < pw_length; nchars++) { + if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) { + password += sepchar(); + } + password += ran1.nextInt(9); + } + } else if (!english) { + for (nchars = 0; nchars < pw_length; nchars++) { + if ((sep > 0) && ((nchars % sep) == 0) && (nchars > 0)) { + password += sepchar(); + } + if (gibberish) { + password += gibber.charAt(ran1.nextInt(gibber.length - 1)); + } else { + password += letters.charAt(ran1.nextInt(25)); + } + } + } else { + position = ran1.nextInt(total_sum - 1); + for (row_position = 0, j = 0; position >= row_position; + row_position += start_freq[j], j++) { + continue; + } + + password = letters.charAt(i = j - 1); + nch = 1; + for (nchars = pw_length - 1; nchars; --nchars) { + + // Now find random position within the row. + + position = ran1.nextInt(row_sums[i] - 1); + for (row_position = 0, j = 0; + position >= row_position; + row_position += frequency[i][j], j++) { + } + + if ((sep > 0) && ((nch % sep) == 0)) { + password += sepchar(); + } + nch++; + password += letters.charAt(i = j - 1); + } + } + + if ((!numeric) && (!gibberish) && mixed) { + var pwm = ''; + var j; + for (j = 0; j < password.length; j++) { + pwm += ran2.nextInt(1) ? (password.charAt(j)) : (password.charAt(j).toUpperCase()); + } + password = pwm; + } + + /* If requested, calculate the MD5 signature for this key and + and save for later appending to the results. */ + + if (makesig) { + var n, m, hex = "0123456789ABCDEF"; + + init(); + for (m = 0; m < password.length; m++) { + update(32 + charcodes.indexOf(password.charAt(m))); + } + finish(); + + for (n = 0; n < 16; n++) { + md5v += hex.charAt(digestBits[n] >> 4); + md5v += hex.charAt(digestBits[n] & 0xF); + } + md5v += "\n"; + } + + aline = "" + line; + while (aline.length < ndig) { + aline = " " + aline; + } + v += aline + ") " + password; + + if ((++lineno) >= npline) { + v += "\n"; + lineno = 0; + } else { + v += " "; + } + } + + if (makesig) { + v += "\n---------- MD5 Signatures ----------\n" + md5v; + } + + document.getElementById('onetime').pad.value = v; + window.status = "Done."; +} + +function loadHandler() { + for (var i = 0; i < 25; i++) { + gen(); + } +}; + +//]]> +</script> + +</head> + +<body class="jsgen" onload="loadHandler();"> + +<h1><img src="key.gif" class="keyicon" alt="" + width="40" height="40" /> One-Time Pad Generator</h1> + +<p> +This page, which requires that your browser support JavaScript +(see <a href="#why"><cite>Why JavaScript</cite></a> below), +generates one-time pads or password lists in a variety of +forms. It is based a high-quality pseudorandom sequence +generator, which can be seeded either from the current date +and time, or from a seed you provide. Fill in the form below +to select the format of the pad and press “Generate” to +create the pad in the text box. You can then copy and paste +the generated pad into another window to use as you wish. +Each of the labels on the request form is linked to a description +of that parameter. +</p> + +<form id="onetime" action="#" onsubmit="return false;"> + +<p class="centre"> +<b>Output:</b> +<a href="#NumberOfKeys">Number of keys</a>: <input type="text" name="nkeys" value="20" size="4" maxlength="12" /> +<a href="#LineLength">Line length</a>: <input type="text" name="linelen" value="48" size="3" maxlength="12" /> +<br /> +<b>Format:</b> +<a href="#KeyLength">Key length</a>: <input type="text" name="klength" value="8" size="3" maxlength="12" /> +<a href="#GroupLength">Group length</a>: <input type="text" name="sep" value="4" size="2" maxlength="12" /> + +<br /> +<b>Composition:</b> +<a href="#KeyText">Key text</a>: <input type="radio" name="keytype" /> Numeric +<input type="radio" name="keytype" /> Word-like +<input type="radio" name="keytype" checked="checked" /> Alphabetic +<input type="radio" name="keytype" /> Gibberish +<br /> +<a href="#LetterCase">Letters:</a> +<select size="i" name="textcase"> + + <option value="1" selected="selected">Lower case</option> + <option value="2">Upper case</option> + <option value="3">Mixed case</option> +</select> + +<input type="checkbox" name="rsep" /> <a href="#RandomSep">Random separators</a> +<input type="checkbox" name="dosig" /> <a href="#Signatures">Include signatures</a> + +<br /> +<b><a href="#Seed">Seed:</a></b> +<input type="radio" name="seedy" checked="checked" /> From clock +<input type="radio" name="seedy" /> User-defined: +<input type="text" name="seeder" value="" size="12" maxlength="128" + onchange="document.getElementById('onetime').seedy[1].checked=true;" /> +<br /> +<input type="button" value=" Generate " onclick="gen();" /> + +<input type="button" value=" Clear " onclick="document.getElementById('onetime').pad.value = '';" /> + +<input type="button" value=" Select " onclick="document.getElementById('onetime').pad.select();" /><br /> +<textarea name="pad" rows="12" cols="72"> + +Uh, oh. It appears your browser either does not support +JavaScript or that JavaScript has been disabled. You'll +have to replace your browser with one supporting JavaScript +(or enable it, if that's the problem) before you can use +this page. +</textarea> +</p> + +</form> + +<script type="text/javascript"> +//<![CDATA[ + // Clear out "sorry, no JavaScript" message from text box. + document.getElementById('onetime').pad.value = ""; +//]]> +</script> + +<h2><a name="details">Details</a></h2> + +<p> +Each of the fields in the one-time pad request form is described +below. +</p> + +<h3><a name="output">Output</a></h3> + +<h4><a name="NumberOfKeys">Number of keys</a></h4> + +<p> +Enter the number of keys you'd like to generate. If you generate +more than fit in the results text box, you can use the scroll +bar to view the additional lines. +</p> + +<h4><a name="LineLength">Line length</a></h4> + +<p> +Lines in the output will be limited to the given length (or contain +only one key if the line length is less than required for a single +key). If the line length is greater than the width of the results +box, you can use the horizontal scroll bar to view the rest of the +line. Enter <tt>0</tt> to force one key per line; this is handy +when you're preparing a list of keys to be read by a computer program. +</p> + +<h3><a name="format">Format</a></h3> + +<h4><a name="KeyLength">Key length</a></h4> + +<p> +Each key will contain this number of characters, not counting +separators between groups. +</p> + +<h4><a name="GroupLength">Group length</a></h4> + +<p> +If a nonzero value is entered in this field, the key will be broken +into groups of the given number of characters by separators. Humans +find it easier to read and remember sequences of characters when +divided into groups of five or fewer characters. +</p> + +<h3><a name="composition">Composition</a></h3> + +<h4><a name="KeyText">Key text</a></h4> + +<p> +This set of radio buttons lets you select the character set used in +the keys. The alternatives are listed in order of +increasing security. +</p> + +<blockquote> +<dl> +<dt><b>Numeric</b></dt> +<dd>Keys contain only the decimal digits “0” through “9”. + <em>Least secure.</em></dd> + +<dt><b>Word-like</b></dt> +<dd>Keys are composed of alphabetic characters which obey the + digraph statistics of English text. Such keys contain + sequences of vowels and consonants familiar to speakers + of Western languages, and are therefore usually easier to + memorise but, for a given key length, are less secure than + purely random letters.</dd> + +<dt><b>Alphabetic</b></dt> +<dd>Keys consist of letters of the alphabet chosen at random. + Each character has an equal probability of being one of + the 26 letters.</dd> + +<dt><b>Gibberish</b></dt> +<dd>Keys use most of the printable ASCII character set, excluding + only characters frequently used for quoting purposes. This + option provides the greatest security for a given key length, + but most people find keys like this difficult to memorise or + even transcribe from a printed pad. If a human is in the loop, + it's often better to use a longer alphabetic or word-like key. + <em>Most secure.</em></dd> +</dl> + +</blockquote> + +<h4><a name="LetterCase">Letters</a></h4> + +<p> +The case of letters in keys generated with Word-like, Alphabetic, and +Gibberish key text will be as chosen. Most people find it easier to +read lower case letters than all capitals, but for some applications +(for example, where keys must be scanned optically by hardware that +only recognises capital letters), capitals are required. Selecting +“Mixed case” creates keys with a mix of upper- and +lower-case letters; such keys are more secure than those with uniform +letter case, but do not pass the “telephone test”: you +can't read them across a (hopefully secure) voice link without having +to indicate whether each letter is or is not a capital. +</p> + +<h4><a name="RandomSep">Random separators</a></h4> + +<p> +When the <a href="#KeyLength">Key length</a> is longer than +a nonzero <a href="#GroupLength">Group length</a> specification, +the key is divided into sequences of the given group length +by separator characters. By default, a hyphen, “<tt>-</tt>”, is used +to separate groups. If you check this box, separators will be +chosen at random among punctuation marks generally acceptable +for applications such as passwords. If you're generating passwords +for a computer system, random separators dramatically increase +the difficulty of guessing passwords by exhaustive search. +</p> + +<h4><a name="Signatures">Include signatures</a></h4> + +<p> + +When this box is checked, at the end of the list of keys, preceded by +a line beginning with ten dashes “<tt>-</tt>”, the 128 bit MD5 signature of +each key is given, one per line, with signatures expressed as 32 +hexadecimal digits. Key signatures can be used to increase security +when keys are used to control access to computer systems or databases. +Instead of storing a copy of the keys, the computer stores their +signatures. When the user enters a key, its signature is computed +with the same MD5 algorithm used to generate it initially, and the key +is accepted only if the signature matches. Since discovering +a key which will generate a given signature is believed to be +computationally prohibitive, even if the list of signatures stored on +the computer is compromised, that information will not permit an +intruder to deduce a valid key. +</p> + +<p> +Signature calculation is a computationally intense process for which +JavaScript is not ideally suited; be patient while signatures are +generated, especially if your computer has modest +processing speed. +</p> + +<p> +For signature-based validation to be secure, it is essential +the original keys be long enough to prohibit discovery of matching +signatures by exhaustive search. Suppose, for example, one used +four digit numeric keys, as used for Personal Identification +Numbers (PINs) by many credit card systems. Since only 10,000 +different keys exist, one could simply compute the signatures of +every possible key from 0000 through 9999, permitting an attacker who +came into possession of the table of signatures to recover the +keys by a simple lookup process. For maximum security, keys must +contain at least as much information as the 128 bit signatures +computed from them. This implies a minimum key length (not counting +non-random separator characters) for the various key formats as +follows: +</p> + +<table class="c" border="border" cellpadding="4"> +<tr><th>Key Composition</th> <th>Minimum Characters</th></tr> + +<tr><td>Numeric</td> <td class="c">39</td></tr> +<tr><td>Word-like</td> <td class="c">30</td></tr> +<tr><td>Alphabetic</td> <td class="c">28</td></tr> +<tr><td>Gibberish</td> <td class="c">20</td></tr> +</table> + +<p> +It should be noted that for many practical applications there is no +need for anything approaching 128-bit security. The guidelines above +apply only in the case where maximum protection in the event of +undetected compromise of key signatures occurs. In many +cases, much shorter keys are acceptable, especially when it is assumed +that a compromise of the system's password or signature database would +be only part of a much more serious subversion of all resources +on the system. +</p> + +<h3><a name="Seed">Seed</a></h3> + +<p> +The <em>seed</em> is the starting value which determines all +subsequent values in the pseudorandom sequence used to generate +the one-time pad. Given the seed, the pad can be reproduced. The +seed is a 31-bit number which can be derived from the date and +time at which the one-time pad was requested, or from a +user-defined seed value. If the user-defined seed consists +entirely of decimal digits, it is used directly as the seed, +modulo 2<sup>31</sup>; if a string containing non-digit characters +is entered, it is used to compute a <em>hash code</em> which is +used to seed the generator. + +</p> + +<p> +When the clock is used to create the seed, the seed value is entered +in the User-defined box to allow you, by checking “User-defined”, +to produce additional pads with the same seed. +</p> + +<h2><a name="why">Why JavaScript?</a></h2> + +<p> +At first glance, JavaScript may seem an odd choice for programming +a page such as this. The one-time pad generator program is rather +large and complicated, and downloading it to your browser takes longer +than would be required for a Java applet or to transfer a +one-time pad generated by a CGI program on the Web server. I chose +JavaScript for two reasons: <em>security</em> and <em>transparency</em>. + +</p> + +<p> +<b>Security.</b> +The sole reason for the existence of one-time pads is to +provide a source of information known only to people to whom +they have been distributed in a secure manner. This means +the generation process cannot involve any link whose security +is suspect. If the pad were generated on a Web server and +transmitted to you, it would have to pass over the +Internet, where any intermediate site might make a copy +of your pad before you even received it. Even if some +mechanism such as encryption could absolutely prevent the +pad's being intercepted, you'd still have no way to be sure +the site generating the pad didn't keep a copy +in a file, conveniently tagged with your Internet address. +</p> + +<p> +In order to have any degree of security, it is essential +that the pad be generated on <em>your</em> computer, without +involving any transmission or interaction with other +sites on the Internet. A Web browser with JavaScript makes +this possible, since the generation program embedded in this +page runs entirely on your own computer and does not +transmit anything over the Internet. Its output appears +only in the text box, allowing you to cut and paste it +to another application. From there on, its security is +up to you. +</p> + +<p> +Security is never absolute. A one-time pad generated with +this page might be compromised in a variety of ways, including +the following: + +</p> + +<ul> +<li> Your Web browser and/or JavaScript interpreter may + contain bugs or deliberate security violations + which report activity on your computer back to some + other Internet site.</li> + +<li> Some other applet running on another page of your + browser, perhaps without your being aware of its + existence, is spying on other windows.</li> + +<li> Some other application running on your computer + may have compromised your system's security and + be snooping on your activity.</li> + +<li> Your Web browser may be keeping a “history log” + + or “cache” of data you generate. Somebody may + come along later and recover a copy of the pad + from that log.</li> + +<li> The implementation of this page may contain a bug + or deliberate error which makes its output + predictable. This is why <a href="#trans"><cite>transparency</cite></a>, + discussed below, is essential.</li> + +<li> Your computer's security may have been compromised + physically; when's the last time you checked that a + bug that transmits your keystrokes and/or screen + contents to that white van parked down the street + wasn't lurking inside your computer cabinet?</li> +</ul> + +<p> +One can whip oneself into a fine fever of paranoia worrying about +things like this. One way to rule out the most probable risks +is to download a copy of the generator page and run it +from a “<tt>file:</tt>” URL on a computer which has no network +connection whatsoever and is located in a secure location +under your control. And look very carefully at any files +created by your Web browser. You may find the most interesting +things squirreled away there…. +</p> + +<p> +<b><a name="trans">Transparency</a>.</b> +Any security-related tool is only as good as its design +and implementation. <em>Transparency</em> means that, in +essence, all the moving parts are visible so you can judge +for yourself whether the tool merits your confidence. In +the case of a program, this means that source code must +be available, and that you can verify that the program +you're running corresponds to the source code provided. + +</p> + +<p> +The very nature of JavaScript achieves this transparency. +The program is embedded into this actual Web page; to +examine it you need only use your browser's “View Source” +facility, or save the page into a file on your computer +and read it with a text editor. JavaScript's being +an interpreted language eliminates the risk of your running +a program different from the purported source code: with +an interpreted language what you read is what you run. +</p> + +<p> +Transparency is important even if you don't know enough about +programming or security to determine whether the program +contains any flaws. The very fact that it can be examined +by anybody allows those with the required expertise to pass +judgment, and you can form your own conclusions based on +their analysis. +</p> + +<h2>Credits</h2> + +<p> + +The pseudorandom sequence generator is based on L'Ecuyer's +two-sequence generator as described in +<cite>Communications of the ACM</cite>, Vol. 31 (1968), page 742. +A Bays-Durham shuffle is used to guard against regularities +lurking in L'Ecuyer's algorithm; see +<cite>ACM Transactions on Mathematical Software</cite>, Vol. 2 (1976) +pages 59–64 for details. +</p> + +<p> +The JavaScript implementation of the +<a href="http://www.ietf.org/rfc/rfc1321.txt"><b>MD5 message-digest algorithm</b></a> +was developed by Henri Torgemane; please view the source code of this +page to examine the code, including the copyright notice and +conditions of use. The MD5 algorithm was developed by Ron Rivest. +</p> + +<p /> + +<hr /> + +<p /> + +<table class="r"> +<tr><td align="center"> + <a class="i" href="http://validator.w3.org/check?uri=referer"><img + class="button" + src="valid-xhtml10.png" + alt="Valid XHTML 1.0" height="31" width="88" /></a> +</td></tr> +</table> + +<address> +by <a href="/">John Walker</a><br /> +May 26, 1997<br /> + +Updated: November 2006 +</address> + +<p class="centre"> +<em>This document is in the public domain.</em> +</p> +</body> +</html> + diff --git a/build/pgo/js-input/key.gif b/build/pgo/js-input/key.gif Binary files differnew file mode 100644 index 000000000..050311fc6 --- /dev/null +++ b/build/pgo/js-input/key.gif diff --git a/build/pgo/js-input/sunspider/3d-cube.html b/build/pgo/js-input/sunspider/3d-cube.html new file mode 100644 index 000000000..453167d44 --- /dev/null +++ b/build/pgo/js-input/sunspider/3d-cube.html @@ -0,0 +1,387 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider 3d-cube</title> + +</head> + +<body> +<h3>3d-cube</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// 3D Cube Rotation +// http://www.speich.net/computer/moztesting/3d.htm +// Created by Simon Speich + +var Q = new Array(); +var MTrans = new Array(); // transformation matrix +var MQube = new Array(); // position information of qube +var I = new Array(); // entity matrix +var Origin = new Object(); +var Testing = new Object(); +var LoopTimer; + +var DisplArea = new Object(); +DisplArea.Width = 300; +DisplArea.Height = 300; + +function DrawLine(From, To) { + var x1 = From.V[0]; + var x2 = To.V[0]; + var y1 = From.V[1]; + var y2 = To.V[1]; + var dx = Math.abs(x2 - x1); + var dy = Math.abs(y2 - y1); + var x = x1; + var y = y1; + var IncX1, IncY1; + var IncX2, IncY2; + var Den; + var Num; + var NumAdd; + var NumPix; + + if (x2 >= x1) { IncX1 = 1; IncX2 = 1; } + else { IncX1 = -1; IncX2 = -1; } + if (y2 >= y1) { IncY1 = 1; IncY2 = 1; } + else { IncY1 = -1; IncY2 = -1; } + if (dx >= dy) { + IncX1 = 0; + IncY2 = 0; + Den = dx; + Num = dx / 2; + NumAdd = dy; + NumPix = dx; + } + else { + IncX2 = 0; + IncY1 = 0; + Den = dy; + Num = dy / 2; + NumAdd = dx; + NumPix = dy; + } + + NumPix = Math.round(Q.LastPx + NumPix); + + var i = Q.LastPx; + for (; i < NumPix; i++) { + Num += NumAdd; + if (Num >= Den) { + Num -= Den; + x += IncX1; + y += IncY1; + } + x += IncX2; + y += IncY2; + } + Q.LastPx = NumPix; +} + +function CalcCross(V0, V1) { + var Cross = new Array(); + Cross[0] = V0[1]*V1[2] - V0[2]*V1[1]; + Cross[1] = V0[2]*V1[0] - V0[0]*V1[2]; + Cross[2] = V0[0]*V1[1] - V0[1]*V1[0]; + return Cross; +} + +function CalcNormal(V0, V1, V2) { + var A = new Array(); var B = new Array(); + for (var i = 0; i < 3; i++) { + A[i] = V0[i] - V1[i]; + B[i] = V2[i] - V1[i]; + } + A = CalcCross(A, B); + var Length = Math.sqrt(A[0]*A[0] + A[1]*A[1] + A[2]*A[2]); + for (var i = 0; i < 3; i++) A[i] = A[i] / Length; + A[3] = 1; + return A; +} + +function CreateP(X,Y,Z) { + this.V = [X,Y,Z,1]; +} + +// multiplies two matrices +function MMulti(M1, M2) { + var M = [[],[],[],[]]; + var i = 0; + var j = 0; + for (; i < 4; i++) { + j = 0; + for (; j < 4; j++) M[i][j] = M1[i][0] * M2[0][j] + M1[i][1] * M2[1][j] + M1[i][2] * M2[2][j] + M1[i][3] * M2[3][j]; + } + return M; +} + +//multiplies matrix with vector +function VMulti(M, V) { + var Vect = new Array(); + var i = 0; + for (;i < 4; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2] + M[i][3] * V[3]; + return Vect; +} + +function VMulti2(M, V) { + var Vect = new Array(); + var i = 0; + for (;i < 3; i++) Vect[i] = M[i][0] * V[0] + M[i][1] * V[1] + M[i][2] * V[2]; + return Vect; +} + +// add to matrices +function MAdd(M1, M2) { + var M = [[],[],[],[]]; + var i = 0; + var j = 0; + for (; i < 4; i++) { + j = 0; + for (; j < 4; j++) M[i][j] = M1[i][j] + M2[i][j]; + } + return M; +} + +function Translate(M, Dx, Dy, Dz) { + var T = [ + [1,0,0,Dx], + [0,1,0,Dy], + [0,0,1,Dz], + [0,0,0,1] + ]; + return MMulti(T, M); +} + +function RotateX(M, Phi) { + var a = Phi; + a *= Math.PI / 180; + var Cos = Math.cos(a); + var Sin = Math.sin(a); + var R = [ + [1,0,0,0], + [0,Cos,-Sin,0], + [0,Sin,Cos,0], + [0,0,0,1] + ]; + return MMulti(R, M); +} + +function RotateY(M, Phi) { + var a = Phi; + a *= Math.PI / 180; + var Cos = Math.cos(a); + var Sin = Math.sin(a); + var R = [ + [Cos,0,Sin,0], + [0,1,0,0], + [-Sin,0,Cos,0], + [0,0,0,1] + ]; + return MMulti(R, M); +} + +function RotateZ(M, Phi) { + var a = Phi; + a *= Math.PI / 180; + var Cos = Math.cos(a); + var Sin = Math.sin(a); + var R = [ + [Cos,-Sin,0,0], + [Sin,Cos,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + return MMulti(R, M); +} + +function DrawQube() { + // calc current normals + var CurN = new Array(); + var i = 5; + Q.LastPx = 0; + for (; i > -1; i--) CurN[i] = VMulti2(MQube, Q.Normal[i]); + if (CurN[0][2] < 0) { + if (!Q.Line[0]) { DrawLine(Q[0], Q[1]); Q.Line[0] = true; }; + if (!Q.Line[1]) { DrawLine(Q[1], Q[2]); Q.Line[1] = true; }; + if (!Q.Line[2]) { DrawLine(Q[2], Q[3]); Q.Line[2] = true; }; + if (!Q.Line[3]) { DrawLine(Q[3], Q[0]); Q.Line[3] = true; }; + } + if (CurN[1][2] < 0) { + if (!Q.Line[2]) { DrawLine(Q[3], Q[2]); Q.Line[2] = true; }; + if (!Q.Line[9]) { DrawLine(Q[2], Q[6]); Q.Line[9] = true; }; + if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; }; + if (!Q.Line[10]) { DrawLine(Q[7], Q[3]); Q.Line[10] = true; }; + } + if (CurN[2][2] < 0) { + if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; }; + if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; }; + if (!Q.Line[6]) { DrawLine(Q[6], Q[7]); Q.Line[6] = true; }; + if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; }; + } + if (CurN[3][2] < 0) { + if (!Q.Line[4]) { DrawLine(Q[4], Q[5]); Q.Line[4] = true; }; + if (!Q.Line[8]) { DrawLine(Q[5], Q[1]); Q.Line[8] = true; }; + if (!Q.Line[0]) { DrawLine(Q[1], Q[0]); Q.Line[0] = true; }; + if (!Q.Line[11]) { DrawLine(Q[0], Q[4]); Q.Line[11] = true; }; + } + if (CurN[4][2] < 0) { + if (!Q.Line[11]) { DrawLine(Q[4], Q[0]); Q.Line[11] = true; }; + if (!Q.Line[3]) { DrawLine(Q[0], Q[3]); Q.Line[3] = true; }; + if (!Q.Line[10]) { DrawLine(Q[3], Q[7]); Q.Line[10] = true; }; + if (!Q.Line[7]) { DrawLine(Q[7], Q[4]); Q.Line[7] = true; }; + } + if (CurN[5][2] < 0) { + if (!Q.Line[8]) { DrawLine(Q[1], Q[5]); Q.Line[8] = true; }; + if (!Q.Line[5]) { DrawLine(Q[5], Q[6]); Q.Line[5] = true; }; + if (!Q.Line[9]) { DrawLine(Q[6], Q[2]); Q.Line[9] = true; }; + if (!Q.Line[1]) { DrawLine(Q[2], Q[1]); Q.Line[1] = true; }; + } + Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false]; + Q.LastPx = 0; +} + +function Loop() { + if (Testing.LoopCount > Testing.LoopMax) return; + var TestingStr = String(Testing.LoopCount); + while (TestingStr.length < 3) TestingStr = "0" + TestingStr; + MTrans = Translate(I, -Q[8].V[0], -Q[8].V[1], -Q[8].V[2]); + MTrans = RotateX(MTrans, 1); + MTrans = RotateY(MTrans, 3); + MTrans = RotateZ(MTrans, 5); + MTrans = Translate(MTrans, Q[8].V[0], Q[8].V[1], Q[8].V[2]); + MQube = MMulti(MTrans, MQube); + var i = 8; + for (; i > -1; i--) { + Q[i].V = VMulti(MTrans, Q[i].V); + } + DrawQube(); + Testing.LoopCount++; + Loop(); +} + +function Init(CubeSize) { + // init/reset vars + Origin.V = [150,150,20,1]; + Testing.LoopCount = 0; + Testing.LoopMax = 50; + Testing.TimeMax = 0; + Testing.TimeAvg = 0; + Testing.TimeMin = 0; + Testing.TimeTemp = 0; + Testing.TimeTotal = 0; + Testing.Init = false; + + // transformation matrix + MTrans = [ + [1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + + // position information of qube + MQube = [ + [1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + + // entity matrix + I = [ + [1,0,0,0], + [0,1,0,0], + [0,0,1,0], + [0,0,0,1] + ]; + + // create qube + Q[0] = new CreateP(-CubeSize,-CubeSize, CubeSize); + Q[1] = new CreateP(-CubeSize, CubeSize, CubeSize); + Q[2] = new CreateP( CubeSize, CubeSize, CubeSize); + Q[3] = new CreateP( CubeSize,-CubeSize, CubeSize); + Q[4] = new CreateP(-CubeSize,-CubeSize,-CubeSize); + Q[5] = new CreateP(-CubeSize, CubeSize,-CubeSize); + Q[6] = new CreateP( CubeSize, CubeSize,-CubeSize); + Q[7] = new CreateP( CubeSize,-CubeSize,-CubeSize); + + // center of gravity + Q[8] = new CreateP(0, 0, 0); + + // anti-clockwise edge check + Q.Edge = [[0,1,2],[3,2,6],[7,6,5],[4,5,1],[4,0,3],[1,5,6]]; + + // calculate squad normals + Q.Normal = new Array(); + for (var i = 0; i < Q.Edge.length; i++) Q.Normal[i] = CalcNormal(Q[Q.Edge[i][0]].V, Q[Q.Edge[i][1]].V, Q[Q.Edge[i][2]].V); + + // line drawn ? + Q.Line = [false,false,false,false,false,false,false,false,false,false,false,false]; + + // create line pixels + Q.NumPx = 9 * 2 * CubeSize; + for (var i = 0; i < Q.NumPx; i++) CreateP(0,0,0); + + MTrans = Translate(MTrans, Origin.V[0], Origin.V[1], Origin.V[2]); + MQube = MMulti(MTrans, MQube); + + var i = 0; + for (; i < 9; i++) { + Q[i].V = VMulti(MTrans, Q[i].V); + } + DrawQube(); + Testing.Init = true; + Loop(); +} + +for ( var i = 20; i <= 160; i *= 2 ) { + Init(i); +} + +Q = null; +MTrans = null; +MQube = null; +I = null; +Origin = null; +Testing = null; +LoopTime = null; +DisplArea = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/3d-morph.html b/build/pgo/js-input/sunspider/3d-morph.html new file mode 100644 index 000000000..aca991d39 --- /dev/null +++ b/build/pgo/js-input/sunspider/3d-morph.html @@ -0,0 +1,104 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider 3d-morph</title> + +</head> + +<body> +<h3>3d-morph</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +var loops = 15 +var nx = 120 +var nz = 120 + +function morph(a, f) { + var PI2nx = Math.PI * 8/nx + var sin = Math.sin + var f30 = -(50 * sin(f*Math.PI*2)) + + for (var i = 0; i < nz; ++i) { + for (var j = 0; j < nx; ++j) { + a[3*(i*nx+j)+1] = sin((j-1) * PI2nx ) * -f30 + } + } +} + + +var a = Array() +for (var i=0; i < nx*nz*3; ++i) + a[i] = 0 + +for (var i = 0; i < loops; ++i) { + morph(a, i/loops) +} + +testOutput = 0; +for (var i = 0; i < nx; i++) + testOutput += a[3*(i*nx+i)+1]; +a = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/3d-raytrace.html b/build/pgo/js-input/sunspider/3d-raytrace.html new file mode 100644 index 000000000..2097d4238 --- /dev/null +++ b/build/pgo/js-input/sunspider/3d-raytrace.html @@ -0,0 +1,490 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider 3d-raytrace</title> +</head> + +<body> +<h3>3d-raytrace</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +function createVector(x,y,z) { + return new Array(x,y,z); +} + +function sqrLengthVector(self) { + return self[0] * self[0] + self[1] * self[1] + self[2] * self[2]; +} + +function lengthVector(self) { + return Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); +} + +function addVector(self, v) { + self[0] += v[0]; + self[1] += v[1]; + self[2] += v[2]; + return self; +} + +function subVector(self, v) { + self[0] -= v[0]; + self[1] -= v[1]; + self[2] -= v[2]; + return self; +} + +function scaleVector(self, scale) { + self[0] *= scale; + self[1] *= scale; + self[2] *= scale; + return self; +} + +function normaliseVector(self) { + var len = Math.sqrt(self[0] * self[0] + self[1] * self[1] + self[2] * self[2]); + self[0] /= len; + self[1] /= len; + self[2] /= len; + return self; +} + +function add(v1, v2) { + return new Array(v1[0] + v2[0], v1[1] + v2[1], v1[2] + v2[2]); +} + +function sub(v1, v2) { + return new Array(v1[0] - v2[0], v1[1] - v2[1], v1[2] - v2[2]); +} + +function scalev(v1, v2) { + return new Array(v1[0] * v2[0], v1[1] * v2[1], v1[2] * v2[2]); +} + +function dot(v1, v2) { + return v1[0] * v2[0] + v1[1] * v2[1] + v1[2] * v2[2]; +} + +function scale(v, scale) { + return [v[0] * scale, v[1] * scale, v[2] * scale]; +} + +function cross(v1, v2) { + return [v1[1] * v2[2] - v1[2] * v2[1], + v1[2] * v2[0] - v1[0] * v2[2], + v1[0] * v2[1] - v1[1] * v2[0]]; + +} + +function normalise(v) { + var len = lengthVector(v); + return [v[0] / len, v[1] / len, v[2] / len]; +} + +function transformMatrix(self, v) { + var vals = self; + var x = vals[0] * v[0] + vals[1] * v[1] + vals[2] * v[2] + vals[3]; + var y = vals[4] * v[0] + vals[5] * v[1] + vals[6] * v[2] + vals[7]; + var z = vals[8] * v[0] + vals[9] * v[1] + vals[10] * v[2] + vals[11]; + return [x, y, z]; +} + +function invertMatrix(self) { + var temp = new Array(16); + var tx = -self[3]; + var ty = -self[7]; + var tz = -self[11]; + for (h = 0; h < 3; h++) + for (v = 0; v < 3; v++) + temp[h + v * 4] = self[v + h * 4]; + for (i = 0; i < 11; i++) + self[i] = temp[i]; + self[3] = tx * self[0] + ty * self[1] + tz * self[2]; + self[7] = tx * self[4] + ty * self[5] + tz * self[6]; + self[11] = tx * self[8] + ty * self[9] + tz * self[10]; + return self; +} + + +// Triangle intersection using barycentric coord method +function Triangle(p1, p2, p3) { + var edge1 = sub(p3, p1); + var edge2 = sub(p2, p1); + var normal = cross(edge1, edge2); + if (Math.abs(normal[0]) > Math.abs(normal[1])) + if (Math.abs(normal[0]) > Math.abs(normal[2])) + this.axis = 0; + else + this.axis = 2; + else + if (Math.abs(normal[1]) > Math.abs(normal[2])) + this.axis = 1; + else + this.axis = 2; + var u = (this.axis + 1) % 3; + var v = (this.axis + 2) % 3; + var u1 = edge1[u]; + var v1 = edge1[v]; + + var u2 = edge2[u]; + var v2 = edge2[v]; + this.normal = normalise(normal); + this.nu = normal[u] / normal[this.axis]; + this.nv = normal[v] / normal[this.axis]; + this.nd = dot(normal, p1) / normal[this.axis]; + var det = u1 * v2 - v1 * u2; + this.eu = p1[u]; + this.ev = p1[v]; + this.nu1 = u1 / det; + this.nv1 = -v1 / det; + this.nu2 = v2 / det; + this.nv2 = -u2 / det; + this.material = [0.7, 0.7, 0.7]; +} + +Triangle.prototype.intersect = function(orig, dir, near, far) { + var u = (this.axis + 1) % 3; + var v = (this.axis + 2) % 3; + var d = dir[this.axis] + this.nu * dir[u] + this.nv * dir[v]; + var t = (this.nd - orig[this.axis] - this.nu * orig[u] - this.nv * orig[v]) / d; + if (t < near || t > far) + return null; + var Pu = orig[u] + t * dir[u] - this.eu; + var Pv = orig[v] + t * dir[v] - this.ev; + var a2 = Pv * this.nu1 + Pu * this.nv1; + if (a2 < 0) + return null; + var a3 = Pu * this.nu2 + Pv * this.nv2; + if (a3 < 0) + return null; + + if ((a2 + a3) > 1) + return null; + return t; +} + +function Scene(a_triangles) { + this.triangles = a_triangles; + this.lights = []; + this.ambient = [0,0,0]; + this.background = [0.8,0.8,1]; +} +var zero = new Array(0,0,0); + +Scene.prototype.intersect = function(origin, dir, near, far) { + var closest = null; + for (i = 0; i < this.triangles.length; i++) { + var triangle = this.triangles[i]; + var d = triangle.intersect(origin, dir, near, far); + if (d == null || d > far || d < near) + continue; + far = d; + closest = triangle; + } + + if (!closest) + return [this.background[0],this.background[1],this.background[2]]; + + var normal = closest.normal; + var hit = add(origin, scale(dir, far)); + if (dot(dir, normal) > 0) + normal = [-normal[0], -normal[1], -normal[2]]; + + var colour = null; + if (closest.shader) { + colour = closest.shader(closest, hit, dir); + } else { + colour = closest.material; + } + + // do reflection + var reflected = null; + if (colour.reflection > 0.001) { + var reflection = addVector(scale(normal, -2*dot(dir, normal)), dir); + reflected = this.intersect(hit, reflection, 0.0001, 1000000); + if (colour.reflection >= 0.999999) + return reflected; + } + + var l = [this.ambient[0], this.ambient[1], this.ambient[2]]; + for (var i = 0; i < this.lights.length; i++) { + var light = this.lights[i]; + var toLight = sub(light, hit); + var distance = lengthVector(toLight); + scaleVector(toLight, 1.0/distance); + distance -= 0.0001; + if (this.blocked(hit, toLight, distance)) + continue; + var nl = dot(normal, toLight); + if (nl > 0) + addVector(l, scale(light.colour, nl)); + } + l = scalev(l, colour); + if (reflected) { + l = addVector(scaleVector(l, 1 - colour.reflection), scaleVector(reflected, colour.reflection)); + } + return l; +} + +Scene.prototype.blocked = function(O, D, far) { + var near = 0.0001; + var closest = null; + for (i = 0; i < this.triangles.length; i++) { + var triangle = this.triangles[i]; + var d = triangle.intersect(O, D, near, far); + if (d == null || d > far || d < near) + continue; + return true; + } + + return false; +} + + +// this camera code is from notes i made ages ago, it is from *somewhere* -- i cannot remember where +// that somewhere is +function Camera(origin, lookat, up) { + var zaxis = normaliseVector(subVector(lookat, origin)); + var xaxis = normaliseVector(cross(up, zaxis)); + var yaxis = normaliseVector(cross(xaxis, subVector([0,0,0], zaxis))); + var m = new Array(16); + m[0] = xaxis[0]; m[1] = xaxis[1]; m[2] = xaxis[2]; + m[4] = yaxis[0]; m[5] = yaxis[1]; m[6] = yaxis[2]; + m[8] = zaxis[0]; m[9] = zaxis[1]; m[10] = zaxis[2]; + invertMatrix(m); + m[3] = 0; m[7] = 0; m[11] = 0; + this.origin = origin; + this.directions = new Array(4); + this.directions[0] = normalise([-0.7, 0.7, 1]); + this.directions[1] = normalise([ 0.7, 0.7, 1]); + this.directions[2] = normalise([ 0.7, -0.7, 1]); + this.directions[3] = normalise([-0.7, -0.7, 1]); + this.directions[0] = transformMatrix(m, this.directions[0]); + this.directions[1] = transformMatrix(m, this.directions[1]); + this.directions[2] = transformMatrix(m, this.directions[2]); + this.directions[3] = transformMatrix(m, this.directions[3]); +} + +Camera.prototype.generateRayPair = function(y) { + rays = new Array(new Object(), new Object()); + rays[0].origin = this.origin; + rays[1].origin = this.origin; + rays[0].dir = addVector(scale(this.directions[0], y), scale(this.directions[3], 1 - y)); + rays[1].dir = addVector(scale(this.directions[1], y), scale(this.directions[2], 1 - y)); + return rays; +} + +function renderRows(camera, scene, pixels, width, height, starty, stopy) { + for (var y = starty; y < stopy; y++) { + var rays = camera.generateRayPair(y / height); + for (var x = 0; x < width; x++) { + var xp = x / width; + var origin = addVector(scale(rays[0].origin, xp), scale(rays[1].origin, 1 - xp)); + var dir = normaliseVector(addVector(scale(rays[0].dir, xp), scale(rays[1].dir, 1 - xp))); + var l = scene.intersect(origin, dir); + pixels[y][x] = l; + } + } +} + +Camera.prototype.render = function(scene, pixels, width, height) { + var cam = this; + var row = 0; + renderRows(cam, scene, pixels, width, height, 0, height); +} + + + +function raytraceScene() +{ + var startDate = new Date().getTime(); + var numTriangles = 2 * 6; + var triangles = new Array();//numTriangles); + var tfl = createVector(-10, 10, -10); + var tfr = createVector( 10, 10, -10); + var tbl = createVector(-10, 10, 10); + var tbr = createVector( 10, 10, 10); + var bfl = createVector(-10, -10, -10); + var bfr = createVector( 10, -10, -10); + var bbl = createVector(-10, -10, 10); + var bbr = createVector( 10, -10, 10); + + // cube!!! + // front + var i = 0; + + triangles[i++] = new Triangle(tfl, tfr, bfr); + triangles[i++] = new Triangle(tfl, bfr, bfl); + // back + triangles[i++] = new Triangle(tbl, tbr, bbr); + triangles[i++] = new Triangle(tbl, bbr, bbl); + // triangles[i-1].material = [0.7,0.2,0.2]; + // triangles[i-1].material.reflection = 0.8; + // left + triangles[i++] = new Triangle(tbl, tfl, bbl); + // triangles[i-1].reflection = 0.6; + triangles[i++] = new Triangle(tfl, bfl, bbl); + // triangles[i-1].reflection = 0.6; + // right + triangles[i++] = new Triangle(tbr, tfr, bbr); + triangles[i++] = new Triangle(tfr, bfr, bbr); + // top + triangles[i++] = new Triangle(tbl, tbr, tfr); + triangles[i++] = new Triangle(tbl, tfr, tfl); + // bottom + triangles[i++] = new Triangle(bbl, bbr, bfr); + triangles[i++] = new Triangle(bbl, bfr, bfl); + + //Floor!!!! + var green = createVector(0.0, 0.4, 0.0); + var grey = createVector(0.4, 0.4, 0.4); + grey.reflection = 1.0; + var floorShader = function(tri, pos, view) { + var x = ((pos[0]/32) % 2 + 2) % 2; + var z = ((pos[2]/32 + 0.3) % 2 + 2) % 2; + if (x < 1 != z < 1) { + //in the real world we use the fresnel term... + // var angle = 1-dot(view, tri.normal); + // angle *= angle; + // angle *= angle; + // angle *= angle; + //grey.reflection = angle; + return grey; + } else + return green; + } + var ffl = createVector(-1000, -30, -1000); + var ffr = createVector( 1000, -30, -1000); + var fbl = createVector(-1000, -30, 1000); + var fbr = createVector( 1000, -30, 1000); + triangles[i++] = new Triangle(fbl, fbr, ffr); + triangles[i-1].shader = floorShader; + triangles[i++] = new Triangle(fbl, ffr, ffl); + triangles[i-1].shader = floorShader; + + var _scene = new Scene(triangles); + _scene.lights[0] = createVector(20, 38, -22); + _scene.lights[0].colour = createVector(0.7, 0.3, 0.3); + _scene.lights[1] = createVector(-23, 40, 17); + _scene.lights[1].colour = createVector(0.7, 0.3, 0.3); + _scene.lights[2] = createVector(23, 20, 17); + _scene.lights[2].colour = createVector(0.7, 0.7, 0.7); + _scene.ambient = createVector(0.1, 0.1, 0.1); + // _scene.background = createVector(0.7, 0.7, 1.0); + + var size = 30; + var pixels = new Array(); + for (var y = 0; y < size; y++) { + pixels[y] = new Array(); + for (var x = 0; x < size; x++) { + pixels[y][x] = 0; + } + } + + var _camera = new Camera(createVector(-40, 40, 40), createVector(0, 0, 0), createVector(0, 1, 0)); + _camera.render(_scene, pixels, size, size); + + return pixels; +} + +function arrayToCanvasCommands(pixels) +{ + var s = '<canvas id="renderCanvas" width="30px" height="30px"></canvas><scr' + 'ipt>\nvar pixels = ['; + var size = 30; + for (var y = 0; y < size; y++) { + s += "["; + for (var x = 0; x < size; x++) { + s += "[" + pixels[y][x] + "],"; + } + s+= "],"; + } + s += '];\n var canvas = document.getElementById("renderCanvas").getContext("2d");\n\ +\n\ +\n\ + var size = 30;\n\ + canvas.fillStyle = "red";\n\ + canvas.fillRect(0, 0, size, size);\n\ + canvas.scale(1, -1);\n\ + canvas.translate(0, -size);\n\ +\n\ + if (!canvas.setFillColor)\n\ + canvas.setFillColor = function(r, g, b, a) {\n\ + this.fillStyle = "rgb("+[Math.floor(r * 255), Math.floor(g * 255), Math.floor(b * 255)]+")";\n\ + }\n\ +\n\ +for (var y = 0; y < size; y++) {\n\ + for (var x = 0; x < size; x++) {\n\ + var l = pixels[y][x];\n\ + canvas.setFillColor(l[0], l[1], l[2], 1);\n\ + canvas.fillRect(x, y, 1, 1);\n\ + }\n\ +}</scr' + 'ipt>'; + + return s; +} + +testOutput = arrayToCanvasCommands(raytraceScene()); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/access-binary-trees.html b/build/pgo/js-input/sunspider/access-binary-trees.html new file mode 100644 index 000000000..c2c6cf3d9 --- /dev/null +++ b/build/pgo/js-input/sunspider/access-binary-trees.html @@ -0,0 +1,100 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-binary-trees</title> + +</head> + +<body> +<h3>access-binary-trees</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* The Great Computer Language Shootout + http://shootout.alioth.debian.org/ + contributed by Isaac Gouy */ + +function TreeNode(left,right,item){ + this.left = left; + this.right = right; + this.item = item; +} + +TreeNode.prototype.itemCheck = function(){ + if (this.left==null) return this.item; + else return this.item + this.left.itemCheck() - this.right.itemCheck(); +} + +function bottomUpTree(item,depth){ + if (depth>0){ + return new TreeNode( + bottomUpTree(2*item-1, depth-1) + ,bottomUpTree(2*item, depth-1) + ,item + ); + } + else { + return new TreeNode(null,null,item); + } +} + +var ret; + +for ( var n = 4; n <= 7; n += 1 ) { + var minDepth = 4; + var maxDepth = Math.max(minDepth + 2, n); + var stretchDepth = maxDepth + 1; + + var check = bottomUpTree(0,stretchDepth).itemCheck(); + + var longLivedTree = bottomUpTree(0,maxDepth); + for (var depth=minDepth; depth<=maxDepth; depth+=2){ + var iterations = 1 << (maxDepth - depth + minDepth); + + check = 0; + for (var i=1; i<=iterations; i++){ + check += bottomUpTree(i,depth).itemCheck(); + check += bottomUpTree(-i,depth).itemCheck(); + } + } + + ret = longLivedTree.itemCheck(); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/access-fannkuch.html b/build/pgo/js-input/sunspider/access-fannkuch.html new file mode 100644 index 000000000..02b306ff2 --- /dev/null +++ b/build/pgo/js-input/sunspider/access-fannkuch.html @@ -0,0 +1,116 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-fannkuch</title> + +</head> + +<body> +<h3>access-fannkuch</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* The Great Computer Language Shootout + http://shootout.alioth.debian.org/ + contributed by Isaac Gouy */ + +function fannkuch(n) { + var check = 0; + var perm = Array(n); + var perm1 = Array(n); + var count = Array(n); + var maxPerm = Array(n); + var maxFlipsCount = 0; + var m = n - 1; + + for (var i = 0; i < n; i++) perm1[i] = i; + var r = n; + + while (true) { + // write-out the first 30 permutations + if (check < 30){ + var s = ""; + for(var i=0; i<n; i++) s += (perm1[i]+1).toString(); + check++; + } + + while (r != 1) { count[r - 1] = r; r--; } + if (!(perm1[0] == 0 || perm1[m] == m)) { + for (var i = 0; i < n; i++) perm[i] = perm1[i]; + + var flipsCount = 0; + var k; + + while (!((k = perm[0]) == 0)) { + var k2 = (k + 1) >> 1; + for (var i = 0; i < k2; i++) { + var temp = perm[i]; perm[i] = perm[k - i]; perm[k - i] = temp; + } + flipsCount++; + } + + if (flipsCount > maxFlipsCount) { + maxFlipsCount = flipsCount; + for (var i = 0; i < n; i++) maxPerm[i] = perm1[i]; + } + } + + while (true) { + if (r == n) return maxFlipsCount; + var perm0 = perm1[0]; + var i = 0; + while (i < r) { + var j = i + 1; + perm1[i] = perm1[j]; + i = j; + } + perm1[r] = perm0; + + count[r] = count[r] - 1; + if (count[r] > 0) break; + r++; + } + } +} + +var n = 8; +var ret = fannkuch(n); + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/access-nbody.html b/build/pgo/js-input/sunspider/access-nbody.html new file mode 100644 index 000000000..4ef73c855 --- /dev/null +++ b/build/pgo/js-input/sunspider/access-nbody.html @@ -0,0 +1,219 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-nbody</title> + +</head> + +<body> +<h3>access-nbody</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* The Great Computer Language Shootout + http://shootout.alioth.debian.org/ + contributed by Isaac Gouy */ + +var PI = 3.141592653589793; +var SOLAR_MASS = 4 * PI * PI; +var DAYS_PER_YEAR = 365.24; + +function Body(x,y,z,vx,vy,vz,mass){ + this.x = x; + this.y = y; + this.z = z; + this.vx = vx; + this.vy = vy; + this.vz = vz; + this.mass = mass; +} + +Body.prototype.offsetMomentum = function(px,py,pz) { + this.vx = -px / SOLAR_MASS; + this.vy = -py / SOLAR_MASS; + this.vz = -pz / SOLAR_MASS; + return this; +} + +function Jupiter(){ + return new Body( + 4.84143144246472090e+00, + -1.16032004402742839e+00, + -1.03622044471123109e-01, + 1.66007664274403694e-03 * DAYS_PER_YEAR, + 7.69901118419740425e-03 * DAYS_PER_YEAR, + -6.90460016972063023e-05 * DAYS_PER_YEAR, + 9.54791938424326609e-04 * SOLAR_MASS + ); +} + +function Saturn(){ + return new Body( + 8.34336671824457987e+00, + 4.12479856412430479e+00, + -4.03523417114321381e-01, + -2.76742510726862411e-03 * DAYS_PER_YEAR, + 4.99852801234917238e-03 * DAYS_PER_YEAR, + 2.30417297573763929e-05 * DAYS_PER_YEAR, + 2.85885980666130812e-04 * SOLAR_MASS + ); +} + +function Uranus(){ + return new Body( + 1.28943695621391310e+01, + -1.51111514016986312e+01, + -2.23307578892655734e-01, + 2.96460137564761618e-03 * DAYS_PER_YEAR, + 2.37847173959480950e-03 * DAYS_PER_YEAR, + -2.96589568540237556e-05 * DAYS_PER_YEAR, + 4.36624404335156298e-05 * SOLAR_MASS + ); +} + +function Neptune(){ + return new Body( + 1.53796971148509165e+01, + -2.59193146099879641e+01, + 1.79258772950371181e-01, + 2.68067772490389322e-03 * DAYS_PER_YEAR, + 1.62824170038242295e-03 * DAYS_PER_YEAR, + -9.51592254519715870e-05 * DAYS_PER_YEAR, + 5.15138902046611451e-05 * SOLAR_MASS + ); +} + +function Sun(){ + return new Body(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, SOLAR_MASS); +} + + +function NBodySystem(bodies){ + this.bodies = bodies; + var px = 0.0; + var py = 0.0; + var pz = 0.0; + var size = this.bodies.length; + for (var i=0; i<size; i++){ + var b = this.bodies[i]; + var m = b.mass; + px += b.vx * m; + py += b.vy * m; + pz += b.vz * m; + } + this.bodies[0].offsetMomentum(px,py,pz); +} + +NBodySystem.prototype.advance = function(dt){ + var dx, dy, dz, distance, mag; + var size = this.bodies.length; + + for (var i=0; i<size; i++) { + var bodyi = this.bodies[i]; + for (var j=i+1; j<size; j++) { + var bodyj = this.bodies[j]; + dx = bodyi.x - bodyj.x; + dy = bodyi.y - bodyj.y; + dz = bodyi.z - bodyj.z; + + distance = Math.sqrt(dx*dx + dy*dy + dz*dz); + mag = dt / (distance * distance * distance); + + bodyi.vx -= dx * bodyj.mass * mag; + bodyi.vy -= dy * bodyj.mass * mag; + bodyi.vz -= dz * bodyj.mass * mag; + + bodyj.vx += dx * bodyi.mass * mag; + bodyj.vy += dy * bodyi.mass * mag; + bodyj.vz += dz * bodyi.mass * mag; + } + } + + for (var i=0; i<size; i++) { + var body = this.bodies[i]; + body.x += dt * body.vx; + body.y += dt * body.vy; + body.z += dt * body.vz; + } +} + +NBodySystem.prototype.energy = function(){ + var dx, dy, dz, distance; + var e = 0.0; + var size = this.bodies.length; + + for (var i=0; i<size; i++) { + var bodyi = this.bodies[i]; + + e += 0.5 * bodyi.mass * + ( bodyi.vx * bodyi.vx + + bodyi.vy * bodyi.vy + + bodyi.vz * bodyi.vz ); + + for (var j=i+1; j<size; j++) { + var bodyj = this.bodies[j]; + dx = bodyi.x - bodyj.x; + dy = bodyi.y - bodyj.y; + dz = bodyi.z - bodyj.z; + + distance = Math.sqrt(dx*dx + dy*dy + dz*dz); + e -= (bodyi.mass * bodyj.mass) / distance; + } + } + return e; +} + +var ret; + +for ( var n = 3; n <= 24; n *= 2 ) { + (function(){ + var bodies = new NBodySystem( Array( + Sun(),Jupiter(),Saturn(),Uranus(),Neptune() + )); + var max = n * 100; + + ret = bodies.energy(); + for (var i=0; i<max; i++){ + bodies.advance(0.01); + } + ret = bodies.energy(); + })(); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/access-nsieve.html b/build/pgo/js-input/sunspider/access-nsieve.html new file mode 100644 index 000000000..c3ed067f1 --- /dev/null +++ b/build/pgo/js-input/sunspider/access-nsieve.html @@ -0,0 +1,88 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider access-nsieve</title> + +</head> + +<body> +<h3>access-nsieve</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org/ +// +// modified by Isaac Gouy + +function pad(number,width){ + var s = number.toString(); + var prefixWidth = width - s.length; + if (prefixWidth>0){ + for (var i=1; i<=prefixWidth; i++) s = " " + s; + } + return s; +} + +function nsieve(m, isPrime){ + var i, k, count; + + for (i=2; i<=m; i++) { isPrime[i] = true; } + count = 0; + + for (i=2; i<=m; i++){ + if (isPrime[i]) { + for (k=i+i; k<=m; k+=i) isPrime[k] = false; + count++; + } + } + return count; +} + +function sieve() { + for (var i = 1; i <= 3; i++ ) { + var m = (1<<i)*10000; + var flags = Array(m+1); + nsieve(m, flags); + } +} + +sieve(); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/bitops-3bit-bits-in-byte.html b/build/pgo/js-input/sunspider/bitops-3bit-bits-in-byte.html new file mode 100644 index 000000000..c40be94ef --- /dev/null +++ b/build/pgo/js-input/sunspider/bitops-3bit-bits-in-byte.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-3bit-bits-in-byte</title> + +</head> + +<body> +<h3>bitops-3bit-bits-in-byte</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// Copyright (c) 2004 by Arthur Langereis (arthur_ext at domain xfinitegames, tld com + +// 1 op = 6 ANDs, 3 SHRs, 3 SHLs, 4 assigns, 2 ADDs +// O(1) +function fast3bitlookup(b) { +var c, bi3b = 0xE994; // 0b1110 1001 1001 0100; // 3 2 2 1 2 1 1 0 +c = 3 & (bi3b >> ((b << 1) & 14)); +c += 3 & (bi3b >> ((b >> 2) & 14)); +c += 3 & (bi3b >> ((b >> 5) & 6)); +return c; + +/* +lir4,0xE994; 9 instructions, no memory access, minimal register dependence, 6 shifts, 2 adds, 1 inline assign +rlwinmr5,r3,1,28,30 +rlwinmr6,r3,30,28,30 +rlwinmr7,r3,27,29,30 +rlwnmr8,r4,r5,30,31 +rlwnmr9,r4,r6,30,31 +rlwnmr10,r4,r7,30,31 +addr3,r8,r9 +addr3,r3,r10 +*/ +} + + +function TimeFunc(func) { +var x, y, t; +for(var x=0; x<500; x++) +for(var y=0; y<256; y++) func(y); +} + +TimeFunc(fast3bitlookup); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/bitops-bits-in-byte.html b/build/pgo/js-input/sunspider/bitops-bits-in-byte.html new file mode 100644 index 000000000..4022c777f --- /dev/null +++ b/build/pgo/js-input/sunspider/bitops-bits-in-byte.html @@ -0,0 +1,72 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-bits-in-byte</title> + +</head> + +<body> +<h3>bitops-bits-in-byte</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// Copyright (c) 2004 by Arthur Langereis (arthur_ext at domain xfinitegames, tld com) + + +// 1 op = 2 assigns, 16 compare/branches, 8 ANDs, (0-8) ADDs, 8 SHLs +// O(n) +function bitsinbyte(b) { +var m = 1, c = 0; +while(m<0x100) { +if(b & m) c++; +m <<= 1; +} +return c; +} + +function TimeFunc(func) { +var x, y, t; +for(var x=0; x<350; x++) +for(var y=0; y<256; y++) func(y); +} + +TimeFunc(bitsinbyte); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/bitops-bitwise-and.html b/build/pgo/js-input/sunspider/bitops-bitwise-and.html new file mode 100644 index 000000000..cca513040 --- /dev/null +++ b/build/pgo/js-input/sunspider/bitops-bitwise-and.html @@ -0,0 +1,78 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-bitwise-and</title> + +</head> + +<body> +<h3>bitops-bitwise-and</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +bitwiseAndValue = 4294967296; +for (var i = 0; i < 600000; i++) + bitwiseAndValue = bitwiseAndValue & i; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/bitops-nsieve-bits.html b/build/pgo/js-input/sunspider/bitops-nsieve-bits.html new file mode 100644 index 000000000..1849f9da2 --- /dev/null +++ b/build/pgo/js-input/sunspider/bitops-nsieve-bits.html @@ -0,0 +1,82 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider bitops-nsieve-bits</title> + +</head> + +<body> +<h3>bitops-nsieve-bits</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org +// +// Contributed by Ian Osgood + +function pad(n,width) { + var s = n.toString(); + while (s.length < width) s = ' ' + s; + return s; +} + +function primes(isPrime, n) { + var i, count = 0, m = 10000<<n, size = m+31>>5; + + for (i=0; i<size; i++) isPrime[i] = 0xffffffff; + + for (i=2; i<m; i++) + if (isPrime[i>>5] & 1<<(i&31)) { + for (var j=i+i; j<m; j+=i) + isPrime[j>>5] &= ~(1<<(j&31)); + count++; + } +} + +function sieve() { + for (var i = 4; i <= 4; i++) { + var isPrime = new Array((10000<<i)+31>>5); + primes(isPrime, i); + } +} + +sieve(); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/controlflow-recursive.html b/build/pgo/js-input/sunspider/controlflow-recursive.html new file mode 100644 index 000000000..9a9651d4b --- /dev/null +++ b/build/pgo/js-input/sunspider/controlflow-recursive.html @@ -0,0 +1,75 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider controlflow-recursive</title> + +</head> + +<body> +<h3>controlflow-recursive</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Computer Language Shootout +// http://shootout.alioth.debian.org/ +// contributed by Isaac Gouy + +function ack(m,n){ + if (m==0) { return n+1; } + if (n==0) { return ack(m-1,1); } + return ack(m-1, ack(m,n-1) ); +} + +function fib(n) { + if (n < 2){ return 1; } + return fib(n-2) + fib(n-1); +} + +function tak(x,y,z) { + if (y >= x) return z; + return tak(tak(x-1,y,z), tak(y-1,z,x), tak(z-1,x,y)); +} + +for ( var i = 3; i <= 5; i++ ) { + ack(3,i); + fib(17.0+i); + tak(3*i+3,2*i+2,i+1); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/crypto-aes.html b/build/pgo/js-input/sunspider/crypto-aes.html new file mode 100644 index 000000000..12f26b2fb --- /dev/null +++ b/build/pgo/js-input/sunspider/crypto-aes.html @@ -0,0 +1,472 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider crypto-aes</title> + +</head> + +<body> +<h3>crypto-aes</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * AES Cipher function: encrypt 'input' with Rijndael algorithm + * + * takes byte-array 'input' (16 bytes) + * 2D byte-array key schedule 'w' (Nr+1 x Nb bytes) + * + * applies Nr rounds (10/12/14) using key schedule w for 'add round key' stage + * + * returns byte-array encrypted value (16 bytes) + */ +function Cipher(input, w) { // main Cipher function [§5.1] + var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) + var Nr = w.length/Nb - 1; // no of rounds: 10/12/14 for 128/192/256-bit keys + + var state = [[],[],[],[]]; // initialise 4xNb byte-array 'state' with input [§3.4] + for (var i=0; i<4*Nb; i++) state[i%4][Math.floor(i/4)] = input[i]; + + state = AddRoundKey(state, w, 0, Nb); + + for (var round=1; round<Nr; round++) { + state = SubBytes(state, Nb); + state = ShiftRows(state, Nb); + state = MixColumns(state, Nb); + state = AddRoundKey(state, w, round, Nb); + } + + state = SubBytes(state, Nb); + state = ShiftRows(state, Nb); + state = AddRoundKey(state, w, Nr, Nb); + + var output = new Array(4*Nb); // convert state to 1-d array before returning [§3.4] + for (var i=0; i<4*Nb; i++) output[i] = state[i%4][Math.floor(i/4)]; + return output; +} + + +function SubBytes(s, Nb) { // apply SBox to state S [§5.1.1] + for (var r=0; r<4; r++) { + for (var c=0; c<Nb; c++) s[r][c] = Sbox[s[r][c]]; + } + return s; +} + + +function ShiftRows(s, Nb) { // shift row r of state S left by r bytes [§5.1.2] + var t = new Array(4); + for (var r=1; r<4; r++) { + for (var c=0; c<4; c++) t[c] = s[r][(c+r)%Nb]; // shift into temp copy + for (var c=0; c<4; c++) s[r][c] = t[c]; // and copy back + } // note that this will work for Nb=4,5,6, but not 7,8 (always 4 for AES): + return s; // see fp.gladman.plus.com/cryptography_technology/rijndael/aes.spec.311.pdf +} + + +function MixColumns(s, Nb) { // combine bytes of each col of state S [§5.1.3] + for (var c=0; c<4; c++) { + var a = new Array(4); // 'a' is a copy of the current column from 's' + var b = new Array(4); // 'b' is a•{02} in GF(2^8) + for (var i=0; i<4; i++) { + a[i] = s[i][c]; + b[i] = s[i][c]&0x80 ? s[i][c]<<1 ^ 0x011b : s[i][c]<<1; + } + // a[n] ^ b[n] is a•{03} in GF(2^8) + s[0][c] = b[0] ^ a[1] ^ b[1] ^ a[2] ^ a[3]; // 2*a0 + 3*a1 + a2 + a3 + s[1][c] = a[0] ^ b[1] ^ a[2] ^ b[2] ^ a[3]; // a0 * 2*a1 + 3*a2 + a3 + s[2][c] = a[0] ^ a[1] ^ b[2] ^ a[3] ^ b[3]; // a0 + a1 + 2*a2 + 3*a3 + s[3][c] = a[0] ^ b[0] ^ a[1] ^ a[2] ^ b[3]; // 3*a0 + a1 + a2 + 2*a3 + } + return s; +} + + +function AddRoundKey(state, w, rnd, Nb) { // xor Round Key into state S [§5.1.4] + for (var r=0; r<4; r++) { + for (var c=0; c<Nb; c++) state[r][c] ^= w[rnd*4+c][r]; + } + return state; +} + + +function KeyExpansion(key) { // generate Key Schedule (byte-array Nr+1 x Nb) from Key [§5.2] + var Nb = 4; // block size (in words): no of columns in state (fixed at 4 for AES) + var Nk = key.length/4 // key length (in words): 4/6/8 for 128/192/256-bit keys + var Nr = Nk + 6; // no of rounds: 10/12/14 for 128/192/256-bit keys + + var w = new Array(Nb*(Nr+1)); + var temp = new Array(4); + + for (var i=0; i<Nk; i++) { + var r = [key[4*i], key[4*i+1], key[4*i+2], key[4*i+3]]; + w[i] = r; + } + + for (var i=Nk; i<(Nb*(Nr+1)); i++) { + w[i] = new Array(4); + for (var t=0; t<4; t++) temp[t] = w[i-1][t]; + if (i % Nk == 0) { + temp = SubWord(RotWord(temp)); + for (var t=0; t<4; t++) temp[t] ^= Rcon[i/Nk][t]; + } else if (Nk > 6 && i%Nk == 4) { + temp = SubWord(temp); + } + for (var t=0; t<4; t++) w[i][t] = w[i-Nk][t] ^ temp[t]; + } + + return w; +} + +function SubWord(w) { // apply SBox to 4-byte word w + for (var i=0; i<4; i++) w[i] = Sbox[w[i]]; + return w; +} + +function RotWord(w) { // rotate 4-byte word w left by one byte + w[4] = w[0]; + for (var i=0; i<4; i++) w[i] = w[i+1]; + return w; +} + + +// Sbox is pre-computed multiplicative inverse in GF(2^8) used in SubBytes and KeyExpansion [§5.1.1] +var Sbox = [0x63,0x7c,0x77,0x7b,0xf2,0x6b,0x6f,0xc5,0x30,0x01,0x67,0x2b,0xfe,0xd7,0xab,0x76, + 0xca,0x82,0xc9,0x7d,0xfa,0x59,0x47,0xf0,0xad,0xd4,0xa2,0xaf,0x9c,0xa4,0x72,0xc0, + 0xb7,0xfd,0x93,0x26,0x36,0x3f,0xf7,0xcc,0x34,0xa5,0xe5,0xf1,0x71,0xd8,0x31,0x15, + 0x04,0xc7,0x23,0xc3,0x18,0x96,0x05,0x9a,0x07,0x12,0x80,0xe2,0xeb,0x27,0xb2,0x75, + 0x09,0x83,0x2c,0x1a,0x1b,0x6e,0x5a,0xa0,0x52,0x3b,0xd6,0xb3,0x29,0xe3,0x2f,0x84, + 0x53,0xd1,0x00,0xed,0x20,0xfc,0xb1,0x5b,0x6a,0xcb,0xbe,0x39,0x4a,0x4c,0x58,0xcf, + 0xd0,0xef,0xaa,0xfb,0x43,0x4d,0x33,0x85,0x45,0xf9,0x02,0x7f,0x50,0x3c,0x9f,0xa8, + 0x51,0xa3,0x40,0x8f,0x92,0x9d,0x38,0xf5,0xbc,0xb6,0xda,0x21,0x10,0xff,0xf3,0xd2, + 0xcd,0x0c,0x13,0xec,0x5f,0x97,0x44,0x17,0xc4,0xa7,0x7e,0x3d,0x64,0x5d,0x19,0x73, + 0x60,0x81,0x4f,0xdc,0x22,0x2a,0x90,0x88,0x46,0xee,0xb8,0x14,0xde,0x5e,0x0b,0xdb, + 0xe0,0x32,0x3a,0x0a,0x49,0x06,0x24,0x5c,0xc2,0xd3,0xac,0x62,0x91,0x95,0xe4,0x79, + 0xe7,0xc8,0x37,0x6d,0x8d,0xd5,0x4e,0xa9,0x6c,0x56,0xf4,0xea,0x65,0x7a,0xae,0x08, + 0xba,0x78,0x25,0x2e,0x1c,0xa6,0xb4,0xc6,0xe8,0xdd,0x74,0x1f,0x4b,0xbd,0x8b,0x8a, + 0x70,0x3e,0xb5,0x66,0x48,0x03,0xf6,0x0e,0x61,0x35,0x57,0xb9,0x86,0xc1,0x1d,0x9e, + 0xe1,0xf8,0x98,0x11,0x69,0xd9,0x8e,0x94,0x9b,0x1e,0x87,0xe9,0xce,0x55,0x28,0xdf, + 0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16]; + +// Rcon is Round Constant used for the Key Expansion [1st col is 2^(r-1) in GF(2^8)] [§5.2] +var Rcon = [ [0x00, 0x00, 0x00, 0x00], + [0x01, 0x00, 0x00, 0x00], + [0x02, 0x00, 0x00, 0x00], + [0x04, 0x00, 0x00, 0x00], + [0x08, 0x00, 0x00, 0x00], + [0x10, 0x00, 0x00, 0x00], + [0x20, 0x00, 0x00, 0x00], + [0x40, 0x00, 0x00, 0x00], + [0x80, 0x00, 0x00, 0x00], + [0x1b, 0x00, 0x00, 0x00], + [0x36, 0x00, 0x00, 0x00] ]; + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * Use AES to encrypt 'plaintext' with 'password' using 'nBits' key, in 'Counter' mode of operation + * - see http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf + * for each block + * - outputblock = cipher(counter, key) + * - cipherblock = plaintext xor outputblock + */ +function AESEncryptCtr(plaintext, password, nBits) { + if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys + + // for this example script, generate the key by applying Cipher to 1st 16/24/32 chars of password; + // for real-world applications, a more secure approach would be to hash the password e.g. with SHA-1 + var nBytes = nBits/8; // no bytes in key + var pwBytes = new Array(nBytes); + for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff; + var key = Cipher(pwBytes, KeyExpansion(pwBytes)); + key = key.concat(key.slice(0, nBytes-16)); // key is now 16/24/32 bytes long + + // initialise counter block (NIST SP800-38A §B.2): millisecond time-stamp for nonce in 1st 8 bytes, + // block counter in 2nd 8 bytes + var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES + var counterBlock = new Array(blockSize); // block size fixed at 16 bytes / 128 bits (Nb=4) for AES + var nonce = (new Date()).getTime(); // milliseconds since 1-Jan-1970 + + // encode nonce in two stages to cater for JavaScript 32-bit limit on bitwise ops + for (var i=0; i<4; i++) counterBlock[i] = (nonce >>> i*8) & 0xff; + for (var i=0; i<4; i++) counterBlock[i+4] = (nonce/0x100000000 >>> i*8) & 0xff; + + // generate key schedule - an expansion of the key into distinct Key Rounds for each round + var keySchedule = KeyExpansion(key); + + var blockCount = Math.ceil(plaintext.length/blockSize); + var ciphertext = new Array(blockCount); // ciphertext as array of strings + + for (var b=0; b<blockCount; b++) { + // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) + // again done in two stages for 32-bit ops + for (var c=0; c<4; c++) counterBlock[15-c] = (b >>> c*8) & 0xff; + for (var c=0; c<4; c++) counterBlock[15-c-4] = (b/0x100000000 >>> c*8) + + var cipherCntr = Cipher(counterBlock, keySchedule); // -- encrypt counter block -- + + // calculate length of final block: + var blockLength = b<blockCount-1 ? blockSize : (plaintext.length-1)%blockSize+1; + + var ct = ''; + for (var i=0; i<blockLength; i++) { // -- xor plaintext with ciphered counter byte-by-byte -- + var plaintextByte = plaintext.charCodeAt(b*blockSize+i); + var cipherByte = plaintextByte ^ cipherCntr[i]; + ct += String.fromCharCode(cipherByte); + } + // ct is now ciphertext for this block + + ciphertext[b] = escCtrlChars(ct); // escape troublesome characters in ciphertext + } + + // convert the nonce to a string to go on the front of the ciphertext + var ctrTxt = ''; + for (var i=0; i<8; i++) ctrTxt += String.fromCharCode(counterBlock[i]); + ctrTxt = escCtrlChars(ctrTxt); + + // use '-' to separate blocks, use Array.join to concatenate arrays of strings for efficiency + return ctrTxt + '-' + ciphertext.join('-'); +} + + +/* + * Use AES to decrypt 'ciphertext' with 'password' using 'nBits' key, in Counter mode of operation + * + * for each block + * - outputblock = cipher(counter, key) + * - cipherblock = plaintext xor outputblock + */ +function AESDecryptCtr(ciphertext, password, nBits) { + if (!(nBits==128 || nBits==192 || nBits==256)) return ''; // standard allows 128/192/256 bit keys + + var nBytes = nBits/8; // no bytes in key + var pwBytes = new Array(nBytes); + for (var i=0; i<nBytes; i++) pwBytes[i] = password.charCodeAt(i) & 0xff; + var pwKeySchedule = KeyExpansion(pwBytes); + var key = Cipher(pwBytes, pwKeySchedule); + key = key.concat(key.slice(0, nBytes-16)); // key is now 16/24/32 bytes long + + var keySchedule = KeyExpansion(key); + + ciphertext = ciphertext.split('-'); // split ciphertext into array of block-length strings + + // recover nonce from 1st element of ciphertext + var blockSize = 16; // block size fixed at 16 bytes / 128 bits (Nb=4) for AES + var counterBlock = new Array(blockSize); + var ctrTxt = unescCtrlChars(ciphertext[0]); + for (var i=0; i<8; i++) counterBlock[i] = ctrTxt.charCodeAt(i); + + var plaintext = new Array(ciphertext.length-1); + + for (var b=1; b<ciphertext.length; b++) { + // set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) + for (var c=0; c<4; c++) counterBlock[15-c] = ((b-1) >>> c*8) & 0xff; + for (var c=0; c<4; c++) counterBlock[15-c-4] = ((b/0x100000000-1) >>> c*8) & 0xff; + + var cipherCntr = Cipher(counterBlock, keySchedule); // encrypt counter block + + ciphertext[b] = unescCtrlChars(ciphertext[b]); + + var pt = ''; + for (var i=0; i<ciphertext[b].length; i++) { + // -- xor plaintext with ciphered counter byte-by-byte -- + var ciphertextByte = ciphertext[b].charCodeAt(i); + var plaintextByte = ciphertextByte ^ cipherCntr[i]; + pt += String.fromCharCode(plaintextByte); + } + // pt is now plaintext for this block + + plaintext[b-1] = pt; // b-1 'cos no initial nonce block in plaintext + } + + return plaintext.join(''); +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +function escCtrlChars(str) { // escape control chars which might cause problems handling ciphertext + return str.replace(/[\0\t\n\v\f\r\xa0'"!-]/g, function(c) { return '!' + c.charCodeAt(0) + '!'; }); +} // \xa0 to cater for bug in Firefox; include '-' to leave it free for use as a block marker + +function unescCtrlChars(str) { // unescape potentially problematic control characters + return str.replace(/!\d\d?\d?!/g, function(c) { return String.fromCharCode(c.slice(1,-1)); }); +} +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + +/* + * if escCtrlChars()/unescCtrlChars() still gives problems, use encodeBase64()/decodeBase64() instead + */ +var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; + +function encodeBase64(str) { // http://tools.ietf.org/html/rfc4648 + var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc=''; + + str = encodeUTF8(str); // encode multi-byte chars into UTF-8 for byte-array + + do { // pack three octets into four hexets + o1 = str.charCodeAt(i++); + o2 = str.charCodeAt(i++); + o3 = str.charCodeAt(i++); + + bits = o1<<16 | o2<<8 | o3; + + h1 = bits>>18 & 0x3f; + h2 = bits>>12 & 0x3f; + h3 = bits>>6 & 0x3f; + h4 = bits & 0x3f; + + // end of string? index to '=' in b64 + if (isNaN(o3)) h4 = 64; + if (isNaN(o2)) h3 = 64; + + // use hexets to index into b64, and append result to encoded string + enc += b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4); + } while (i < str.length); + + return enc; +} + +function decodeBase64(str) { + var o1, o2, o3, h1, h2, h3, h4, bits, i=0, enc=''; + + do { // unpack four hexets into three octets using index points in b64 + h1 = b64.indexOf(str.charAt(i++)); + h2 = b64.indexOf(str.charAt(i++)); + h3 = b64.indexOf(str.charAt(i++)); + h4 = b64.indexOf(str.charAt(i++)); + + bits = h1<<18 | h2<<12 | h3<<6 | h4; + + o1 = bits>>16 & 0xff; + o2 = bits>>8 & 0xff; + o3 = bits & 0xff; + + if (h3 == 64) enc += String.fromCharCode(o1); + else if (h4 == 64) enc += String.fromCharCode(o1, o2); + else enc += String.fromCharCode(o1, o2, o3); + } while (i < str.length); + + return decodeUTF8(enc); // decode UTF-8 byte-array back to Unicode +} + +function encodeUTF8(str) { // encode multi-byte string into utf-8 multiple single-byte characters + str = str.replace( + /[\u0080-\u07ff]/g, // U+0080 - U+07FF = 2-byte chars + function(c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xc0 | cc>>6, 0x80 | cc&0x3f); } + ); + str = str.replace( + /[\u0800-\uffff]/g, // U+0800 - U+FFFF = 3-byte chars + function(c) { + var cc = c.charCodeAt(0); + return String.fromCharCode(0xe0 | cc>>12, 0x80 | cc>>6&0x3F, 0x80 | cc&0x3f); } + ); + return str; +} + +function decodeUTF8(str) { // decode utf-8 encoded string back into multi-byte characters + str = str.replace( + /[\u00c0-\u00df][\u0080-\u00bf]/g, // 2-byte chars + function(c) { + var cc = (c.charCodeAt(0)&0x1f)<<6 | c.charCodeAt(1)&0x3f; + return String.fromCharCode(cc); } + ); + str = str.replace( + /[\u00e0-\u00ef][\u0080-\u00bf][\u0080-\u00bf]/g, // 3-byte chars + function(c) { + var cc = (c.charCodeAt(0)&0x0f)<<12 | (c.charCodeAt(1)&0x3f<<6) | c.charCodeAt(2)&0x3f; + return String.fromCharCode(cc); } + ); + return str; +} + + +function byteArrayToHexStr(b) { // convert byte array to hex string for displaying test vectors + var s = ''; + for (var i=0; i<b.length; i++) s += b[i].toString(16) + ' '; + return s; +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ + + +var plainText = "ROMEO: But, soft! what light through yonder window breaks?\n\ +It is the east, and Juliet is the sun.\n\ +Arise, fair sun, and kill the envious moon,\n\ +Who is already sick and pale with grief,\n\ +That thou her maid art far more fair than she:\n\ +Be not her maid, since she is envious;\n\ +Her vestal livery is but sick and green\n\ +And none but fools do wear it; cast it off.\n\ +It is my lady, O, it is my love!\n\ +O, that she knew she were!\n\ +She speaks yet she says nothing: what of that?\n\ +Her eye discourses; I will answer it.\n\ +I am too bold, 'tis not to me she speaks:\n\ +Two of the fairest stars in all the heaven,\n\ +Having some business, do entreat her eyes\n\ +To twinkle in their spheres till they return.\n\ +What if her eyes were there, they in her head?\n\ +The brightness of her cheek would shame those stars,\n\ +As daylight doth a lamp; her eyes in heaven\n\ +Would through the airy region stream so bright\n\ +That birds would sing and think it were not night.\n\ +See, how she leans her cheek upon her hand!\n\ +O, that I were a glove upon that hand,\n\ +That I might touch that cheek!\n\ +JULIET: Ay me!\n\ +ROMEO: She speaks:\n\ +O, speak again, bright angel! for thou art\n\ +As glorious to this night, being o'er my head\n\ +As is a winged messenger of heaven\n\ +Unto the white-upturned wondering eyes\n\ +Of mortals that fall back to gaze on him\n\ +When he bestrides the lazy-pacing clouds\n\ +And sails upon the bosom of the air."; + +var password = "O Romeo, Romeo! wherefore art thou Romeo?"; + +var cipherText = AESEncryptCtr(plainText, password, 256); +var decryptedText = AESDecryptCtr(cipherText, password, 256); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/crypto-md5.html b/build/pgo/js-input/sunspider/crypto-md5.html new file mode 100644 index 000000000..8395107ce --- /dev/null +++ b/build/pgo/js-input/sunspider/crypto-md5.html @@ -0,0 +1,336 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider crypto-md5</title> + +</head> + +<body> +<h3>crypto-md5</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message + * Digest Algorithm, as defined in RFC 1321. + * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for more info. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));} +function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));} +function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));} +function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); } +function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); } +function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); } + +/* + * Perform a simple self-test to see if the VM is working + */ +function md5_vm_test() +{ + return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72"; +} + +/* + * Calculate the MD5 of an array of little-endian words, and a bit length + */ +function core_md5(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << ((len) % 32); + x[(((len + 64) >>> 9) << 4) + 14] = len; + + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + + a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936); + d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586); + c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819); + b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330); + a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897); + d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426); + c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341); + b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983); + a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416); + d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417); + c = md5_ff(c, d, a, b, x[i+10], 17, -42063); + b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162); + a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682); + d = md5_ff(d, a, b, c, x[i+13], 12, -40341101); + c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290); + b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329); + + a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510); + d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632); + c = md5_gg(c, d, a, b, x[i+11], 14, 643717713); + b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302); + a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691); + d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083); + c = md5_gg(c, d, a, b, x[i+15], 14, -660478335); + b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848); + a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438); + d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690); + c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961); + b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501); + a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467); + d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784); + c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473); + b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734); + + a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558); + d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463); + c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562); + b = md5_hh(b, c, d, a, x[i+14], 23, -35309556); + a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060); + d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353); + c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632); + b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640); + a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174); + d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222); + c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979); + b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189); + a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487); + d = md5_hh(d, a, b, c, x[i+12], 11, -421815835); + c = md5_hh(c, d, a, b, x[i+15], 16, 530742520); + b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651); + + a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844); + d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415); + c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905); + b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055); + a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571); + d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606); + c = md5_ii(c, d, a, b, x[i+10], 15, -1051523); + b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799); + a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359); + d = md5_ii(d, a, b, c, x[i+15], 10, -30611744); + c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380); + b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649); + a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070); + d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379); + c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259); + b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551); + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + } + return Array(a, b, c, d); + +} + +/* + * These functions implement the four basic operations the algorithm uses. + */ +function md5_cmn(q, a, b, x, s, t) +{ + return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b); +} +function md5_ff(a, b, c, d, x, s, t) +{ + return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t); +} +function md5_gg(a, b, c, d, x, s, t) +{ + return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t); +} +function md5_hh(a, b, c, d, x, s, t) +{ + return md5_cmn(b ^ c ^ d, a, b, x, s, t); +} +function md5_ii(a, b, c, d, x, s, t) +{ + return md5_cmn(c ^ (b | (~d)), a, b, x, s, t); +} + +/* + * Calculate the HMAC-MD5, of a key and some data + */ +function core_hmac_md5(key, data) +{ + var bkey = str2binl(key); + if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz); + return core_md5(opad.concat(hash), 512 + 128); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function bit_rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert a string to an array of little-endian words + * If chrsz is ASCII, characters >255 have their hi-byte silently ignored. + */ +function str2binl(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32); + return bin; +} + +/* + * Convert an array of little-endian words to a string + */ +function binl2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask); + return str; +} + +/* + * Convert an array of little-endian words to a hex string. + */ +function binl2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of little-endian words to a base-64 string + */ +function binl2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + +var plainText = "Rebellious subjects, enemies to peace,\n\ +Profaners of this neighbour-stained steel,--\n\ +Will they not hear? What, ho! you men, you beasts,\n\ +That quench the fire of your pernicious rage\n\ +With purple fountains issuing from your veins,\n\ +On pain of torture, from those bloody hands\n\ +Throw your mistemper'd weapons to the ground,\n\ +And hear the sentence of your moved prince.\n\ +Three civil brawls, bred of an airy word,\n\ +By thee, old Capulet, and Montague,\n\ +Have thrice disturb'd the quiet of our streets,\n\ +And made Verona's ancient citizens\n\ +Cast by their grave beseeming ornaments,\n\ +To wield old partisans, in hands as old,\n\ +Canker'd with peace, to part your canker'd hate:\n\ +If ever you disturb our streets again,\n\ +Your lives shall pay the forfeit of the peace.\n\ +For this time, all the rest depart away:\n\ +You Capulet; shall go along with me:\n\ +And, Montague, come you this afternoon,\n\ +To know our further pleasure in this case,\n\ +To old Free-town, our common judgment-place.\n\ +Once more, on pain of death, all men depart." + +for (var i = 0; i <4; i++) { + plainText += plainText; +} + +var md5Output = hex_md5(plainText); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/crypto-sha1.html b/build/pgo/js-input/sunspider/crypto-sha1.html new file mode 100644 index 000000000..01d0b56f3 --- /dev/null +++ b/build/pgo/js-input/sunspider/crypto-sha1.html @@ -0,0 +1,274 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider crypto-sha1</title> + +</head> + +<body> +<h3>crypto-sha1</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined + * in FIPS PUB 180-1 + * Version 2.1a Copyright Paul Johnston 2000 - 2002. + * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet + * Distributed under the BSD License + * See http://pajhome.org.uk/crypt/md5 for details. + */ + +/* + * Configurable variables. You may need to tweak these to be compatible with + * the server-side, but the defaults work in most cases. + */ +var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */ +var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */ +var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */ + +/* + * These are the functions you'll usually want to call + * They take string arguments and return either hex or base-64 encoded strings + */ +function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));} +function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));} +function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));} +function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));} +function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));} +function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));} + +/* + * Perform a simple self-test to see if the VM is working + */ +function sha1_vm_test() +{ + return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d"; +} + +/* + * Calculate the SHA-1 of an array of big-endian words, and a bit length + */ +function core_sha1(x, len) +{ + /* append padding */ + x[len >> 5] |= 0x80 << (24 - len % 32); + x[((len + 64 >> 9) << 4) + 15] = len; + + var w = Array(80); + var a = 1732584193; + var b = -271733879; + var c = -1732584194; + var d = 271733878; + var e = -1009589776; + + for(var i = 0; i < x.length; i += 16) + { + var olda = a; + var oldb = b; + var oldc = c; + var oldd = d; + var olde = e; + + for(var j = 0; j < 80; j++) + { + if(j < 16) w[j] = x[i + j]; + else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1); + var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), + safe_add(safe_add(e, w[j]), sha1_kt(j))); + e = d; + d = c; + c = rol(b, 30); + b = a; + a = t; + } + + a = safe_add(a, olda); + b = safe_add(b, oldb); + c = safe_add(c, oldc); + d = safe_add(d, oldd); + e = safe_add(e, olde); + } + return Array(a, b, c, d, e); + +} + +/* + * Perform the appropriate triplet combination function for the current + * iteration + */ +function sha1_ft(t, b, c, d) +{ + if(t < 20) return (b & c) | ((~b) & d); + if(t < 40) return b ^ c ^ d; + if(t < 60) return (b & c) | (b & d) | (c & d); + return b ^ c ^ d; +} + +/* + * Determine the appropriate additive constant for the current iteration + */ +function sha1_kt(t) +{ + return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 : + (t < 60) ? -1894007588 : -899497514; +} + +/* + * Calculate the HMAC-SHA1 of a key and some data + */ +function core_hmac_sha1(key, data) +{ + var bkey = str2binb(key); + if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz); + + var ipad = Array(16), opad = Array(16); + for(var i = 0; i < 16; i++) + { + ipad[i] = bkey[i] ^ 0x36363636; + opad[i] = bkey[i] ^ 0x5C5C5C5C; + } + + var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz); + return core_sha1(opad.concat(hash), 512 + 160); +} + +/* + * Add integers, wrapping at 2^32. This uses 16-bit operations internally + * to work around bugs in some JS interpreters. + */ +function safe_add(x, y) +{ + var lsw = (x & 0xFFFF) + (y & 0xFFFF); + var msw = (x >> 16) + (y >> 16) + (lsw >> 16); + return (msw << 16) | (lsw & 0xFFFF); +} + +/* + * Bitwise rotate a 32-bit number to the left. + */ +function rol(num, cnt) +{ + return (num << cnt) | (num >>> (32 - cnt)); +} + +/* + * Convert an 8-bit or 16-bit string to an array of big-endian words + * In 8-bit function, characters >255 have their hi-byte silently ignored. + */ +function str2binb(str) +{ + var bin = Array(); + var mask = (1 << chrsz) - 1; + for(var i = 0; i < str.length * chrsz; i += chrsz) + bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32); + return bin; +} + +/* + * Convert an array of big-endian words to a string + */ +function binb2str(bin) +{ + var str = ""; + var mask = (1 << chrsz) - 1; + for(var i = 0; i < bin.length * 32; i += chrsz) + str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask); + return str; +} + +/* + * Convert an array of big-endian words to a hex string. + */ +function binb2hex(binarray) +{ + var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i++) + { + str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) + + hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF); + } + return str; +} + +/* + * Convert an array of big-endian words to a base-64 string + */ +function binb2b64(binarray) +{ + var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + var str = ""; + for(var i = 0; i < binarray.length * 4; i += 3) + { + var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16) + | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 ) + | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF); + for(var j = 0; j < 4; j++) + { + if(i * 8 + j * 6 > binarray.length * 32) str += b64pad; + else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F); + } + } + return str; +} + + +var plainText = "Two households, both alike in dignity,\n\ +In fair Verona, where we lay our scene,\n\ +From ancient grudge break to new mutiny,\n\ +Where civil blood makes civil hands unclean.\n\ +From forth the fatal loins of these two foes\n\ +A pair of star-cross'd lovers take their life;\n\ +Whole misadventured piteous overthrows\n\ +Do with their death bury their parents' strife.\n\ +The fearful passage of their death-mark'd love,\n\ +And the continuance of their parents' rage,\n\ +Which, but their children's end, nought could remove,\n\ +Is now the two hours' traffic of our stage;\n\ +The which if you with patient ears attend,\n\ +What here shall miss, our toil shall strive to mend."; + +for (var i = 0; i <4; i++) { + plainText += plainText; +} + +var sha1Output = hex_sha1(plainText); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/date-format-tofte.html b/build/pgo/js-input/sunspider/date-format-tofte.html new file mode 100644 index 000000000..b8e477342 --- /dev/null +++ b/build/pgo/js-input/sunspider/date-format-tofte.html @@ -0,0 +1,349 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider date-format-tofte</title> + +</head> + +<body> +<h3>date-format-tofte</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +function arrayExists(array, x) { + for (var i = 0; i < array.length; i++) { + if (array[i] == x) return true; + } + return false; +} + +Date.prototype.formatDate = function (input,time) { + // formatDate : + // a PHP date like function, for formatting date strings + // See: http://www.php.net/date + // + // input : format string + // time : epoch time (seconds, and optional) + // + // if time is not passed, formatting is based on + // the current "this" date object's set time. + // + // supported: + // a, A, B, d, D, F, g, G, h, H, i, j, l (lowercase L), L, + // m, M, n, O, r, s, S, t, U, w, W, y, Y, z + // + // unsupported: + // I (capital i), T, Z + + var switches = ["a", "A", "B", "d", "D", "F", "g", "G", "h", "H", + "i", "j", "l", "L", "m", "M", "n", "O", "r", "s", + "S", "t", "U", "w", "W", "y", "Y", "z"]; + var daysLong = ["Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"]; + var daysShort = ["Sun", "Mon", "Tue", "Wed", + "Thu", "Fri", "Sat"]; + var monthsShort = ["Jan", "Feb", "Mar", "Apr", + "May", "Jun", "Jul", "Aug", "Sep", + "Oct", "Nov", "Dec"]; + var monthsLong = ["January", "February", "March", "April", + "May", "June", "July", "August", "September", + "October", "November", "December"]; + var daysSuffix = ["st", "nd", "rd", "th", "th", "th", "th", // 1st - 7th + "th", "th", "th", "th", "th", "th", "th", // 8th - 14th + "th", "th", "th", "th", "th", "th", "st", // 15th - 21st + "nd", "rd", "th", "th", "th", "th", "th", // 22nd - 28th + "th", "th", "st"]; // 29th - 31st + + function a() { + // Lowercase Ante meridiem and Post meridiem + return self.getHours() > 11? "pm" : "am"; + } + function A() { + // Uppercase Ante meridiem and Post meridiem + return self.getHours() > 11? "PM" : "AM"; + } + + function B(){ + // Swatch internet time. code simply grabbed from ppk, + // since I was feeling lazy: + // http://www.xs4all.nl/~ppk/js/beat.html + var off = (self.getTimezoneOffset() + 60)*60; + var theSeconds = (self.getHours() * 3600) + + (self.getMinutes() * 60) + + self.getSeconds() + off; + var beat = Math.floor(theSeconds/86.4); + if (beat > 1000) beat -= 1000; + if (beat < 0) beat += 1000; + if ((""+beat).length == 1) beat = "00"+beat; + if ((""+beat).length == 2) beat = "0"+beat; + return beat; + } + + function d() { + // Day of the month, 2 digits with leading zeros + return new String(self.getDate()).length == 1? + "0"+self.getDate() : self.getDate(); + } + function D() { + // A textual representation of a day, three letters + return daysShort[self.getDay()]; + } + function F() { + // A full textual representation of a month + return monthsLong[self.getMonth()]; + } + function g() { + // 12-hour format of an hour without leading zeros + return self.getHours() > 12? self.getHours()-12 : self.getHours(); + } + function G() { + // 24-hour format of an hour without leading zeros + return self.getHours(); + } + function h() { + // 12-hour format of an hour with leading zeros + if (self.getHours() > 12) { + var s = new String(self.getHours()-12); + return s.length == 1? + "0"+ (self.getHours()-12) : self.getHours()-12; + } else { + var s = new String(self.getHours()); + return s.length == 1? + "0"+self.getHours() : self.getHours(); + } + } + function H() { + // 24-hour format of an hour with leading zeros + return new String(self.getHours()).length == 1? + "0"+self.getHours() : self.getHours(); + } + function i() { + // Minutes with leading zeros + return new String(self.getMinutes()).length == 1? + "0"+self.getMinutes() : self.getMinutes(); + } + function j() { + // Day of the month without leading zeros + return self.getDate(); + } + function l() { + // A full textual representation of the day of the week + return daysLong[self.getDay()]; + } + function L() { + // leap year or not. 1 if leap year, 0 if not. + // the logic should match iso's 8601 standard. + var y_ = Y(); + if ( + (y_ % 4 == 0 && y_ % 100 != 0) || + (y_ % 4 == 0 && y_ % 100 == 0 && y_ % 400 == 0) + ) { + return 1; + } else { + return 0; + } + } + function m() { + // Numeric representation of a month, with leading zeros + return self.getMonth() < 9? + "0"+(self.getMonth()+1) : + self.getMonth()+1; + } + function M() { + // A short textual representation of a month, three letters + return monthsShort[self.getMonth()]; + } + function n() { + // Numeric representation of a month, without leading zeros + return self.getMonth()+1; + } + function O() { + // Difference to Greenwich time (GMT) in hours + var os = Math.abs(self.getTimezoneOffset()); + var h = ""+Math.floor(os/60); + var m = ""+(os%60); + h.length == 1? h = "0"+h:1; + m.length == 1? m = "0"+m:1; + return self.getTimezoneOffset() < 0 ? "+"+h+m : "-"+h+m; + } + function r() { + // RFC 822 formatted date + var r; // result + // Thu , 21 Dec 2000 + r = D() + ", " + j() + " " + M() + " " + Y() + + // 16 : 01 : 07 +0200 + " " + H() + ":" + i() + ":" + s() + " " + O(); + return r; + } + function S() { + // English ordinal suffix for the day of the month, 2 characters + return daysSuffix[self.getDate()-1]; + } + function s() { + // Seconds, with leading zeros + return new String(self.getSeconds()).length == 1? + "0"+self.getSeconds() : self.getSeconds(); + } + function t() { + + // thanks to Matt Bannon for some much needed code-fixes here! + var daysinmonths = [null,31,28,31,30,31,30,31,31,30,31,30,31]; + if (L()==1 && n()==2) return 29; // leap day + return daysinmonths[n()]; + } + function U() { + // Seconds since the Unix Epoch (January 1 1970 00:00:00 GMT) + return Math.round(self.getTime()/1000); + } + function W() { + // Weeknumber, as per ISO specification: + // http://www.cl.cam.ac.uk/~mgk25/iso-time.html + + // if the day is three days before newyears eve, + // there's a chance it's "week 1" of next year. + // here we check for that. + var beforeNY = 364+L() - z(); + var afterNY = z(); + var weekday = w()!=0?w()-1:6; // makes sunday (0), into 6. + if (beforeNY <= 2 && weekday <= 2-beforeNY) { + return 1; + } + // similarly, if the day is within threedays of newyears + // there's a chance it belongs in the old year. + var ny = new Date("January 1 " + Y() + " 00:00:00"); + var nyDay = ny.getDay()!=0?ny.getDay()-1:6; + if ( + (afterNY <= 2) && + (nyDay >=4) && + (afterNY >= (6-nyDay)) + ) { + // Since I'm not sure we can just always return 53, + // i call the function here again, using the last day + // of the previous year, as the date, and then just + // return that week. + var prevNY = new Date("December 31 " + (Y()-1) + " 00:00:00"); + return prevNY.formatDate("W"); + } + + // week 1, is the week that has the first thursday in it. + // note that this value is not zero index. + if (nyDay <= 3) { + // first day of the year fell on a thursday, or earlier. + return 1 + Math.floor( ( z() + nyDay ) / 7 ); + } else { + // first day of the year fell on a friday, or later. + return 1 + Math.floor( ( z() - ( 7 - nyDay ) ) / 7 ); + } + } + function w() { + // Numeric representation of the day of the week + return self.getDay(); + } + + function Y() { + // A full numeric representation of a year, 4 digits + + // we first check, if getFullYear is supported. if it + // is, we just use that. ppks code is nice, but wont + // work with dates outside 1900-2038, or something like that + if (self.getFullYear) { + var newDate = new Date("January 1 2001 00:00:00 +0000"); + var x = newDate .getFullYear(); + if (x == 2001) { + // i trust the method now + return self.getFullYear(); + } + } + // else, do this: + // codes thanks to ppk: + // http://www.xs4all.nl/~ppk/js/introdate.html + var x = self.getYear(); + var y = x % 100; + y += (y < 38) ? 2000 : 1900; + return y; + } + function y() { + // A two-digit representation of a year + var y = Y()+""; + return y.substring(y.length-2,y.length); + } + function z() { + // The day of the year, zero indexed! 0 through 366 + var t = new Date("January 1 " + Y() + " 00:00:00"); + var diff = self.getTime() - t.getTime(); + return Math.floor(diff/1000/60/60/24); + } + + var self = this; + if (time) { + // save time + var prevTime = self.getTime(); + self.setTime(time); + } + + var ia = input.split(""); + var ij = 0; + while (ia[ij]) { + if (ia[ij] == "\\") { + // this is our way of allowing users to escape stuff + ia.splice(ij,1); + } else { + if (arrayExists(switches,ia[ij])) { + ia[ij] = eval(ia[ij] + "()"); + } + } + ij++; + } + // reset time, back to what it was + if (prevTime) { + self.setTime(prevTime); + } + return ia.join(""); +} + +var date = new Date("1/1/2007 1:11:11"); + +for (i = 0; i < 500; ++i) { + var shortFormat = date.formatDate("Y-m-d"); + var longFormat = date.formatDate("l, F d, Y g:i:s A"); + date.setTime(date.getTime() + 84266956); +} + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/date-format-xparb.html b/build/pgo/js-input/sunspider/date-format-xparb.html new file mode 100644 index 000000000..dd35713d1 --- /dev/null +++ b/build/pgo/js-input/sunspider/date-format-xparb.html @@ -0,0 +1,467 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider date-format-xparb</title> + +</head> + +<body> +<h3>date-format-xparb</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) 2004 Baron Schwartz <baron at sequent dot org> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, version 2.1. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS + * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more + * details. + */ + +Date.parseFunctions = {count:0}; +Date.parseRegexes = []; +Date.formatFunctions = {count:0}; + +Date.prototype.dateFormat = function(format) { + if (Date.formatFunctions[format] == null) { + Date.createNewFormat(format); + } + var func = Date.formatFunctions[format]; + return this[func](); +} + +Date.createNewFormat = function(format) { + var funcName = "format" + Date.formatFunctions.count++; + Date.formatFunctions[format] = funcName; + var code = "Date.prototype." + funcName + " = function(){return "; + var special = false; + var ch = ''; + for (var i = 0; i < format.length; ++i) { + ch = format.charAt(i); + if (!special && ch == "\\") { + special = true; + } + else if (special) { + special = false; + code += "'" + String.escape(ch) + "' + "; + } + else { + code += Date.getFormatCode(ch); + } + } + eval(code.substring(0, code.length - 3) + ";}"); +} + +Date.getFormatCode = function(character) { + switch (character) { + case "d": + return "String.leftPad(this.getDate(), 2, '0') + "; + case "D": + return "Date.dayNames[this.getDay()].substring(0, 3) + "; + case "j": + return "this.getDate() + "; + case "l": + return "Date.dayNames[this.getDay()] + "; + case "S": + return "this.getSuffix() + "; + case "w": + return "this.getDay() + "; + case "z": + return "this.getDayOfYear() + "; + case "W": + return "this.getWeekOfYear() + "; + case "F": + return "Date.monthNames[this.getMonth()] + "; + case "m": + return "String.leftPad(this.getMonth() + 1, 2, '0') + "; + case "M": + return "Date.monthNames[this.getMonth()].substring(0, 3) + "; + case "n": + return "(this.getMonth() + 1) + "; + case "t": + return "this.getDaysInMonth() + "; + case "L": + return "(this.isLeapYear() ? 1 : 0) + "; + case "Y": + return "this.getFullYear() + "; + case "y": + return "('' + this.getFullYear()).substring(2, 4) + "; + case "a": + return "(this.getHours() < 12 ? 'am' : 'pm') + "; + case "A": + return "(this.getHours() < 12 ? 'AM' : 'PM') + "; + case "g": + return "((this.getHours() %12) ? this.getHours() % 12 : 12) + "; + case "G": + return "this.getHours() + "; + case "h": + return "String.leftPad((this.getHours() %12) ? this.getHours() % 12 : 12, 2, '0') + "; + case "H": + return "String.leftPad(this.getHours(), 2, '0') + "; + case "i": + return "String.leftPad(this.getMinutes(), 2, '0') + "; + case "s": + return "String.leftPad(this.getSeconds(), 2, '0') + "; + case "O": + return "this.getGMTOffset() + "; + case "T": + return "this.getTimezone() + "; + case "Z": + return "(this.getTimezoneOffset() * -60) + "; + default: + return "'" + String.escape(character) + "' + "; + } +} + +Date.parseDate = function(input, format) { + if (Date.parseFunctions[format] == null) { + Date.createParser(format); + } + var func = Date.parseFunctions[format]; + return Date[func](input); +} + +Date.createParser = function(format) { + var funcName = "parse" + Date.parseFunctions.count++; + var regexNum = Date.parseRegexes.length; + var currentGroup = 1; + Date.parseFunctions[format] = funcName; + + var code = "Date." + funcName + " = function(input){\n" + + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1;\n" + + "var d = new Date();\n" + + "y = d.getFullYear();\n" + + "m = d.getMonth();\n" + + "d = d.getDate();\n" + + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n" + + "if (results && results.length > 0) {" + var regex = ""; + + var special = false; + var ch = ''; + for (var i = 0; i < format.length; ++i) { + ch = format.charAt(i); + if (!special && ch == "\\") { + special = true; + } + else if (special) { + special = false; + regex += String.escape(ch); + } + else { + obj = Date.formatCodeToRegex(ch, currentGroup); + currentGroup += obj.g; + regex += obj.s; + if (obj.g && obj.c) { + code += obj.c; + } + } + } + + code += "if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n" + + "{return new Date(y, m, d, h, i, s);}\n" + + "else if (y > 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n" + + "{return new Date(y, m, d, h, i);}\n" + + "else if (y > 0 && m >= 0 && d > 0 && h >= 0)\n" + + "{return new Date(y, m, d, h);}\n" + + "else if (y > 0 && m >= 0 && d > 0)\n" + + "{return new Date(y, m, d);}\n" + + "else if (y > 0 && m >= 0)\n" + + "{return new Date(y, m);}\n" + + "else if (y > 0)\n" + + "{return new Date(y);}\n" + + "}return null;}"; + + Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$"); + eval(code); +} + +Date.formatCodeToRegex = function(character, currentGroup) { + switch (character) { + case "D": + return {g:0, + c:null, + s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"}; + case "j": + case "d": + return {g:1, + c:"d = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{1,2})"}; + case "l": + return {g:0, + c:null, + s:"(?:" + Date.dayNames.join("|") + ")"}; + case "S": + return {g:0, + c:null, + s:"(?:st|nd|rd|th)"}; + case "w": + return {g:0, + c:null, + s:"\\d"}; + case "z": + return {g:0, + c:null, + s:"(?:\\d{1,3})"}; + case "W": + return {g:0, + c:null, + s:"(?:\\d{2})"}; + case "F": + return {g:1, + c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n", + s:"(" + Date.monthNames.join("|") + ")"}; + case "M": + return {g:1, + c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n", + s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"}; + case "n": + case "m": + return {g:1, + c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n", + s:"(\\d{1,2})"}; + case "t": + return {g:0, + c:null, + s:"\\d{1,2}"}; + case "L": + return {g:0, + c:null, + s:"(?:1|0)"}; + case "Y": + return {g:1, + c:"y = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{4})"}; + case "y": + return {g:1, + c:"var ty = parseInt(results[" + currentGroup + "], 10);\n" + + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n", + s:"(\\d{1,2})"}; + case "a": + return {g:1, + c:"if (results[" + currentGroup + "] == 'am') {\n" + + "if (h == 12) { h = 0; }\n" + + "} else { if (h < 12) { h += 12; }}", + s:"(am|pm)"}; + case "A": + return {g:1, + c:"if (results[" + currentGroup + "] == 'AM') {\n" + + "if (h == 12) { h = 0; }\n" + + "} else { if (h < 12) { h += 12; }}", + s:"(AM|PM)"}; + case "g": + case "G": + case "h": + case "H": + return {g:1, + c:"h = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{1,2})"}; + case "i": + return {g:1, + c:"i = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{2})"}; + case "s": + return {g:1, + c:"s = parseInt(results[" + currentGroup + "], 10);\n", + s:"(\\d{2})"}; + case "O": + return {g:0, + c:null, + s:"[+-]\\d{4}"}; + case "T": + return {g:0, + c:null, + s:"[A-Z]{3}"}; + case "Z": + return {g:0, + c:null, + s:"[+-]\\d{1,5}"}; + default: + return {g:0, + c:null, + s:String.escape(character)}; + } +} + +Date.prototype.getTimezone = function() { + return this.toString().replace( + /^.*? ([A-Z]{3}) [0-9]{4}.*$/, "$1").replace( + /^.*?\(([A-Z])[a-z]+ ([A-Z])[a-z]+ ([A-Z])[a-z]+\)$/, "$1$2$3"); +} + +Date.prototype.getGMTOffset = function() { + return (this.getTimezoneOffset() > 0 ? "-" : "+") + + String.leftPad(Math.floor(this.getTimezoneOffset() / 60), 2, "0") + + String.leftPad(this.getTimezoneOffset() % 60, 2, "0"); +} + +Date.prototype.getDayOfYear = function() { + var num = 0; + Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; + for (var i = 0; i < this.getMonth(); ++i) { + num += Date.daysInMonth[i]; + } + return num + this.getDate() - 1; +} + +Date.prototype.getWeekOfYear = function() { + // Skip to Thursday of this week + var now = this.getDayOfYear() + (4 - this.getDay()); + // Find the first Thursday of the year + var jan1 = new Date(this.getFullYear(), 0, 1); + var then = (7 - jan1.getDay() + 4); + document.write(then); + return String.leftPad(((now - then) / 7) + 1, 2, "0"); +} + +Date.prototype.isLeapYear = function() { + var year = this.getFullYear(); + return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year))); +} + +Date.prototype.getFirstDayOfMonth = function() { + var day = (this.getDay() - (this.getDate() - 1)) % 7; + return (day < 0) ? (day + 7) : day; +} + +Date.prototype.getLastDayOfMonth = function() { + var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7; + return (day < 0) ? (day + 7) : day; +} + +Date.prototype.getDaysInMonth = function() { + Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28; + return Date.daysInMonth[this.getMonth()]; +} + +Date.prototype.getSuffix = function() { + switch (this.getDate()) { + case 1: + case 21: + case 31: + return "st"; + case 2: + case 22: + return "nd"; + case 3: + case 23: + return "rd"; + default: + return "th"; + } +} + +String.escape = function(string) { + return string.replace(/('|\\)/g, "\\$1"); +} + +String.leftPad = function (val, size, ch) { + var result = new String(val); + if (ch == null) { + ch = " "; + } + while (result.length < size) { + result = ch + result; + } + return result; +} + +Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31]; +Date.monthNames = + ["January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December"]; +Date.dayNames = + ["Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday"]; +Date.y2kYear = 50; +Date.monthNumbers = { + Jan:0, + Feb:1, + Mar:2, + Apr:3, + May:4, + Jun:5, + Jul:6, + Aug:7, + Sep:8, + Oct:9, + Nov:10, + Dec:11}; +Date.patterns = { + ISO8601LongPattern:"Y-m-d H:i:s", + ISO8601ShortPattern:"Y-m-d", + ShortDatePattern: "n/j/Y", + LongDatePattern: "l, F d, Y", + FullDateTimePattern: "l, F d, Y g:i:s A", + MonthDayPattern: "F d", + ShortTimePattern: "g:i A", + LongTimePattern: "g:i:s A", + SortableDateTimePattern: "Y-m-d\\TH:i:s", + UniversalSortableDateTimePattern: "Y-m-d H:i:sO", + YearMonthPattern: "F, Y"}; + +var date = new Date("1/1/2007 1:11:11"); + +for (i = 0; i < 4000; ++i) { + var shortFormat = date.dateFormat("Y-m-d"); + var longFormat = date.dateFormat("l, F d, Y g:i:s A"); + date.setTime(date.getTime() + 84266956); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/math-cordic.html b/build/pgo/js-input/sunspider/math-cordic.html new file mode 100644 index 000000000..ec28f9ddd --- /dev/null +++ b/build/pgo/js-input/sunspider/math-cordic.html @@ -0,0 +1,145 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider math-cordic</title> + +</head> + +<body> +<h3>math-cordic</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* + * Copyright (C) Rich Moore. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/////. Start CORDIC + +var AG_CONST = 0.6072529350; + +function FIXED(X) +{ + return X * 65536.0; +} + +function FLOAT(X) +{ + return X / 65536.0; +} + +function DEG2RAD(X) +{ + return 0.017453 * (X); +} + +var Angles = [ + FIXED(45.0), FIXED(26.565), FIXED(14.0362), FIXED(7.12502), + FIXED(3.57633), FIXED(1.78991), FIXED(0.895174), FIXED(0.447614), + FIXED(0.223811), FIXED(0.111906), FIXED(0.055953), + FIXED(0.027977) + ]; + + +function cordicsincos() { + var X; + var Y; + var TargetAngle; + var CurrAngle; + var Step; + + X = FIXED(AG_CONST); /* AG_CONST * cos(0) */ + Y = 0; /* AG_CONST * sin(0) */ + + TargetAngle = FIXED(28.027); + CurrAngle = 0; + for (Step = 0; Step < 12; Step++) { + var NewX; + if (TargetAngle > CurrAngle) { + NewX = X - (Y >> Step); + Y = (X >> Step) + Y; + X = NewX; + CurrAngle += Angles[Step]; + } else { + NewX = X + (Y >> Step); + Y = -(X >> Step) + Y; + X = NewX; + CurrAngle -= Angles[Step]; + } + } +} + +///// End CORDIC + +function cordic( runs ) { + var start = new Date(); + + for ( var i = 0 ; i < runs ; i++ ) { + cordicsincos(); + } + + var end = new Date(); + + return end.getTime() - start.getTime(); +} + +cordic(25000); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/math-partial-sums.html b/build/pgo/js-input/sunspider/math-partial-sums.html new file mode 100644 index 000000000..b78b96248 --- /dev/null +++ b/build/pgo/js-input/sunspider/math-partial-sums.html @@ -0,0 +1,83 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider math-partial-sums</title> + +</head> + +<body> +<h3>math-partial-sums</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Computer Language Shootout +// http://shootout.alioth.debian.org/ +// contributed by Isaac Gouy + +function partial(n){ + var a1 = a2 = a3 = a4 = a5 = a6 = a7 = a8 = a9 = 0.0; + var twothirds = 2.0/3.0; + var alt = -1.0; + var k2 = k3 = sk = ck = 0.0; + + for (var k = 1; k <= n; k++){ + k2 = k*k; + k3 = k2*k; + sk = Math.sin(k); + ck = Math.cos(k); + alt = -alt; + + a1 += Math.pow(twothirds,k-1); + a2 += Math.pow(k,-0.5); + a3 += 1.0/(k*(k+1.0)); + a4 += 1.0/(k3 * sk*sk); + a5 += 1.0/(k3 * ck*ck); + a6 += 1.0/k; + a7 += 1.0/k2; + a8 += alt/k; + a9 += alt/(2*k -1); + } +} + +for (var i = 1024; i <= 16384; i *= 2) { + partial(i); +} + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/math-spectral-norm.html b/build/pgo/js-input/sunspider/math-spectral-norm.html new file mode 100644 index 000000000..2949f9d78 --- /dev/null +++ b/build/pgo/js-input/sunspider/math-spectral-norm.html @@ -0,0 +1,101 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider math-spectral-norm</title> + +</head> + +<body> +<h3>math-spectral-norm</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org/ +// +// contributed by Ian Osgood + +function A(i,j) { + return 1/((i+j)*(i+j+1)/2+i+1); +} + +function Au(u,v) { + for (var i=0; i<u.length; ++i) { + var t = 0; + for (var j=0; j<u.length; ++j) + t += A(i,j) * u[j]; + v[i] = t; + } +} + +function Atu(u,v) { + for (var i=0; i<u.length; ++i) { + var t = 0; + for (var j=0; j<u.length; ++j) + t += A(j,i) * u[j]; + v[i] = t; + } +} + +function AtAu(u,v,w) { + Au(u,w); + Atu(w,v); +} + +function spectralnorm(n) { + var i, u=[], v=[], w=[], vv=0, vBv=0; + for (i=0; i<n; ++i) { + u[i] = 1; v[i] = w[i] = 0; + } + for (i=0; i<10; ++i) { + AtAu(u,v,w); + AtAu(v,u,w); + } + for (i=0; i<n; ++i) { + vBv += u[i]*v[i]; + vv += v[i]*v[i]; + } + return Math.sqrt(vBv/vv); +} + +for (var i = 6; i <= 48; i *= 2) { + spectralnorm(i); +} + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/regexp-dna.html b/build/pgo/js-input/sunspider/regexp-dna.html new file mode 100644 index 000000000..4a00399b8 --- /dev/null +++ b/build/pgo/js-input/sunspider/regexp-dna.html @@ -0,0 +1,1762 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider regexp-dna</title> + +</head> + +<body> +<h3>regexp-dna</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Computer Language Shootout +// http://shootout.alioth.debian.org/ +// +// contributed by Jesse Millikan +// Base on the Ruby version by jose fco. gonzalez + +var l; +var dnaInput = ">ONE Homo sapiens alu\n\ +GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\n\ +TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\n\ +AAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\n\ +GCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\n\ +CCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\n\ +GGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\n\ +GGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\n\ +TTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\n\ +AATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\n\ +GCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGT\n\ +AATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACC\n\ +AGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTG\n\ +GTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACC\n\ +CGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAG\n\ +AGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTT\n\ +TGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACA\n\ +TGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCT\n\ +GTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGG\n\ +TTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGT\n\ +CTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGG\n\ +CGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCG\n\ +TCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTA\n\ +CTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCG\n\ +AGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCG\n\ +GGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACC\n\ +TGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAA\n\ +TACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGA\n\ +GGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACT\n\ +GCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTC\n\ +ACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGT\n\ +TCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGC\n\ +CGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCG\n\ +CTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTG\n\ +GGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCC\n\ +CAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCT\n\ +GGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGC\n\ +GCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGA\n\ +GGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGA\n\ +GACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGA\n\ +GGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTG\n\ +AAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAAT\n\ +CCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCA\n\ +GTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAA\n\ +AAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGC\n\ +GGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCT\n\ +ACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGG\n\ +GAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATC\n\ +GCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGC\n\ +GGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGG\n\ +TCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAA\n\ +AAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAG\n\ +GAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACT\n\ +CCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCC\n\ +TGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAG\n\ +ACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGC\n\ +GTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGA\n\ +ACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGA\n\ +CAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCA\n\ +CTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCA\n\ +ACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCG\n\ +CCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGG\n\ +AGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTC\n\ +CGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCG\n\ +AGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACC\n\ +CCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAG\n\ +CTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAG\n\ +CCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGG\n\ +CCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATC\n\ +ACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAA\n\ +AAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGC\n\ +TGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCC\n\ +ACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGG\n\ +CTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGG\n\ +AGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATT\n\ +AGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAA\n\ +TCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGC\n\ +CTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAA\n\ +TCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAG\n\ +CCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGT\n\ +GGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCG\n\ +GGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAG\n\ +CGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTG\n\ +GGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATG\n\ +GTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGT\n\ +AATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTT\n\ +GCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCT\n\ +CAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCG\n\ +GGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTC\n\ +TCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACT\n\ +CGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAG\n\ +ATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGG\n\ +CGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTG\n\ +AGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATA\n\ +CAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGG\n\ +CAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGC\n\ +ACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCAC\n\ +GCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTC\n\ +GAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCG\n\ +GGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCT\n\ +TGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGG\n\ +CGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCA\n\ +GCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGG\n\ +CCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGC\n\ +GCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGG\n\ +CGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGA\n\ +CTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGG\n\ +CCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAA\n\ +ACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCC\n\ +CAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGT\n\ +GAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAA\n\ +AGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGG\n\ +ATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTAC\n\ +TAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGA\n\ +GGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGC\n\ +GCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGG\n\ +TGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTC\n\ +AGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAA\n\ +ATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGA\n\ +GAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC\n\ +AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTG\n\ +TAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGAC\n\ +CAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGT\n\ +GGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAAC\n\ +CCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACA\n\ +GAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACT\n\ +TTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAAC\n\ +ATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCC\n\ +TGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAG\n\ +GTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCG\n\ +TCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAG\n\ +GCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCC\n\ +GTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCT\n\ +ACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCC\n\ +GAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCC\n\ +GGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCAC\n\ +CTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAA\n\ +ATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTG\n\ +AGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCAC\n\ +TGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCT\n\ +CACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAG\n\ +TTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAG\n\ +CCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATC\n\ +GCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCT\n\ +GGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATC\n\ +CCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCC\n\ +TGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGG\n\ +CGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG\n\ +AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCG\n\ +AGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGG\n\ +AGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGT\n\ +GAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAA\n\ +TCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGC\n\ +AGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCA\n\ +AAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGG\n\ +CGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTC\n\ +TACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCG\n\ +GGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGAT\n\ +CGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCG\n\ +CGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAG\n\ +GTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACA\n\ +AAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCA\n\ +GGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCAC\n\ +TCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGC\n\ +CTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGA\n\ +GACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGG\n\ +CGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTG\n\ +AACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCG\n\ +ACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGC\n\ +ACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCC\n\ +AACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGC\n\ +GCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCG\n\ +GAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACT\n\ +CCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCC\n\ +GAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAAC\n\ +CCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA\n\ +GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGA\n\ +GCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAG\n\ +GCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGAT\n\ +CACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTA\n\ +AAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGG\n\ +CTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGC\n\ +CACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTG\n\ +GCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAG\n\ +GAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAAT\n\ +TAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGA\n\ +ATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAG\n\ +CCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTA\n\ +ATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCA\n\ +GCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGG\n\ +TGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCC\n\ +GGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGA\n\ +GCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTT\n\ +GGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACAT\n\ +GGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTG\n\ +TAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGT\n\ +TGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTC\n\ +TCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGC\n\ +GGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGT\n\ +CTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTAC\n\ +TCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGA\n\ +GATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGG\n\ +GCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCT\n\ +GAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT\n\ +ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAG\n\ +GCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTG\n\ +CACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCA\n\ +CGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTT\n\ +CGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCC\n\ +GGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGC\n\ +TTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGG\n\ +GCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCC\n\ +AGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTG\n\ +GCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCG\n\ +CGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAG\n\ +GCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAG\n\ +ACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAG\n\ +GCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGA\n\ +AACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATC\n\ +CCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAG\n\ +TGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAA\n\ +AAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCG\n\ +GATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTA\n\ +CTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGG\n\ +AGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCG\n\ +CGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCG\n\ +GTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGT\n\ +CAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAA\n\ +AATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGG\n\ +AGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTC\n\ +CAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCT\n\ +GTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA\n\ +CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCG\n\ +TGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAA\n\ +CCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGAC\n\ +AGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCAC\n\ +TTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAA\n\ +CATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGC\n\ +CTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGA\n\ +GGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCC\n\ +GTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGA\n\ +GGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCC\n\ +CGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGC\n\ +TACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGC\n\ +CGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGC\n\ +CGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCA\n\ +CCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAA\n\ +AATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCT\n\ +GAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCA\n\ +CTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGC\n\ +TCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGA\n\ +GTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTA\n\ +GCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAAT\n\ +CGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCC\n\ +TGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAAT\n\ +CCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGC\n\ +CTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTG\n\ +GCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGG\n\ +GAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGC\n\ +GAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG\n\ +GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGG\n\ +TGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTA\n\ +ATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTG\n\ +CAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTC\n\ +AAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGG\n\ +GCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCT\n\ +CTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTC\n\ +GGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGA\n\ +TCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGC\n\ +GCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGA\n\ +GGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATAC\n\ +AAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGC\n\ +AGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCA\n\ +CTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACG\n\ +CCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCG\n\ +AGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGG\n\ +GCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTT\n\ +GAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGC\n\ +GACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAG\n\ +CACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGC\n\ +CAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCG\n\ +CGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGC\n\ +GGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGAC\n\ +TCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGC\n\ +CGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAA\n\ +CCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCC\n\ +AGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTG\n\ +AGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA\n\ +GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGA\n\ +TCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACT\n\ +AAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAG\n\ +GCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCG\n\ +CCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGT\n\ +GGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCA\n\ +GGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAA\n\ +TTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAG\n\ +AATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCA\n\ +GCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGT\n\ +AATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACC\n\ +AGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTG\n\ +GTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACC\n\ +CGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAG\n\ +AGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTT\n\ +TGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACA\n\ +TGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCT\n\ +GTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGG\n\ +TTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGT\n\ +CTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGG\n\ +CGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCG\n\ +TCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTA\n\ +CTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCG\n\ +AGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCG\n\ +GGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACC\n\ +TGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAA\n\ +TACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGA\n\ +GGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACT\n\ +GCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTC\n\ +ACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGT\n\ +TCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGC\n\ +CGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCG\n\ +CTTGAACCCGGGAGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTG\n\ +GGCGACAGAGCGAGACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCC\n\ +CAGCACTTTGGGAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCT\n\ +GGCCAACATGGTGAAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGC\n\ +GCGCGCCTGTAATCCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGA\n\ +GGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGA\n\ +GACTCCGTCTCAAAAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGA\n\ +GGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTG\n\ +AAACCCCGTCTCTACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAAT\n\ +CCCAGCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGGAGGCGGAGGTTGCA\n\ +GTGAGCCGAGATCGCGCCACTGCACTCCAGCCTGGGCGACAGAGCGAGACTCCGTCTCAA\n\ +AAAGGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGGGAGGCCGAGGCGGGC\n\ +GGATCACCTGAGGTCAGGAGTTCGAGACCAGCCTGGCCAACATGGTGAAACCCCGTCTCT\n\ +ACTAAAAATACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCAGCTACTCGG\n\ +GAGGCTGAGGCAGGAGAATC\n\ +>TWO IUB ambiguity codes\n\ +cttBtatcatatgctaKggNcataaaSatgtaaaDcDRtBggDtctttataattcBgtcg\n\ +tactDtDagcctatttSVHtHttKtgtHMaSattgWaHKHttttagacatWatgtRgaaa\n\ +NtactMcSMtYtcMgRtacttctWBacgaaatatagScDtttgaagacacatagtVgYgt\n\ +cattHWtMMWcStgttaggKtSgaYaaccWStcgBttgcgaMttBYatcWtgacaYcaga\n\ +gtaBDtRacttttcWatMttDBcatWtatcttactaBgaYtcttgttttttttYaaScYa\n\ +HgtgttNtSatcMtcVaaaStccRcctDaataataStcYtRDSaMtDttgttSagtRRca\n\ +tttHatSttMtWgtcgtatSSagactYaaattcaMtWatttaSgYttaRgKaRtccactt\n\ +tattRggaMcDaWaWagttttgacatgttctacaaaRaatataataaMttcgDacgaSSt\n\ +acaStYRctVaNMtMgtaggcKatcttttattaaaaagVWaHKYagtttttatttaacct\n\ +tacgtVtcVaattVMBcttaMtttaStgacttagattWWacVtgWYagWVRctDattBYt\n\ +gtttaagaagattattgacVatMaacattVctgtBSgaVtgWWggaKHaatKWcBScSWa\n\ +accRVacacaaactaccScattRatatKVtactatatttHttaagtttSKtRtacaaagt\n\ +RDttcaaaaWgcacatWaDgtDKacgaacaattacaRNWaatHtttStgttattaaMtgt\n\ +tgDcgtMgcatBtgcttcgcgaDWgagctgcgaggggVtaaScNatttacttaatgacag\n\ +cccccacatYScaMgtaggtYaNgttctgaMaacNaMRaacaaacaKctacatagYWctg\n\ +ttWaaataaaataRattagHacacaagcgKatacBttRttaagtatttccgatctHSaat\n\ +actcNttMaagtattMtgRtgaMgcataatHcMtaBSaRattagttgatHtMttaaKagg\n\ +YtaaBataSaVatactWtataVWgKgttaaaacagtgcgRatatacatVtHRtVYataSa\n\ +KtWaStVcNKHKttactatccctcatgWHatWaRcttactaggatctataDtDHBttata\n\ +aaaHgtacVtagaYttYaKcctattcttcttaataNDaaggaaaDYgcggctaaWSctBa\n\ +aNtgctggMBaKctaMVKagBaactaWaDaMaccYVtNtaHtVWtKgRtcaaNtYaNacg\n\ +gtttNattgVtttctgtBaWgtaattcaagtcaVWtactNggattctttaYtaaagccgc\n\ +tcttagHVggaYtgtNcDaVagctctctKgacgtatagYcctRYHDtgBattDaaDgccK\n\ +tcHaaStttMcctagtattgcRgWBaVatHaaaataYtgtttagMDMRtaataaggatMt\n\ +ttctWgtNtgtgaaaaMaatatRtttMtDgHHtgtcattttcWattRSHcVagaagtacg\n\ +ggtaKVattKYagactNaatgtttgKMMgYNtcccgSKttctaStatatNVataYHgtNa\n\ +BKRgNacaactgatttcctttaNcgatttctctataScaHtataRagtcRVttacDSDtt\n\ +aRtSatacHgtSKacYagttMHtWataggatgactNtatSaNctataVtttRNKtgRacc\n\ +tttYtatgttactttttcctttaaacatacaHactMacacggtWataMtBVacRaSaatc\n\ +cgtaBVttccagccBcttaRKtgtgcctttttRtgtcagcRttKtaaacKtaaatctcac\n\ +aattgcaNtSBaaccgggttattaaBcKatDagttactcttcattVtttHaaggctKKga\n\ +tacatcBggScagtVcacattttgaHaDSgHatRMaHWggtatatRgccDttcgtatcga\n\ +aacaHtaagttaRatgaVacttagattVKtaaYttaaatcaNatccRttRRaMScNaaaD\n\ +gttVHWgtcHaaHgacVaWtgttScactaagSgttatcttagggDtaccagWattWtRtg\n\ +ttHWHacgattBtgVcaYatcggttgagKcWtKKcaVtgaYgWctgYggVctgtHgaNcV\n\ +taBtWaaYatcDRaaRtSctgaHaYRttagatMatgcatttNattaDttaattgttctaa\n\ +ccctcccctagaWBtttHtBccttagaVaatMcBHagaVcWcagBVttcBtaYMccagat\n\ +gaaaaHctctaacgttagNWRtcggattNatcRaNHttcagtKttttgWatWttcSaNgg\n\ +gaWtactKKMaacatKatacNattgctWtatctaVgagctatgtRaHtYcWcttagccaa\n\ +tYttWttaWSSttaHcaaaaagVacVgtaVaRMgattaVcDactttcHHggHRtgNcctt\n\ +tYatcatKgctcctctatVcaaaaKaaaagtatatctgMtWtaaaacaStttMtcgactt\n\ +taSatcgDataaactaaacaagtaaVctaggaSccaatMVtaaSKNVattttgHccatca\n\ +cBVctgcaVatVttRtactgtVcaattHgtaaattaaattttYtatattaaRSgYtgBag\n\ +aHSBDgtagcacRHtYcBgtcacttacactaYcgctWtattgSHtSatcataaatataHt\n\ +cgtYaaMNgBaatttaRgaMaatatttBtttaaaHHKaatctgatWatYaacttMctctt\n\ +ttVctagctDaaagtaVaKaKRtaacBgtatccaaccactHHaagaagaaggaNaaatBW\n\ +attccgStaMSaMatBttgcatgRSacgttVVtaaDMtcSgVatWcaSatcttttVatag\n\ +ttactttacgatcaccNtaDVgSRcgVcgtgaacgaNtaNatatagtHtMgtHcMtagaa\n\ +attBgtataRaaaacaYKgtRccYtatgaagtaataKgtaaMttgaaRVatgcagaKStc\n\ +tHNaaatctBBtcttaYaBWHgtVtgacagcaRcataWctcaBcYacYgatDgtDHccta\n\ +aagacYRcaggattHaYgtKtaatgcVcaataMYacccatatcacgWDBtgaatcBaata\n\ +cKcttRaRtgatgaBDacggtaattaaYtataStgVHDtDctgactcaaatKtacaatgc\n\ +gYatBtRaDatHaactgtttatatDttttaaaKVccYcaaccNcBcgHaaVcattHctcg\n\ +attaaatBtatgcaaaaatYMctSactHatacgaWacattacMBgHttcgaatVaaaaca\n\ +BatatVtctgaaaaWtctRacgBMaatSgRgtgtcgactatcRtattaScctaStagKga\n\ +DcWgtYtDDWKRgRtHatRtggtcgaHgggcgtattaMgtcagccaBggWVcWctVaaat\n\ +tcgNaatcKWagcNaHtgaaaSaaagctcYctttRVtaaaatNtataaccKtaRgtttaM\n\ +tgtKaBtRtNaggaSattHatatWactcagtgtactaKctatttgRYYatKatgtccgtR\n\ +tttttatttaatatVgKtttgtatgtNtataRatWYNgtRtHggtaaKaYtKSDcatcKg\n\ +taaYatcSRctaVtSMWtVtRWHatttagataDtVggacagVcgKWagBgatBtaaagNc\n\ +aRtagcataBggactaacacRctKgttaatcctHgDgttKHHagttgttaatgHBtatHc\n\ +DaagtVaBaRccctVgtgDtacRHSctaagagcggWYaBtSaKtHBtaaactYacgNKBa\n\ +VYgtaacttagtVttcttaatgtBtatMtMtttaattaatBWccatRtttcatagVgMMt\n\ +agctStKctaMactacDNYgKYHgaWcgaHgagattacVgtttgtRaSttaWaVgataat\n\ +gtgtYtaStattattMtNgWtgttKaccaatagNYttattcgtatHcWtctaaaNVYKKt\n\ +tWtggcDtcgaagtNcagatacgcattaagaccWctgcagcttggNSgaNcHggatgtVt\n\ +catNtRaaBNcHVagagaaBtaaSggDaatWaatRccaVgggStctDaacataKttKatt\n\ +tggacYtattcSatcttagcaatgaVBMcttDattctYaaRgatgcattttNgVHtKcYR\n\ +aatRKctgtaaacRatVSagctgtWacBtKVatctgttttKcgtctaaDcaagtatcSat\n\ +aWVgcKKataWaYttcccSaatgaaaacccWgcRctWatNcWtBRttYaattataaNgac\n\ +acaatagtttVNtataNaYtaatRaVWKtBatKagtaatataDaNaaaaataMtaagaaS\n\ +tccBcaatNgaataWtHaNactgtcDtRcYaaVaaaaaDgtttRatctatgHtgttKtga\n\ +aNSgatactttcgagWaaatctKaaDaRttgtggKKagcDgataaattgSaacWaVtaNM\n\ +acKtcaDaaatttctRaaVcagNacaScRBatatctRatcctaNatWgRtcDcSaWSgtt\n\ +RtKaRtMtKaatgttBHcYaaBtgatSgaSWaScMgatNtctcctatttctYtatMatMt\n\ +RRtSaattaMtagaaaaStcgVgRttSVaScagtgDtttatcatcatacRcatatDctta\n\ +tcatVRtttataaHtattcYtcaaaatactttgVctagtaaYttagatagtSYacKaaac\n\ +gaaKtaaatagataatSatatgaaatSgKtaatVtttatcctgKHaatHattagaaccgt\n\ +YaaHactRcggSBNgtgctaaBagBttgtRttaaattYtVRaaaattgtaatVatttctc\n\ +ttcatgBcVgtgKgaHaaatattYatagWacNctgaaMcgaattStagWaSgtaaKagtt\n\ +ttaagaDgatKcctgtaHtcatggKttVDatcaaggtYcgccagNgtgcVttttagagat\n\ +gctaccacggggtNttttaSHaNtatNcctcatSaaVgtactgBHtagcaYggYVKNgta\n\ +KBcRttgaWatgaatVtagtcgattYgatgtaatttacDacSctgctaaaStttaWMagD\n\ +aaatcaVYctccgggcgaVtaaWtStaKMgDtttcaaMtVgBaatccagNaaatcYRMBg\n\ +gttWtaaScKttMWtYataRaDBMaDataatHBcacDaaKDactaMgagttDattaHatH\n\ +taYatDtattDcRNStgaatattSDttggtattaaNSYacttcDMgYgBatWtaMagact\n\ +VWttctttgYMaYaacRgHWaattgRtaagcattctMKVStatactacHVtatgatcBtV\n\ +NataaBttYtSttacKgggWgYDtgaVtYgatDaacattYgatggtRDaVDttNactaSa\n\ +MtgNttaacaaSaBStcDctaccacagacgcaHatMataWKYtaYattMcaMtgSttDag\n\ +cHacgatcaHttYaKHggagttccgatYcaatgatRaVRcaagatcagtatggScctata\n\ +ttaNtagcgacgtgKaaWaactSgagtMYtcttccaKtStaacggMtaagNttattatcg\n\ +tctaRcactctctDtaacWYtgaYaSaagaWtNtatttRacatgNaatgttattgWDDcN\n\ +aHcctgaaHacSgaataaRaataMHttatMtgaSDSKatatHHaNtacagtccaYatWtc\n\ +actaactatKDacSaStcggataHgYatagKtaatKagStaNgtatactatggRHacttg\n\ +tattatgtDVagDVaRctacMYattDgtttYgtctatggtKaRSttRccRtaaccttaga\n\ +gRatagSaaMaacgcaNtatgaaatcaRaagataatagatactcHaaYKBctccaagaRa\n\ +BaStNagataggcgaatgaMtagaatgtcaKttaaatgtaWcaBttaatRcggtgNcaca\n\ +aKtttScRtWtgcatagtttWYaagBttDKgcctttatMggNttattBtctagVtacata\n\ +aaYttacacaaRttcYtWttgHcaYYtaMgBaBatctNgcDtNttacgacDcgataaSat\n\ +YaSttWtcctatKaatgcagHaVaacgctgcatDtgttaSataaaaYSNttatagtaNYt\n\ +aDaaaNtggggacttaBggcHgcgtNtaaMcctggtVtaKcgNacNtatVaSWctWtgaW\n\ +cggNaBagctctgaYataMgaagatBSttctatacttgtgtKtaattttRagtDtacata\n\ +tatatgatNHVgBMtKtaKaNttDHaagatactHaccHtcatttaaagttVaMcNgHata\n\ +tKtaNtgYMccttatcaaNagctggacStttcNtggcaVtattactHaSttatgNMVatt\n\ +MMDtMactattattgWMSgtHBttStStgatatRaDaagattttctatMtaaaaaggtac\n\ +taaVttaSacNaatactgMttgacHaHRttgMacaaaatagttaatatWKRgacDgaRta\n\ +tatttattatcYttaWtgtBRtWatgHaaattHataagtVaDtWaVaWtgStcgtMSgaS\n\ +RgMKtaaataVacataatgtaSaatttagtcgaaHtaKaatgcacatcggRaggSKctDc\n\ +agtcSttcccStYtccRtctctYtcaaKcgagtaMttttcRaYDttgttatctaatcata\n\ +NctctgctatcaMatactataggDaHaaSttMtaDtcNatataattctMcStaaBYtaNa\n\ +gatgtaatHagagSttgWHVcttatKaYgDctcttggtgttMcRaVgSgggtagacaata\n\ +aDtaattSaDaNaHaBctattgNtaccaaRgaVtKNtaaYggHtaKKgHcatctWtctDt\n\ +ttctttggSDtNtaStagttataaacaattgcaBaBWggHgcaaaBtYgctaatgaaatW\n\ +cDcttHtcMtWWattBHatcatcaaatctKMagtDNatttWaBtHaaaNgMttaaStagt\n\ +tctctaatDtcRVaYttgttMtRtgtcaSaaYVgSWDRtaatagctcagDgcWWaaaBaa\n\ +RaBctgVgggNgDWStNaNBKcBctaaKtttDcttBaaggBttgaccatgaaaNgttttt\n\ +tttatctatgttataccaaDRaaSagtaVtDtcaWatBtacattaWacttaSgtattggD\n\ +gKaaatScaattacgWcagKHaaccaYcRcaRttaDttRtttHgaHVggcttBaRgtccc\n\ +tDatKaVtKtcRgYtaKttacgtatBtStaagcaattaagaRgBagSaattccSWYttta\n\ +ttVaataNctgHgttaaNBgcVYgtRtcccagWNaaaacaDNaBcaaaaRVtcWMgBagM\n\ +tttattacgDacttBtactatcattggaaatVccggttRttcatagttVYcatYaSHaHc\n\ +ttaaagcNWaHataaaRWtctVtRYtagHtaaaYMataHYtNBctNtKaatattStgaMc\n\ +BtRgctaKtgcScSttDgYatcVtggaaKtaagatWccHccgKYctaNNctacaWctttt\n\ +gcRtgtVcgaKttcMRHgctaHtVaataaDtatgKDcttatBtDttggNtacttttMtga\n\ +acRattaaNagaactcaaaBBVtcDtcgaStaDctgaaaSgttMaDtcgttcaccaaaag\n\ +gWtcKcgSMtcDtatgtttStaaBtatagDcatYatWtaaaBacaKgcaDatgRggaaYc\n\ +taRtccagattDaWtttggacBaVcHtHtaacDacYgtaatataMagaatgHMatcttat\n\ +acgtatttttatattacHactgttataMgStYaattYaccaattgagtcaaattaYtgta\n\ +tcatgMcaDcgggtcttDtKgcatgWRtataatatRacacNRBttcHtBgcRttgtgcgt\n\ +catacMtttBctatctBaatcattMttMYgattaaVYatgDaatVagtattDacaacDMa\n\ +tcMtHcccataagatgBggaccattVWtRtSacatgctcaaggggYtttDtaaNgNtaaB\n\ +atggaatgtctRtaBgBtcNYatatNRtagaacMgagSaSDDSaDcctRagtVWSHtVSR\n\ +ggaacaBVaccgtttaStagaacaMtactccagtttVctaaRaaHttNcttagcaattta\n\ +ttaatRtaaaatctaacDaBttggSagagctacHtaaRWgattcaaBtctRtSHaNtgta\n\ +cattVcaHaNaagtataccacaWtaRtaaVKgMYaWgttaKggKMtKcgWatcaDatYtK\n\ +SttgtacgaccNctSaattcDcatcttcaaaDKttacHtggttHggRRaRcaWacaMtBW\n\ +VHSHgaaMcKattgtaRWttScNattBBatYtaNRgcggaagacHSaattRtttcYgacc\n\ +BRccMacccKgatgaacttcgDgHcaaaaaRtatatDtatYVtttttHgSHaSaatagct\n\ +NYtaHYaVYttattNtttgaaaYtaKttWtctaNtgagaaaNctNDctaaHgttagDcRt\n\ +tatagccBaacgcaRBtRctRtggtaMYYttWtgataatcgaataattattataVaaaaa\n\ +ttacNRVYcaaMacNatRttcKatMctgaagactaattataaYgcKcaSYaatMNctcaa\n\ +cgtgatttttBacNtgatDccaattattKWWcattttatatatgatBcDtaaaagttgaa\n\ +VtaHtaHHtBtataRBgtgDtaataMttRtDgDcttattNtggtctatctaaBcatctaR\n\ +atgNacWtaatgaagtcMNaacNgHttatactaWgcNtaStaRgttaaHacccgaYStac\n\ +aaaatWggaYaWgaattattcMaactcBKaaaRVNcaNRDcYcgaBctKaacaaaaaSgc\n\ +tccYBBHYaVagaatagaaaacagYtctVccaMtcgtttVatcaatttDRtgWctagtac\n\ +RttMctgtDctttcKtWttttataaatgVttgBKtgtKWDaWagMtaaagaaattDVtag\n\ +gttacatcatttatgtcgMHaVcttaBtVRtcgtaYgBRHatttHgaBcKaYWaatcNSc\n\ +tagtaaaaatttacaatcactSWacgtaatgKttWattagttttNaggtctcaagtcact\n\ +attcttctaagKggaataMgtttcataagataaaaatagattatDgcBVHWgaBKttDgc\n\ +atRHaagcaYcRaattattatgtMatatattgHDtcaDtcaaaHctStattaatHaccga\n\ +cNattgatatattttgtgtDtRatagSacaMtcRtcattcccgacacSattgttKaWatt\n\ +NHcaacttccgtttSRtgtctgDcgctcaaMagVtBctBMcMcWtgtaacgactctcttR\n\ +ggRKSttgYtYatDccagttDgaKccacgVatWcataVaaagaataMgtgataaKYaaat\n\ +cHDaacgataYctRtcYatcgcaMgtNttaBttttgatttaRtStgcaacaaaataccVg\n\ +aaDgtVgDcStctatatttattaaaaRKDatagaaagaKaaYYcaYSgKStctccSttac\n\ +agtcNactttDVttagaaagMHttRaNcSaRaMgBttattggtttaRMggatggcKDgWR\n\ +tNaataataWKKacttcKWaaagNaBttaBatMHtccattaacttccccYtcBcYRtaga\n\ +ttaagctaaYBDttaNtgaaaccHcaRMtKtaaHMcNBttaNaNcVcgVttWNtDaBatg\n\ +ataaVtcWKcttRggWatcattgaRagHgaattNtatttctctattaattaatgaDaaMa\n\ +tacgttgggcHaYVaaNaDDttHtcaaHtcVVDgBVagcMacgtgttaaBRNtatRtcag\n\ +taagaggtttaagacaVaaggttaWatctccgtVtaDtcDatttccVatgtacNtttccg\n\ +tHttatKgScBatgtVgHtYcWagcaKtaMYaaHgtaattaSaHcgcagtWNaatNccNN\n\ +YcacgVaagaRacttctcattcccRtgtgtaattagcSttaaStWaMtctNNcSMacatt\n\ +ataaactaDgtatWgtagtttaagaaaattgtagtNagtcaataaatttgatMMYactaa\n\ +tatcggBWDtVcYttcDHtVttatacYaRgaMaacaStaatcRttttVtagaDtcacWat\n\ +ttWtgaaaagaaagNRacDtttStVatBaDNtaactatatcBSMcccaSttccggaMatg\n\ +attaaWatKMaBaBatttgataNctgttKtVaagtcagScgaaaDggaWgtgttttKtWt\n\ +atttHaatgtagttcactaaKMagttSYBtKtaYgaactcagagRtatagtVtatcaaaW\n\ +YagcgNtaDagtacNSaaYDgatBgtcgataacYDtaaactacagWDcYKaagtttatta\n\ +gcatcgagttKcatDaattgattatDtcagRtWSKtcgNtMaaaaacaMttKcaWcaaSV\n\ +MaaaccagMVtaMaDtMaHaBgaacataBBVtaatVYaNSWcSgNtDNaaKacacBttta\n\ +tKtgtttcaaHaMctcagtaacgtcgYtactDcgcctaNgagagcYgatattttaaattt\n\ +ccattttacatttDaaRctattttWctttacgtDatYtttcagacgcaaVttagtaaKaa\n\ +aRtgVtccataBggacttatttgtttaWNtgttVWtaWNVDaattgtatttBaagcBtaa\n\ +BttaaVatcHcaVgacattccNggtcgacKttaaaRtagRtctWagaYggtgMtataatM\n\ +tgaaRttattttgWcttNtDRRgMDKacagaaaaggaaaRStcccagtYccVattaNaaK\n\ +StNWtgacaVtagaagcttSaaDtcacaacgDYacWDYtgtttKatcVtgcMaDaSKStV\n\ +cgtagaaWaKaagtttcHaHgMgMtctataagBtKaaaKKcactggagRRttaagaBaaN\n\ +atVVcgRcKSttDaactagtSttSattgttgaaRYatggttVttaataaHttccaagDtg\n\ +atNWtaagHtgcYtaactRgcaatgMgtgtRaatRaNaacHKtagactactggaatttcg\n\ +ccataacgMctRgatgttaccctaHgtgWaYcactcacYaattcttaBtgacttaaacct\n\ +gYgaWatgBttcttVttcgttWttMcNYgtaaaatctYgMgaaattacNgaHgaacDVVM\n\ +tttggtHtctaaRgtacagacgHtVtaBMNBgattagcttaRcttacaHcRctgttcaaD\n\ +BggttKaacatgKtttYataVaNattccgMcgcgtagtRaVVaattaKaatggttRgaMc\n\ +agtatcWBttNtHagctaatctagaaNaaacaYBctatcgcVctBtgcaaagDgttVtga\n\ +HtactSNYtaaNccatgtgDacgaVtDcgKaRtacDcttgctaagggcagMDagggtBWR\n\ +tttSgccttttttaacgtcHctaVtVDtagatcaNMaVtcVacatHctDWNaataRgcgt\n\ +aVHaggtaaaaSgtttMtattDgBtctgatSgtRagagYtctSaKWaataMgattRKtaa\n\ +catttYcgtaacacattRWtBtcggtaaatMtaaacBatttctKagtcDtttgcBtKYYB\n\ +aKttctVttgttaDtgattttcttccacttgSaaacggaaaNDaattcYNNaWcgaaYat\n\ +tttMgcBtcatRtgtaaagatgaWtgaccaYBHgaatagataVVtHtttVgYBtMctaMt\n\ +cctgaDcYttgtccaaaRNtacagcMctKaaaggatttacatgtttaaWSaYaKttBtag\n\ +DacactagctMtttNaKtctttcNcSattNacttggaacaatDagtattRtgSHaataat\n\ +gccVgacccgatactatccctgtRctttgagaSgatcatatcgDcagWaaHSgctYYWta\n\ +tHttggttctttatVattatcgactaagtgtagcatVgtgHMtttgtttcgttaKattcM\n\ +atttgtttWcaaStNatgtHcaaaDtaagBaKBtRgaBgDtSagtatMtaacYaatYtVc\n\ +KatgtgcaacVaaaatactKcRgtaYtgtNgBBNcKtcttaccttKgaRaYcaNKtactt\n\ +tgagSBtgtRagaNgcaaaNcacagtVtttHWatgttaNatBgtttaatNgVtctgaata\n\ +tcaRtattcttttttttRaaKcRStctcggDgKagattaMaaaKtcaHacttaataataK\n\ +taRgDtKVBttttcgtKaggHHcatgttagHggttNctcgtatKKagVagRaaaggaaBt\n\ +NatttVKcRttaHctaHtcaaatgtaggHccaBataNaNaggttgcWaatctgatYcaaa\n\ +HaatWtaVgaaBttagtaagaKKtaaaKtRHatMaDBtBctagcatWtatttgWttVaaa\n\ +ScMNattRactttgtYtttaaaagtaagtMtaMaSttMBtatgaBtttaKtgaatgagYg\n\ +tNNacMtcNRacMMHcttWtgtRtctttaacaacattattcYaMagBaacYttMatcttK\n\ +cRMtgMNccattaRttNatHaHNaSaaHMacacaVaatacaKaSttHatattMtVatWga\n\ +ttttttaYctttKttHgScWaacgHtttcaVaaMgaacagNatcgttaacaaaaagtaca\n\ +HBNaattgttKtcttVttaaBtctgctacgBgcWtttcaggacacatMgacatcccagcg\n\ +gMgaVKaBattgacttaatgacacacaaaaaatRKaaBctacgtRaDcgtagcVBaacDS\n\ +BHaaaaSacatatacagacRNatcttNaaVtaaaataHattagtaaaaSWccgtatWatg\n\ +gDttaactattgcccatcttHaSgYataBttBaactattBtcHtgatcaataSttaBtat\n\ +KSHYttWggtcYtttBttaataccRgVatStaHaKagaatNtagRMNgtcttYaaSaact\n\ +cagDSgagaaYtMttDtMRVgWKWtgMaKtKaDttttgactatacataatcNtatNaHat\n\ +tVagacgYgatatatttttgtStWaaatctWaMgagaRttRatacgStgattcttaagaD\n\ +taWccaaatRcagcagaaNKagtaaDggcgccBtYtagSBMtactaaataMataBSacRM\n\ +gDgattMMgtcHtcaYDtRaDaacggttDaggcMtttatgttaNctaattaVacgaaMMt\n\ +aatDccSgtattgaRtWWaccaccgagtactMcgVNgctDctaMScatagcgtcaactat\n\ +acRacgHRttgctatttaatgaattataYKttgtaagWgtYttgcHgMtaMattWaWVta\n\ +RgcttgYgttBHtYataSccStBtgtagMgtDtggcVaaSBaatagDttgBgtctttctc\n\ +attttaNagtHKtaMWcYactVcgcgtatMVtttRacVagDaatcttgctBBcRDgcaac\n\ +KttgatSKtYtagBMagaRtcgBattHcBWcaactgatttaatttWDccatttatcgagS\n\ +KaWttataHactaHMttaatHtggaHtHagaatgtKtaaRactgtttMatacgatcaagD\n\ +gatKaDctataMggtHDtggHacctttRtatcttYattttgacttgaaSaataaatYcgB\n\ +aaaaccgNatVBttMacHaKaataagtatKgtcaagactcttaHttcggaattgttDtct\n\ +aaccHttttWaaatgaaatataaaWattccYDtKtaaaacggtgaggWVtctattagtga\n\ +ctattaagtMgtttaagcatttgSgaaatatccHaaggMaaaattttcWtatKctagDtY\n\ +tMcctagagHcactttactatacaaacattaacttaHatcVMYattYgVgtMttaaRtga\n\ +aataaDatcaHgtHHatKcDYaatcttMtNcgatYatgSaMaNtcttKcWataScKggta\n\ +tcttacgcttWaaagNatgMgHtctttNtaacVtgttcMaaRatccggggactcMtttaY\n\ +MtcWRgNctgNccKatcttgYDcMgattNYaRagatHaaHgKctcataRDttacatBatc\n\ +cattgDWttatttaWgtcggagaaaaatacaatacSNtgggtttccttacSMaagBatta\n\ +caMaNcactMttatgaRBacYcYtcaaaWtagctSaacttWgDMHgaggatgBVgcHaDt\n\ +ggaactttggtcNatNgtaKaBcccaNtaagttBaacagtatacDYttcctNgWgcgSMc\n\ +acatStctHatgRcNcgtacacaatRttMggaNKKggataaaSaYcMVcMgtaMaHtgat\n\ +tYMatYcggtcttcctHtcDccgtgRatcattgcgccgatatMaaYaataaYSggatagc\n\ +gcBtNtaaaScaKgttBgagVagttaKagagtatVaactaSacWactSaKatWccaKaaa\n\ +atBKgaaKtDMattttgtaaatcRctMatcaaMagMttDgVatggMaaWgttcgaWatga\n\ +aatttgRtYtattaWHKcRgctacatKttctaccaaHttRatctaYattaaWatVNccat\n\ +NgagtcKttKataStRaatatattcctRWatDctVagttYDgSBaatYgttttgtVaatt\n\ +taatagcagMatRaacttBctattgtMagagattaaactaMatVtHtaaatctRgaaaaa\n\ +aaatttWacaacaYccYDSaattMatgaccKtaBKWBattgtcaagcHKaagttMMtaat\n\ +ttcKcMagNaaKagattggMagaggtaatttYacatcWaaDgatMgKHacMacgcVaaca\n\ +DtaDatatYggttBcgtatgWgaSatttgtagaHYRVacaRtctHaaRtatgaactaata\n\ +tctSSBgggaaHMWtcaagatKgagtDaSatagttgattVRatNtctMtcSaagaSHaat\n\ +aNataataRaaRgattctttaataaagWaRHcYgcatgtWRcttgaaggaMcaataBRaa\n\ +ccagStaaacNtttcaatataYtaatatgHaDgcStcWttaacctaRgtYaRtataKtgM\n\ +ttttatgactaaaatttacYatcccRWtttHRtattaaatgtttatatttgttYaatMca\n\ +RcSVaaDatcgtaYMcatgtagacatgaaattgRtcaaYaaYtRBatKacttataccaNa\n\ +aattVaBtctggacaagKaaYaaatatWtMtatcYaaVNtcgHaactBaagKcHgtctac\n\ +aatWtaDtSgtaHcataHtactgataNctRgttMtDcDttatHtcgtacatcccaggStt\n\ +aBgtcacacWtccNMcNatMVaVgtccDYStatMaccDatggYaRKaaagataRatttHK\n\ +tSaaatDgataaacttaHgttgVBtcttVttHgDacgaKatgtatatNYataactctSat\n\ +atatattgcHRRYttStggaactHgttttYtttaWtatMcttttctatctDtagVHYgMR\n\ +BgtHttcctaatYRttKtaagatggaVRataKDctaMtKBNtMtHNtWtttYcVtattMc\n\ +gRaacMcctNSctcatttaaagDcaHtYccSgatgcaatYaaaaDcttcgtaWtaattct\n\ +cgttttScttggtaatctttYgtctaactKataHacctMctcttacHtKataacacagcN\n\ +RatgKatttttSaaatRYcgDttaMRcgaaattactMtgcgtaagcgttatBtttttaat\n\ +taagtNacatHgttcRgacKcBBtVgatKttcgaBaatactDRgtRtgaNacWtcacYtt\n\ +aaKcgttctHaKttaNaMgWgWaggtctRgaKgWttSttBtDcNtgtttacaaatYcDRt\n\ +gVtgcctattcNtctaaaDMNttttNtggctgagaVctDaacVtWccaagtaacacaNct\n\ +gaScattccDHcVBatcgatgtMtaatBgHaatDctMYgagaatgYWKcctaatNaStHa\n\ +aaKccgHgcgtYaaYtattgtStgtgcaaRtattaKatattagaWVtcaMtBagttatta\n\ +gNaWHcVgcaattttDcMtgtaRHVYtHtctgtaaaaHVtMKacatcgNaatttMatatg\n\ +ttgttactagWYtaRacgataKagYNKcattataNaRtgaacKaYgcaaYYacaNccHat\n\ +MatDcNgtHttRaWttagaaDcaaaaaatagggtKDtStaDaRtaVtHWKNtgtattVct\n\ +SVgRgataDaRaWataBgaagaaKtaataaYgDcaStaNgtaDaaggtattHaRaWMYaY\n\ +aWtggttHYgagVtgtgcttttcaaDKcagVcgttagacNaaWtagtaataDttctggtt\n\ +VcatcataaagtgKaaaNaMtaBBaattaatWaattgctHaVKaSgDaaVKaHtatatat\n\ +HatcatSBagNgHtatcHYMHgttDgtaHtBttWatcgtttaRaattgStKgSKNWKatc\n\ +agDtctcagatttctRtYtBatBgHHtKaWtgYBgacVVWaKtacKcDttKMaKaVcggt\n\ +gttataagaataaHaatattagtataatMHgttYgaRttagtaRtcaaVatacggtcMcg\n\ +agtaaRttacWgactKRYataaaagSattYaWgagatYagKagatgSaagKgttaatMgg\n\ +tataatgttWYttatgagaaacctNVataatHcccKtDctcctaatactggctHggaSag\n\ +gRtKHaWaattcgSatMatttagaggcYtctaMcgctcataSatatgRagacNaaDagga\n\ +VBagaYttKtacNaKgtSYtagttggaWcatcWttaatctatgaVtcgtgtMtatcaYcg\n\ +tRccaaYgDctgcMgtgtWgacWtgataacacgcgctBtgttaKtYDtatDcatcagKaV\n\ +MctaatcttgVcaaRgcRMtDcgattaHttcaNatgaatMtactacVgtRgatggaWttt\n\ +actaaKatgagSaaKggtaNtactVaYtaaKRagaacccacaMtaaMtKtatBcttgtaa\n\ +WBtMctaataaVcDaaYtcRHBtcgttNtaaHatttBNgRStVDattBatVtaagttaYa\n\ +tVattaagaBcacggtSgtVtatttaRattgatgtaHDKgcaatattKtggcctatgaWD\n\ +KRYcggattgRctatNgatacaatMNttctgtcRBYRaaaHctNYattcHtaWcaattct\n\ +BtMKtVgYataatMgYtcagcttMDataVtggRtKtgaatgccNcRttcaMtRgattaac\n\ +attRcagcctHtWMtgtDRagaKaBtgDttYaaaaKatKgatctVaaYaacWcgcatagB\n\ +VtaNtRtYRaggBaaBtgKgttacataagagcatgtRattccacttaccatRaaatgWgD\n\ +aMHaYVgVtaSctatcgKaatatattaDgacccYagtgtaYNaaatKcagtBRgagtcca\n\ +tgKgaaaccBgaagBtgSttWtacgatWHaYatcgatttRaaNRgcaNaKVacaNtDgat\n\ +tgHVaatcDaagcgtatgcNttaDataatcSataaKcaataaHWataBtttatBtcaKtK\n\ +tatagttaDgSaYctacaRatNtaWctSaatatttYaKaKtaccWtatcRagacttaYtt\n\ +VcKgSDcgagaagatccHtaattctSttatggtKYgtMaHagVaBRatttctgtRgtcta\n\ +tgggtaHKgtHacHtSYacgtacacHatacKaaBaVaccaDtatcSaataaHaagagaat\n\ +ScagactataaRttagcaaVcaHataKgDacatWccccaagcaBgagWatctaYttgaaa\n\ +tctVNcYtttWagHcgcgcDcVaaatgttKcHtNtcaatagtgtNRaactttttcaatgg\n\ +WgBcgDtgVgtttctacMtaaataaaRggaaacWaHttaRtNtgctaaRRtVBctYtVta\n\ +tDcattDtgaccYatagatYRKatNYKttNgcctagtaWtgaactaMVaacctgaStttc\n\ +tgaKVtaaVaRKDttVtVctaDNtataaaDtccccaagtWtcgatcactDgYaBcatcct\n\ +MtVtacDaaBtYtMaKNatNtcaNacgDatYcatcgcaRatWBgaacWttKttagYtaat\n\ +tcggttgSWttttDWctttacYtatatWtcatDtMgtBttgRtVDggttaacYtacgtac\n\ +atgaattgaaWcttMStaDgtatattgaDtcRBcattSgaaVBRgagccaaKtttcDgcg\n\ +aSMtatgWattaKttWtgDBMaggBBttBaatWttRtgcNtHcgttttHtKtcWtagHSt\n\ +aacagttgatatBtaWSaWggtaataaMttaKacDaatactcBttcaatatHttcBaaSa\n\ +aatYggtaRtatNtHcaatcaHtagVtgtattataNggaMtcttHtNagctaaaggtaga\n\ +YctMattNaMVNtcKtactBKcaHHcBttaSagaKacataYgctaKaYgttYcgacWVtt\n\ +WtSagcaacatcccHaccKtcttaacgaKttcacKtNtacHtatatRtaaatacactaBt\n\ +ttgaHaRttggttWtatYagcatYDatcggagagcWBataagRtacctataRKgtBgatg\n\ +aDatataSttagBaHtaatNtaDWcWtgtaattacagKttcNtMagtattaNgtctcgtc\n\ +ctcttBaHaKcKccgtRcaaYagSattaagtKataDatatatagtcDtaacaWHcaKttD\n\ +gaaRcgtgYttgtcatatNtatttttatggccHtgDtYHtWgttatYaacaattcaWtat\n\ +NgctcaaaSttRgctaatcaaatNatcgtttaBtNNVtgttataagcaaagattBacgtD\n\ +atttNatttaaaDcBgtaSKgacgtagataatttcHMVNttgttBtDtgtaWKaaRMcKM\n\ +tHtaVtagataWctccNNaSWtVaHatctcMgggDgtNHtDaDttatatVWttgttattt\n\ +aacctttcacaaggaSaDcggttttttatatVtctgVtaacaStDVaKactaMtttaSNa\n\ +gtgaaattaNacttSKctattcctctaSagKcaVttaagNaVcttaVaaRNaHaaHttat\n\ +gtHttgtgatMccaggtaDcgaccgtWgtWMtttaHcRtattgScctatttKtaaccaag\n\ +tYagaHgtWcHaatgccKNRtttagtMYSgaDatctgtgaWDtccMNcgHgcaaacNDaa\n\ +aRaStDWtcaaaaHKtaNBctagBtgtattaactaattttVctagaatggcWSatMaccc\n\ +ttHttaSgSgtgMRcatRVKtatctgaaaccDNatYgaaVHNgatMgHRtacttaaaRta\n\ +tStRtDtatDttYatattHggaBcttHgcgattgaKcKtttcRataMtcgaVttWacatN\n\ +catacctRataDDatVaWNcggttgaHtgtMacVtttaBHtgagVttMaataattatgtt\n\ +cttagtttgtgcDtSatttgBtcaacHattaaBagVWcgcaSYttMgcttacYKtVtatc\n\ +aYaKctgBatgcgggcYcaaaaacgNtctagKBtattatctttKtaVttatagtaYtRag\n\ +NtaYataaVtgaatatcHgcaaRataHtacacatgtaNtgtcgYatWMatttgaactacR\n\ +ctaWtWtatacaatctBatatgYtaagtatgtgtatSttactVatcttYtaBcKgRaSgg\n\ +RaaaaatgcagtaaaWgtaRgcgataatcBaataccgtatttttccatcNHtatWYgatH\n\ +SaaaDHttgctgtccHtggggcctaataatttttctatattYWtcattBtgBRcVttaVM\n\ +RSgctaatMagtYtttaaaaatBRtcBttcaaVtaacagctccSaaSttKNtHtKYcagc\n\ +agaaaccccRtttttaaDcDtaStatccaagcgctHtatcttaDRYgatDHtWcaaaBcW\n\ +gKWHttHataagHacgMNKttMKHccaYcatMVaacgttaKgYcaVaaBtacgcaacttt\n\ +MctaaHaatgtBatgagaSatgtatgSRgHgWaVWgataaatatttccKagVgataattW\n\ +aHNcYggaaatgctHtKtaDtctaaagtMaatVDVactWtSaaWaaMtaHtaSKtcBRaN\n\ +cttStggtBttacNagcatagRgtKtgcgaacaacBcgKaatgataagatgaaaattgta\n\ +ctgcgggtccHHWHaaNacaBttNKtKtcaaBatatgctaHNgtKcDWgtttatNgVDHg\n\ +accaacWctKaaggHttgaRgYaatHcaBacaatgagcaaattactgtaVaaYaDtagat\n\ +tgagNKggtggtgKtWKaatacagDRtatRaMRtgattDggtcaaYRtatttNtagaDtc\n\ +acaaSDctDtataatcgtactaHttatacaatYaacaaHttHatHtgcgatRRttNgcat\n\ +SVtacWWgaaggagtatVMaVaaattScDDKNcaYBYaDatHgtctatBagcaacaagaa\n\ +tgagaaRcataaKNaRtBDatcaaacgcattttttaaBtcSgtacaRggatgtMNaattg\n\ +gatatWtgagtattaaaVctgcaYMtatgatttttYgaHtgtcttaagWBttHttgtctt\n\ +attDtcgtatWtataataSgctaHagcDVcNtaatcaagtaBDaWaDgtttagYctaNcc\n\ +DtaKtaHcttaataacccaRKtacaVaatNgcWRaMgaattatgaBaaagattVYaHMDc\n\ +aDHtcRcgYtcttaaaWaaaVKgatacRtttRRKYgaatacaWVacVcRtatMacaBtac\n\ +tggMataaattttHggNagSctacHgtBagcgtcgtgattNtttgatSaaggMttctttc\n\ +ttNtYNagBtaaacaaatttMgaccttacataattgYtcgacBtVMctgStgMDtagtaR\n\ +ctHtatgttcatatVRNWataDKatWcgaaaaagttaaaagcacgHNacgtaatctttMR\n\ +tgacttttDacctataaacgaaatatgattagaactccSYtaBctttaataacWgaaaYa\n\ +tagatgWttcatKtNgatttttcaagHtaYgaaRaDaagtaggagcttatVtagtctttc\n\ +attaaaatcgKtattaRttacagVaDatgcatVgattgggtctttHVtagKaaRBtaHta\n\ +aggccccaaaaKatggtttaMWgtBtaaacttcactttKHtcgatctccctaYaBacMgt\n\ +cttBaBaNgcgaaacaatctagtHccHtKttcRtRVttccVctttcatacYagMVtMcag\n\ +aMaaacaataBctgYtaatRaaagattaaccatVRatHtaRagcgcaBcgDttStttttc\n\ +VtttaDtKgcaaWaaaaatSccMcVatgtKgtaKgcgatatgtagtSaaaDttatacaaa\n\ +catYaRRcVRHctKtcgacKttaaVctaDaatgttMggRcWaacttttHaDaKaDaBctg\n\ +taggcgtttaHBccatccattcNHtDaYtaataMttacggctNVaacDattgatatttta\n\ +cVttSaattacaaRtataNDgacVtgaacataVRttttaDtcaaacataYDBtttaatBa\n\ +DtttYDaDaMccMttNBttatatgagaaMgaNtattHccNataattcaHagtgaaggDga\n\ +tgtatatatgYatgaStcataaBStWacgtcccataRMaaDattggttaaattcMKtctM\n\ +acaBSactcggaatDDgatDgcWctaacaccgggaVcacWKVacggtaNatatacctMta\n\ +tgatagtgcaKagggVaDtgtaacttggagtcKatatcgMcttRaMagcattaBRaStct\n\ +YSggaHYtacaactMBaagDcaBDRaaacMYacaHaattagcattaaaHgcgctaaggSc\n\ +cKtgaaKtNaBtatDDcKBSaVtgatVYaagVtctSgMctacgttaacWaaattctSgtD\n\ +actaaStaaattgcagBBRVctaatatacctNttMcRggctttMttagacRaHcaBaacV\n\ +KgaataHttttMgYgattcYaNRgttMgcVaaacaVVcDHaatttgKtMYgtatBtVVct\n\ +WgVtatHtacaaHttcacgatagcagtaaNattBatatatttcVgaDagcggttMaagtc\n\ +ScHagaaatgcYNggcgtttttMtStggtRatctacttaaatVVtBacttHNttttaRca\n\ +aatcacagHgagagtMgatcSWaNRacagDtatactaaDKaSRtgattctccatSaaRtt\n\ +aaYctacacNtaRtaactggatgaccYtacactttaattaattgattYgttcagDtNKtt\n\ +agDttaaaaaaaBtttaaNaYWKMBaaaacVcBMtatWtgBatatgaacVtattMtYatM\n\ +NYDKNcKgDttDaVtaaaatgggatttctgtaaatWtctcWgtVVagtcgRgacttcccc\n\ +taDcacagcRcagagtgtWSatgtacatgttaaSttgtaaHcgatgggMagtgaacttat\n\ +RtttaVcaccaWaMgtactaatSSaHtcMgaaYtatcgaaggYgggcgtgaNDtgttMNg\n\ +aNDMtaattcgVttttaacatgVatgtWVMatatcaKgaaattcaBcctccWcttgaaWH\n\ +tWgHtcgNWgaRgctcBgSgaattgcaaHtgattgtgNagtDttHHgBttaaWcaaWagc\n\ +aSaHHtaaaVctRaaMagtaDaatHtDMtcVaWMtagSagcttHSattaacaaagtRacM\n\ +tRtctgttagcMtcaBatVKtKtKacgagaSNatSactgtatatcBctgagVtYactgta\n\ +aattaaaggcYgDHgtaacatSRDatMMccHatKgttaacgactKtgKagtcttcaaHRV\n\ +tccttKgtSataatttacaactggatDNgaacttcaRtVaagDcaWatcBctctHYatHa\n\ +DaaatttagYatSatccaWtttagaaatVaacBatHcatcgtacaatatcgcNYRcaata\n\ +YaRaYtgattVttgaatgaVaactcRcaNStgtgtattMtgaggtNttBaDRcgaaaagc\n\ +tNgBcWaWgtSaDcVtgVaatMKBtttcgtttctaaHctaaagYactgMtatBDtcStga\n\ +ccgtSDattYaataHctgggaYYttcggttaWaatctggtRagWMaDagtaacBccacta\n\ +cgHWMKaatgatWatcctgHcaBaSctVtcMtgtDttacctaVgatYcWaDRaaaaRtag\n\ +atcgaMagtggaRaWctctgMgcWttaagKBRtaaDaaWtctgtaagYMttactaHtaat\n\ +cttcataacggcacBtSgcgttNHtgtHccatgttttaaagtatcgaKtMttVcataYBB\n\ +aKtaMVaVgtattNDSataHcagtWMtaggtaSaaKgttgBtVtttgttatcatKcgHac\n\ +acRtctHatNVagSBgatgHtgaRaSgttRcctaacaaattDNttgacctaaYtBgaaaa\n\ +tagttattactcttttgatgtNNtVtgtatMgtcttRttcatttgatgacacttcHSaaa\n\ +ccaWWDtWagtaRDDVNacVaRatgttBccttaatHtgtaaacStcVNtcacaSRttcYa\n\ +gacagaMMttttgMcNttBcgWBtactgVtaRttctccaaYHBtaaagaBattaYacgat\n\ +ttacatctgtaaMKaRYtttttactaaVatWgctBtttDVttctggcDaHaggDaagtcg\n\ +aWcaagtagtWttHtgKtVataStccaMcWcaagataagatcactctHatgtcYgaKcat\n\ +cagatactaagNSStHcctRRNtattgtccttagttagMVgtatagactaactctVcaat\n\ +MctgtttgtgttgccttatWgtaBVtttctggMcaaKgDWtcgtaaYStgSactatttHg\n\ +atctgKagtagBtVacRaagRtMctatgggcaaaKaaaatacttcHctaRtgtDcttDat\n\ +taggaaatttcYHaRaaBttaatggcacKtgctHVcaDcaaaVDaaaVcgMttgtNagcg\n\ +taDWgtcgttaatDgKgagcSatatcSHtagtagttggtgtHaWtaHKtatagctgtVga\n\ +ttaBVaatgaataagtaatVatSttaHctttKtttgtagttaccttaatcgtagtcctgB\n\ +cgactatttVcMacHaaaggaatgDatggKtaHtgStatattaaSagctWcctccRtata\n\ +BaDYcgttgcNaagaggatRaaaYtaWgNtSMcaatttactaacatttaaWttHtatBat\n\ +tgtcgacaatNgattgcNgtMaaaKaBDattHacttggtRtttaYaacgVactBtaBaKt\n\ +gBttatgVttgtVttcaatcWcNctDBaaBgaDHacBttattNtgtDtatttVSaaacag\n\ +gatgcRatSgtaSaNtgBatagttcHBgcBBaaattaHgtDattatDaKaatBaaYaaMa\n\ +ataaataKtttYtagtBgMatNcatgtttgaNagtgttgtgKaNaSagtttgaSMaYBca\n\ +aaacDStagttVacaaaaactaaWttBaagtctgtgcgtMgtaattctcctacctcaNtt\n\ +taaccaaaaVtBcacataacaccccBcWMtatVtggaatgaWtcaaWaaaaaaaaWtDta\n\ +atatRcctDWtcctaccMtVVatKttaWaaKaaatataaagScHBagaggBaSMtaWaVt\n\ +atattactSaaaKNaactatNatccttgaYctattcaaaVgatttYHcRagattttaSat\n\ +aggttattcVtaaagaKgtattattKtRttNcggcRgtgtgtWYtaacHgKatKgatYta\n\ +cYagDtWcHBDctctgRaYKaYagcactKcacSaRtBttttBHKcMtNtcBatttatttt\n\ +tgSatVgaaagaWtcDtagDatatgMacaacRgatatatgtttgtKtNRaatatNatgYc\n\ +aHtgHataacKtgagtagtaacYttaNccaaatHcacaacaVDtagtaYtccagcattNt\n\ +acKtBtactaaagaBatVtKaaHBctgStgtBgtatgaSNtgDataaccctgtagcaBgt\n\ +gatcttaDataStgaMaccaSBBgWagtacKcgattgaDgNNaaaacacagtSatBacKD\n\ +gcgtataBKcatacactaSaatYtYcDaactHttcatRtttaatcaattataRtttgtaa\n\ +gMcgNttcatcBtYBagtNWNMtSHcattcRctttttRWgaKacKttgggagBcgttcgc\n\ +MaWHtaatactgtctctatttataVgtttaBScttttaBMaNaatMacactYtBMggtHa\n\ +cMagtaRtctgcatttaHtcaaaatttgagKtgNtactBacaHtcgtatttctMaSRagc\n\ +agttaatgtNtaaattgagagWcKtaNttagVtacgatttgaatttcgRtgtWcVatcgt\n\ +taaDVctgtttBWgaccagaaagtcSgtVtatagaBccttttcctaaattgHtatcggRa\n\ +ttttcaaggcYSKaagWaWtRactaaaacccBatMtttBaatYtaagaactSttcgaaSc\n\ +aatagtattgaccaagtgttttctaacatgtttNVaatcaaagagaaaNattaaRtttta\n\ +VaaaccgcaggNMtatattVctcaagaggaacgBgtttaacaagttcKcYaatatactaa\n\ +ccBaaaSggttcNtattctagttRtBacgScVctcaatttaatYtaaaaaaatgSaatga\n\ +tagaMBRatgRcMcgttgaWHtcaVYgaatYtaatctttYttatRaWtctgBtDcgatNa\n\ +tcKaBaDgatgtaNatWKctccgatattaacattNaaacDatgBgttctgtDtaaaMggt\n\ +gaBaSHataacgccSctaBtttaRBtcNHcDatcDcctagagtcRtaBgWttDRVHagat\n\ +tYatgtatcWtaHtttYcattWtaaagtctNgtStggRNcgcggagSSaaagaaaatYcH\n\ +DtcgctttaatgYcKBVSgtattRaYBaDaaatBgtatgaHtaaRaRgcaSWNtagatHa\n\ +acttNctBtcaccatctMcatattccaSatttgcgaDagDgtatYtaaaVDtaagtttWV\n\ +aagtagYatRttaagDcNgacKBcScagHtattatcDaDactaaaaaYgHttBcgaDttg\n\ +gataaaKSRcBMaBcgaBSttcWtgNBatRaccgattcatttataacggHVtaattcaca\n\ +agagVttaaRaatVVRKcgWtVgacctgDgYaaHaWtctttcacMagggatVgactagMa\n\ +aataKaaNWagKatagNaaWtaaaatttgaattttatttgctaaVgaHatBatcaaBWcB\n\ +gttcMatcgBaaNgttcgSNaggSaRtttgHtRtattaNttcDcatSaVttttcgaaaaa\n\ +ttgHatctaRaggSaNatMDaaatDcacgattttagaHgHaWtYgattaatHNSttatMS\n\ +gggNtcKtYatRggtttgtMWVtttaYtagcagBagHaYagttatatggtBacYcattaR\n\ +SataBatMtttaaatctHcaaaSaaaagttNSaaWcWRccRtKaagtBWtcaaattSttM\n\ +tattggaaaccttaacgttBtWatttatatWcDaatagattcctScacctaagggRaaYt\n\ +aNaatgVtBcttaaBaacaMVaaattatStYgRcctgtactatcMcVKatttcgSgatRH\n\ +MaaaHtagtaaHtVgcaaataatatcgKKtgccaatBNgaaWcVttgagttaKatagttc\n\ +aggKDatDtattgaKaVcaKtaataDataataHSaHcattagttaatRVYcNaHtaRcaa\n\ +ggtNHcgtcaaccaBaaagYtHWaaaRcKgaYaaDttgcWYtataRgaatatgtYtgcKt\n\ +aNttWacatYHctRaDtYtattcBttttatcSataYaYgttWaRagcacHMgtttHtYtt\n\ +YaatcggtatStttcgtRSattaaDaKMaatatactaNBaWgctacacYtgaYVgtgHta\n\ +aaRaaRgHtagtWattataaaSDaaWtgMattatcgaaaagtaYRSaWtSgNtBgagcRY\n\ +aMDtactaacttaWgtatctagacaagNtattHggataatYttYatcataDcgHgttBtt\n\ +ctttVttgccgaaWtaaaacgKgtatctaaaaaNtccDtaDatBMaMggaatNKtatBaa\n\ +atVtccRaHtaSacataHattgtttKVYattcataVaattWtcgtgMttcttKtgtctaa\n\ +cVtatctatatBRataactcgKatStatattcatHHRttKtccaacgtgggtgRgtgaMt\n\ +attattggctatcgtgacMtRcBDtcttgtactaatRHttttaagatcgVMDStattatY\n\ +BtttDttgtBtNttgRcMtYtgBacHaWaBaatDKctaagtgaaactaatgRaaKgatcc\n\ +aagNaaaatattaggWNtaagtatacttttKcgtcggSYtcttgRctataYcttatataa\n\ +agtatattaatttataVaacacaDHatctatttttKYVatHRactttaBHccaWagtact\n\ +BtcacgaVgcgttRtttttttSVgtSagtBaaattctgaHgactcttgMcattttagVta\n\ +agaattHctHtcaDaaNtaacRggWatagttcgtSttgaDatcNgNagctagDgatcNtt\n\ +KgttgtaDtctttRaaYStRatDtgMggactSttaDtagSaVtBDttgtDgccatcacaM\n\ +attaaaMtNacaVcgSWcVaaDatcaHaatgaattaMtatccVtctBtaattgtWattat\n\ +BRcWcaatgNNtactWYtDaKttaaatcactcagtRaaRgatggtKgcgccaaHgaggat\n\ +StattYcaNMtcaBttacttatgagDaNtaMgaaWtgtttcttctaHtMNgttatctaWW\n\ +atMtBtaaatagDVatgtBYtatcggcttaagacMRtaHScgatatYgRDtcattatSDa\n\ +HggaaataNgaWSRRaaaBaatagBattaDctttgHWNttacaataaaaaaatacggttt\n\ +gHgVtaHtWMttNtBtctagtMcgKMgHgYtataHaNagWtcaacYattaataYRgtaWK\n\ +gaBctataaccgatttaHaNBRaRaMtccggtNgacMtctcatttgcaattcWgMactta\n\ +caaDaaNtactWatVtttagccttMaatcagVaagtctVaaDaBtattaattaYtNaYtg\n\ +gattaKtaKctYaMtattYgatattataatKtVgDcttatatNBtcgttgtStttttMag\n\ +aggttaHYSttcKgtcKtDNtataagttataagSgttatDtRttattgttttSNggRtca\n\ +aKMNatgaatattgtBWtaMacctgggYgaSgaagYataagattacgagaatBtggtRcV\n\ +HtgYggaDgaYaKagWagctatagacgaaHgtWaNgacttHRatVaWacKYtgRVNgVcS\n\ +gRWctacatcKSactctgWYtBggtataagcttNRttVtgRcaWaaatDMatYattaact\n\ +ttcgaagRatSctgccttgcRKaccHtttSNVagtagHagBagttagaccaRtataBcca\n\ +taatSHatRtcHagacBWatagcaMtacaRtgtgaaBatctKRtScttccaNaatcNgta\n\ +atatWtcaMgactctBtWtaaNactHaaaaRctcgcatggctMcaaNtcagaaaaacaca\n\ +gtggggWttRttagtaagaVctVMtcgaatcttcMaaaHcaHBttcgattatgtcaDagc\n\ +YRtBtYcgacMgtDcagcgaNgttaataatagcagKYYtcgtaBtYctMaRtaRtDagaa\n\ +aacacatgYaBttgattattcgaaNttBctSataaMataWRgaHtttccgtDgaYtatgg\n\ +tDgHKgMtatttVtMtVagttaRatMattRagataaccctKctMtSttgaHagtcStcta\n\ +tttccSagatgttccacgaggYNttHRacgattcDatatDcataaaatBBttatcgaHtN\n\ +HaaatatDNaggctgaNcaaggagttBttMgRagVatBcRtaWgatgBtSgaKtcgHttt\n\ +gaatcaaDaHttcSBgHcagtVaaSttDcagccgttNBtgttHagYtattctttRWaaVt\n\ +SttcatatKaaRaaaNacaVtVctMtSDtDtRHRcgtaatgctcttaaatSacacaatcg\n\ +HattcaWcttaaaatHaaatcNctWttaNMcMtaKctVtcctaagYgatgatcYaaaRac\n\ +tctaRDaYagtaacgtDgaggaaatctcaaacatcaScttcKttNtaccatNtaNataca\n\ +tttHaaDHgcaDatMWaaBttcRggctMaagctVYcacgatcaDttatYtaatcKatWat\n\ +caatVYtNagatttgattgaYttttYgacttVtcKaRagaaaHVgDtaMatKYagagttN\n\ +atWttaccNtYtcDWgSatgaRgtMatgKtcgacaagWtacttaagtcgKtgatccttNc\n\ +ttatagMatHVggtagcgHctatagccctYttggtaattKNaacgaaYatatVctaataM\n\ +aaaYtgVtcKaYtaataacagaatHcacVagatYWHttagaaSMaatWtYtgtaaagNaa\n\ +acaVgaWtcacNWgataNttcaSagctMDaRttgNactaccgataMaaatgtttattDtc\n\ +aagacgctDHYYatggttcaagccNctccttcMctttagacBtaaWtaWVHggaaaaNat\n\ +ttaDtDtgctaaHHtMtatNtMtagtcatttgcaaaRatacagRHtatDNtgtDgaatVg\n\ +tVNtcaaatYBMaaaagcaKgtgatgatMgWWMaHttttMgMagatDtataaattaacca\n\ +actMtacataaattgRataatacgBtKtaataattRgtatDagDtcRDacctatRcagag\n\ +cSHatNtcaScNtttggacNtaaggaccgtgKNttgttNcttgaaRgYgRtNtcagttBc\n\ +ttttcHtKtgcttYaaNgYagtaaatgaatggWaMattBHtatctatSgtcYtgcHtaat\n\ +tHgaaMtHcagaaSatggtatgccaHBtYtcNattWtgtNgctttaggtttgtWatNtgH\n\ +tgcDttactttttttgcNtactKtWRaVcttcatagtgSNKaNccgaataaBttataata\n\ +YtSagctttaaatSttggctaaKSaatRccgWHgagDttaaatcatgagMtcgagtVtaD\n\ +ggaBtatttgDacataaacgtagYRagBWtgDStKDgatgaagttcattatttaKWcata\n\ +aatWRgatataRgttRacaaNKttNtKagaaYaStaactScattattaacgatttaaatg\n\ +DtaattagatHgaYataaactatggggatVHtgccgtNgatNYcaStRtagaccacWcaM\n\ +tatRagHgVactYtWHtcttcatgatWgagaKggagtatgaWtDtVtNaNtcgYYgtaaa\n\ +ctttaDtBactagtaDctatagtaatatttatatataacgHaaaRagKattSagttYtSt\n\ +>THREE Homo sapiens frequency\n\ +agagagacgatgaaaattaatcgtcaatacgctggcgaacactgagggggacccaatgct\n\ +cttctcggtctaaaaaggaatgtgtcagaaattggtcagttcaaaagtagaccggatctt\n\ +tgcggagaacaattcacggaacgtagcgttgggaaatatcctttctaccacacatcggat\n\ +tttcgccctctcccattatttattgtgttctcacatagaattattgtttagacatccctc\n\ +gttgtatggagagttgcccgagcgtaaaggcataatccatataccgccgggtgagtgacc\n\ +tgaaattgtttttagttgggatttcgctatggattagcttacacgaagagattctaatgg\n\ +tactataggataattataatgctgcgtggcgcagtacaccgttacaaacgtcgttcgcat\n\ +atgtggctaacacggtgaaaatacctacatcgtatttgcaatttcggtcgtttcatagag\n\ +cgcattgaattactcaaaaattatatatgttgattatttgattagactgcgtggaaagaa\n\ +ggggtactcaagccatttgtaaaagctgcatctcgcttaagtttgagagcttacattagt\n\ +ctatttcagtcttctaggaaatgtctgtgtgagtggttgtcgtccataggtcactggcat\n\ +atgcgattcatgacatgctaaactaagaaagtagattactattaccggcatgcctaatgc\n\ +gattgcactgctatgaaggtgcggacgtcgcgcccatgtagccctgataataccaatact\n\ +tacatttggtcagcaattctgacattatacctagcacccataaatttactcagacttgag\n\ +gacaggctcttggagtcgatcttctgtttgtatgcatgtgatcatatagatgaataagcg\n\ +atgcgactagttagggcatagtatagatctgtgtatacagttcagctgaacgtccgcgag\n\ +tggaagtacagctgagatctatcctaaaatgcaaccatatcgttcacacatgatatgaac\n\ +ccagggggaaacattgagttcagttaaattggcagcgaatcccccaagaagaaggcggag\n\ +tgacgttgaacgggcttatggtttttcagtacttcctccgtataagttgagcgaaatgta\n\ +aacagaataatcgttgtgttaacaacattaaaatcgcggaatatgatgagaatacacagt\n\ +gtgagcatttcacttgtaaaatatctttggtagaacttactttgctttaaatatgttaaa\n\ +ccgatctaataatctacaaaacggtagattttgcctagcacattgcgtccttctctattc\n\ +agatagaggcaatactcagaaggttttatccaaagcactgtgttgactaacctaagtttt\n\ +agtctaataatcatgattgattataggtgccgtggactacatgactcgtccacaaataat\n\ +acttagcagatcagcaattggccaagcacccgacttttatttaatggttgtgcaatagtc\n\ +cagattcgtattcgggactctttcaaataatagtttcctggcatctaagtaagaaaagct\n\ +cataaggaagcgatattatgacacgctcttccgccgctgttttgaaacttgagtattgct\n\ +cgtccgaaattgagggtcacttcaaaatttactgagaagacgaagatcgactaaagttaa\n\ +aatgctagtccacagttggtcaagttgaattcatccacgagttatatagctattttaatt\n\ +tatagtcgagtgtacaaaaaacatccacaataagatttatcttagaataacaacccccgt\n\ +atcatcgaaatcctccgttatggcctgactcctcgagcttatagcatttgtgctggcgct\n\ +cttgccaggaacttgctcgcgaggtggtgacgagtgagatgatcagtttcattatgatga\n\ +tacgattttatcgcgactagttaatcatcatagcaagtaaaatttgaattatgtcattat\n\ +catgctccattaacaggttatttaattgatactgacgaaattttttcacaatgggttttc\n\ +tagaatttaatatcagtaattgaagccttcataggggtcctactagtatcctacacgacg\n\ +caggtccgcagtatcctggagggacgtgttactgattaaaagggtcaaaggaatgaaggc\n\ +tcacaatgttacctgcttcaccatagtgagccgatgagttttacattagtactaaatccc\n\ +aaatcatactttacgatgaggcttgctagcgctaaagagaatacatacaccaccacatag\n\ +aattgttagcgatgatatcaaatagactcctggaagtgtcagggggaaactgttcaatat\n\ +ttcgtccacaggactgaccaggcatggaaaagactgacgttggaaactataccatctcac\n\ +gcccgacgcttcactaattgatgatccaaaaaatatagcccggattcctgattagcaaag\n\ +ggttcacagagaaagatattatcgacgtatatcccaaaaaacagacgtaatgtgcatctt\n\ +cgaatcgggatgaatacttgtatcataaaaatgtgacctctagtatacaggttaatgtta\n\ +gtgatacacaatactcgtgggccatgggttctcaaataaaatgtaatattgcgtcgatca\n\ +ctcacccacgtatttggtctaattatgttttatttagtgacaatccaatagataaccggt\n\ +cctattaagggctatatttttagcgaccacgcgtttaaacaaaggattgtatgtagatgg\n\ +taccagtttaattgccagtgggcaatcctaagcaaaatgagattctatcctaaagtttgg\n\ +gcttgatataagatttcggatgtatgggttttataatcgttggagagctcaatcatgagc\n\ +taatacatggatttcgctacctcaccgagagaccttgcatgaagaattctaaccaaaagt\n\ +ttaataggccggattggattgagttaattaagaccttgttcagtcatagtaaaaaccctt\n\ +aaattttaccgattgacaaagtgagcagtcgcaataccctatgcgaaacgcctcgatagt\n\ +gactaggtatacaaggtttttgagttcctttgaaatagttaactaatttaaaattaatta\n\ +acgacatggaaatcacagaacctaatgctttgtaggagttatttatgctgtttactgcct\n\ +ctacaaccctaataaagcagtcctaagaatgaaacgcatcttttagttcagaaagtggta\n\ +tccagggtggtcaatttaataaattcaacatcgggtctcaggatattcggtcatataatt\n\ +tattaagggctcttcgagtcttactctgagtgaaattggaaacagtcatccttttcgttg\n\ +tgaggcatcttacaccgctatcgatatacaatgcattccaccgcggtgtcccgtacacaa\n\ +ggaaacttgttaccttggggatataagaaaactcacacgtctcattattaaactgagtac\n\ +aatttttgcacgagaaagtaatgcaatacaatatgatgaaagccagctaatgaaaaggga\n\ +tggaacgcacctcggatctgttgcactggattaaaatccgattatttttaaaaatattca\n\ +gtgctagagcatatcaggtctacttttttatctggtatgtaaagcccacggagcgatagt\n\ +gagatccttacgactcaacgaaaagttataacataactcccgttagccaaagcccaatcc\n\ +cgattactgccctaccctaacgtctgccatctaaatatcgaacttgttatgatcaatgtg\n\ +actacctcccaccctttccccttcatttgttccactggggataagctagcgttttcagaa\n\ +tcaatgcaataagaatagccaattgtctcacttcatcagagctcttggcaattccaggcg\n\ +ctacgtggttctggaatatattcatttttcaaatagtaatacgtttagtgttgctattgt\n\ +ctacacgtttggatattacgttatgtgagcggacatcaatagttgtctaactctttagta\n\ +agccagagatagcactcttagcgaatggataccatcttccataagtttagttaatagtcc\n\ +gaaacaactgcttcgagcatatttgaacctccttgtaggcaaatagcctcttcaaagcaa\n\ +tcttactaatagatagagtttgttttaagggactactagaaatgggacaatcttaatagt\n\ +atgacctaaactgacatttaaagatatatccaggtggcaagcataaagatcattgcgcca\n\ +cctccaccgtgggattacttatcagtcgatatcctatatgctaagtttgcgacggcagaa\n\ +tacaaactaagctgagttgatgctaaccttacctatgataccccattggaccggttaaca\n\ +gccctacttattccaaataaaagaacttttatgctgtagaagctattatagtgatgcctg\n\ +gtaacttcagtatattaaaatgacacacatacgccatatagagctcctggaactttgaat\n\ +aatgagcgaacttcgaagttgaagagcaagaaaccatatgtcacggttgcctaaagcccg\n\ +gtaaccagacatgtgctatcattgatcattatcgaggttttcataaccttgacccattat\n\ +cggctgtgcgcggacaagtacttaaatcactagtttcttcacctgcttatcggtaagaaa\n\ +taaggttggcaaagaatcgcataagacggacgtagagccgcagcgttgtgcgagtccagg\n\ +tgcatgcgcagcaataggattttaaattttgttccatttttaatttagccgtaaggatgt\n\ +ccgtaaatgattgaaaattggattcaatctttgggcctatgctactggaacctgatcgac\n\ +aaaatttcaaacatacgttaactccgaaagaccgtatttttgcggctagaatagtcagtc\n\ +gcttggagccatataccttaccacttaaacgacgtgctcctgtagttgaaatataaacag\n\ +aacacaaagactaccgatcatatcaactgaagatctttgtaactttgaggcgaagcaccc\n\ +tcttcgagacaactaagagtaaagtaccgggcgccgcaaggagtcgattgggaccctaaa\n\ +tcttgacgaattgctaagaggctcagagctaccactgtaatttctctagagcccataata\n\ +aatgaacgatacatccgtaggtagcacctaagggattataatggaagccaaatgcagtta\n\ +ataatattatatactggcgtacacgattcgacggatctctcacatagtgattcacgaccc\n\ +ccccctttgattgacacagcgtcagcattttgcaagaacgatcttctgcatagggtgcgc\n\ +caccgtaaggatgacgtcgaagctacaactgggtataatttaccatgcttccctgatgct\n\ +gagtgcaatacactaagaatgagtttttaccccatatcaccagtatttgttctgttattg\n\ +cgaagaaatggctatgctgagttggcgactaaagtcacccatcctttttattaggtaacc\n\ +ccctcccttaaactaactgatttgctggagctgccctgcatacatatactttatcattta\n\ +tggacgtccgtgacgcttattatccaccatagtcgatatgctacacggattcattaatgg\n\ +atcgtaggagtttaagttatatttactaagatcggtctcggctactatcccgccttaccc\n\ +ggcgctatttacggccatttttaatatattgacggtaattattcctatggtttcgaccgc\n\ +acgtccttggacaagaaagaatggcaaaaaaaatgtaaaagaaaaaaaatattgagtccc\n\ +taccatcatataaaaaatatgtgatgagtaacttgacgaaatgttagtggttattaaaga\n\ +ctatctattacaccttttgttttctgtcgtagtatattaaagtctagaagccttacagga\n\ +aaatcagggttatacagccgatactccgcagcatgaatcatcgaggaggtgtcctaccat\n\ +cgcgccttgtaatcttgtctgtgtatactgtatttagaccttttatacaaagtaaatatc\n\ +tcggctttatgtgattgggaggggcctactcaaacatgatgacttgacctaataatcact\n\ +gtgcgggcgtcttatgactagctattccttgaaatccaccaccaaatggttaatatgtaa\n\ +aaactttgacgatgaaacaaggtgaatgtgtagttactttgtgtaattagctgcgtcgag\n\ +cattgcttgtaaaaccgtcaatcgcacacgttacttccataaaatttctacgaatacacc\n\ +cttcttaaaaaaaacgtaggaattcacgagtttaacaaacgataactgtataaagtggaa\n\ +gtccgaagaaagcagatgcccgaactactcgaagatgtttcgttttcttaaccatagggg\n\ +cttcttaatggcccactacgcacattttgttcaagcccgagagggacatccccattacgg\n\ +gagtattactaaaactgttccgtaatacgttcagcaagggatgaaaaaggccactgctca\n\ +agttattgacgtgggagtattacatcggaagcctgaatcccacactatgatggtctgtac\n\ +aggcctagggactgcgtctagacggtattaccggcttctaatcatacgatcgtgagtctt\n\ +aacgggaagtaaggctcacacctaccccaaaccatttatctatgtaagtataaaattgtg\n\ +cgtaagtgttcaaagtggacaataaagacgtggcaaaaacccccgcacataagccgcttt\n\ +agatttcacaaataccaatgcggttaaaaacatccttgagtcgtacatacaccatactcg\n\ +cgttaaacggatataacagaagataataaatccggatgtggagtcggtgtaactatagaa\n\ +agccaagtgaaataatgcttaccagtcatttagctatacggctttcatttcatgtcaaga\n\ +gggtggagtttgacctgtacagttgatatatcaccgatacttagaactcacctaaagcta\n\ +aaattgctcgcagcgtgtaatccgcatattacaaacaatagatgggattcattatacata\n\ +agacacgatgatctgctttttcaggttgcgagatgttgcctatcgtcaatcgagtcctgc\n\ +cttacaccacttaaacaaaagtattgacagggaacctattttcgaggtattatatagtcc\n\ +agcttgaatatcaatttgacagttaacctagtgaaaatcagtaagaggaaatacgccaca\n\ +ttctccagtgaaattctacgggttatcgtctagtccaactatcaattataactcacgaga\n\ +tataagtaaattctcgtacttggcctgatttttattatactttggatccttagtaaacag\n\ +gaagggagaaaccttcaacgaaaaacactggattttgttttactctcaaagctcttatat\n\ +gacggaaataccctgtcaagtcttaactttattactagactaatgaaatgggcttggggt\n\ +ggccagaatcatagtacaatttagcggatacactattcggactttcctatcggctgtctg\n\ +gttggataagtatggggactaataggctagacatacctatacttaaactatacaggcgtc\n\ +atctatctctgcaactttggagttccctgatgttctcccgccctttgggttcacatcttc\n\ +tataccgacacccctaataacgattagtttgtgggttagagtaaattaatacggttaata\n\ +ttaatgtatcgttgaaaagctggtgtcgccaataaggtaaccggctaggcagagtatatg\n\ +tcacgaagtataactaccctaatgataagctgtaggaataaaattaatgctgtctctaag\n\ +cgaagagatatttccgactctgttttaatgacgaatctcattacttctgacttgcaaatg\n\ +ttcaatatggcacggtttcacggcacctttgtgacgcatataatgaacttagaagattat\n\ +aacgacggaactttatatgataatccgttacgattaaagaatctgttaaatatcataatg\n\ +gcattcagttctagaccgtgcatcatggtaaacttactttctctgcatggcgacatacat\n\ +ttcgctattcaaattcgcgtgtggttacacccactcgcacctttggaatattaagagaag\n\ +atgatcagaaaatccattcgctcaatttttctgacgtacgtctaatttatcctaggagac\n\ +aaatcgttttatgtctctcacatttttgaagaaaggttcgagagacaatactcaggtcct\n\ +gaactgctagaagatactcggtggagcgtggcaacaatgaaaaactcgtgacataaatga\n\ +atgatacttttccaagttcagttaagtgaatatgtttaacatacccggcttttcgatctt\n\ +aagctgacgctggacgtgcgagtaatgtcagtctcttacatacactagtgactccaagtt\n\ +tcgtcaaaaacgccccctcccttctcgagcccactcacgctatgtattgacgcgaacttg\n\ +ttcgggatcagacttttcaggagttcggtcgcgtgtccctatgtgctaatatataagtta\n\ +gatcgcattagatgctaatctgaatacttatagacgaccttcaacgagaacgggtaccac\n\ +cttgaggctagagttaggtgtgaaacgacaggtagggacatataaaatttgagtgcggct\n\ +ttagttaagggtttaattacctactcaaacatcacgctcgcgcccttcgtacgtaatcga\n\ +ccatctagaggctaaggggactgtactaggtagtgattaatgatatcctagacgcacgtg\n\ +ccttagatcttcagactctgatggtccgcgatcaccgtaattgtagtcctccaactcgat\n\ +cactttgttggcgtcaaagaaattacgatatctaaatacttataatacaataaccaagga\n\ +tgagaatgactcatcgcgttggagttatattgcttgaagttctatggaatgaaagcacgt\n\ +tatctgccgtcccaatatctccagtgagctaattcattggacggtccactttgatcaatc\n\ +cccgaggagatgttcggacactttagtctgtaacacttagcgttgagaccacgaacaatt\n\ +gattactcagtcttgaaggtgttttccaaagttcattttaaataagactacgataggcct\n\ +ttcctattgatataaactacccggctctgttgttcgtgtgagtcgtacttctctgtgttt\n\ +ttctgattatagcaagattcgattcttagtgtaaacagcgatttttatttgacccgtcaa\n\ +tgagaagcgcataggatctaagcaaaattatcaagttgtgccacaaggtaagatctttcc\n\ +agttattgcaggtaggatgtatcccacgttgatagtatgaggtctgacgtcaactgtcta\n\ +ggagagttgaccgcgtgcgggtacaccggatttgcatcgatgttgagaacgcagaactcc\n\ +cactgtcgtggcggcgttcctgatatttagcaagaggcgttgataaagccctcatcatct\n\ +agatctcgacctcatctgccctcttgctccatcattttctacacagactactttcctatc\n\ +tacgttagtataattgctttctatcttagtatcatttagagcttctccgtcaacaggttc\n\ +gtgctattaaagttagtacgaaagggacaacttgtagcaacgcatttaatcggttttcga\n\ +ctacttcgcacaaaatcagataaagaagtttgtcattctattagacattgaattgcgcaa\n\ +ttgacttgtaccacttatgatcgaacactgaatcaagactgtgattaactaaaatagaca\n\ +agccactatatcaactaataaaaacgcccctggtggtcgaacatagttgactacaggata\n\ +attaattggactggagccattacattctctacaatcgtatcacttcccaagtagacaact\n\ +ttgaccttgtagtttcatgtacaaaaaaatgctttcgcaggagcacattggtagttcaat\n\ +agtttcatgggaacctcttgagccgtcttctgtgggtgtgttcggatagtaggtactgat\n\ +aaagtcgtgtcgctttcgatgagagggaattcaccggaaaacaccttggttaacaggata\n\ +gtctatgtaaacttcgagacatgtttaagagttaccagcttaatccacggtgctctacta\n\ +gtatcatcagctgtcttgcctcgcctagaaatatgcattctatcgttatcctatcaacgg\n\ +ttgccgtactgagcagccttattgtggaagagtaatatataaatgtagtcttgtctttac\n\ +gaagcagacgtaagtaataatgacttggaataccaaaactaaacatagtggattatcata\n\ +ctcaagaactctccagataaataacagtttttacgatacgtcaccaatgagcttaaagat\n\ +taggatcctcaaaactgatacaaacgctaattcatttgttattggatccagtatcagtta\n\ +aactgaatggagtgaagattgtagaatgttgttctggcctcgcatggggtctaggtgata\n\ +tacaatttctcatacttacacggtagtggaaatctgattctagcttcgtagctgactata\n\ +ctcaaggaaccactgctcaaggtaggagactagttccgaccctacagtcaaagtggccga\n\ +agcttaaactatagactagttgttaaatgctgatttcaagatatcatctatatacagttt\n\ +ggacaattatgtgtgcgaaactaaaattcatgctattcagatggatttcacttatgcctt\n\ +agaaacagatattgcccgagctcaatcaacagttttagccggaaacaatcgaagcatagg\n\ +gacaatgtatcttttcctaaattgccatgtgcagatttctgagtgtcacgaagcgcataa\n\ +tagaatcttgtgttgcctcaactcgttgaaaagtttaaaacaatcgcagcagtctttttg\n\ +gggtctactgtgtgtttgcaaaataactgaaagaaacgcttgaacaactctgaagtagct\n\ +cgagtactcattaaagtgtaacacattagtgaatatcggccaatgaaccaaacgcttccc\n\ +ggtacgctatctctctcatcgggaggcgatgtgcaggttatctacgaaagcatcccttta\n\ +cgttgagagtgtcgatgcatgaacctcattgtaacaatagcccagcaaattctcatacgt\n\ +gcctcagggtccgggcgtactcctccatggaagggcgcgcatctagtgttataccaactc\n\ +gctttttaactactatgctgtagttctacaggcatagtggccagtattttctaacttctc\n\ +tggatagatgctctcactcctcatccatcacggcttcagtttacgtcttacttgcttgtt\n\ +cagcaacggatggaggcattaagtatcttcactgttccctaaaattgctgttcaatatca\n\ +aagtaaggacgatacagggaaagctcaagcacactcattgaatactgccccagttgcaac\n\ +ctcacttaatctgacaaaaataatgactactctaagtgttgcggaagcagtctcttccac\n\ +gagcttgtctgtatcacttcgtataggcatgtaactcgatagacacgaacaccgagtgag\n\ +aaactatattcttgcttccgtgtgtgtgacaccaggtaattgatgcggatataagctgga\n\ +gatcactcacgcccacacaaggcgctgctacctctttattccaatgtgtaagaatttgct\n\ +aacttcatttctagaccgcagctttgcggtcataatttcacggtacggacccttgggtta\n\ +gagacttgataacacacttcgcagtttccaccgcgcacatgttttagtggcttctaacat\n\ +agaatttttgttgtgacataaagagtgcgtgggagacttgcccgaccgttaagccataat\n\ +caattgaaagccccgtgagtcacatctaattggttgtactgcgcatttagctatccttta\n\ +gctgactcgaagagattcgattcctaatataggttaattagatggctgccgcgcgaagta\n\ +aaacgtgaaaaacgtagtgcgcagatctgcataactcgcgcttaattacttatgagtagt\n\ +tccaagttcgctacgttatgagagagattggaattaagcaaatatgttttatggtgattt\n\ +tgggatgagaaggactgctaagtacggctactaaacaaatttctaaaaccgccatctacc\n\ +ttatcttggagacatttaagttgtatatgtcactagtctagcttttgtctgtgggacgcg\n\ +ttctcggaatgagggaaatgcaagagccgattcatcaaatgcttatctaagaaagtagtg\n\ +gactattacaccaagcacgaatgccagggaactgctttcttgctcaggacctcgcgacaa\n\ +ggtaccccgcataagtcctagaattacatttggtcagcaatgctgacatttgaccgtgaa\n\ +aacataattttaatcagaaggcagctcacccgcttgctctagatcttatctttgtatgaa\n\ +tgtcagaatttactgcaatatccgttccgaatagtgagggcttagtatagttctctgtat\n\ +acaggtcacatcaaactccccctgtcctagtacagctctgagctttaattaattgcatac\n\ +atttccttcaatcatcagatgaaaacaccgcgaatcatgctcttctcgtatagggcaaga\n\ +gaagcaacaaacaactagcccgactcacgttcatccgccgtatccttgttcagttcttac\n\ +tccgtattaggtcagcgaaatctaatcagaataatcggtcgcgtatcaaaattaaaatcc\n\ +cgcttgaggttgacaattaaaacgctgagcagttatcggctattagatagtggggtgaaa\n\ +gtaattggctggaattatgttaaaacgtgatattaagctaaaatacgctacttgttgccg\n\ +acctaattcagtcattcgatattcagttagagccaagaataacaagcttgtataaattga\n\ +acggggtgcactaaacgatgtgttactctaatattcagcttggagtatacctgaaggcga\n\ +attcatgtatcggccaataataagacgttgaagatcacaatttggactagcaaaagaagg\n\ +tgatttatgcgtggggattgagtccactgtacgagtacggtctctggaaaattataggtt\n\ +cagggaatataaggaagtaaagataattaccaagagatttttggtatcgctatgacccag\n\ +aggtgttctaacgtctgttttgatccgcagaatttctgcctcaatgcatatttgacggac\n\ +ttgaactagagcctctaaagttaaatggcgacgcaactgttcctaaacttcaattattac\n\ +tactctttttttcctagggtattgtagaggccagtggacaaaataaatcaaatttaagat\n\ +gtttcggacattaacatcccccgtagcatagaaatcatcagttatccaatctctcatcga\n\ +gcttttacaatttctgctggcgctatggacagcatatgccgcgagacctccgcaagactc\n\ +acttgatcactgtaagtatcttcattagaggttagagcctatagttaagctgctgaccta\n\ +gtaaaattggtattttctaattttattgctcaagttaaaggttagtgaagggataatgac\n\ +gttatttttgaacaatgggttgtattcaattttatatcacgaatggaacccttcattccc\n\ +ggcataatactagacgacacgaacaagctccgatctatcagccaggcacgtgttaaggtt\n\ +taattccggcaaaccaatgaagcatcaaaaggtgacctgatgcaacttagggtcacgatg\n\ +agtttttcaggactacttattacctattaataagttaacatgagccttcataccccgtaa\n\ +gacaatacatactccaccaattagaattctgagccatcttatctttttgtatcatcgaag\n\ +ggtatggccgaataggttaattagttactcctaacgtctctacaggcatgcatttgacgc\n\ +accttcgaaaatagtcaatctctcgccacacgcgtctagtatgcagcatcaaaaatatag\n\ +tccacggtttccggattaccaaacgcggcaaagagaaacattgtatcgacggagataact\n\ +taatacagaaggaaggggcatcttcgaatacggatgaataattctatctgtttattctga\n\ +catcttgttttcaggttaatcttacgcattcaaatgacgcctgccccatgcgtgcgcaat\n\ +tattttctaatattgacgagagcaatctcactccttttgggtctatttatgttttattga\n\ +ggcacaagcctatacagaacaggtactattaaggccgtgagtgtgagactcaaaccgtgg\n\ +aaacaaaggatgggttgttcttggtacaagttttagtgcatgtgggcaatccttaccaaa\n\ +atcagatgctatccttaactttgggctgcatttaagatggcggttggaggcctgtgagaa\n\ +tcctgcgtgtcatctttaatgaccgaattcatccatgtagattcagatcacacactcatt\n\ +ccttgatgttgtctaaacaaaagttgttgtggacgcattggagggagttaagtaacaact\n\ +tgggatcgcatacttataaaaattatatgttaaactttcacaaacgctgaagtccaaagt\n\ +aactagcccaaacgcctcgagagtcactaggtattaatggtgtttgagttcctgtgaaat\n\ +agtgttcgaaggtaaaatttatgtaccaaatcgaaagaacacttaataaggcttgcttgc\n\ +acggaggtatgatgtttactgactctacaaccctaattttccagtacgtacattcattcc\n\ +aataggttagttctcaaagtgctatacaggctcctcaattgatgatatgcttcagccgct\n\ +ctatggatattagctcattttatttaggaagcccgcttagaggcttactatgagggaaat\n\ +gccaaaatgtcatacttttcggtgtgtcccatatgacaccgctttacatagaatttgaat\n\ +taaaacgcgctctcccgttcactaccatacttggtaccgtgcgcatattacatatagata\n\ +taggatcattttttaaagctgtactaggtttgatcgacaatcttatgctatactatatga\n\ +tgtaaccctcataatcaataccgatcgtacgatcctagcataggtggcaagcgattttat\n\ +gccgattattgtgttaaatagtctgtgagtgtgattatcagggctacgttggtagagggg\n\ +ttgtatagacctcgcacacattgtgacatacttaacaatatacgaaaactgatataataa\n\ +atccccttacccaaacaccaatcccgttgaatcaactaccataacgtctcccatataaat\n\ +tgcctacttgtttgcataaatctgaatacataacaccattgcaccttcttgtgttccaat\n\ +cccgttaagattgccttgtcagatgatatgcaagaacaatagcatttgctagcaattatt\n\ +aacagctcttcgaattgcctccacataacgcgggagggtatattttaatttggcaaatac\n\ +taagtactgttggcgtcatatgctattaacggttggatattaagttatgtcagccgtaag\n\ +caagagtgggcgaaatattttgttacccagtgagagcactcttagagtttggatacaata\n\ +ggccatatgttgacttaagaggacgtaactacgccgtacaccattgttcaaccgacttct\n\ +tggcaaatagaatcgtattagcaatcttaagaatagagacacgttcgtgttagggtatac\n\ +tacaaatccgaaaatcttaagaggatcacctaaactgaaatttatacatatttcaacgtg\n\ +gatagatttaacataattcagccacctccaacctgggagtaattttcagtagatttacta\n\ +gatgattagtggcccaacgcacttgactatataagatctggggatcctaacctgacctat\n\ +gagacaaaattggaaacgttaacagcccttatgtgtacaaagaaaagtaagttgttgctg\n\ +ttcaacagatgatagtcatgacgcgtaacttcactatagtaaattgaaacaaatacgcaa\n\ +tttagacagaatggtacggtcatgaatgacagtaattcgaagtgctagaccaacttaaaa\n\ +taggtaaacgtgcccgaaaccccccttaacagaaagctgctatcatggtgcagtatcgac\n\ +gtgttcagaaacttgtaacttttgagcaggtccgagcacatggaagtatatcacgtgttt\n\ +ctgaaccggcttatccctaagatatatccgtcgcaaactttcgatttagtcccacgtaga\n\ +gcccaagcgttgtgcgactccacgtgcatgcccagaaatacgagtttaaatttggttaca\n\ +tggttaattttgaccgaagcatcgcactttatgattgataattggattcaatatgtcgcc\n\ +ctatgcgaatgcaacatgatccacaatttggctataagacgtttaatccgtatcacactt\n\ +tgtttgcggctagtatagtaacgcccgtgcaccaagagtcagtaacaattataagtactc\n\ +cgcaggtacttcaaatataaaaactaatcaaacacgacccatatgatcatctgaagatat\n\ +ttggaactttctcgacaaccaccctcgtactcaatacttacactaatcgacaggcacacg\n\ +caacgtgtacagtcgcaccatattgagtcaagatttgcttagtggcgatgagcgtacacg\n\ +cttatttctctagtcacaattagttatctacgagacatcacgagggagcaaataagcgat\n\ +gttatggctacacataggcacgtatgaatatgatataagccagttaaacagtcgaaccat\n\ +cgagcaaattctcatgcaccaacccacacgttgaggcacaaagagtaagctgtttgaatg\n\ +taacttcttctgctgagcgggccccaacgtaaggatcaactagaagagaaaactcggtat\n\ +tagtttaaatgcgtcacggagcatgagtgcatttcactaagaatgtctgtgtaaccaata\n\ +taacatctatttgttatctgattgcctacttatggctttgcggtcgtggcgactaatgtc\n\ +tccaatccttttgaggtcggtaccaactccctttaaattacgctgtgcaggctcatgcac\n\ +tgcatacatatacggtagcaggtagggacctcacgcacccttattataatcaatagtagt\n\ +tatcagtcaacgaggcaggaatgctgaggtcgaggtgttggtatattttctatgtgccgt\n\ +ctaggcgactatcacgcattaccaggcgagatttaagccaattttgaatatagtcaacgt\n\ +aatttttactatgggttccaccgaaacgccttgcacaactaagaatcccataaaatatcg\n\ +atatcaaataaaagattgtgtcaataccttcatatatattttttcggttgactaacgtga\n\ +actaaggttaggggttttgtatgtctatataggaaacagtttcttttctgtcctacttta\n\ +gtaaagtcttcaagccttactccaaaatcacggtgattaagccgttactcagcagcatga\n\ +ttctgcctgctcgggtcctaaaatccagccttgtaagagtcgctgtgtattagctaggga\n\ +gacctttgttaaaaaggatatatcgcggcgggatgtgagtgcgtggcgcatactcaatct\n\ +tcagctcgtgtcattataatatctctcccccacgcttttcactagatatgccgtgtaagc\n\ +aaacaccttatgcttaatttcgaaaatattggtacttgaaaaaagctgtaggggtactta\n\ +atgtctggtaggagatcaggagagaattgagtgtaaaaccgtaaagccctcacctgactt\n\ +catgtaaatggcttagaagactccatgatttaataaatactacgaaggaaagactggatc\n\ +taaagataactctagtaaggccaactcccttcaatgctgttgccagttataatccaagag\n\ +ctgtccttttctgaaccatagcggcttctgaagcgaactagaagcaaagttggttctagc\n\ +cagacagccacataccctgtacgggtgtattactaaaactggtccggtattagttcacca\n\ +agggaggaattaggcaaaggatctaggtatgcaagtcggagtattacatccctaccctga\n\ +atccatcaataggttcctctgtactggccttcgcaatgagtattcaaggttgtacagccg\n\ +tataataataagatagtgactatgaacgggaagtaacccgctcaccttccccaaaacatt\n\ +gttatatctaagtattaaagtctgccgtagtgttaatactcgaaaataaacaactggcaa\n\ +attacaccgcacttaagccgcttttgatttatatttttccaatgcgcttttaaaaataat\n\ +tcagtcctacatactaattaagacccttaaacggagatatcacaagttaagttttaacca\n\ +tctcgactaggtggaactatagatacccaactcaatttatcattacctgtaatgttccta\n\ +gaaggattgcatttcatgtcaagacggtggagtttcacagcgaaacttcagtgtgaacag\n\ +attctgagaaatcacctaaacctattagtcagagcacccggttagaaccagttgtcaaaa\n\ +aatagagcggttgcatgagacagaagtaacgatgagatccgttgtaacgttgagacatct\n\ +ggcctatcgtcaatacagtcctcccttaaaaatatttttaaatactaggcaaacccaaca\n\ +taggttagtcctatgtgatacgccacatggtatatcattttgtaacgttacctagggata\n\ +atcaggaagtggaattacgcaaaagtagacagtgaaatgcttagggttatagtctagtcc\n\ +aaagataaaggataaagcacgtcagagaactatattagccgaatgggaatcattgttagg\n\ +agactgtggatcatgtctaaaaagcaacgcagaaacagtcatcgaaaaaatctcgttttt\n\ +gtttgaatctaaaagagctttgatgaccgatagtacctgtatactagttactgtattacg\n\ +tgtctaatgatttcggattggggtccccagaatcagacgtcattgtagacgattcaagtt\n\ +taccaatttaatttcccagctctccttggagaactatcgccaataattgcagtcactttc\n\ +cttttctgaaacgataaagccgtcagagttctctgcaacgttggacttacctgaggttct\n\ +aacccactttcggttctaatagtagttaacgacacaacgaataacctttactgtggggct\n\ +ttcacgatattttttcgcttattattaatggttacgtcataagctggtgtccaaattaag\n\ +gttaccggcttcgcagagtagttgtatccaagtataacttccctaatcataagatcgagg\n\ +tagaaaattaatgctgtctctaaccgaacagatatgtcccactatgtggtatggacgttg\n\ +ctaattacttctgaagggaaattggtcattatggatacgtgtctaccatcaggtcggacg\n\ +cagatatggttctgtcttcagttgatccaccgttctttataggataataactgacgatta\n\ +aagattatggtaaatagattaagccaattctcttcttgtcagtgaagcatccttaactga\n\ +cttgctctgcagcccctcatacatttagctattcaaagtaccggctcgtttcaaactctc\n\ +ccacctttggaagaggttgtcaacttgataagtatatcatttacagcattttttcggacg\n\ +tacctctaatgtttcattgcagaaaattagttttttctatcgcacattttgcaagtaacg\n\ +ttagagacacaattatctgcgaatgaactgctagatctgacgaccgggagcctcgcaaat\n\ +atcaaaaaagactgacatatatcaaggagtcgttgacaagtgctggtaagtcaattggtt\n\ +tatctgtcccggcgtttcgatcttaagctgaccatgcacggcagagtaatgtcactctcg\n\ +ttcttacaagtctgtctccaagggtcggcaaaaaagacccctccattctcgagcccactc\n\ +acgatatgtagggacgacaacttgtgcggcttatgaattgtctggactgcgggcgagggt\n\ +ccatatctccgaagttagaagggacatacctttagatgataagatcaattcttattgacg\n\ +aaattcatccacaacggggaacaacttcaccctagacttacgtctgaaaagacacctagc\n\ +gtcttataaaaggtcagtgccccgtttcgtaaggctggaattacctacgcaaacttaaac\n\ +ctcgcgcccttccttacgtatcgacaagatagaggctatcgcgaatgtactacggaggca\n\ +tgaatcatatactagaaccaagtgcctgtgatattaacaagatgatccgacgcgagcacc\n\ +gtaattctaggcataaaactccagcaatttgggggccgaaaacaaatgacgttagctaat\n\ +taattatatgacatgatcaaaggaggtcaatcacgcatcgagttcgacgtatattcattg\n\ +aacttcgtgcgtttgaaagaaacttttatgaaggcaaaattgatcctgtctcctatttca\n\ +tgcgtacctcctagttgataattccccgagcagtggttaggacacttttgtcggtatcaa\n\ +gttccggtctcaaaacgtaaaattctgtaatctgtatggatggtctgtgaattagttaat\n\ +ttttatgaagtcgtcgagacgcagttcctattgatttattctaaacggagatgtgcttcg\n\ +tgggactcggaagtagatctgtgtttatgattattgctactttagatgctgactgttaac\n\ +tccgtgttgtttttcaaccgtatatcacaaccgaattggatagaacctatagtttcaagt\n\ +tctgccacaaggtatcatatttacagttagtgctggttgcttctttcaaacgtggtgagt\n\ +ttgtgctatcacgtcaacggtagagctcagtggaccgagtgcgcgttcaaccctgttcca\n\ +gagagggtgtgatagcacatataccacgctcgtcgaggcgttcatgatagtttgcaagag\n\ +ccggtgttaaacacatattattattgttatccaactaatcggacctatgcataaagcatt\n\ +gtctaaacagaataattgcctatatacggtagttttagtgatttatatcttagtatcagt\n\ +tagagcttcgaactcttcaggttcctcatatttaacgttcttcgaaagcgaaaacttcta\n\ +caaacgaatgtaagcggttttccaagtagtacctataaatcacagaaagatctgtctcag\n\ +tatagttgaaatggtattcagctagtgacgtgtaccaattatcatagttcactcaagcaa\n\ +gacgctcattaacgaatatagacaagacactatatcatataataaaaaagaacatggtgc\n\ +tcgaacatagttgaattcaccatattgaaggggaatgctgacatgtaattcgctactaga\n\ +cgatcaattccctacttgtcaaagttgaactggtacgttcttggaattaaatatgattgc\n\ +gctggaccaaattgcgacttcttgagtttcagggcaaacgattgagccggaggatgtccg\n\ +tctcttacctttcttgcttatgataaacgacggtccctgtacatcactgggaattctcag\n\ +caaaaataattgggtaaatcgagactcgatgtattcggccacaaaggtgttagacgttaa\n\ +agattattcaacggggcgataataggatcataaccggtatgcaagcgcattgaaagagcc\n\ +atgagatccttatccgataaacgctgcacggtatgtgcagccttattgtcgatcacgaat\n\ +ttataaatgtagtctgggctgtaagttgaagacctaagttataatgaagtgcaataccaa\n\ +atcgattcatagtggattatcagactcaagatatctcctgataaattacagttgttaaga\n\ +tacggataaaatgagatttaagattagcagcctctaatctgtttcaatcccgttggaatg\n\ +tggtatgcgatcaaggttaagttaaaatcaagcctgtcttcagtcttgattcttgttctg\n\ +ccatcgcatgcggtctacgtgagttaatatgtagcttacgttctagcttgtgctaatctg\n\ +agtatagattcgtagaggaatattatcaagcttccacgcctcaacgtacgtgtattggtc\n\ +acacaagacactaaaagtggaagtagcgtaaactatagtctagttgttaaatgctcagtt\n\ +cttgttatattcgatatactcttggctaatttatgtctgagtatataaaattaatgatat\n\ +taacttgcatttcacggatcccttagaaaaagattttgaccgagcgcattataaacggtt\n\ +acaccgaatcaatagaagcatacccaatagctttctttgaatttattgcctgcgcaactt\n\ +ggctgactctctagatccgaataattctatatggtcgtgacgaaactagttcattactgt\n\ +ttaaaatgccaacatgtcttttgggccgataatggctctttgcaaaattactcaatgata\n\ +cgattgatcaaagcggtagttgctagtggtagcatgtaagtctatcaaatgtctgattat\n\ +ccgaaaatcttccaaaagagtccacgtaccatatctatctcatagcgacgcgaggggaac\n\ +cttatctaactatcattccatttaccgggtgactctcgatgcaggatccgattgggataa\n\ +attgcccagaaatggctcattcctgactaagggtaaggccgttctcagcaagggaacccc\n\ +gcgaatctaggcttataccatctagattgttaactacttgcctgtagttctacagccata\n\ +ctggacagttgtttctaaatgatcgggattcatgctagcactcctctgaatgcaccgcgt\n\ +aagtttaactattacgtccgtgggcagataaggatggaggctgtatgtatcttaactgtt\n\ +acctaatatggctggtaattatcaaagtaaggaccttaatgccatagcgctagcaatcgc\n\ +tttgtatactgaccatgtgccaacctctcttaatctgtaaaatataatgtcttagctaac\n\ +tgtggacgatcatgtctctgcctagagcttcgctgtatcaattcctatagccagcgtact\n\ +agtgacacaacaacaccgtgtgagaaaagatattagtccttacgtctgtctctctacagc\n\ +ttattgatgaggattgaacatggacatatagctccccctcaaaagcagatgctacctctt\n\ +tattccattctcgaacatttgccgaacttaatttcgacaaacctgaggtcacgtcttaat\n\ +ttatcggtaacgtcacgtccctttgagactggataaatatattaccaggggccaacgagc\n\ +aattgttggaggcgcttctataatacaaggtgtcttgtcaaagaaagacggcgtgcgtct\n\ +cgtgcaactcacttaaccaatattaatgtgaaacccccctctctcacatcttatgcggtg\n\ +tactgccctggtacatttcctgtacaggactccaacagtgtagattcctaagatagctgt\n\ +tggagttgcctcacgccagatcgaaaaactgaataaactagtgagctgagctgcagaaat\n\ +accgcttaattacttatgactagttcaaagggacctacgtgatgtcagacattgcaagga\n\ +agaaattaggtttgtgcgtcattttggctggactagcactccttacttcccctactattc\n\ +aaatgtcgtaaacagcatgagacaggatcgtgctgacatttaaggtctattgggaacgag\n\ +gctacctttggtcgcgcgctcgcgttctccgaatgaccgaaatgcatgagcacagtatgc\n\ +aattgcttatagatctaaggtctggtcgttgaaaccaagcacgtaggcctgggaaatcag\n\ +ttcttcctcagcaactacacaaaagcgtccaagcattagtacttgtagtaaatgtccgaa\n\ +cctatgcgctcatttgaaagtcaaaaaatatttttaagcagtaggcacctaacccgattc\n\ +ctctacttagtagctttctttgattctcagaattgactgcaatatcactgcacaattctg\n\ +tgccattactagacttctctgtattaacgtctcatcttactaacactcgcctaggacaca\n\ +tctgagagtgaagtatttcaatacatttactgaaatcttcagttctaaaatccccgaata\n\ +aggctcttatcggtttggccaacacaagaaaaaaacttcttgcaccactcaccttcatac\n\ +gcaggagcctggggaacttagtaataactatttcggcagacaaagcttataacaagttgc\n\ +cggcgcgtataatatttaaaagaccccttgagctgctcaattaaaacgctcacctggtat\n\ +aggctattagatagtgccgtcttagtaaggggcgggaattatcggataaactgatatttt\n\ +gataaaataaccgacttgttcacgacataagtcactaaggagattttatctttctccaaa\n\ +gtatatcttccttggataatttcaaagcgctgcaatttaagttctgttactagtttatgc\n\ +tgctgggaggtgaccggaaggcgtagtaatctagaggcaaattataagaagttcatcata\n\ +tcattttcgactacaaaaacaaggtgttgtatgccggcgcattgtgtaaactggacgagt\n\ +accctagatggaaaattatacgttaagccaagatttcgatgtaatgataattacctacac\n\ +atttttgctatccataggaacaagagctgttctataggctcgtggcatacgaacatttgc\n\ +tgccgctatgaatattggaagctcttcaactacagactctattcttaattgccgtcgaaa\n\ +atgggccgaatcggctattattaatactcggtttttccgaggggattgttgtcgacagtc\n\ +gtaattattattaatattgatgttggtgaggtcatttaaatacaaccttgcagacaatga\n\ +ataagggatccaatctctcatactccttttacaattgctcatgcccctatgcaaacctta\n\ +tgccgccacacctccgcaactctctcttctgaactgtaagtagcttcattactggtttga\n\ +gactatactgaagctgatgacattctaaaatggctattttcgaatgtgattcataatgtt\n\ +tatcgtttgggatggcagaatcacgttatttttgatatagcccgggtattctattgtata\n\ +gaacgtatgctacaagtcattccccgaagaagactagaagtaaacaacatgcgaccatcg\n\ +ttaagccacgcaaggctgtagctttatttcccgataacctatcttccataaatagcggac\n\ +agcaggatactgacgctcaacatcagtggttatggtctaatttttaacttttaataaggt\n\ +aacttcagcaggcatacacagtaactctttaatttataatcaaattagaagtctgacact\n\ +tcttatatttttctatcatccaacgcgatcgcccattagcttattgtgttactaataacg\n\ +tatctaaaccaatccttttcaagctactgcctatattgtcaatatatacaaacaacagga\n\ +tagtaggctgcttaaaaaatattgtcaaccgtgtacgctttacaatacccggaaatcaca\n\ +aactttgtagacaacgagtgaaatttatacactacgaagggccagcgtacaagacccatg\n\ +aattaggcgatatgtttattctgacatattggtttatccttaatctgtcgctgtaaaatg\n\ +aagccgcccccatccctgcgaattttttttcgaagattcacgactgaaatataaatacgt\n\ +ttggctatatttatgttggagggaggcaatagcctttactgttaaccgaagatttagcca\n\ +gtgagtgtgacactaaaacactggaataaatgcaggcgttcttctgggtaaaaggtttag\n\ +tcaatctcgcctataagttcatatagctctggatataattatctggcccatgcatttatc\n\ +atggcgcttggtgccctgtgtgaagccggcctctcatattgaaggtccgaagtattccat\n\ +gtacattaagatcactctctcattcatgcatcttggcttaacaaatctggttgtccaagc\n\ +tttccaggcacgtatggtacaaattcggatcgaatacttataaaaatgatatgttaaact\n\ +gtctaaaacgctcatctacaaagtaaagtgcactaaccaatagagtctcaagaccgtgta\n\ +atgctggtgcactgaatgtgtaatacggttagaagggattagttatgttacaaatccatt\n\ +gaaaacttaagaagcattgcgtgctcggagggtgcatcttttatcaagagactaacatta\n\ +ttttcaacgacgtacatgctttacaatagggtacttatcaaacgccgagaaacgcgccta\n\ +tagtgatgttatgattatgacccgatatccattggaccgaattttatgtaggttcccagc\n\ +gtactcgcgtaatatctcggtattgccataatgtaatacttgtcggtctctcccagatga\n\ +aaaagcgttacagagtatttcaatgaaaaacagcgcgcaacgtcaatacctttaggggta\n\ +acggccgctgatttcatatagatatacgataagttggtatagctctactaggtggcatcc\n\ +acaatcgttgcatttactatagctggttacaatcataatctataccgttccttacatact\n\ +accatagcgggatagcgtttttttgccgttgattgggtttaagaggatgtcagtctcatt\n\ +atatccgattcggtgggagagccgttgttttcaaatcgcacactttgtgacataatgtac\n\ +aagataacaaaactgatataagatataaactgtcaatatcaccttgacacttgaatcaaa\n\ +gtaaattaactcgcaaatataatttgactaattgggtgcagatttctcaattaataaaaa\n\ +aatggcaccggatgggcttacaagccccttatcattcacttgtatcatgatttccaagaa\n\ +caatagaatttgctagcaagtatgaacagagattcgaattgcatccacagtacgccggag\n\ +cgtttattttaatgtggatatgacgatgtactgttggcggcatttgctagtaaccggtcc\n\ +ttatttacgtagcgcacacgtaagcatgtctgggagaaatatggtggtacaatctcagag\n\ +aaagattacagtttggtttaaataggacttatcgggtcggaagtggaacttaataagcag\n\ +tacacaattgggcaacagacgtcttgcctattacaataggattacaatgcgttagatttc\n\ +agacacgttcgtgtttggctattcgtcaattccctaaatagttagacgatcaactattat\n\ +caaagtgattctttgttcatcctccattcatgtaacagatggcacactacgcataacgcc\n\ +gaggaattttaacgagatttaagagagcagttcgggcacaacccacttgactttataaca\n\ +gctcggcagcataaacggtaatatgtgacaaatttccaaacgttataagaacgtatgtgt\n\ +acttagaaaactaagtggttcatgttcaacagatgtgacgcagcaagcctaacttatcta\n\ +ttggttttgctataaaagaacaaagttacacagaatcctaagggcttgtttcacacttat\n\ +gcctagtgcttcaccatcttaaaatagcgaaaccggcacgaatcaaaccttaaaacaatg\n\ +cgcagatattggtgatggtgactccgggtatgataatggtaactgttgaccagcgcccac\n\ +ctcatcgaagtatagaaagtggttaggataaggatgagaccgaacttatttccggccata\n\ +actttagattttctacctagtacacaacatcagggcggacacgaaaccgccatcacatca\n\ +tataccaggtttaatttgcttaatgggggaagtgtcaacgaaccttcgaactttagcagg\n\ +catatggccattatatatggccccagagcagaatgctacagcagacaaaatttggattta\n\ +tgtagtttaatacctatcaaacttggtgtgaccatacttgtctaacgacagtgcacaaag\n\ +tgtaagttacaattattactactcagcagcttctgcaatgataaaatcttatcatacacg\n\ +tcacatatgataatatctacttagggggaacgggctccacaacctacatagtactcaata\n\ +cttacactattcgacaggcacaccaaacctgtacagtcccaaaagattgagtcaactttg\n\ +cagtactgcagatcacagtaatagcttagttagcgagtcaaaattagttttctacgagac\n\ +tgcacgaccgtgcaaatttccgatgtgttggctacaaatagcaacgtatgaatttgtttg\n\ +aagccacgtaaactgtacaaccttagagataagtctcaggctactaaaaacacgttgtgg\n\ +cactaacaggatcatggttgattcttacttattcggctgaccggcccaataagtaacctt\n\ +caactagaacagaataatcgggagtagtttaattcagtcaaggtgcaggtctcattgtaa\n\ +ctaacaagctctgtgtaaccaagttaaaatcgttttcttagcggattccctacttatgga\n\ +tttgagctcgtccacaatattcgatacaagaagtttgtggtccgtaacaacgaaatttta\n\ +attacgctgtgcagcctcatccaaggaattaatagaaggttgatggtaggctccgaacgc\n\ +tccatgattataatcaagtggactgtgcagtaaacgaggaaggtatcctgacgtcgtggt\n\ +gttcgtttttgttatttgtgccctatacgagtagataaaccatgaacagcacagtgtgaa\n\ +cccatggttgattttaggctaccttatttttaatttccgttacacagaaacgaattccac\n\ +aactaacatgccattaatttttcgatatcttataaaagatggtcgaaattcattcattta\n\ +ttttttttcggttctcgaaagtcaactaagctgtcgcgttttgtttctctttagaggtaa\n\ +aagtggctttgatctcctacgtttggatactagtcaaccattactccatttgatccgtga\n\ +gtatcacctgtctaacatccagcattatgactcctcggcgaagaaaagacacacttctta\n\ +gagtcgatgtgtattagctagggacacagttgtttaatacgatagtgagcccagggaggg\n\ +cagtgcgtcccccagtagatttattcagctagtgtaagtataagatatctcacccacgag\n\ +gttcaagtgatatgcagtcttagaataatacttatcctgaatttcgatattatgggtact\n\ +tcaataatccgctagcgctactttatgtctcgttggacagcaggacacatggcagtctta\n\ +aacactaaagacatcacctgaatgaatgtaatgggattacaagaatcaatgaggtattat\n\ +atacgacgtaggaaactctggatatatacagtaatctagttacgccatcgcacttcattc\n\ +ctctggaaacttagaagacatcagctgtacgtggaggaaccagacccccgtatgtagcca\n\ +aatagaaccaaagttgcttatacaaacacacccaatgacaatggaccgctggagttcgta\n\ +aactcggaacgtagtactgcacaaacccagcatttagcaataggagctacgtatgcaact\n\ +cccacgtggtaataccttcaagctatcaatatataggtgcctagctaatcgcattcgcaa\n\ +gcagtattcaagcttgtaaaccagtataataattacagaggctctatgaaacccaacttt\n\ +ccagctaaaagtcccaattaaatggttatttcgtacttttaaagtcgcccgttctgttat\n\ +tacgcgaattgattctactccaaaattaaacacaaattatcaaccgtttcatttatattt\n\ +gtcaatgcagctgtttaaaataaggctctactaaattataattaagacacttattaccag\n\ +atttctctagttaagtttgaaccagctcgactaccgcgaaagatacattcccttctctat\n\ +ttttcagttcatctatgggtcagagaagcattgaatttattctattcaccctcgtcgttc\n\ +acagcgaatcgtcagtgtgatcagtgtatgagaaatatcctaaaccgtttagtcagacca\n\ +cacgcttagaacaagtggtctaaaaagactgccctggaaggagtaagaagtatacagctg\n\ +atccggtgtatccttcagtcatctgccctatactaattacacgacgcaaggaaaaatagg\n\ +tttattttctaggcaaacccttcataggtgactccgatgtgttacgaatcatgcttgaga\n\ +atgtgctatcgttaccgacggataataacgatctccaatgaaccaaatgtagaatgtcta\n\ +ttgattacccttttactattcgacttagagataggagatagaacctcagtgtactttttt\n\ +agccgaatgggaatctttgggaggtgaatggccataaggtcgtaaatccaaccctcttaa\n\ +agtcttccatattatatcgttgttcgtggaatcgataacagatttgttgacccatagtaa\n\ +atgtatactagtttatgttgtaagtgtagattgttttccgattgccgtccaaactttatg\n\ +tcgtaattgtagaccagtaaagttgaccaaggtaagtgcccagcgatcctgcgagatcga\n\ +tcgccaatttttccagtcactgtaagtgtaggtttagataaagccgtatgagttatatca\n\ +taagggcctcggaaagcagcttcgaaccaaagttcccttataatagtagtttaactataa\n\ +aagtatatactggtctgtcgccctttcacgatttgttttaccggtttatgaagcgttacg\n\ +tcattagagcggctccaatttaaggttaacggcttccatgtgtagttgtatacaaggata\n\ +acttaaagtatctgttcagcgagctagttaagttatcctcgatagaacacaactcagagg\n\ +tcccaagatcgggtttgcaacttgctaatttattctcaaggcaaattgggaattatcgat\n\ +acctgtataccataaggtcgctcgatgtgatgcttatgtcttctggtgatcctaccttag\n\ +ttagtgctgattaacggaacattaatgtttatcgttttgagatttagccaattctctgat\n\ +tctaactcaagatgccttatctgacgtgctatgcagcccctaagtattttacattgtaat\n\ +aggacacgctcctttaaaactcgccaaaaggtcgttgtggttctctactggttaactata\n\ +taatttacagctttgttgagctagttcctctttggtttaagtcctcaatattagttggtt\n\ +cgagcgataagttggctagttaccttagtcactatattagatccgaatgttatgcttcat\n\ +ctgaagaccgccaccctccaaaatttcttttaagactcacttattgcaaggtgtaggtga\n\ +attcggctcgtttctcaagtggtgtatctgtacacgagtttccatattttcatcaacagc\n\ +caccgcacacttatgtcactctaggtattaaaagtcgctctacaaggggacgcaattaag\n\ +aaacagacatgctagtcaaaaataaacatagcgaggcaccactaattcggccgcttatca\n\ +atgggatgctctgcgcgagacgcgccagagctcagtagttagttcggacatacatttact\n\ +tcagatgatcaattagttttctacaaatgcttactctaccccgaaaaaagtcaccagact\n\ +cttacgtctctttagtatccttccgtcttatataaggtcagtcccccgtttcggtaccct\n\ +ggaatttactaagaataatgaaacagcccccaaggacgtacgtttacaaatgatagacca\n\ +gatcgcctagcttattccgacgcatgttgcatagaattgaaccaacggaatgtgagagta\n\ +actagatgagccgaccacagcacccgtttgcgtcgcagaatacgcctgatagttcggcca\n\ +cgaaatcatatgtcctttgagtattaagtatttgtaatgatcaatcgagctcaagcaagc\n\ +ttacacttcctcggatattcagggaacttagtgcctttgaaagatacgttgatcaacgaa\n\ +aaattgataatggctcatatggaatgcctacctcatagtgctgaattaacacagcactgc\n\ +ggacctaacttttcgaggtttcaagttcacgtctcaaaacctaataggctggaatatgta\n\ +gggatcctcggtgaatttgtgattgggtttgttgtagtactgaccaagtgaatattcttt\n\ +ttttctaaaagcagatctgctgccgggcactacgaaggagatctctgtgtatcattattg\n\ +cttcttgacatgatgactcttaaatcactgtgggtgtgcaaaacgatagcacaacccaat\n\ +tcgatagtacatattgttgatacttcgcactaaaccgttcatatttaaaggttgtgctcc\n\ +ttccttcgttaaatactggtgacttggtcctatctactattagctagacctctggggaac\n\ +cacgcccccgtaaaacctgtgcaagagagggggtcatacatcttagacatcgcgcctcca\n\ +ccagggaagcattgggtgattgaccaggtgtgtaacaaatatgattattcttatactaat\n\ +attagcaaagatgcataatgatttgtattaaatgtataattgaattgataagggtctttt\n\ +agtcagtgatagagtagtataaggtagacattagaactcttaaccggacgcagatttttc\n\ +ggtcttagtaagccaattagtcgacaaaacaaggtaagagcggttactagtagtacctat\n\ +aatgcactgaatcttcggtcgaagtatagttctaatgctatgcagattgtgacggcgaca\n\ +aatgttcagacttatatcatgaaacaagctcttgtaagtattgacaaatgaaaagattga\n\ +atatttttaaatacaaaatgcgcctacttattaggggaattaaccagattgaaggccaat\n\ +cctcacatgtaatgagataatagacgataaatgaaattcttgtaatagttgaactgctac\n\ +gtgatgggtattatatatgattgagatcctccaattgccgacgtcttgtcttgatgccca\n\ +aaagattgtcaacgaggagctccctcgcgtacctgtcgtccgtatcataaacgacgcgac\n\ +atgtacagcactccgaagtataagcaataataatgcgggtaatccagactagatcttttc\n\ +ggactcaatgcggtttcacggtaaacatgattaataccggagagtagtcgagcttatcag\n\ +cgatgcaagcgaattcattgtgccaggagatacgttgcagataaaaccggcaacgtatgt\n\ +caacaagttttggcgatctcgttgtttgtattcgacgaggcgcgggaacttcaagaacta\n\ +tcgtatattcaagtccattaccttttagtttcagactggtggagctgactaaagttatat\n\ +catcattttgtacactggtttagttaacgataatttcagatttaacatgaccagacgata\n\ +atcgctgtatatccagttggaatgtggtttgccagaaaggttaacttataatcaagcctc\n\ +tcttcagtcttgattcgtcgtatcccatccattgcgctatacctcagtgtatttggagct\n\ +gtagttataccgtgtgctaagatcagtagacatgacgagagcaatattatctaccttaca\n\ +agcatcaacggacgtctagtcggaacaaaagactctaaaactcgaacttcaggttaatat\n\ +actatagttctgtattcagcagttattcttatattcgatattatcttgcctattggatgt\n\ +ctgactttagtatattaatcatagtatctgccatgtaaaggtgccagtactaaatctgtt\n\ +tcacagtgcgaattataaacggttacaaccattaaagacaacaagaccctatagctttat\n\ +ttgaattttgtcaatgcgcaacttggagctcgcgatacatcccaattagtctatagggtc\n\ +gggacgattctacggcatttctggttataatgacaacatggattgtggcccgagaatcgc\n\ +tctttcattaattaagcaatcattacagtcttataagcgctacttccgagtggtagcagg\n\ +taactcgatataaggtcgcatgagccgaatagcttaaaaaacaggccaccgaacattgat\n\ +agagaataccgaccacagcgcaacctttgattactttcattaaattgtacggctcactcg\n\ +acatcaagcttaagattgcgataatgtgaactcaaatggatcagtactgaagaaccgtaa\n\ +cccacttcgcagaaagcgtacccagagaagatacgctgttacaatatacagggtgaaatt\n\ +attgcctgttcttcgtaaccatttcgccaaacttggttagaaatgatagccattcatgat\n\ +agaaataagctgaatgataccagtatctttaactatgtagtcagggggaagataacgatg\n\ +gtccatgtatgtttctgatatgtgacagtattggccgcgtaatttgctaacgaagctact\n\ +taatgcctttgagcttcatatagatttctttaatcaaaatcggcaaaaagatagtatgag\n\ +ctataatatatgctagtagagaactctggaccatcatctatatgaatactgattcgagcg\n\ +tgcaattactttagcctgcgtactactgactctacaaaacactctgagataagtttgtag\n\ +tcagtaagtcgctctctataaaccttttggatgaccattgtacagccacttatagatccc\n\ +aataaatagcacaggagacagagtttttcaatgctcgatcatttgccgatagtattttcg\n\ +tctaacctcagggcacctattatttgatacctaacctaacggccctttcacaatggagaa\n\ +atatatgacatcgggacaaacacaaatggtgggtggccaggagatatgacatggtggcgt\n\ +ctctaagaaacacggactccctctaggcaaactcacgtaaccaattttaatgtcaaacaa\n\ +aacgctcgaaaagattttgccgtgtaatgacctggtacattgactggtcaggaatacatc\n\ +actgtagttgccgtagtgtcctgttggtgttccatcaagacacatcgtataacgcaattt\n\ +acgacggacatcagatcaagttatacagattatttaagtatcacgtgtgcattgggacat\n\ +aagggatctcacacatgccttggaacatttttgctttgtgccgctttttcgctgcactac\n\ +caatccttacttaccagtatattcaaaggtcgttaacagaatgagaaaggttagggctct\n\ +aagttatcgtcgattgggatagacgagacatttgcgagcgccctccacggatacgaatct\n\ +cccatatcaatgtgaactggatgctatgcagtttagttcttacgtctcctagtggtaaaa\n\ +atcaaagtagcactcgcatagcagttattcagaacctaatacacaaaaccgtcaaacatt\n\ +ttctaattctaggtatgggccgatcataggagctaaggtgaaactcataaatgttttgtt\n\ +agatctagcatcctaaaaagatgcatatactgagtagctggcgtgcattctctcaattgt\n\ +atcctttttaactgaactagtcggtcccatttcgtgactgagatctattaaccgataaga\n\ +ttaataacactcgcattcgtatcagctcagagtgaagtttttcaataatttgactgatat\n\ +attaacttctaaaataaccctttaagcctcggatccgtttcccaatcacatcaaaaattc\n\ +ttattccaactatctacggattaacaacgtgcatggggatcgtagtaagaacttgttccg\n\ +atcactttgagtatatcaagttgacggcccggttattattgaatagaaacattcacctgc\n\ +taaattaaataccgcacatcggatacccgatttcagagggccgtcttactaagggcaggc\n\ +tttgttcggtttaactgagatgttcattattttacagtatgcttcaactaatatgtaacg\n\ +aaggacagtggatctgtctccatagtagatcttcagtcgtgaatttcataccgctcctat\n\ +ttaagttcgcgttcgagttgttgatcatggcacgtgaaagcaacccctagtattctagac\n\ +gaaaattttttctagttcatctgataatttgccaattcaaaaacaaccgctggtttcccg\n\ +gcgcattctctaaaatggaagtcgaacctagagccattatttgtcggtaacccatgagtt\n\ +ccttcttttcagaagttaatacactgtggtcctatacagaggaaaaacagcggttatata\n\ +cgatcgtggcataacaacattggatcaagatagcaatttggctacctattctaattctca\n\ +ctagattcggtattccactacaatatcggcagattaggattggatgaataatcggtgttt\n\ +aagtccggttgcgtctccaatctcctaatttttattaatattgatcttggtgacctattg\n\ +taaataaaaacttcaagactttgaataacggtgaaaagatagaagactcatttgaaaatg\n\ +gatcatccacagatccaaacattagcaagacactaatccccaactagctattctgatcgc\n\ +gatcgtgctgcagtactcctgtcacaatagtctgttcatgatctaattctttttgggctt\n\ +tgttcgatggtgattcagaatctttatccggtcgcttccctgtagctactttgtggggat\n\ +attgcccggggattatagggttgagatcgtttcctaaaagtatttaaaccaagtagactt\n\ +caactaaactacatcagaacatcgtgaagacaccatacgcggtacctttatttaccgata\n\ +acatttcttcaagaaataccggtaagcagcataatgaccctaaacagctcggggtatcgt\n\ +cgtagttttaaattttatttaggttactgctcaaggaataaaaactaactatttaattta\n\ +taataatattacaaggctcacactgattagatttgtctataagacttcgcgatcccccat\n\ +taccggattgtcttaagaataaactagataaaccatgcattttctagataaggcctttag\n\ +tctaattagatacaaaaaacacgatagttgcatccttaatttattgtgtcaaacctggaa\n\ +ccttttaattacccgcaaatcactttatgtcgagactacctctgaaatttattatctacc\n\ +taccgcatgaggacttgaaccatcttgtaggagttatgtttattagctaagattcgttta\n\ +tcctgtagcggtccatgtatattcaacaagcaaaaagcactcagaattgtttttagttga\n\ +gtcaagactgatatataaataagtttccctagttttttcgtggtgggacgatattgaatt\n\ +gaatcttaaccgaagagtttcccactctgtcgcacaataatacacgccaatatttccagc\n\ +cctgcttatgccttaatcggttactcaatctcccattgaagttcattttgatctgcatag\n\ +aagtttcgggcccagccttttttctgccaccttcctccaagctctgtagacgcactctaa\n\ +gattgatgctcacatgtattaattctacattaacataaatatataagtcatgcatcttcg\n\ +agtaaaatatctggttctccaacatgtcctggcacgtatcgttataatgcccatacatgt\n\ +agtattaaaatgattgggttaactggatattaagatcatcgaaattgtaaagtcaaatta\n\ +acaatactgtctcaagaccgtgtattcctcgtgctcggaagggctattacgcttacttcc\n\ +gttttggtatcttaatatgactttcaaaaattaagttgcagtgagtcctacctgcgtgca\n\ +tcggttagcaagagtataaaagttgtttaaacgaactacttgctttacaataccggtcgt\n\ +atatatcgccgtgaatccagaagattgtcttctttggattatcaaccgagatcctgtgga\n\ +ccgatgttttgggaccttcacagaggactccaggtagagctcgcttttgcattaatctaa\n\ +gaattgtacctctctaaaagatctaaaacagtgaatgtgtatttcatggaaaaacacaga\n\ +gaaacgtaaattactttaggccgaaaggcacatgagttattatacatatacgagatggtg\n\ +gtatacatcgaattcggggcatacactatagttgcattgtatttagctgctttaaataat\n\ +atgatattaccttccttacataagacattaccggcataccctggttttcaacttgtgggg\n\ +ctttttgacgatcgcactctcatttgatccgagtagggcggtgacccctgcttttcaaat\n\ +acaaaaatttcgctatgaaggtaatagattacttttcgctgttatgatagaaacggtaaa\n\ +tttaaaattgaaacttctagaaaagtaaagtaacgagaaatgattttgtgaataatgcgg\n\ +tcatgattgcgcaagtaagaaaaaaaggcaaaaggatgcgcggaatagaaacttatcagt\n\ +cacgggtatcttgatttcattcttcttgtcaattgccgacataggatgaaatcagattcc\n\ +aatgcaatacacagtaacccccacccttgattgtaatgtcgatttgaagttgtacgcgtc\n\ +gacgaagtggatagtatacgggccttttgtacggtgcgatcaactatgaatctcggcgag\n\ +ttagatggtcgtacaatctcacacatagaggtcacttgcctgtaatgacgaattttcggc\n\ +taggtactcgaactttattagaagtaaaaatgtgggcaaaagaaggattccattttacaa\n\ +gacgattacaatgagttacatgtctctcaacgtagtctttccctagtagtctttgaacta\n\ +tttaggtactccagaaaattttagcaaagggtttctgtgtgaatccgccattcatgttta\n\ +tgatggaacaataagaataacgccctcgtatgttatcgacagtgaagtcagcagttcggc\n\ +caaaaacatattcaatttagtacagatccccagaagttaagctaagtgctctaaaatggc\n\ +ctaaacggttatcaaagtaggtctaattactatactaacgggtgcatcgtaataactgct\n\ +gtcgatgcaacactatatgatagtgtcgttttgctatatatgtacaatgtgacaaagaag\n\ +ccttagcgattcttgcaaacttaggacttcggattctcaatcttaaatgtccgaaaacgc\n\ +aaagattcaaaaatttaatctatgagcagatatgcctgatggtgactacgcgtatgttaa\n\ +ggctaaatgttgacaaccgcacacataatcgaactattgatagtcgggagcataaccagg\n\ +tgaacgtactttgttcacgacatttattgacatgttctaaatacgtctcaaaatcacggc\n\ +gcactagaaaacgcaatcaaatcattgtcctggtttaagggccgtaatgccggtagtgtc\n\ +aaacttcatgagaactttagctggcttttggccagtatttagggaccaagagcactagcc\n\ +ttaagctgaatattttgccatttatctactgttataactttaaaacttggtggcaccaga\n\ +cttgtcgatacacacgcatcaatctgtaacgtaaaaggtttactaagaacaagcgtagga\n\ +attgagtttatattatatttaaactaaaagatgatattagcttctgagggcgatagggct\n\ +ccaaatcataaagaggaatatattattacacgattagaaacccacaacatacctcgaatc\n\ +gcccaaaagtttgacgaaacttggcagtactccacatctcagtaatacagttgggagagt\n\ +ctcaaatgttgttttattactcaatgaaccaccctcataatttcactgctgttccattaa\n\ +atttgcaaacgatcatttgctttgaagaaacgtaaaatcgacaaaattacagataagtag\n\ +atgcataataaaaaaaactgctcgctataacacgatcatcgtgcattcttacttaggagc\n\ +atcacccgcacaataacgtaccttaaactacaacactattagaccgagtactgtaattca\n\ +cgaaagctcaagctcgcattgtaaagaacttgctctctcgtaaaatgtgataatagtttg\n\ +cggagaggattcaattattttccattgcacctactccactagattcgataaaagaaggtg\n\ +gtcctcccttaaaaagaaatgttaagtaacatcggaaccataagcaaagcatgtaagtga\n\ +accgtcatccttccctaagaaacataaaggtttttaataatgtcgactgtgaactataac\n\ +tgcatcctttcctgacctactccggttccttgttgttatttctgaacgagaccagtagat\n\ +aaacaatgtaaaccacagtgggtaccaatggtgcatgtgacgctaccgttgttttaagtg\n\ +cccgtacaaacataagaagtcataatcttacttgaaattaattttgccttttattttttt\n\ +tcaggctcgaaattaatgatttgttttttttgaccttctagttacgctaatatgcggtcg\n\ +cctgtggtttctattgagtcctataacgggatgggatctaatacgtttggttactagtaa\n\ +acaaggtataaatttgataccggagtatcaactgtataacatcaagctttatgactcata\n\ +cgcgaagtaatgacacaaggctttcaggagatcgcgagtacagagccactaaggggtgta\n\ +ttacgatagtgacaccaccgagcgcactcactccccaagtagatttatgatcctacgcta\n\ +agtattagatatataaccaaagaggttctagtcagtgcaactcttagaataataattagc\n\ +cggttttgcctttttaggcctaatgcaatattcagctagcccttatgtatctcgcgttcc\n\ +acagcaccactcatggcacgcgtttaaactaatcaaatataatctatgaatgttatgcca\n\ +gtacttgaataaatcaggttttttataagtccttgcatactctcgttatatactgttaga\n\ +gtcttaccccatagaaattctttcatctgcaaacttagaagaattctcagctacggggag\n\ +cataaagtccccaggatgttgacaaatacaacaaatgtggcttatacaaacactccatat\n\ +gaaaatcgaaccctcgtggtagttttagccgaaccttgtacggataaatccctccatttt\n\ +ccaatagcagatacctatcctactacctcgtggtattaaattaaagcttgaaatatagag\n\ +ctgcatagcttatccaattcccaagcacgagtctaccgtcgtaaccacgatttgatttac\n\ +agacgctagagcaaacccatctttaaacatataagtaaaaattaaagggtgagtgcgtac\n\ +gtgtttactagcaacttcgcttattaagacaattgtttataagccataattaaaaacata\n\ +tgttcaacaggttcattgatatttgtaattgcacaggtttttaataaggatctacgtaag\n\ +tataatgaacaaactttttaccagagttatattctgtactttgaaaatgctcctctaccg\n\ +ccttagagactttcaattagattttttgcagttaatctatgcgtaagtgaaccatgcaag\n\ +ggatgcgattcaaccgcctcgtgctaaccctatcgtctgtctcataactgtaggtctaat\n\ +ataattttcagttttcgaacacataaccctttgaaaatctgctatttaatgtctcacctg\n\ +catgcactatcttctatactgctcagaacggctatacgtcactatgctccaagtgacgat\n\ +ttaaacgaagcaaggaataataggtttattttagtgcaaaacaattaagtgcggactacg\n\ +tgctctttacaataagccttgtgattgggctataggttaagtcccatattaacgatctcc\n\ +aatgtacaaaatcgacaatcgctttgcattacccggttactagtcgaattacagatagct\n\ +gttagatactcactctaattttggacaacaatcccaatcttggggtcgtctatcgcctga\n\ +agctcgtaaatccttccatcttaaacgattacatattatagacttgttcggggtagagat\n\ +atcacagttgtgcaaacattgtaaatcgatactagtttatgttggtagtctagttgcttt\n\ +taccattccccgaaaaacttgatctactatttcgacaacagtaaacttgaactaggtaag\n\ +tgaaaacagagaatgcctcatagtgccactatttgtccactatatgtaagtgtagcttta\n\ +cataatccactatgactgagatcattacggcctaggaaagcagcgtagaaaaaaagggcc\n\ +cggatattacgactgtaactataaaactagttactggtagcgcgccatgtatagatttgt\n\ +tttaccggttgtggttgcgttaacgaatttcagccgcgaaaattgatccgttaaccagtc\n\ +catctcgacttctataaaacgataaagtaaagttgatgttcagcctccttcttatggttg\n\ +catcgagagtacactactcagtgggaaatagatcggggttcctacttcagattgtattat\n\ +ctaggcaattgccgattgtgccatacctggataaaataagctacctacatgtgatgctta\n\ +tctattatcgtcatactaccttagggtgtcctgttgaacgctacattaatctttagccgt\n\ +ttgagatgttccaatggataggagtctaacgcatgatgaagtttaggaaggcagagcatc\n\ +ccactaagtatgtgacagtgtatttcgaaacgagacgttataaatagaaaaaaggtcctt\n\ +ctggttctattctgctgaactattgaatggaaagattggttgacctacgtactatttgct\n\ +tgaagtcatcaatttgacggggtgagagacatatggtgcatactttacggactctatatt\n\ +ttagatcagaagcttagcagtcttctctacaccccctcacgacataattgcttttaagaa\n\ +tctatgtttgattcctctacgggaattcggatccgttcgcatgtgcggtttatctaaacc\n\ +aggggacatatgttcagctaaagcatacgaacactttgctaactagacgtatgtatagta\n\ +gctataaatcccgacgatatttacaaaaagaaatgagactcaaatatatacatagcgacc\n\ +ctacacttattcgcaccctgatctaggcgatcctagcacccacacccgaaagtgagcact\n\ +agtgtcttccgtattaaatttactgcagttgagattttagttgtctactaaggattactc\n\ +taacccgtaataaggatcaagactcggtactagctttactatcattccctatgtgttttc\n\ +ctaactcacaagggtacgtaccagcctatgtaattacaataatgataaagacacaaagga\n\ +agtaactttacaaatgagtctccagttacactagcttagtccctcccatcttgctttgaa\n\ +gtctaaatacgcaatctctgaggatatacagcagaagaacactcataacgttggagtcca\n\ +agaattagactcatagggcccccaacatttaatatgtactgtgagtttgaaggtgttcta\n\ +ttgttaattcctgctcttgatacatgacacgtactccgtgtttaaggcttcggactgact\n\ +ttctttcataagttgagcaacgaaaatttcagaatcgataagttggattcactaactaat\n\ +acggctgattgaaaactccactccggacctatatggtcgacctttatacgtaaccgatat\n\ +aaaacttataggctggtatatcgagccttcctagcgcaatttcggatggggtttcttcta\n\ +ctactcaacaacggaatagtctttgtttagtaaaccagagctcaggacgcccaatacgta\n\ +ggagagcgctgtggagcatgtgtcattatggactggagcactcttaaatcactctgcgtg\n\ +tgctaaacgatagatcataacatgtcctgagtaaattttcttgatacgtcgcaatatacc\n\ +gttattagttaaacgttctcatccgtcatgcgtgaaatacggctgtcgtgctcagatata\n\ +ctattagcgactcatctcgcctaacacgcacacgtataaactcggaatgactgccgctct\n\ +tacatattagaaatacagactacaccacggaagcattgggtcattctcaaccgctgtata\n\ +aaagatgattagtcttataataagattaccaaagaggcagaatcatgggtagtaaatcta\n\ +ttattcaagtgattaccgtcgtgtaggcagggagtgaggacgagatggtactcaggacaa\n\ +atattaaccggacgaagtggtttacgtcgtactttcactattagtagtaaatacaaggta\n\ +acaccggggaatagtactaaatataatgatatctatcttcgggagaacgagtcgtctatt\n\ +gctttgaacattctcaaggcgtaaaatgtgctgacttatagcatgatacaaccgattgtt\n\ +acttttgtctattcaaaagattgaatagttttttatacaaaagccgcatacttatgacgg\n\ +ctagtatacagtttcatcccctagcatcaatgctatggacagtattgaacttataggaaa\n\ +ttcttctaatagggcaaatccgtcgtgatgcctattttttttcagtcacatcctcaaatg\n\ +gcactagtattgtcgggatcccattaacaggctcaaccacgagctcacgcgaggacatgt\n\ +agtccgtatctttaacgaagcgacagcgacagaactcccatggataaccaattataaggc\n\ +ccgtaatcctctagacatcgtttaccaataaatccgctttctccgtaatcatgttgaata\n\ +ccccagagtagtccagatgataaccgatgaaacacaagtctttctcaatgcacttacggt\n\ +gaacttattaccgccaacgtagctcatcaaggttgcgacatctagttgtgtgtttgcgac\n\ +gagcccagcgaacttcatcaactttcgtatattcaacgccttgtaattttactttaagac\n\ +gcctggtgatgtagattcttagataatcagtttgttatcggctgtactttaccataattt\n\ +cacaggtttcaggtcaagaagattatagctgtatatacagttccatgctcggtgcacaga\n\ +aacgtgatcggataataatcaatcgcttatgtcgtctttaggcgtatccaatacatgccc\n\ +cgataccgcagtgtatttcgacatgtaggtataccgtcgcatttgagctcgagtcaggac\n\ +gtcagctagattagattccttaatagaatataccgacctctagtccgaactaaactatag\n\ +ataacgccaacttcaggttaattgtctagtcgtctgtttgcagatgggattcttagatga\n\ +gtgagtatcggccatattggttcgagcactttagtttttgatgcataggatatgcaatgt\n\ +atagctgaaagtactttatctgtttcaaactcacattgattaaaccggtaaacctttaaa\n\ +gactacaagaaaatattcagtgagggcaattttgtcaatcacaatcttccagctagagat\n\ +acttcacaatttgtcttgaggctacgcaacattagacggattttcgcgttttattgaaat\n\ +aatcgaggggcccaagagtatccatagttcattttgtaagatttctttacaggcttatta\n\ +cagcttcttcagactcctacatgcttacgagttatatgctagcatgtgaacaatagatta\n\ +atatacaggaaaacgtacattgagagagatgaccctacacagcgcaaccgttgagtactt\n\ +tcattaaagggtaacgctctcgagacagcatccttaagatggccttattgtcaaatcatt\n\ +tgcagaagtacgcaagatccctaaccaacgtagaagaatccctacaaacacatgagacgc\n\ +ggtgaaaatagacagggtgttagtattcaatcttcggagtatcaatttcgccaatcttgg\n\ +tgagaaagcataccctttcttcagagaaagaagatcaatcataacactatctttaacgag\n\ +gtacgcacgcgcatcattacctgcctccatggatctttaggatagcggaaagtattggca\n\ +gcgtattgtgatttcgttcctactttatcaatttcacattcatatacatgtcttttatca\n\ +aaatcgccaataagataggatgagctatattagatgctagtagagttcgcgccaacatca\n\ +tcgataggaatactcaggacagcgtgataggacttttcaatccctaatactctctataat\n\ +tataactctctcttaagtttggaggcagtaacgcgctctatataatcagtttgctgcacc\n\ +attcttcagcctctgatacatacaaataaattccacagcagtaagagggtttaattgaga\n\ +catcttgggaacttaggattttactctaacatcaccgaaacgattattggataccgtacc\n\ +taaacgaactttctcaaggcagtaatataggacatccgcaataacacaaatgctgcctcc\n\ +ccaggagttatgtcttcctggaggctatatcttacacccactcactataggcaaactaaa\n\ +gtttaaatgttgattgtctaaaaaaaagatagataagagttggccggcgtagcacatgcg\n\ +aaagtgaatcgtaagctataattctctggacttgaagttctgtcctgttcctctgcaaga\n\ +aacaaacttcctttaaagctatttacgacgcacatctcagcaagttataaacatgttgga\n\ +agtttctagtcggaattcccaaagaacggatctatctaatgcattcctacatttttcctg\n\ +tctgccgatggtgccatcctattcaaagaatttcttaaaagtagattaaatgggactttt\n\ +aacaatgagtaaccttacgcctctaagggttcctcgagtgccatacaccagtcaggtccg\n\ +agccacatacacggagaacattctaacatagcattctcaactcgatcatttgcaggttac\n\ +ttctttcctatcctagtgctaaaaatcatacttgcaatcccatagcacggattaagaacc\n\ +taagaaacaattcagtaaaacatgttcgaattcttggtatgggaacatcattgcagctat\n\ +ggtctaacgcattaatgtttgggtacatcttccatcatataaacaggaagagtctgacga\n\ +cagggagtgcttgcgatcatgtctatcattgtgaaatcaaattgtagctcacatgtcgtc\n\ +tatgagagcgtgtatccgataagatttagaaaaatagaagtcgtataagatctcactgaa\n\ +cttttgaatgaatgtgaagcatatatgatctgctttaataaaactttatccataggatac\n\ +gtttccaaatcaattcaataattattagtcaaaatagataaggatgaacaacctgaaggc\n\ +cgatcggacgtagaaagtggtcccatcactttgagttgatattgttgaaccacacgttat\n\ +tatggttttcaaacagtctcaggatattgtatatacagataatccgataccagttgtctg\n\ +acgcccctcttacgtaccccaccctttgtgacgtttaaagcagttgttcagtattttaaa\n\ +ctaggcggcaactaatttggaaagaagcacagtggatatgtctaaattcttgttattcag\n\ +gcctgaatttaatacaccgcatagttaacttcgcggtagagttgttcatcatgcctcctc\n\ +taagctaccacttctatgatacaccaatagttgttctacggaatctgataattggccaag\n\ +tcataaacttccgctgcgttcaacccccttgctcgaatatccaactcgaaaagacagcct\n\ +tttggtgtccggaacaaatcagttacttcttttctgatgttaattctctgtggtcagata\n\ +cagaccaaaaactccgcggatttaccatcctccaagaacaaatttgcatcaacatagcat\n\ +tttggctacatattctaagtctcaatagtttaggttttcaactacattatcccaacatta\n\ +ggattggaggaataatagctgggtaagtccccttgcgtctacaatcgactattttttatg\n\ +aatatgcttctgccgcacctatggttattaaaaaagtcatgactttgaagaaccctgaaa\n\ +agatagatgaatcaggtgtaatggcagcagccaaagagcatataattagcaacactctaa\n\ +gaacattatagatatgatgatagcgatcgtcatgatgttatccggtcacaatagtagctt\n\ +catcagctaattcgttttgccagtggtgacttgcgctggaagaatcgttatacggtccct\n\ +tccctcttgatacggtgggggcttattcaaccgcgtggattgggttgtcatacttgcatt\n\ +aaacgatgtaaaccatctagtagtcaactatactaaatcacaaaatagtgatcaatacat\n\ +acccgcttcatggttttaaccatttaattgattaaagatattccgctaagaaccattatc\n\ +tacctaaactgatcgccgtatcctagtagtttgaaatttgatgtaccgtaatgatcaacg\n\ +aagtaaaacgttatattgtatgtagaataataggtcttggagctaaatgatgtgattggt\n\ +agtgaagacttacccttacaactttaccggtttctcggaagaatatactagagaatcaat\n\ +gcatgggctacataagcactttagtctaatgagataaaaaatacacgagtcttccatcat\n\ +gaattttttgtcgaaaaactcgaacctggtaatttaaaccatatatctttatgtcgtcaa\n\ +taactctcatatgttttatataacttcccaatcacgacttgtaactgcttgttcgactga\n\ +gctgtttgagctatgaggccgggatccggttgagctacatctatttgctacaagaaaaat\n\ +gaaagcacatttgttgggagttctggctacactcatagagaaataagtggcccgagtggg\n\ +tgcggcctgcctccatattcaagtgtatcttaaaccaagtggttccaacgctcgcgctaa\n\ +agaattaaagcctttatttcctccacggagtagcccgtaatccggttcgaaagagaccat\n\ +tgaagttaattttcatatccagtgaagtttaggcacaagcatgtgttctgccacatgcct\n\ +caaagcgctcttcaaccaagatatgattcatcctaacttcgatgaatgcgtctgtaacat\n\ +aaatatagaaggaatgattcggcgagttaattttcgccttctccaacatggcatccctac\n\ +gttcgttataaggaccatacatgtaggttttaaaggtttgcggttaatcgatatttacat\n\ +catagaaattctatagtcaaatttacaagactctagatactcactcgttgcagccggcta\n\ +ggaagcgctttgtaccttacttcccttttcgttgcgtaatatgaatttcatatagtaagt\n\ +tcaaggcactcatacctccgtgaagagggtagatagactattaaagttgtttaatagtac\n\ +gtattgatggaaatgacccgtaggagatttaccactcaatccacaagattcgctgctgtg\n\ +cattatcaaaacagtgcatgtcgaaacatgggttgggtccttcaaacacgaatccaggta\n\ +gagatacctttgcaattttt\n"; + +dnaInput = dnaInput + dnaInput + dnaInput; + +var ilen, clen, + seqs = [ + /agggtaaa|tttaccct/ig, + /[cgt]gggtaaa|tttaccc[acg]/ig, + /a[act]ggtaaa|tttacc[agt]t/ig, + /ag[act]gtaaa|tttac[agt]ct/ig, + /agg[act]taaa|ttta[agt]cct/ig, + /aggg[acg]aaa|ttt[cgt]ccct/ig, + /agggt[cgt]aa|tt[acg]accct/ig, + /agggta[cgt]a|t[acg]taccct/ig, + /agggtaa[cgt]|[acg]ttaccct/ig], + subs = { + B: '(c|g|t)', D: '(a|g|t)', H: '(a|c|t)', K: '(g|t)', + M: '(a|c)', N: '(a|c|g|t)', R: '(a|g)', S: '(c|t)', + V: '(a|c|g)', W: '(a|t)', Y: '(c|t)' } + +ilen = dnaInput.length; + +// There is no in-place substitution +dnaInput = dnaInput.replace(/>.*\n|\n/g,"") +clen = dnaInput.length + +var dnaOutputString = ""; + +for(i in seqs) + dnaOutputString += seqs[i].source + " " + (dnaInput.match(seqs[i]) || []).length + "\n"; + // match returns null if no matches, so replace with empty + +for(k in subs) + dnaInput = dnaInput.replace(k, subs[k]) // FIXME: Would like this to be a global substitution in a future version of SunSpider. + // search string, replacement string, flags + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/string-base64.html b/build/pgo/js-input/sunspider/string-base64.html new file mode 100644 index 000000000..53280ef2b --- /dev/null +++ b/build/pgo/js-input/sunspider/string-base64.html @@ -0,0 +1,151 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-base64</title> + +</head> + +<body> +<h3>string-base64</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +/* 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: http://lxr.mozilla.org/mozilla/source/extensions/xml-rpc/src/nsXmlRpcClient.js#956 + +/* Convert data (an array of integers) to a Base64 string. */ +var toBase64Table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; +var base64Pad = '='; + +function toBase64(data) { + var result = ''; + var length = data.length; + var i; + // Convert every three bytes to 4 ascii characters. + for (i = 0; i < (length - 2); i += 3) { + result += toBase64Table[data[i] >> 2]; + result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += toBase64Table[((data[i+1] & 0x0f) << 2) + (data[i+2] >> 6)]; + result += toBase64Table[data[i+2] & 0x3f]; + } + + // Convert the remaining 1 or 2 bytes, pad out to 4 characters. + if (length%3) { + i = length - (length%3); + result += toBase64Table[data[i] >> 2]; + if ((length%3) == 2) { + result += toBase64Table[((data[i] & 0x03) << 4) + (data[i+1] >> 4)]; + result += toBase64Table[(data[i+1] & 0x0f) << 2]; + result += base64Pad; + } else { + result += toBase64Table[(data[i] & 0x03) << 4]; + result += base64Pad + base64Pad; + } + } + + return result; +} + +/* Convert Base64 data to a string */ +var toBinaryTable = [ + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, + -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63, + 52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1, + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14, + 15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1, + -1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40, + 41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1 +]; + +function base64ToString(data) { + var result = ''; + var leftbits = 0; // number of bits decoded, but yet to be appended + var leftdata = 0; // bits decoded, but yet to be appended + + // Convert one by one. + for (var i = 0; i < data.length; i++) { + var c = toBinaryTable[data.charCodeAt(i) & 0x7f]; + var padding = (data[i] == base64Pad); + // Skip illegal characters and whitespace + if (c == -1) continue; + + // Collect data into leftdata, update bitcount + leftdata = (leftdata << 6) | c; + leftbits += 6; + + // If we have 8 or more bits, append 8 bits to the result + if (leftbits >= 8) { + leftbits -= 8; + // Append if not padding. + if (!padding) + result += String.fromCharCode((leftdata >> leftbits) & 0xff); + leftdata &= (1 << leftbits) - 1; + } + } + + // If there are any bits left, the base64 string was corrupted + if (leftbits) + throw Components.Exception('Corrupted base64 string'); + + return result; +} + +var str = ""; + +for ( var i = 0; i < 8192; i++ ) + str += String.fromCharCode( (25 * Math.random()) + 97 ); + +for ( var i = 8192; i <= 16384; i *= 2 ) { + + var base64; + + base64 = toBase64(str); + base64ToString(base64); + + // Double the string + str += str; +} + +toBinaryTable = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/string-fasta.html b/build/pgo/js-input/sunspider/string-fasta.html new file mode 100644 index 000000000..240e60147 --- /dev/null +++ b/build/pgo/js-input/sunspider/string-fasta.html @@ -0,0 +1,135 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-fasta</title> + +</head> + +<body> +<h3>string-fasta</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// The Great Computer Language Shootout +// http://shootout.alioth.debian.org +// +// Contributed by Ian Osgood + +var last = 42, A = 3877, C = 29573, M = 139968; + +function rand(max) { + last = (last * A + C) % M; + return max * last / M; +} + +var ALU = + "GGCCGGGCGCGGTGGCTCACGCCTGTAATCCCAGCACTTTGG" + + "GAGGCCGAGGCGGGCGGATCACCTGAGGTCAGGAGTTCGAGA" + + "CCAGCCTGGCCAACATGGTGAAACCCCGTCTCTACTAAAAAT" + + "ACAAAAATTAGCCGGGCGTGGTGGCGCGCGCCTGTAATCCCA" + + "GCTACTCGGGAGGCTGAGGCAGGAGAATCGCTTGAACCCGGG" + + "AGGCGGAGGTTGCAGTGAGCCGAGATCGCGCCACTGCACTCC" + + "AGCCTGGGCGACAGAGCGAGACTCCGTCTCAAAAA"; + +var IUB = { + a:0.27, c:0.12, g:0.12, t:0.27, + B:0.02, D:0.02, H:0.02, K:0.02, + M:0.02, N:0.02, R:0.02, S:0.02, + V:0.02, W:0.02, Y:0.02 +} + +var HomoSap = { + a: 0.3029549426680, + c: 0.1979883004921, + g: 0.1975473066391, + t: 0.3015094502008 +} + +function makeCumulative(table) { + var last = null; + for (var c in table) { + if (last) table[c] += table[last]; + last = c; + } +} + +function fastaRepeat(n, seq) { + var seqi = 0, lenOut = 60; + while (n>0) { + if (n<lenOut) lenOut = n; + if (seqi + lenOut < seq.length) { + ret = seq.substring(seqi, seqi+lenOut); + seqi += lenOut; + } else { + var s = seq.substring(seqi); + seqi = lenOut - s.length; + ret = s + seq.substring(0, seqi); + } + n -= lenOut; + } +} + +function fastaRandom(n, table) { + var line = new Array(60); + makeCumulative(table); + while (n>0) { + if (n<line.length) line = new Array(n); + for (var i=0; i<line.length; i++) { + var r = rand(1); + for (var c in table) { + if (r < table[c]) { + line[i] = c; + break; + } + } + } + ret = line.join(''); + n -= line.length; + } +} + +var ret; + +var count = 7; +ret = fastaRepeat(2*count*100000, ALU); +ret = fastaRandom(3*count*1000, IUB); +ret = fastaRandom(5*count*1000, HomoSap); + + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/string-tagcloud.html b/build/pgo/js-input/sunspider/string-tagcloud.html new file mode 100644 index 000000000..893a927ac --- /dev/null +++ b/build/pgo/js-input/sunspider/string-tagcloud.html @@ -0,0 +1,315 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-tagcloud</title> + +</head> + +<body> +<h3>string-tagcloud</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + + +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* + Portions from: + json.js + 2007-10-10 + + Public Domain +*/ + +// This test parses a JSON string giving tag names and popularity, and +// generates html markup for a "tagcloud" view. + +if (!Object.prototype.toJSONString) { + + Array.prototype.toJSONString = function (w) { + var a = [], // The array holding the partial texts. + i, // Loop counter. + l = this.length, + v; // The value to be stringified. + + for (i = 0; i < l; i += 1) { + v = this[i]; + switch (typeof v) { + case 'object': + + if (v && typeof v.toJSONString === 'function') { + a.push(v.toJSONString(w)); + } else { + a.push('null'); + } + break; + + case 'string': + case 'number': + case 'boolean': + a.push(v.toJSONString()); + break; + default: + a.push('null'); + } + } + + return '[' + a.join(',') + ']'; + }; + + + Boolean.prototype.toJSONString = function () { + return String(this); + }; + + + Date.prototype.toJSONString = function () { + + function f(n) { + + return n < 10 ? '0' + n : n; + } + + return '"' + this.getUTCFullYear() + '-' + + f(this.getUTCMonth() + 1) + '-' + + f(this.getUTCDate()) + 'T' + + f(this.getUTCHours()) + ':' + + f(this.getUTCMinutes()) + ':' + + f(this.getUTCSeconds()) + 'Z"'; + }; + + + Number.prototype.toJSONString = function () { + + return isFinite(this) ? String(this) : 'null'; + }; + + + Object.prototype.toJSONString = function (w) { + var a = [], // The array holding the partial texts. + k, // The current key. + i, // The loop counter. + v; // The current value. + + if (w) { + for (i = 0; i < w.length; i += 1) { + k = w[i]; + if (typeof k === 'string') { + v = this[k]; + switch (typeof v) { + case 'object': + + if (v) { + if (typeof v.toJSONString === 'function') { + a.push(k.toJSONString() + ':' + + v.toJSONString(w)); + } + } else { + a.push(k.toJSONString() + ':null'); + } + break; + + case 'string': + case 'number': + case 'boolean': + a.push(k.toJSONString() + ':' + v.toJSONString()); + + } + } + } + } else { + + for (k in this) { + if (typeof k === 'string' && + Object.prototype.hasOwnProperty.apply(this, [k])) { + v = this[k]; + switch (typeof v) { + case 'object': + + if (v) { + if (typeof v.toJSONString === 'function') { + a.push(k.toJSONString() + ':' + + v.toJSONString()); + } + } else { + a.push(k.toJSONString() + ':null'); + } + break; + + case 'string': + case 'number': + case 'boolean': + a.push(k.toJSONString() + ':' + v.toJSONString()); + + } + } + } + } + + return '{' + a.join(',') + '}'; + }; + + + (function (s) { + + var m = { + '\b': '\\b', + '\t': '\\t', + '\n': '\\n', + '\f': '\\f', + '\r': '\\r', + '"' : '\\"', + '\\': '\\\\' + }; + + + s.parseJSON = function (filter) { + var j; + + function walk(k, v) { + var i, n; + if (v && typeof v === 'object') { + for (i in v) { + if (Object.prototype.hasOwnProperty.apply(v, [i])) { + n = walk(i, v[i]); + if (n !== undefined) { + v[i] = n; + } + } + } + } + return filter(k, v); + } + + if (/^[\],:{}\s]*$/.test(this.replace(/\\./g, '@'). + replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g, ']'). + replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) { + + j = eval('(' + this + ')'); + + return typeof filter === 'function' ? walk('', j) : j; + } + + throw new SyntaxError('parseJSON'); + }; + + + s.toJSONString = function () { + + if (/["\\\x00-\x1f]/.test(this)) { + return '"' + this.replace(/[\x00-\x1f\\"]/g, function (a) { + var c = m[a]; + if (c) { + return c; + } + c = a.charCodeAt(); + return '\\u00' + Math.floor(c / 16).toString(16) + + (c % 16).toString(16); + }) + '"'; + } + return '"' + this + '"'; + }; + })(String.prototype); +} + +var tagInfoJSON = '[\n {\n \"tag\": "titillation",\n \"popularity\": 4294967296\n },\n {\n \"tag\": "foamless",\n \"popularity\": 1257718401\n },\n {\n \"tag\": "snarler",\n \"popularity\": 613166183\n },\n {\n \"tag\": "multangularness",\n \"popularity\": 368304452\n },\n {\n \"tag\": "Fesapo unventurous",\n \"popularity\": 248026512\n },\n {\n \"tag\": "esthesioblast",\n \"popularity\": 179556755\n },\n {\n \"tag\": "echeneidoid",\n \"popularity\": 136641578\n },\n {\n \"tag\": "embryoctony",\n \"popularity\": 107852576\n },\n {\n \"tag\": "undilatory",\n \"popularity\": 87537981\n },\n {\n \"tag\": "predisregard",\n \"popularity\": 72630939\n },\n {\n \"tag\": "allergenic",\n \"popularity\": 61345190\n },\n {\n \"tag\": "uncloudy",\n \"popularity\": 52580571\n },\n {\n \"tag\": "unforeseeably",\n \"popularity\": 45628109\n },\n {\n \"tag\": "sturniform",\n \"popularity\": 40013489\n },\n {\n \"tag\": "anesthetize",\n \"popularity\": 35409226\n },\n {\n \"tag\": "ametabolia",\n \"popularity\": 31583050\n },\n {\n \"tag\": "angiopathy",\n \"popularity\": 28366350\n },\n {\n \"tag\": "sultanaship",\n \"popularity\": 25634218\n },\n {\n \"tag\": "Frenchwise",\n \"popularity\": 23292461\n },\n {\n \"tag\": "cerviconasal",\n \"popularity\": 21268909\n },\n {\n \"tag\": "mercurialness",\n \"popularity\": 19507481\n },\n {\n \"tag\": "glutelin venditate",\n \"popularity\": 17964042\n },\n {\n \"tag\": "acred overblack",\n \"popularity\": 16603454\n },\n {\n \"tag\": "Atik",\n \"popularity\": 15397451\n },\n {\n \"tag\": "puncturer",\n \"popularity\": 14323077\n },\n {\n \"tag\": "pukatea",\n \"popularity\": 13361525\n },\n {\n \"tag\": "suberize",\n \"popularity\": 12497261\n },\n {\n \"tag\": "Godfrey",\n \"popularity\": 11717365\n },\n {\n \"tag\": "tetraptote",\n \"popularity\": 11011011\n },\n {\n \"tag\": "lucidness",\n \"popularity\": 10369074\n },\n {\n \"tag\": "tartness",\n \"popularity\": 9783815\n },\n {\n \"tag\": "axfetch",\n \"popularity\": 9248634\n },\n {\n \"tag\": "preacquittal",\n \"popularity\": 8757877\n },\n {\n \"tag\": "matris",\n \"popularity\": 8306671\n },\n {\n \"tag\": "hyphenate",\n \"popularity\": 7890801\n },\n {\n \"tag\": "semifabulous",\n \"popularity\": 7506606\n },\n {\n \"tag\": "oppressiveness",\n \"popularity\": 7150890\n },\n {\n \"tag\": "Protococcales",\n \"popularity\": 6820856\n },\n {\n \"tag\": "unpreventive",\n \"popularity\": 6514045\n },\n {\n \"tag\": "Cordia",\n \"popularity\": 6228289\n },\n {\n \"tag\": "Wakamba leaflike",\n \"popularity\": 5961668\n },\n {\n \"tag\": "dacryoma",\n \"popularity\": 5712480\n },\n {\n \"tag\": "inguinal",\n \"popularity\": 5479211\n },\n {\n \"tag\": "responseless",\n \"popularity\": 5260507\n },\n {\n \"tag\": "supplementarily",\n \"popularity\": 5055158\n },\n {\n \"tag\": "emu",\n \"popularity\": 4862079\n },\n {\n \"tag\": "countermeet",\n \"popularity\": 4680292\n },\n {\n \"tag\": "purrer",\n \"popularity\": 4508918\n },\n {\n \"tag\": "Corallinaceae",\n \"popularity\": 4347162\n },\n {\n \"tag\": "speculum",\n \"popularity\": 4194304\n },\n {\n \"tag\": "crimpness",\n \"popularity\": 4049690\n },\n {\n \"tag\": "antidetonant",\n \"popularity\": 3912727\n },\n {\n \"tag\": "topeewallah",\n \"popularity\": 3782875\n },\n {\n \"tag\": "fidalgo ballant",\n \"popularity\": 3659640\n },\n {\n \"tag\": "utriculose",\n \"popularity\": 3542572\n },\n {\n \"tag\": "testata",\n \"popularity\": 3431259\n },\n {\n \"tag\": "beltmaking",\n \"popularity\": 3325322\n },\n {\n \"tag\": "necrotype",\n \"popularity\": 3224413\n },\n {\n \"tag\": "ovistic",\n \"popularity\": 3128215\n },\n {\n \"tag\": "swindlership",\n \"popularity\": 3036431\n },\n {\n \"tag\": "augustal",\n \"popularity\": 2948792\n },\n {\n \"tag\": "Titoist",\n \"popularity\": 2865047\n },\n {\n \"tag\": "trisoctahedral",\n \"popularity\": 2784963\n },\n {\n \"tag\": "sequestrator",\n \"popularity\": 2708327\n },\n {\n \"tag\": "sideburns",\n \"popularity\": 2634939\n },\n {\n \"tag\": "paraphrasia",\n \"popularity\": 2564616\n },\n {\n \"tag\": "graminology unbay",\n \"popularity\": 2497185\n },\n {\n \"tag\": "acaridomatium emargination",\n \"popularity\": 2432487\n },\n {\n \"tag\": "roofward",\n \"popularity\": 2370373\n },\n {\n \"tag\": "lauder",\n \"popularity\": 2310705\n },\n {\n \"tag\": "subjunctive",\n \"popularity\": 2253354\n },\n {\n \"tag\": "subelongate",\n \"popularity\": 2198199\n },\n {\n \"tag\": "guacimo",\n \"popularity\": 2145128\n },\n {\n \"tag\": "cockade",\n \"popularity\": 2094033\n },\n {\n \"tag\": "misgauge",\n \"popularity\": 2044818\n },\n {\n \"tag\": "unexpensive",\n \"popularity\": 1997388\n },\n {\n \"tag\": "chebel",\n \"popularity\": 1951657\n },\n {\n \"tag\": "unpursuing",\n \"popularity\": 1907543\n },\n {\n \"tag\": "kilobar",\n \"popularity\": 1864969\n },\n {\n \"tag\": "obsecration",\n \"popularity\": 1823863\n },\n {\n \"tag\": "nacarine",\n \"popularity\": 1784157\n },\n {\n \"tag\": "spirituosity",\n \"popularity\": 1745787\n },\n {\n \"tag\": "movableness deity",\n \"popularity\": 1708692\n },\n {\n \"tag\": "exostracism",\n \"popularity\": 1672816\n },\n {\n \"tag\": "archipterygium",\n \"popularity\": 1638104\n },\n {\n \"tag\": "monostrophic",\n \"popularity\": 1604506\n },\n {\n \"tag\": "gynecide",\n \"popularity\": 1571974\n },\n {\n \"tag\": "gladden",\n \"popularity\": 1540462\n },\n {\n \"tag\": "throughbred",\n \"popularity\": 1509927\n },\n {\n \"tag\": "groper",\n \"popularity\": 1480329\n },\n {\n \"tag\": "Xenosaurus",\n \"popularity\": 1451628\n },\n {\n \"tag\": "photoetcher",\n \"popularity\": 1423788\n },\n {\n \"tag\": "glucosid",\n \"popularity\": 1396775\n },\n {\n \"tag\": "Galtonian",\n \"popularity\": 1370555\n },\n {\n \"tag\": "mesosporic",\n \"popularity\": 1345097\n },\n {\n \"tag\": "theody",\n \"popularity\": 1320370\n },\n {\n \"tag\": "zaffer",\n \"popularity\": 1296348\n },\n {\n \"tag\": "probiology",\n \"popularity\": 1273003\n },\n {\n \"tag\": "rhizomic",\n \"popularity\": 1250308\n },\n {\n \"tag\": "superphosphate",\n \"popularity\": 1228240\n },\n {\n \"tag\": "Hippolytan",\n \"popularity\": 1206776\n },\n {\n \"tag\": "garget",\n \"popularity\": 1185892\n },\n {\n \"tag\": "diploplacula",\n \"popularity\": 1165568\n },\n {\n \"tag\": "orohydrographical",\n \"popularity\": 1145785\n },\n {\n \"tag\": "enhypostatize",\n \"popularity\": 1126521\n },\n {\n \"tag\": "polisman",\n \"popularity\": 1107759\n },\n {\n \"tag\": "acetometer",\n \"popularity\": 1089482\n },\n {\n \"tag\": "unsnatched",\n \"popularity\": 1071672\n },\n {\n \"tag\": "yabber",\n \"popularity\": 1054313\n },\n {\n \"tag\": "demiwolf",\n \"popularity\": 1037390\n },\n {\n \"tag\": "chromascope",\n \"popularity\": 1020888\n },\n {\n \"tag\": "seamanship",\n \"popularity\": 1004794\n },\n {\n \"tag\": "nonfenestrated",\n \"popularity\": 989092\n },\n {\n \"tag\": "hydrophytism",\n \"popularity\": 973771\n },\n {\n \"tag\": "dotter",\n \"popularity\": 958819\n },\n {\n \"tag\": "thermoperiodism",\n \"popularity\": 944222\n },\n {\n \"tag\": "unlawyerlike",\n \"popularity\": 929970\n },\n {\n \"tag\": "enantiomeride citywards",\n \"popularity\": 916052\n },\n {\n \"tag\": "unmetallurgical",\n \"popularity\": 902456\n },\n {\n \"tag\": "prickled",\n \"popularity\": 889174\n },\n {\n \"tag\": "strangerwise manioc",\n \"popularity\": 876195\n },\n {\n \"tag\": "incisorial",\n \"popularity\": 863510\n },\n {\n \"tag\": "irrationalize",\n \"popularity\": 851110\n },\n {\n \"tag\": "nasology",\n \"popularity\": 838987\n },\n {\n \"tag\": "fatuism",\n \"popularity\": 827131\n },\n {\n \"tag\": "Huk",\n \"popularity\": 815535\n },\n {\n \"tag\": "properispomenon",\n \"popularity\": 804192\n },\n {\n \"tag\": "unpummelled",\n \"popularity\": 793094\n },\n {\n \"tag\": "technographically",\n \"popularity\": 782233\n },\n {\n \"tag\": "underfurnish",\n \"popularity\": 771603\n },\n {\n \"tag\": "sinter",\n \"popularity\": 761198\n },\n {\n \"tag\": "lateroanterior",\n \"popularity\": 751010\n },\n {\n \"tag\": "nonpersonification",\n \"popularity\": 741034\n },\n {\n \"tag\": "Sitophilus",\n \"popularity\": 731264\n },\n {\n \"tag\": "unstudded overexerted",\n \"popularity\": 721694\n },\n {\n \"tag\": "tracheation",\n \"popularity\": 712318\n },\n {\n \"tag\": "thirteenth begloze",\n \"popularity\": 703131\n },\n {\n \"tag\": "bespice",\n \"popularity\": 694129\n },\n {\n \"tag\": "doppia",\n \"popularity\": 685305\n },\n {\n \"tag\": "unadorned",\n \"popularity\": 676656\n },\n {\n \"tag\": "dovelet engraff",\n \"popularity\": 668176\n },\n {\n \"tag\": "diphyozooid",\n \"popularity\": 659862\n },\n {\n \"tag\": "mure",\n \"popularity\": 651708\n },\n {\n \"tag\": "Tripitaka",\n \"popularity\": 643710\n },\n {\n \"tag\": "Billjim",\n \"popularity\": 635865\n },\n {\n \"tag\": "pyramidical",\n \"popularity\": 628169\n },\n {\n \"tag\": "circumlocutionist",\n \"popularity\": 620617\n },\n {\n \"tag\": "slapstick",\n \"popularity\": 613207\n },\n {\n \"tag\": "preobedience",\n \"popularity\": 605934\n },\n {\n \"tag\": "unfriarlike",\n \"popularity\": 598795\n },\n {\n \"tag\": "microchromosome",\n \"popularity\": 591786\n },\n {\n \"tag\": "Orphicism",\n \"popularity\": 584905\n },\n {\n \"tag\": "peel",\n \"popularity\": 578149\n },\n {\n \"tag\": "obediential",\n \"popularity\": 571514\n },\n {\n \"tag\": "Peripatidea",\n \"popularity\": 564997\n },\n {\n \"tag\": "undoubtful",\n \"popularity\": 558596\n },\n {\n \"tag\": "lodgeable",\n \"popularity\": 552307\n },\n {\n \"tag\": "pustulated woodchat",\n \"popularity\": 546129\n },\n {\n \"tag\": "antepast",\n \"popularity\": 540057\n },\n {\n \"tag\": "sagittoid matrimoniously",\n \"popularity\": 534091\n },\n {\n \"tag\": "Albizzia",\n \"popularity\": 528228\n },\n {\n \"tag\": "Elateridae unnewness",\n \"popularity\": 522464\n },\n {\n \"tag\": "convertingness",\n \"popularity\": 516798\n },\n {\n \"tag\": "Pelew",\n \"popularity\": 511228\n },\n {\n \"tag\": "recapitulation",\n \"popularity\": 505751\n },\n {\n \"tag\": "shack",\n \"popularity\": 500365\n },\n {\n \"tag\": "unmellowed",\n \"popularity\": 495069\n },\n {\n \"tag\": "pavis capering",\n \"popularity\": 489859\n },\n {\n \"tag\": "fanfare",\n \"popularity\": 484735\n },\n {\n \"tag\": "sole",\n \"popularity\": 479695\n },\n {\n \"tag\": "subarcuate",\n \"popularity\": 474735\n },\n {\n \"tag\": "multivious",\n \"popularity\": 469856\n },\n {\n \"tag\": "squandermania",\n \"popularity\": 465054\n },\n {\n \"tag\": "scintle",\n \"popularity\": 460329\n },\n {\n \"tag\": "hash chirognomic",\n \"popularity\": 455679\n },\n {\n \"tag\": "linseed",\n \"popularity\": 451101\n },\n {\n \"tag\": "redoubtable",\n \"popularity\": 446596\n },\n {\n \"tag\": "poachy reimpact",\n \"popularity\": 442160\n },\n {\n \"tag\": "limestone",\n \"popularity\": 437792\n },\n {\n \"tag\": "serranid",\n \"popularity\": 433492\n },\n {\n \"tag\": "pohna",\n \"popularity\": 429258\n },\n {\n \"tag\": "warwolf",\n \"popularity\": 425088\n },\n {\n \"tag\": "ruthenous",\n \"popularity\": 420981\n },\n {\n \"tag\": "dover",\n \"popularity\": 416935\n },\n {\n \"tag\": "deuteroalbumose",\n \"popularity\": 412950\n },\n {\n \"tag\": "pseudoprophetic",\n \"popularity\": 409025\n },\n {\n \"tag\": "dissoluteness",\n \"popularity\": 405157\n },\n {\n \"tag\": "preinvention",\n \"popularity\": 401347\n },\n {\n \"tag\": "swagbellied",\n \"popularity\": 397592\n },\n {\n \"tag\": "Ophidia",\n \"popularity\": 393892\n },\n {\n \"tag\": "equanimity",\n \"popularity\": 390245\n },\n {\n \"tag\": "troutful",\n \"popularity\": 386651\n },\n {\n \"tag\": "uke",\n \"popularity\": 383108\n },\n {\n \"tag\": "preacquaint",\n \"popularity\": 379616\n },\n {\n \"tag\": "shoq",\n \"popularity\": 376174\n },\n {\n \"tag\": "yox",\n \"popularity\": 372780\n },\n {\n \"tag\": "unelemental",\n \"popularity\": 369434\n },\n {\n \"tag\": "Yavapai",\n \"popularity\": 366134\n },\n {\n \"tag\": "joulean",\n \"popularity\": 362880\n },\n {\n \"tag\": "dracontine",\n \"popularity\": 359672\n },\n {\n \"tag\": "hardmouth",\n \"popularity\": 356507\n },\n {\n \"tag\": "sylvanize",\n \"popularity\": 353386\n },\n {\n \"tag\": "intraparenchymatous meadowbur",\n \"popularity\": 350308\n },\n {\n \"tag\": "uncharily",\n \"popularity\": 347271\n },\n {\n \"tag\": "redtab flexibly",\n \"popularity\": 344275\n },\n {\n \"tag\": "centervelic",\n \"popularity\": 341319\n },\n {\n \"tag\": "unravellable",\n \"popularity\": 338403\n },\n {\n \"tag\": "infortunately",\n \"popularity\": 335526\n },\n {\n \"tag\": "cannel",\n \"popularity\": 332687\n },\n {\n \"tag\": "oxyblepsia",\n \"popularity\": 329885\n },\n {\n \"tag\": "Damon",\n \"popularity\": 327120\n },\n {\n \"tag\": "etherin",\n \"popularity\": 324391\n },\n {\n \"tag\": "luminal",\n \"popularity\": 321697\n },\n {\n \"tag\": "interrogatorily presbyte",\n \"popularity\": 319038\n },\n {\n \"tag\": "hemiclastic",\n \"popularity\": 316414\n },\n {\n \"tag\": "poh flush",\n \"popularity\": 313823\n },\n {\n \"tag\": "Psoroptes",\n \"popularity\": 311265\n },\n {\n \"tag\": "dispirit",\n \"popularity\": 308740\n },\n {\n \"tag\": "nashgab",\n \"popularity\": 306246\n },\n {\n \"tag\": "Aphidiinae",\n \"popularity\": 303784\n },\n {\n \"tag\": "rhapsody nonconstruction",\n \"popularity\": 301353\n },\n {\n \"tag\": "Osmond",\n \"popularity\": 298952\n },\n {\n \"tag\": "Leonis",\n \"popularity\": 296581\n },\n {\n \"tag\": "Lemnian",\n \"popularity\": 294239\n },\n {\n \"tag\": "acetonic gnathonic",\n \"popularity\": 291926\n },\n {\n \"tag\": "surculus",\n \"popularity\": 289641\n },\n {\n \"tag\": "diagonally",\n \"popularity\": 287384\n },\n {\n \"tag\": "counterpenalty",\n \"popularity\": 285154\n },\n {\n \"tag\": "Eugenie",\n \"popularity\": 282952\n },\n {\n \"tag\": "hornbook",\n \"popularity\": 280776\n },\n {\n \"tag\": "miscoin",\n \"popularity\": 278626\n },\n {\n \"tag\": "admi",\n \"popularity\": 276501\n },\n {\n \"tag\": "Tarmac",\n \"popularity\": 274402\n },\n {\n \"tag\": "inexplicable",\n \"popularity\": 272328\n },\n {\n \"tag\": "rascallion",\n \"popularity\": 270278\n },\n {\n \"tag\": "dusterman",\n \"popularity\": 268252\n },\n {\n \"tag\": "osteostomous unhoroscopic",\n \"popularity\": 266250\n },\n {\n \"tag\": "spinibulbar",\n \"popularity\": 264271\n },\n {\n \"tag\": "phototelegraphically",\n \"popularity\": 262315\n },\n {\n \"tag\": "Manihot",\n \"popularity\": 260381\n },\n {\n \"tag\": "neighborhood",\n \"popularity\": 258470\n },\n {\n \"tag\": "Vincetoxicum",\n \"popularity\": 256581\n },\n {\n \"tag\": "khirka",\n \"popularity\": 254713\n },\n {\n \"tag\": "conscriptive",\n \"popularity\": 252866\n },\n {\n \"tag\": "synechthran",\n \"popularity\": 251040\n },\n {\n \"tag\": "Guttiferales",\n \"popularity\": 249235\n },\n {\n \"tag\": "roomful",\n \"popularity\": 247450\n },\n {\n \"tag\": "germinal",\n \"popularity\": 245685\n },\n {\n \"tag\": "untraitorous",\n \"popularity\": 243939\n },\n {\n \"tag\": "nondissenting",\n \"popularity\": 242213\n },\n {\n \"tag\": "amotion",\n \"popularity\": 240506\n },\n {\n \"tag\": "badious",\n \"popularity\": 238817\n },\n {\n \"tag\": "sumpit",\n \"popularity\": 237147\n },\n {\n \"tag\": "ectozoic",\n \"popularity\": 235496\n },\n {\n \"tag\": "elvet",\n \"popularity\": 233862\n },\n {\n \"tag\": "underclerk",\n \"popularity\": 232246\n },\n {\n \"tag\": "reticency",\n \"popularity\": 230647\n },\n {\n \"tag\": "neutroclusion",\n \"popularity\": 229065\n },\n {\n \"tag\": "unbelieving",\n \"popularity\": 227500\n },\n {\n \"tag\": "histogenetic",\n \"popularity\": 225952\n },\n {\n \"tag\": "dermamyiasis",\n \"popularity\": 224421\n },\n {\n \"tag\": "telenergy",\n \"popularity\": 222905\n },\n {\n \"tag\": "axiomatic",\n \"popularity\": 221406\n },\n {\n \"tag\": "undominoed",\n \"popularity\": 219922\n },\n {\n \"tag\": "periosteoma",\n \"popularity\": 218454\n },\n {\n \"tag\": "justiciaryship",\n \"popularity\": 217001\n },\n {\n \"tag\": "autoluminescence",\n \"popularity\": 215563\n },\n {\n \"tag\": "osmous",\n \"popularity\": 214140\n },\n {\n \"tag\": "borgh",\n \"popularity\": 212731\n },\n {\n \"tag\": "bedebt",\n \"popularity\": 211337\n },\n {\n \"tag\": "considerableness adenoidism",\n \"popularity\": 209957\n },\n {\n \"tag\": "sailorizing",\n \"popularity\": 208592\n },\n {\n \"tag\": "Montauk",\n \"popularity\": 207240\n },\n {\n \"tag\": "Bridget",\n \"popularity\": 205901\n },\n {\n \"tag\": "Gekkota",\n \"popularity\": 204577\n },\n {\n \"tag\": "subcorymbose",\n \"popularity\": 203265\n },\n {\n \"tag\": "undersap",\n \"popularity\": 201967\n },\n {\n \"tag\": "poikilothermic",\n \"popularity\": 200681\n },\n {\n \"tag\": "enneatical",\n \"popularity\": 199409\n },\n {\n \"tag\": "martinetism",\n \"popularity\": 198148\n },\n {\n \"tag\": "sustanedly",\n \"popularity\": 196901\n },\n {\n \"tag\": "declaration",\n \"popularity\": 195665\n },\n {\n \"tag\": "myringoplasty",\n \"popularity\": 194442\n },\n {\n \"tag\": "Ginkgo",\n \"popularity\": 193230\n },\n {\n \"tag\": "unrecurrent",\n \"popularity\": 192031\n },\n {\n \"tag\": "proprecedent",\n \"popularity\": 190843\n },\n {\n \"tag\": "roadman",\n \"popularity\": 189666\n },\n {\n \"tag\": "elemin",\n \"popularity\": 188501\n },\n {\n \"tag\": "maggot",\n \"popularity\": 187347\n },\n {\n \"tag\": "alitrunk",\n \"popularity\": 186204\n },\n {\n \"tag\": "introspection",\n \"popularity\": 185071\n },\n {\n \"tag\": "batiker",\n \"popularity\": 183950\n },\n {\n \"tag\": "backhatch oversettle",\n \"popularity\": 182839\n },\n {\n \"tag\": "thresherman",\n \"popularity\": 181738\n },\n {\n \"tag\": "protemperance",\n \"popularity\": 180648\n },\n {\n \"tag\": "undern",\n \"popularity\": 179568\n },\n {\n \"tag\": "tweeg",\n \"popularity\": 178498\n },\n {\n \"tag\": "crosspath",\n \"popularity\": 177438\n },\n {\n \"tag\": "Tangaridae",\n \"popularity\": 176388\n },\n {\n \"tag\": "scrutation",\n \"popularity\": 175348\n },\n {\n \"tag\": "piecemaker",\n \"popularity\": 174317\n },\n {\n \"tag\": "paster",\n \"popularity\": 173296\n },\n {\n \"tag\": "unpretendingness",\n \"popularity\": 172284\n },\n {\n \"tag\": "inframundane",\n \"popularity\": 171281\n },\n {\n \"tag\": "kiblah",\n \"popularity\": 170287\n },\n {\n \"tag\": "playwrighting",\n \"popularity\": 169302\n },\n {\n \"tag\": "gonepoiesis snowslip",\n \"popularity\": 168326\n },\n {\n \"tag\": "hoodwise",\n \"popularity\": 167359\n },\n {\n \"tag\": "postseason",\n \"popularity\": 166401\n },\n {\n \"tag\": "equivocality",\n \"popularity\": 165451\n },\n {\n \"tag\": "Opiliaceae nuclease",\n \"popularity\": 164509\n },\n {\n \"tag\": "sextipara",\n \"popularity\": 163576\n },\n {\n \"tag\": "weeper",\n \"popularity\": 162651\n },\n {\n \"tag\": "frambesia",\n \"popularity\": 161735\n },\n {\n \"tag\": "answerable",\n \"popularity\": 160826\n },\n {\n \"tag\": "Trichosporum",\n \"popularity\": 159925\n },\n {\n \"tag\": "cajuputol",\n \"popularity\": 159033\n },\n {\n \"tag\": "pleomorphous",\n \"popularity\": 158148\n },\n {\n \"tag\": "aculeolate",\n \"popularity\": 157270\n },\n {\n \"tag\": "wherever",\n \"popularity\": 156400\n },\n {\n \"tag\": "collapse",\n \"popularity\": 155538\n },\n {\n \"tag\": "porky",\n \"popularity\": 154683\n },\n {\n \"tag\": "perule",\n \"popularity\": 153836\n },\n {\n \"tag\": "Nevada",\n \"popularity\": 152996\n },\n {\n \"tag\": "conalbumin",\n \"popularity\": 152162\n },\n {\n \"tag\": "tsunami",\n \"popularity\": 151336\n },\n {\n \"tag\": "Gulf",\n \"popularity\": 150517\n },\n {\n \"tag\": "hertz",\n \"popularity\": 149705\n },\n {\n \"tag\": "limmock",\n \"popularity\": 148900\n },\n {\n \"tag\": "Tartarize",\n \"popularity\": 148101\n },\n {\n \"tag\": "entosphenoid",\n \"popularity\": 147310\n },\n {\n \"tag\": "ibis",\n \"popularity\": 146524\n },\n {\n \"tag\": "unyeaned",\n \"popularity\": 145746\n },\n {\n \"tag\": "tritural",\n \"popularity\": 144973\n },\n {\n \"tag\": "hundredary",\n \"popularity\": 144207\n },\n {\n \"tag\": "stolonlike",\n \"popularity\": 143448\n },\n {\n \"tag\": "chorister",\n \"popularity\": 142694\n },\n {\n \"tag\": "mismove",\n \"popularity\": 141947\n },\n {\n \"tag\": "Andine",\n \"popularity\": 141206\n },\n {\n \"tag\": "Annette proneur escribe",\n \"popularity\": 140471\n },\n {\n \"tag\": "exoperidium",\n \"popularity\": 139742\n },\n {\n \"tag\": "disedge",\n \"popularity\": 139019\n },\n {\n \"tag\": "hypochloruria",\n \"popularity\": 138302\n },\n {\n \"tag\": "prepupa",\n \"popularity\": 137590\n },\n {\n \"tag\": "assent",\n \"popularity\": 136884\n },\n {\n \"tag\": "hydrazobenzene",\n \"popularity\": 136184\n },\n {\n \"tag\": "emballonurid",\n \"popularity\": 135489\n },\n {\n \"tag\": "roselle",\n \"popularity\": 134800\n },\n {\n \"tag\": "unifiedly",\n \"popularity\": 134117\n },\n {\n \"tag\": "clang",\n \"popularity\": 133439\n },\n {\n \"tag\": "acetolytic",\n \"popularity\": 132766\n },\n {\n \"tag\": "cladodont",\n \"popularity\": 132098\n },\n {\n \"tag\": "recoast",\n \"popularity\": 131436\n },\n {\n \"tag\": "celebrated tydie Eocarboniferous",\n \"popularity\": 130779\n },\n {\n \"tag\": "superconsciousness",\n \"popularity\": 130127\n },\n {\n \"tag\": "soberness",\n \"popularity\": 129480\n },\n {\n \"tag\": "panoramist",\n \"popularity\": 128838\n },\n {\n \"tag\": "Orbitolina",\n \"popularity\": 128201\n },\n {\n \"tag\": "overlewd",\n \"popularity\": 127569\n },\n {\n \"tag\": "demiquaver",\n \"popularity\": 126942\n },\n {\n \"tag\": "kamelaukion",\n \"popularity\": 126319\n },\n {\n \"tag\": "flancard",\n \"popularity\": 125702\n },\n {\n \"tag\": "tricuspid",\n \"popularity\": 125089\n },\n {\n \"tag\": "bepelt",\n \"popularity\": 124480\n },\n {\n \"tag\": "decuplet",\n \"popularity\": 123877\n },\n {\n \"tag\": "Rockies",\n \"popularity\": 123278\n },\n {\n \"tag\": "unforgeability",\n \"popularity\": 122683\n },\n {\n \"tag\": "mocha",\n \"popularity\": 122093\n },\n {\n \"tag\": "scrunge",\n \"popularity\": 121507\n },\n {\n \"tag\": "delighter",\n \"popularity\": 120926\n },\n {\n \"tag\": "willey Microtinae",\n \"popularity\": 120349\n },\n {\n \"tag\": "unhuntable",\n \"popularity\": 119777\n },\n {\n \"tag\": "historically",\n \"popularity\": 119208\n },\n {\n \"tag\": "vicegerentship",\n \"popularity\": 118644\n },\n {\n \"tag\": "hemangiosarcoma",\n \"popularity\": 118084\n },\n {\n \"tag\": "harpago",\n \"popularity\": 117528\n },\n {\n \"tag\": "unionoid",\n \"popularity\": 116976\n },\n {\n \"tag\": "wiseman",\n \"popularity\": 116429\n },\n {\n \"tag\": "diclinism",\n \"popularity\": 115885\n },\n {\n \"tag\": "Maud",\n \"popularity\": 115345\n },\n {\n \"tag\": "scaphocephalism",\n \"popularity\": 114809\n },\n {\n \"tag\": "obtenebration",\n \"popularity\": 114277\n },\n {\n \"tag\": "cymar predreadnought",\n \"popularity\": 113749\n },\n {\n \"tag\": "discommend",\n \"popularity\": 113225\n },\n {\n \"tag\": "crude",\n \"popularity\": 112704\n },\n {\n \"tag\": "upflash",\n \"popularity\": 112187\n },\n {\n \"tag\": "saltimbank",\n \"popularity\": 111674\n },\n {\n \"tag\": "posthysterical",\n \"popularity\": 111165\n },\n {\n \"tag\": "trample",\n \"popularity\": 110659\n },\n {\n \"tag\": "ungirthed",\n \"popularity\": 110157\n },\n {\n \"tag\": "unshakable",\n \"popularity\": 109658\n },\n {\n \"tag\": "hepatocystic",\n \"popularity\": 109163\n },\n {\n \"tag\": "psammophyte",\n \"popularity\": 108671\n },\n {\n \"tag\": "millionfold",\n \"popularity\": 108183\n },\n {\n \"tag\": "outtaste",\n \"popularity\": 107698\n },\n {\n \"tag\": "poppycockish",\n \"popularity\": 107217\n },\n {\n \"tag\": "viduine",\n \"popularity\": 106739\n },\n {\n \"tag\": "pleasureman",\n \"popularity\": 106264\n },\n {\n \"tag\": "cholesterolemia",\n \"popularity\": 105792\n },\n {\n \"tag\": "hostlerwife",\n \"popularity\": 105324\n },\n {\n \"tag\": "figure undergrass",\n \"popularity\": 104859\n },\n {\n \"tag\": "bedrape",\n \"popularity\": 104398\n },\n {\n \"tag\": "nuttishness",\n \"popularity\": 103939\n },\n {\n \"tag\": "fow",\n \"popularity\": 103484\n },\n {\n \"tag\": "rachianesthesia",\n \"popularity\": 103031\n },\n {\n \"tag\": "recruitable",\n \"popularity\": 102582\n },\n {\n \"tag\": "semianatomical Oenotheraceae",\n \"popularity\": 102136\n },\n {\n \"tag\": "extracapsular",\n \"popularity\": 101693\n },\n {\n \"tag\": "unsigneted",\n \"popularity\": 101253\n },\n {\n \"tag\": "fissural",\n \"popularity\": 100816\n },\n {\n \"tag\": "ayous",\n \"popularity\": 100381\n },\n {\n \"tag\": "crestfallenness odontograph",\n \"popularity\": 99950\n },\n {\n \"tag\": "monopodium",\n \"popularity\": 99522\n },\n {\n \"tag\": "germfree",\n \"popularity\": 99096\n },\n {\n \"tag\": "dauphin",\n \"popularity\": 98673\n },\n {\n \"tag\": "nonagesimal",\n \"popularity\": 98254\n },\n {\n \"tag\": "waterchat",\n \"popularity\": 97836\n },\n {\n \"tag\": "Entelodon",\n \"popularity\": 97422\n },\n {\n \"tag\": "semischolastic",\n \"popularity\": 97010\n },\n {\n \"tag\": "somata",\n \"popularity\": 96602\n },\n {\n \"tag\": "expositorily",\n \"popularity\": 96195\n },\n {\n \"tag\": "bass",\n \"popularity\": 95792\n },\n {\n \"tag\": "calorimetry",\n \"popularity\": 95391\n },\n {\n \"tag\": "entireness",\n \"popularity\": 94993\n },\n {\n \"tag\": "ratline soppiness",\n \"popularity\": 94597\n },\n {\n \"tag\": "shor",\n \"popularity\": 94204\n },\n {\n \"tag\": "coprecipitation",\n \"popularity\": 93813\n },\n {\n \"tag\": "unblushingly",\n \"popularity\": 93425\n },\n {\n \"tag\": "macarize",\n \"popularity\": 93040\n },\n {\n \"tag\": "scruplesomeness",\n \"popularity\": 92657\n },\n {\n \"tag\": "offsaddle",\n \"popularity\": 92276\n },\n {\n \"tag\": "hypertragical",\n \"popularity\": 91898\n },\n {\n \"tag\": "uncassock loined",\n \"popularity\": 91522\n },\n {\n \"tag\": "interlobate",\n \"popularity\": 91149\n },\n {\n \"tag\": "releasor orrisroot stoloniferously",\n \"popularity\": 90778\n },\n {\n \"tag\": "elementoid",\n \"popularity\": 90410\n },\n {\n \"tag\": "Lentilla",\n \"popularity\": 90043\n },\n {\n \"tag\": "distressing",\n \"popularity\": 89679\n },\n {\n \"tag\": "hydrodrome",\n \"popularity\": 89318\n },\n {\n \"tag\": "Jeannette",\n \"popularity\": 88958\n },\n {\n \"tag\": "Kuli",\n \"popularity\": 88601\n },\n {\n \"tag\": "taxinomist",\n \"popularity\": 88246\n },\n {\n \"tag\": "southwestwardly",\n \"popularity\": 87894\n },\n {\n \"tag\": "polyparia",\n \"popularity\": 87543\n },\n {\n \"tag\": "exmeridian",\n \"popularity\": 87195\n },\n {\n \"tag\": "splenius regimentaled",\n \"popularity\": 86849\n },\n {\n \"tag\": "Sphaeropsidaceae",\n \"popularity\": 86505\n },\n {\n \"tag\": "unbegun",\n \"popularity\": 86163\n },\n {\n \"tag\": "something",\n \"popularity\": 85823\n },\n {\n \"tag\": "contaminable nonexpulsion",\n \"popularity\": 85486\n },\n {\n \"tag\": "douser",\n \"popularity\": 85150\n },\n {\n \"tag\": "prostrike",\n \"popularity\": 84817\n },\n {\n \"tag\": "worky",\n \"popularity\": 84485\n },\n {\n \"tag\": "folliful",\n \"popularity\": 84156\n },\n {\n \"tag\": "prioracy",\n \"popularity\": 83828\n },\n {\n \"tag\": "undermentioned",\n \"popularity\": 83503\n },\n {\n \"tag\": "Judaica",\n \"popularity\": 83179\n },\n {\n \"tag\": "multifarious",\n \"popularity\": 82858\n },\n {\n \"tag\": "poogye",\n \"popularity\": 82538\n },\n {\n \"tag\": "Sparganium",\n \"popularity\": 82221\n },\n {\n \"tag\": "thurrock",\n \"popularity\": 81905\n },\n {\n \"tag\": "outblush",\n \"popularity\": 81591\n },\n {\n \"tag\": "Strophanthus supraordination",\n \"popularity\": 81279\n },\n {\n \"tag\": "gingerroot",\n \"popularity\": 80969\n },\n {\n \"tag\": "unconscient",\n \"popularity\": 80661\n },\n {\n \"tag\": "unconstitutionally",\n \"popularity\": 80354\n },\n {\n \"tag\": "plaguily",\n \"popularity\": 80050\n },\n {\n \"tag\": "waterily equatorwards",\n \"popularity\": 79747\n },\n {\n \"tag\": "nondeposition",\n \"popularity\": 79446\n },\n {\n \"tag\": "dronishly",\n \"popularity\": 79147\n },\n {\n \"tag\": "gateado",\n \"popularity\": 78849\n },\n {\n \"tag\": "dislink",\n \"popularity\": 78553\n },\n {\n \"tag\": "Joceline",\n \"popularity\": 78259\n },\n {\n \"tag\": "amphiboliferous",\n \"popularity\": 77967\n },\n {\n \"tag\": "bushrope",\n \"popularity\": 77676\n },\n {\n \"tag\": "plumicorn sulphosalicylic",\n \"popularity\": 77387\n },\n {\n \"tag\": "nonefficiency",\n \"popularity\": 77100\n },\n {\n \"tag\": "hieroscopy",\n \"popularity\": 76815\n },\n {\n \"tag\": "causativeness",\n \"popularity\": 76531\n },\n {\n \"tag\": "swird paleoeremology",\n \"popularity\": 76249\n },\n {\n \"tag\": "camphoric",\n \"popularity\": 75968\n },\n {\n \"tag\": "retaining",\n \"popularity\": 75689\n },\n {\n \"tag\": "thyreoprotein",\n \"popularity\": 75411\n },\n {\n \"tag\": "carbona",\n \"popularity\": 75136\n },\n {\n \"tag\": "protectively",\n \"popularity\": 74861\n },\n {\n \"tag\": "mosasaur",\n \"popularity\": 74589\n },\n {\n \"tag\": "reciprocator",\n \"popularity\": 74317\n },\n {\n \"tag\": "detentive",\n \"popularity\": 74048\n },\n {\n \"tag\": "supravital",\n \"popularity\": 73780\n },\n {\n \"tag\": "Vespertilionidae",\n \"popularity\": 73513\n },\n {\n \"tag\": "parka",\n \"popularity\": 73248\n },\n {\n \"tag\": "pickaway",\n \"popularity\": 72984\n },\n {\n \"tag\": "oleaceous",\n \"popularity\": 72722\n },\n {\n \"tag\": "anticogitative",\n \"popularity\": 72462\n },\n {\n \"tag\": "woe",\n \"popularity\": 72203\n },\n {\n \"tag\": "skeuomorph",\n \"popularity\": 71945\n },\n {\n \"tag\": "helpmeet",\n \"popularity\": 71689\n },\n {\n \"tag\": "Hexactinellida brickmaking",\n \"popularity\": 71434\n },\n {\n \"tag\": "resink",\n \"popularity\": 71180\n },\n {\n \"tag\": "diluter",\n \"popularity\": 70928\n },\n {\n \"tag\": "micromicron",\n \"popularity\": 70677\n },\n {\n \"tag\": "parentage",\n \"popularity\": 70428\n },\n {\n \"tag\": "galactorrhoea",\n \"popularity\": 70180\n },\n {\n \"tag\": "gey",\n \"popularity\": 69934\n },\n {\n \"tag\": "gesticulatory",\n \"popularity\": 69689\n },\n {\n \"tag\": "wergil",\n \"popularity\": 69445\n },\n {\n \"tag\": "Lecanora",\n \"popularity\": 69202\n },\n {\n \"tag\": "malanders karst",\n \"popularity\": 68961\n },\n {\n \"tag\": "vibetoite",\n \"popularity\": 68721\n },\n {\n \"tag\": "unrequitedness",\n \"popularity\": 68483\n },\n {\n \"tag\": "outwash",\n \"popularity\": 68245\n },\n {\n \"tag\": "unsacred",\n \"popularity\": 68009\n },\n {\n \"tag\": "unabetted dividend",\n \"popularity\": 67775\n },\n {\n \"tag\": "untraveling",\n \"popularity\": 67541\n },\n {\n \"tag\": "thermobattery",\n \"popularity\": 67309\n },\n {\n \"tag\": "polypragmist",\n \"popularity\": 67078\n },\n {\n \"tag\": "irrefutableness",\n \"popularity\": 66848\n },\n {\n \"tag\": "remiges",\n \"popularity\": 66620\n },\n {\n \"tag\": "implode",\n \"popularity\": 66393\n },\n {\n \"tag\": "superfluousness",\n \"popularity\": 66166\n },\n {\n \"tag\": "croakily unalleviated",\n \"popularity\": 65942\n },\n {\n \"tag\": "edicule",\n \"popularity\": 65718\n },\n {\n \"tag\": "entophytous",\n \"popularity\": 65495\n },\n {\n \"tag\": "benefactorship Toryish",\n \"popularity\": 65274\n },\n {\n \"tag\": "pseudoamateurish",\n \"popularity\": 65054\n },\n {\n \"tag\": "flueless Iguanodontoidea snipnose",\n \"popularity\": 64835\n },\n {\n \"tag\": "zealotical Zamicrus interpole",\n \"popularity\": 64617\n },\n {\n \"tag\": "whereabout",\n \"popularity\": 64401\n },\n {\n \"tag\": "benzazide",\n \"popularity\": 64185\n },\n {\n \"tag\": "pokeweed",\n \"popularity\": 63971\n },\n {\n \"tag\": "calamitoid",\n \"popularity\": 63757\n },\n {\n \"tag\": "sporozoal",\n \"popularity\": 63545\n },\n {\n \"tag\": "physcioid Welshwoman",\n \"popularity\": 63334\n },\n {\n \"tag\": "wanting",\n \"popularity\": 63124\n },\n {\n \"tag\": "unencumbering",\n \"popularity\": 62915\n },\n {\n \"tag\": "Tupi",\n \"popularity\": 62707\n },\n {\n \"tag\": "potbank",\n \"popularity\": 62501\n },\n {\n \"tag\": "bulked",\n \"popularity\": 62295\n },\n {\n \"tag\": "uparise",\n \"popularity\": 62090\n },\n {\n \"tag\": "Sudra",\n \"popularity\": 61887\n },\n {\n \"tag\": "hyperscrupulosity",\n \"popularity\": 61684\n },\n {\n \"tag\": "subterraneously unmaid",\n \"popularity\": 61483\n },\n {\n \"tag\": "poisonousness",\n \"popularity\": 61282\n },\n {\n \"tag\": "phare",\n \"popularity\": 61083\n },\n {\n \"tag\": "dicynodont",\n \"popularity\": 60884\n },\n {\n \"tag\": "chewer",\n \"popularity\": 60687\n },\n {\n \"tag\": "uliginous",\n \"popularity\": 60490\n },\n {\n \"tag\": "tinman",\n \"popularity\": 60295\n },\n {\n \"tag\": "coconut",\n \"popularity\": 60100\n },\n {\n \"tag\": "phryganeoid",\n \"popularity\": 59907\n },\n {\n \"tag\": "bismillah",\n \"popularity\": 59714\n },\n {\n \"tag\": "tautomeric",\n \"popularity\": 59523\n },\n {\n \"tag\": "jerquer",\n \"popularity\": 59332\n },\n {\n \"tag\": "Dryopithecinae",\n \"popularity\": 59143\n },\n {\n \"tag\": "ghizite",\n \"popularity\": 58954\n },\n {\n \"tag\": "unliveable",\n \"popularity\": 58766\n },\n {\n \"tag\": "craftsmaster",\n \"popularity\": 58579\n },\n {\n \"tag\": "semiscenic",\n \"popularity\": 58394\n },\n {\n \"tag\": "danaid",\n \"popularity\": 58209\n },\n {\n \"tag\": "flawful",\n \"popularity\": 58025\n },\n {\n \"tag\": "risibleness",\n \"popularity\": 57841\n },\n {\n \"tag\": "Muscovite",\n \"popularity\": 57659\n },\n {\n \"tag\": "snaringly",\n \"popularity\": 57478\n },\n {\n \"tag\": "brilliantwise",\n \"popularity\": 57297\n },\n {\n \"tag\": "plebeity",\n \"popularity\": 57118\n },\n {\n \"tag\": "historicalness",\n \"popularity\": 56939\n },\n {\n \"tag\": "piecemeal",\n \"popularity\": 56761\n },\n {\n \"tag\": "maxillipedary",\n \"popularity\": 56584\n },\n {\n \"tag\": "Hypenantron",\n \"popularity\": 56408\n },\n {\n \"tag\": "quaintness avigate",\n \"popularity\": 56233\n },\n {\n \"tag\": "ave",\n \"popularity\": 56059\n },\n {\n \"tag\": "mediaevally",\n \"popularity\": 55885\n },\n {\n \"tag\": "brucite",\n \"popularity\": 55712\n },\n {\n \"tag\": "Schwendenerian",\n \"popularity\": 55541\n },\n {\n \"tag\": "julole",\n \"popularity\": 55370\n },\n {\n \"tag\": "palaeolith",\n \"popularity\": 55199\n },\n {\n \"tag\": "cotyledonary",\n \"popularity\": 55030\n },\n {\n \"tag\": "rond",\n \"popularity\": 54861\n },\n {\n \"tag\": "boomster tassoo",\n \"popularity\": 54694\n },\n {\n \"tag\": "cattishly",\n \"popularity\": 54527\n },\n {\n \"tag\": "tonguefence",\n \"popularity\": 54360\n },\n {\n \"tag\": "hexastylar triskele",\n \"popularity\": 54195\n },\n {\n \"tag\": "ariot",\n \"popularity\": 54030\n },\n {\n \"tag\": "intarsist",\n \"popularity\": 53867\n },\n {\n \"tag\": "Oscines",\n \"popularity\": 53704\n },\n {\n \"tag\": "Spaniolize",\n \"popularity\": 53541\n },\n {\n \"tag\": "smellfungus",\n \"popularity\": 53380\n },\n {\n \"tag\": "redisplay",\n \"popularity\": 53219\n },\n {\n \"tag\": "phosphene",\n \"popularity\": 53059\n },\n {\n \"tag\": "phycomycete",\n \"popularity\": 52900\n },\n {\n \"tag\": "prophetic",\n \"popularity\": 52741\n },\n {\n \"tag\": "overtrustful",\n \"popularity\": 52584\n },\n {\n \"tag\": "pinitol",\n \"popularity\": 52427\n },\n {\n \"tag\": "asthmatic",\n \"popularity\": 52270\n },\n {\n \"tag\": "convulsive",\n \"popularity\": 52115\n },\n {\n \"tag\": "draughtswoman",\n \"popularity\": 51960\n },\n {\n \"tag\": "unetymologizable",\n \"popularity\": 51806\n },\n {\n \"tag\": "centrarchoid",\n \"popularity\": 51652\n },\n {\n \"tag\": "mesioincisal",\n \"popularity\": 51500\n },\n {\n \"tag\": "transbaikal",\n \"popularity\": 51348\n },\n {\n \"tag\": "silveriness",\n \"popularity\": 51196\n },\n {\n \"tag\": "costotomy",\n \"popularity\": 51046\n },\n {\n \"tag\": "caracore",\n \"popularity\": 50896\n },\n {\n \"tag\": "depotentiation",\n \"popularity\": 50747\n },\n {\n \"tag\": "glossoepiglottidean",\n \"popularity\": 50598\n },\n {\n \"tag\": "upswell",\n \"popularity\": 50450\n },\n {\n \"tag\": "flecnodal",\n \"popularity\": 50303\n },\n {\n \"tag\": "coventrate",\n \"popularity\": 50157\n },\n {\n \"tag\": "duchesse",\n \"popularity\": 50011\n },\n {\n \"tag\": "excisemanship trophied",\n \"popularity\": 49866\n },\n {\n \"tag\": "cytinaceous",\n \"popularity\": 49721\n },\n {\n \"tag\": "assuringly",\n \"popularity\": 49577\n },\n {\n \"tag\": "unconducted upliftitis",\n \"popularity\": 49434\n },\n {\n \"tag\": "rachicentesis",\n \"popularity\": 49292\n },\n {\n \"tag\": "antiangular",\n \"popularity\": 49150\n },\n {\n \"tag\": "advisal",\n \"popularity\": 49008\n },\n {\n \"tag\": "birdcatcher",\n \"popularity\": 48868\n },\n {\n \"tag\": "secularistic",\n \"popularity\": 48728\n },\n {\n \"tag\": "grandeeism superinformal",\n \"popularity\": 48588\n },\n {\n \"tag\": "unapprehension",\n \"popularity\": 48449\n },\n {\n \"tag\": "excipulum",\n \"popularity\": 48311\n },\n {\n \"tag\": "decimole",\n \"popularity\": 48174\n },\n {\n \"tag\": "semidrachm",\n \"popularity\": 48037\n },\n {\n \"tag\": "uvulotome",\n \"popularity\": 47901\n },\n {\n \"tag\": "Lemaneaceae",\n \"popularity\": 47765\n },\n {\n \"tag\": "corrade",\n \"popularity\": 47630\n },\n {\n \"tag\": "Kuroshio",\n \"popularity\": 47495\n },\n {\n \"tag\": "Araliophyllum",\n \"popularity\": 47361\n },\n {\n \"tag\": "victoriousness cardiosphygmograph",\n \"popularity\": 47228\n },\n {\n \"tag\": "reinvent",\n \"popularity\": 47095\n },\n {\n \"tag\": "Macrotolagus",\n \"popularity\": 46963\n },\n {\n \"tag\": "strenuousness",\n \"popularity\": 46831\n },\n {\n \"tag\": "deviability",\n \"popularity\": 46700\n },\n {\n \"tag\": "phyllospondylous",\n \"popularity\": 46570\n },\n {\n \"tag\": "bisect rudderhole",\n \"popularity\": 46440\n },\n {\n \"tag\": "crownwork",\n \"popularity\": 46311\n },\n {\n \"tag\": "Ascalabota",\n \"popularity\": 46182\n },\n {\n \"tag\": "prostatomyomectomy",\n \"popularity\": 46054\n },\n {\n \"tag\": "neurosyphilis",\n \"popularity\": 45926\n },\n {\n \"tag\": "tabloid scraplet",\n \"popularity\": 45799\n },\n {\n \"tag\": "nonmedullated servility",\n \"popularity\": 45673\n },\n {\n \"tag\": "melopoeic practicalization",\n \"popularity\": 45547\n },\n {\n \"tag\": "nonrhythmic",\n \"popularity\": 45421\n },\n {\n \"tag\": "deplorer",\n \"popularity\": 45296\n },\n {\n \"tag\": "Ophion",\n \"popularity\": 45172\n },\n {\n \"tag\": "subprioress",\n \"popularity\": 45048\n },\n {\n \"tag\": "semiregular",\n \"popularity\": 44925\n },\n {\n \"tag\": "praelection",\n \"popularity\": 44802\n },\n {\n \"tag\": "discinct",\n \"popularity\": 44680\n },\n {\n \"tag\": "preplace",\n \"popularity\": 44558\n },\n {\n \"tag\": "paternoster",\n \"popularity\": 44437\n },\n {\n \"tag\": "suboccipital",\n \"popularity\": 44316\n },\n {\n \"tag\": "Teutophil",\n \"popularity\": 44196\n },\n {\n \"tag\": "tracheole",\n \"popularity\": 44076\n },\n {\n \"tag\": "subsmile",\n \"popularity\": 43957\n },\n {\n \"tag\": "nonapostatizing",\n \"popularity\": 43839\n },\n {\n \"tag\": "cleidotomy",\n \"popularity\": 43720\n },\n {\n \"tag\": "hingle",\n \"popularity\": 43603\n },\n {\n \"tag\": "jocoque",\n \"popularity\": 43486\n },\n {\n \"tag\": "trundler notidanian",\n \"popularity\": 43369\n },\n {\n \"tag\": "strangling misdaub",\n \"popularity\": 43253\n },\n {\n \"tag\": "noncancellable",\n \"popularity\": 43137\n },\n {\n \"tag\": "lavabo",\n \"popularity\": 43022\n },\n {\n \"tag\": "lanterloo",\n \"popularity\": 42907\n },\n {\n \"tag\": "uncitizenly",\n \"popularity\": 42793\n },\n {\n \"tag\": "autoturning",\n \"popularity\": 42679\n },\n {\n \"tag\": "Haganah",\n \"popularity\": 42566\n },\n {\n \"tag\": "Glecoma",\n \"popularity\": 42453\n },\n {\n \"tag\": "membered",\n \"popularity\": 42341\n },\n {\n \"tag\": "consuetudinal",\n \"popularity\": 42229\n },\n {\n \"tag\": "gatehouse",\n \"popularity\": 42117\n },\n {\n \"tag\": "tetherball",\n \"popularity\": 42006\n },\n {\n \"tag\": "counterrevolutionist numismatical",\n \"popularity\": 41896\n },\n {\n \"tag\": "pagehood plateiasmus",\n \"popularity\": 41786\n },\n {\n \"tag\": "pelterer",\n \"popularity\": 41676\n },\n {\n \"tag\": "splenemphraxis",\n \"popularity\": 41567\n },\n {\n \"tag\": "Crypturidae",\n \"popularity\": 41458\n },\n {\n \"tag\": "caboodle",\n \"popularity\": 41350\n },\n {\n \"tag\": "Filaria",\n \"popularity\": 41242\n },\n {\n \"tag\": "noninvincibility",\n \"popularity\": 41135\n },\n {\n \"tag\": "preadvertisement",\n \"popularity\": 41028\n },\n {\n \"tag\": "bathrobe",\n \"popularity\": 40921\n },\n {\n \"tag\": "nitrifier",\n \"popularity\": 40815\n },\n {\n \"tag\": "furthermore",\n \"popularity\": 40709\n },\n {\n \"tag\": "recrate",\n \"popularity\": 40604\n },\n {\n \"tag\": "inexist",\n \"popularity\": 40499\n },\n {\n \"tag\": "Mocoan",\n \"popularity\": 40395\n },\n {\n \"tag\": "forint",\n \"popularity\": 40291\n },\n {\n \"tag\": "cardiomyoliposis",\n \"popularity\": 40187\n },\n {\n \"tag\": "channeling",\n \"popularity\": 40084\n },\n {\n \"tag\": "quebrachine",\n \"popularity\": 39981\n },\n {\n \"tag\": "magistery",\n \"popularity\": 39879\n },\n {\n \"tag\": "koko",\n \"popularity\": 39777\n },\n {\n \"tag\": "nobilify",\n \"popularity\": 39676\n },\n {\n \"tag\": "articulate taprooted",\n \"popularity\": 39575\n },\n {\n \"tag\": "cardiotonic Nicaragua",\n \"popularity\": 39474\n },\n {\n \"tag\": "assertiveness",\n \"popularity\": 39374\n },\n {\n \"tag\": "springtail",\n \"popularity\": 39274\n },\n {\n \"tag\": "spontoon",\n \"popularity\": 39174\n },\n {\n \"tag\": "plesiobiosis",\n \"popularity\": 39075\n },\n {\n \"tag\": "rooinek",\n \"popularity\": 38976\n },\n {\n \"tag\": "hairif falsehood",\n \"popularity\": 38878\n },\n {\n \"tag\": "synodally",\n \"popularity\": 38780\n },\n {\n \"tag\": "biodynamics",\n \"popularity\": 38683\n },\n {\n \"tag\": "trickling",\n \"popularity\": 38585\n },\n {\n \"tag\": "oxfly daystar",\n \"popularity\": 38489\n },\n {\n \"tag\": "epicycloidal",\n \"popularity\": 38392\n },\n {\n \"tag\": "shorthand",\n \"popularity\": 38296\n },\n {\n \"tag\": "herpolhode",\n \"popularity\": 38201\n },\n {\n \"tag\": "polysynthesism",\n \"popularity\": 38105\n },\n {\n \"tag\": "cany",\n \"popularity\": 38010\n },\n {\n \"tag\": "sideage",\n \"popularity\": 37916\n },\n {\n \"tag\": "strainableness",\n \"popularity\": 37822\n },\n {\n \"tag\": "superformidable",\n \"popularity\": 37728\n },\n {\n \"tag\": "slendang",\n \"popularity\": 37634\n },\n {\n \"tag\": "impropriation",\n \"popularity\": 37541\n },\n {\n \"tag\": "ficklehearted",\n \"popularity\": 37449\n },\n {\n \"tag\": "wintrify",\n \"popularity\": 37356\n },\n {\n \"tag\": "geomorphogenist",\n \"popularity\": 37264\n },\n {\n \"tag\": "smuggleable",\n \"popularity\": 37173\n },\n {\n \"tag\": "delapsion",\n \"popularity\": 37081\n },\n {\n \"tag\": "projective",\n \"popularity\": 36990\n },\n {\n \"tag\": "unglue exfoliation",\n \"popularity\": 36900\n },\n {\n \"tag\": "Acerae",\n \"popularity\": 36810\n },\n {\n \"tag\": "unstaged",\n \"popularity\": 36720\n },\n {\n \"tag\": "ranal",\n \"popularity\": 36630\n },\n {\n \"tag\": "worrier",\n \"popularity\": 36541\n },\n {\n \"tag\": "unhid",\n \"popularity\": 36452\n },\n {\n \"tag\": "adequation",\n \"popularity\": 36363\n },\n {\n \"tag\": "strongylid Sokotri",\n \"popularity\": 36275\n },\n {\n \"tag\": "fumingly",\n \"popularity\": 36187\n },\n {\n \"tag\": "gynosporangium phaenogenetic",\n \"popularity\": 36100\n },\n {\n \"tag\": "uniunguiculate",\n \"popularity\": 36012\n },\n {\n \"tag\": "prudelike",\n \"popularity\": 35926\n },\n {\n \"tag\": "seminomata",\n \"popularity\": 35839\n },\n {\n \"tag\": "trinklet",\n \"popularity\": 35753\n },\n {\n \"tag\": "risorial",\n \"popularity\": 35667\n },\n {\n \"tag\": "pericardiocentesis",\n \"popularity\": 35581\n },\n {\n \"tag\": "filmist",\n \"popularity\": 35496\n },\n {\n \"tag\": "Nana",\n \"popularity\": 35411\n },\n {\n \"tag\": "cynipoid",\n \"popularity\": 35326\n },\n {\n \"tag\": "cteniform",\n \"popularity\": 35242\n },\n {\n \"tag\": "semiflex",\n \"popularity\": 35158\n },\n {\n \"tag\": "solstitially",\n \"popularity\": 35074\n },\n {\n \"tag\": "Algarsife",\n \"popularity\": 34991\n },\n {\n \"tag\": "noncriminal",\n \"popularity\": 34908\n },\n {\n \"tag\": "compassion",\n \"popularity\": 34825\n },\n {\n \"tag\": "Buddhic",\n \"popularity\": 34743\n },\n {\n \"tag\": "vellicative dactylically hotfoot",\n \"popularity\": 34661\n },\n {\n \"tag\": "chicory",\n \"popularity\": 34579\n },\n {\n \"tag\": "transperitoneally",\n \"popularity\": 34497\n },\n {\n \"tag\": "pennae",\n \"popularity\": 34416\n },\n {\n \"tag\": "Flamandize",\n \"popularity\": 34335\n },\n {\n \"tag\": "underviewer",\n \"popularity\": 34254\n },\n {\n \"tag\": "assoil",\n \"popularity\": 34174\n },\n {\n \"tag\": "saccharobacillus",\n \"popularity\": 34094\n },\n {\n \"tag\": "biacetylene",\n \"popularity\": 34014\n },\n {\n \"tag\": "mouchardism",\n \"popularity\": 33935\n },\n {\n \"tag\": "anisomeric",\n \"popularity\": 33856\n },\n {\n \"tag\": "digestive",\n \"popularity\": 33777\n },\n {\n \"tag\": "darlingly",\n \"popularity\": 33698\n },\n {\n \"tag\": "liman",\n \"popularity\": 33620\n },\n {\n \"tag\": "soldanrie",\n \"popularity\": 33542\n },\n {\n \"tag\": "sully",\n \"popularity\": 33464\n },\n {\n \"tag\": "brightsmith",\n \"popularity\": 33387\n },\n {\n \"tag\": "inwrap antiliturgist ureterocervical",\n \"popularity\": 33309\n },\n {\n \"tag\": "discommodity",\n \"popularity\": 33232\n },\n {\n \"tag\": "typical aggrandizer",\n \"popularity\": 33156\n },\n {\n \"tag\": "xenogeny",\n \"popularity\": 33079\n },\n {\n \"tag\": "uncountrified",\n \"popularity\": 33003\n },\n {\n \"tag\": "Podarge",\n \"popularity\": 32928\n },\n {\n \"tag\": "uninterviewed",\n \"popularity\": 32852\n },\n {\n \"tag\": "underprior",\n \"popularity\": 32777\n },\n {\n \"tag\": "leiomyomatous",\n \"popularity\": 32702\n },\n {\n \"tag\": "postdysenteric",\n \"popularity\": 32627\n },\n {\n \"tag\": "Fusicladium",\n \"popularity\": 32553\n },\n {\n \"tag\": "Dulcinea",\n \"popularity\": 32478\n },\n {\n \"tag\": "interspersion",\n \"popularity\": 32404\n },\n {\n \"tag\": "preobligate",\n \"popularity\": 32331\n },\n {\n \"tag\": "subaggregate",\n \"popularity\": 32257\n },\n {\n \"tag\": "grammarianism",\n \"popularity\": 32184\n },\n {\n \"tag\": "palikar",\n \"popularity\": 32111\n },\n {\n \"tag\": "facileness",\n \"popularity\": 32039\n },\n {\n \"tag\": "deuterofibrinose",\n \"popularity\": 31966\n },\n {\n \"tag\": "pseudesthesia",\n \"popularity\": 31894\n },\n {\n \"tag\": "sedimentary",\n \"popularity\": 31822\n },\n {\n \"tag\": "typewrite",\n \"popularity\": 31751\n },\n {\n \"tag\": "immemorable",\n \"popularity\": 31679\n },\n {\n \"tag\": "Myrtus",\n \"popularity\": 31608\n },\n {\n \"tag\": "hauchecornite",\n \"popularity\": 31537\n },\n {\n \"tag\": "galleylike",\n \"popularity\": 31467\n },\n {\n \"tag\": "thimber",\n \"popularity\": 31396\n },\n {\n \"tag\": "Hegelianism",\n \"popularity\": 31326\n },\n {\n \"tag\": "strig",\n \"popularity\": 31256\n },\n {\n \"tag\": "skyre",\n \"popularity\": 31187\n },\n {\n \"tag\": "eupepticism",\n \"popularity\": 31117\n },\n {\n \"tag\": "eponymism",\n \"popularity\": 31048\n },\n {\n \"tag\": "flunkeyhood",\n \"popularity\": 30979\n },\n {\n \"tag\": "Abama",\n \"popularity\": 30911\n },\n {\n \"tag\": "adiadochokinesis",\n \"popularity\": 30842\n },\n {\n \"tag\": "spendthrifty",\n \"popularity\": 30774\n },\n {\n \"tag\": "chalcedony",\n \"popularity\": 30706\n },\n {\n \"tag\": "authorism",\n \"popularity\": 30638\n },\n {\n \"tag\": "nasturtium",\n \"popularity\": 30571\n },\n {\n \"tag\": "Acanthocereus",\n \"popularity\": 30504\n },\n {\n \"tag\": "uncollapsible",\n \"popularity\": 30437\n },\n {\n \"tag\": "excursionist",\n \"popularity\": 30370\n },\n {\n \"tag\": "fogbow",\n \"popularity\": 30303\n },\n {\n \"tag\": "overlie",\n \"popularity\": 30237\n },\n {\n \"tag\": "velours",\n \"popularity\": 30171\n },\n {\n \"tag\": "zoodendria madrigal stagbush",\n \"popularity\": 30105\n },\n {\n \"tag\": "imi",\n \"popularity\": 30039\n },\n {\n \"tag\": "cojudge",\n \"popularity\": 29974\n },\n {\n \"tag\": "depurate argal",\n \"popularity\": 29909\n },\n {\n \"tag\": "unrecognition",\n \"popularity\": 29844\n },\n {\n \"tag\": "paunchful",\n \"popularity\": 29779\n },\n {\n \"tag\": "invalued",\n \"popularity\": 29714\n },\n {\n \"tag\": "probang",\n \"popularity\": 29650\n },\n {\n \"tag\": "chetvert",\n \"popularity\": 29586\n },\n {\n \"tag\": "enactable",\n \"popularity\": 29522\n },\n {\n \"tag\": "detoxicate adhibit",\n \"popularity\": 29458\n },\n {\n \"tag\": "kullaite",\n \"popularity\": 29395\n },\n {\n \"tag\": "undazzling",\n \"popularity\": 29332\n },\n {\n \"tag\": "excalation",\n \"popularity\": 29269\n },\n {\n \"tag\": "sievings",\n \"popularity\": 29206\n },\n {\n \"tag\": "disenthral",\n \"popularity\": 29143\n },\n {\n \"tag\": "disinterestedly",\n \"popularity\": 29081\n },\n {\n \"tag\": "stanner",\n \"popularity\": 29018\n },\n {\n \"tag\": "recapitulative",\n \"popularity\": 28956\n },\n {\n \"tag\": "objectivist",\n \"popularity\": 28895\n },\n {\n \"tag\": "hypermetropia",\n \"popularity\": 28833\n },\n {\n \"tag\": "incumbency",\n \"popularity\": 28772\n },\n {\n \"tag\": "protegee",\n \"popularity\": 28711\n },\n {\n \"tag\": "zealotic",\n \"popularity\": 28650\n },\n {\n \"tag\": "predebit",\n \"popularity\": 28589\n },\n {\n \"tag\": "cupolar",\n \"popularity\": 28528\n },\n {\n \"tag\": "unattributed",\n \"popularity\": 28468\n },\n {\n \"tag\": "louisine",\n \"popularity\": 28408\n },\n {\n \"tag\": "illustrate",\n \"popularity\": 28348\n },\n {\n \"tag\": "inofficiousness",\n \"popularity\": 28288\n },\n {\n \"tag\": "Americawards",\n \"popularity\": 28228\n },\n {\n \"tag\": "foreflap",\n \"popularity\": 28169\n },\n {\n \"tag\": "eruditeness",\n \"popularity\": 28110\n },\n {\n \"tag\": "copiopsia",\n \"popularity\": 28051\n },\n {\n \"tag\": "sporuliferous",\n \"popularity\": 27992\n },\n {\n \"tag\": "muttering",\n \"popularity\": 27934\n },\n {\n \"tag\": "prepsychology adrip",\n \"popularity\": 27875\n },\n {\n \"tag\": "unfriendly",\n \"popularity\": 27817\n },\n {\n \"tag\": "sulphanilic",\n \"popularity\": 27759\n },\n {\n \"tag\": "Coelococcus",\n \"popularity\": 27701\n },\n {\n \"tag\": "undoubtfulness",\n \"popularity\": 27643\n },\n {\n \"tag\": "flaringly",\n \"popularity\": 27586\n },\n {\n \"tag\": "unordain",\n \"popularity\": 27529\n },\n {\n \"tag\": "fratchety",\n \"popularity\": 27472\n },\n {\n \"tag\": "decadentism dolefully",\n \"popularity\": 27415\n },\n {\n \"tag\": "synthronus",\n \"popularity\": 27358\n },\n {\n \"tag\": "maiid",\n \"popularity\": 27301\n },\n {\n \"tag\": "rhinobyon",\n \"popularity\": 27245\n },\n {\n \"tag\": "Didynamia",\n \"popularity\": 27189\n },\n {\n \"tag\": "millionairedom",\n \"popularity\": 27133\n },\n {\n \"tag\": "mulierine",\n \"popularity\": 27077\n },\n {\n \"tag\": "Mayo",\n \"popularity\": 27021\n },\n {\n \"tag\": "perceivedness",\n \"popularity\": 26966\n },\n {\n \"tag\": "unadoration",\n \"popularity\": 26911\n },\n {\n \"tag\": "regraft",\n \"popularity\": 26856\n },\n {\n \"tag\": "witch",\n \"popularity\": 26801\n },\n {\n \"tag\": "ungrow",\n \"popularity\": 26746\n },\n {\n \"tag\": "glossopharyngeus",\n \"popularity\": 26691\n },\n {\n \"tag\": "unstirrable",\n \"popularity\": 26637\n },\n {\n \"tag\": "synodsman",\n \"popularity\": 26583\n },\n {\n \"tag\": "placentalian",\n \"popularity\": 26529\n },\n {\n \"tag\": "corpulently",\n \"popularity\": 26475\n },\n {\n \"tag\": "photochromoscope",\n \"popularity\": 26421\n },\n {\n \"tag\": "indusiate retinasphaltum chokestrap",\n \"popularity\": 26368\n },\n {\n \"tag\": "murdrum",\n \"popularity\": 26314\n },\n {\n \"tag\": "belatedness",\n \"popularity\": 26261\n },\n {\n \"tag\": "Cochin",\n \"popularity\": 26208\n },\n {\n \"tag\": "Leonist",\n \"popularity\": 26155\n },\n {\n \"tag\": "keeker confined",\n \"popularity\": 26102\n },\n {\n \"tag\": "unintellectual",\n \"popularity\": 26050\n },\n {\n \"tag\": "nymphaline bait",\n \"popularity\": 25997\n },\n {\n \"tag\": "sarcosporidiosis",\n \"popularity\": 25945\n },\n {\n \"tag\": "catawamptiously",\n \"popularity\": 25893\n },\n {\n \"tag\": "outshame",\n \"popularity\": 25841\n },\n {\n \"tag\": "animalism",\n \"popularity\": 25790\n },\n {\n \"tag\": "epithalamial",\n \"popularity\": 25738\n },\n {\n \"tag\": "ganner",\n \"popularity\": 25687\n },\n {\n \"tag\": "desilicify",\n \"popularity\": 25635\n },\n {\n \"tag\": "dandyism",\n \"popularity\": 25584\n },\n {\n \"tag\": "hyleg",\n \"popularity\": 25533\n },\n {\n \"tag\": "photophysical",\n \"popularity\": 25483\n },\n {\n \"tag\": "underload",\n \"popularity\": 25432\n },\n {\n \"tag\": "unintrusive",\n \"popularity\": 25382\n },\n {\n \"tag\": "succinamic",\n \"popularity\": 25331\n },\n {\n \"tag\": "matchy",\n \"popularity\": 25281\n },\n {\n \"tag\": "concordal",\n \"popularity\": 25231\n },\n {\n \"tag\": "exteriority",\n \"popularity\": 25181\n },\n {\n \"tag\": "sterculiad",\n \"popularity\": 25132\n },\n {\n \"tag\": "sulfoxylic",\n \"popularity\": 25082\n },\n {\n \"tag\": "oversubscription",\n \"popularity\": 25033\n },\n {\n \"tag\": "chiasmic",\n \"popularity\": 24984\n },\n {\n \"tag\": "pseudoparthenogenesis",\n \"popularity\": 24935\n },\n {\n \"tag\": "indorse",\n \"popularity\": 24886\n },\n {\n \"tag\": "Krishnaite",\n \"popularity\": 24837\n },\n {\n \"tag\": "calcinize",\n \"popularity\": 24788\n },\n {\n \"tag\": "rhodium",\n \"popularity\": 24740\n },\n {\n \"tag\": "tragopan",\n \"popularity\": 24692\n },\n {\n \"tag\": "overwhelmingly",\n \"popularity\": 24643\n },\n {\n \"tag\": "procidence accorporate",\n \"popularity\": 24595\n },\n {\n \"tag\": "polemize speelless",\n \"popularity\": 24548\n },\n {\n \"tag\": "radiocarpal goran",\n \"popularity\": 24500\n },\n {\n \"tag\": "counteroffer Pelodytes",\n \"popularity\": 24452\n },\n {\n \"tag\": "lionhearted",\n \"popularity\": 24405\n },\n {\n \"tag\": "paramastoid",\n \"popularity\": 24358\n },\n {\n \"tag\": "murine",\n \"popularity\": 24310\n },\n {\n \"tag\": "woodbined",\n \"popularity\": 24263\n },\n {\n \"tag\": "packthread",\n \"popularity\": 24217\n },\n {\n \"tag\": "citreous",\n \"popularity\": 24170\n },\n {\n \"tag\": "unfallaciously",\n \"popularity\": 24123\n },\n {\n \"tag\": "tentwork reincarnadine",\n \"popularity\": 24077\n },\n {\n \"tag\": "verminousness",\n \"popularity\": 24030\n },\n {\n \"tag\": "sillometer",\n \"popularity\": 23984\n },\n {\n \"tag\": "jointy",\n \"popularity\": 23938\n },\n {\n \"tag\": "streptolysin",\n \"popularity\": 23892\n },\n {\n \"tag\": "Florentinism",\n \"popularity\": 23847\n },\n {\n \"tag\": "monosomatous",\n \"popularity\": 23801\n },\n {\n \"tag\": "capsulociliary",\n \"popularity\": 23756\n },\n {\n \"tag\": "organum",\n \"popularity\": 23710\n },\n {\n \"tag\": "overtly",\n \"popularity\": 23665\n },\n {\n \"tag\": "ophthalmoscopical",\n \"popularity\": 23620\n },\n {\n \"tag\": "supposititiously",\n \"popularity\": 23575\n },\n {\n \"tag\": "radiochemistry",\n \"popularity\": 23530\n },\n {\n \"tag\": "flaxtail",\n \"popularity\": 23486\n },\n {\n \"tag\": "pretympanic",\n \"popularity\": 23441\n },\n {\n \"tag\": "auscultation",\n \"popularity\": 23397\n },\n {\n \"tag\": "hairdresser",\n \"popularity\": 23352\n },\n {\n \"tag\": "chaffless",\n \"popularity\": 23308\n },\n {\n \"tag\": "polioencephalitis",\n \"popularity\": 23264\n },\n {\n \"tag\": "axolotl",\n \"popularity\": 23220\n },\n {\n \"tag\": "smous",\n \"popularity\": 23177\n },\n {\n \"tag\": "morgen disenamour toothed",\n \"popularity\": 23133\n },\n {\n \"tag\": "chaiseless",\n \"popularity\": 23089\n },\n {\n \"tag\": "frugally",\n \"popularity\": 23046\n },\n {\n \"tag\": "combustive antievolutionist cinenegative",\n \"popularity\": 23003\n },\n {\n \"tag\": "malacolite",\n \"popularity\": 22960\n },\n {\n \"tag\": "borne",\n \"popularity\": 22917\n },\n {\n \"tag\": "mercaptole",\n \"popularity\": 22874\n },\n {\n \"tag\": "judicatory",\n \"popularity\": 22831\n },\n {\n \"tag\": "noctivagation",\n \"popularity\": 22789\n },\n {\n \"tag\": "synthete",\n \"popularity\": 22746\n },\n {\n \"tag\": "tomboyism",\n \"popularity\": 22704\n },\n {\n \"tag\": "serranoid",\n \"popularity\": 22661\n },\n {\n \"tag\": "impostorism",\n \"popularity\": 22619\n },\n {\n \"tag\": "flagellosis Talitha",\n \"popularity\": 22577\n },\n {\n \"tag\": "pseudoviscous",\n \"popularity\": 22535\n },\n {\n \"tag\": "Galleriidae",\n \"popularity\": 22494\n },\n {\n \"tag\": "undulation didelph Comintern",\n \"popularity\": 22452\n },\n {\n \"tag\": "triangulopyramidal",\n \"popularity\": 22411\n },\n {\n \"tag\": "middlings",\n \"popularity\": 22369\n },\n {\n \"tag\": "piperazin",\n \"popularity\": 22328\n },\n {\n \"tag\": "endostitis",\n \"popularity\": 22287\n },\n {\n \"tag\": "swordlike",\n \"popularity\": 22246\n },\n {\n \"tag\": "forthwith",\n \"popularity\": 22205\n },\n {\n \"tag\": "menaceful",\n \"popularity\": 22164\n },\n {\n \"tag\": "explantation defective",\n \"popularity\": 22123\n },\n {\n \"tag\": "arrear",\n \"popularity\": 22083\n },\n {\n \"tag\": "engraft",\n \"popularity\": 22042\n },\n {\n \"tag\": "revolunteer",\n \"popularity\": 22002\n },\n {\n \"tag\": "foliaceous",\n \"popularity\": 21962\n },\n {\n \"tag\": "pseudograph",\n \"popularity\": 21922\n },\n {\n \"tag\": "maenaite",\n \"popularity\": 21882\n },\n {\n \"tag\": "interfinger",\n \"popularity\": 21842\n },\n {\n \"tag\": "macroscopically",\n \"popularity\": 21802\n },\n {\n \"tag\": "bluewood",\n \"popularity\": 21762\n },\n {\n \"tag\": "chikara",\n \"popularity\": 21723\n },\n {\n \"tag\": "reprehension diazeuxis nickelous",\n \"popularity\": 21683\n },\n {\n \"tag\": "vacuation",\n \"popularity\": 21644\n },\n {\n \"tag\": "Sartish",\n \"popularity\": 21605\n },\n {\n \"tag\": "pseudogyny",\n \"popularity\": 21566\n },\n {\n \"tag\": "friedcake",\n \"popularity\": 21527\n },\n {\n \"tag\": "thraw",\n \"popularity\": 21488\n },\n {\n \"tag\": "bifid",\n \"popularity\": 21449\n },\n {\n \"tag\": "truthlessly",\n \"popularity\": 21411\n },\n {\n \"tag\": "lungy",\n \"popularity\": 21372\n },\n {\n \"tag\": "fluoborite",\n \"popularity\": 21334\n },\n {\n \"tag\": "anthropolithic",\n \"popularity\": 21295\n },\n {\n \"tag\": "coachee straw",\n \"popularity\": 21257\n },\n {\n \"tag\": "dehorner Grecize",\n \"popularity\": 21219\n },\n {\n \"tag\": "spondylopyosis",\n \"popularity\": 21181\n },\n {\n \"tag\": "institutionary",\n \"popularity\": 21143\n },\n {\n \"tag\": "agentry",\n \"popularity\": 21105\n },\n {\n \"tag\": "musing bietle",\n \"popularity\": 21068\n },\n {\n \"tag\": "cormophyte",\n \"popularity\": 21030\n },\n {\n \"tag\": "semielliptic",\n \"popularity\": 20993\n },\n {\n \"tag\": "ependytes",\n \"popularity\": 20955\n },\n {\n \"tag\": "coachmaster",\n \"popularity\": 20918\n },\n {\n \"tag\": "overexuberant",\n \"popularity\": 20881\n },\n {\n \"tag\": "selectable",\n \"popularity\": 20844\n },\n {\n \"tag\": "saclike",\n \"popularity\": 20807\n },\n {\n \"tag\": "mullion",\n \"popularity\": 20770\n },\n {\n \"tag\": "pantheonize prevalency",\n \"popularity\": 20733\n },\n {\n \"tag\": "trophosperm",\n \"popularity\": 20697\n },\n {\n \"tag\": "paraphrasist",\n \"popularity\": 20660\n },\n {\n \"tag\": "undercarry",\n \"popularity\": 20624\n },\n {\n \"tag\": "thallogenic",\n \"popularity\": 20587\n },\n {\n \"tag\": "bulgy forbid",\n \"popularity\": 20551\n },\n {\n \"tag\": "proliquor gratulatory",\n \"popularity\": 20515\n },\n {\n \"tag\": "booker",\n \"popularity\": 20479\n },\n {\n \"tag\": "wizen",\n \"popularity\": 20443\n },\n {\n \"tag\": "synchondrosially",\n \"popularity\": 20407\n },\n {\n \"tag\": "herbless",\n \"popularity\": 20371\n },\n {\n \"tag\": "arfvedsonite",\n \"popularity\": 20336\n },\n {\n \"tag\": "Neuroptera",\n \"popularity\": 20300\n },\n {\n \"tag\": "fingerstone",\n \"popularity\": 20265\n },\n {\n \"tag\": "Odontoglossae",\n \"popularity\": 20229\n },\n {\n \"tag\": "transmigrator",\n \"popularity\": 20194\n },\n {\n \"tag\": "Dehaites",\n \"popularity\": 20159\n },\n {\n \"tag\": "Molinist",\n \"popularity\": 20124\n },\n {\n \"tag\": "novelistic",\n \"popularity\": 20089\n },\n {\n \"tag\": "astelic",\n \"popularity\": 20054\n },\n {\n \"tag\": "pyelometry",\n \"popularity\": 20019\n },\n {\n \"tag\": "pigmentation",\n \"popularity\": 19984\n },\n {\n \"tag\": "epinaos",\n \"popularity\": 19950\n },\n {\n \"tag\": "outdare",\n \"popularity\": 19915\n },\n {\n \"tag\": "Funje philaristocracy",\n \"popularity\": 19881\n },\n {\n \"tag\": "keddah",\n \"popularity\": 19846\n },\n {\n \"tag\": "axoidean",\n \"popularity\": 19812\n },\n {\n \"tag\": "ovule",\n \"popularity\": 19778\n },\n {\n \"tag\": "solidify",\n \"popularity\": 19744\n },\n {\n \"tag\": "noncelestial",\n \"popularity\": 19710\n },\n {\n \"tag\": "overmultiplication",\n \"popularity\": 19676\n },\n {\n \"tag\": "hexatetrahedron",\n \"popularity\": 19642\n },\n {\n \"tag\": "pliciform",\n \"popularity\": 19609\n },\n {\n \"tag\": "zimbalon",\n \"popularity\": 19575\n },\n {\n \"tag\": "annexational",\n \"popularity\": 19542\n },\n {\n \"tag\": "eurhodol",\n \"popularity\": 19508\n },\n {\n \"tag\": "yark",\n \"popularity\": 19475\n },\n {\n \"tag\": "illegality nitroalizarin",\n \"popularity\": 19442\n },\n {\n \"tag\": "quadratum",\n \"popularity\": 19409\n },\n {\n \"tag\": "saccharine",\n \"popularity\": 19376\n },\n {\n \"tag\": "unemploy",\n \"popularity\": 19343\n },\n {\n \"tag\": "uniclinal unipotent",\n \"popularity\": 19310\n },\n {\n \"tag\": "turbo",\n \"popularity\": 19277\n },\n {\n \"tag\": "sybarism",\n \"popularity\": 19244\n },\n {\n \"tag\": "motacilline",\n \"popularity\": 19212\n },\n {\n \"tag\": "weaselly",\n \"popularity\": 19179\n },\n {\n \"tag\": "plastid",\n \"popularity\": 19147\n },\n {\n \"tag\": "wasting",\n \"popularity\": 19114\n },\n {\n \"tag\": "begrime fluting",\n \"popularity\": 19082\n },\n {\n \"tag\": "Nephilinae",\n \"popularity\": 19050\n },\n {\n \"tag\": "disregardance",\n \"popularity\": 19018\n },\n {\n \"tag\": "Shakerlike",\n \"popularity\": 18986\n },\n {\n \"tag\": "uniped",\n \"popularity\": 18954\n },\n {\n \"tag\": "knap",\n \"popularity\": 18922\n },\n {\n \"tag\": "electivism undergardener",\n \"popularity\": 18890\n },\n {\n \"tag\": "hulverheaded",\n \"popularity\": 18858\n },\n {\n \"tag\": "unruptured",\n \"popularity\": 18827\n },\n {\n \"tag\": "solemnize credently",\n \"popularity\": 18795\n },\n {\n \"tag\": "pentastomoid possessingly",\n \"popularity\": 18764\n },\n {\n \"tag\": "octose",\n \"popularity\": 18733\n },\n {\n \"tag\": "psithurism indefensibility",\n \"popularity\": 18701\n },\n {\n \"tag\": "torrentuous cyanometer subcrenate",\n \"popularity\": 18670\n },\n {\n \"tag\": "photoplaywright tapaculo",\n \"popularity\": 18639\n },\n {\n \"tag\": "univalence",\n \"popularity\": 18608\n },\n {\n \"tag\": "Porthetria",\n \"popularity\": 18577\n },\n {\n \"tag\": "funambulo",\n \"popularity\": 18546\n },\n {\n \"tag\": "pedion",\n \"popularity\": 18515\n },\n {\n \"tag\": "horticulturally",\n \"popularity\": 18485\n },\n {\n \"tag\": "marennin",\n \"popularity\": 18454\n },\n {\n \"tag\": "horselaugh",\n \"popularity\": 18423\n },\n {\n \"tag\": "semiexecutive",\n \"popularity\": 18393\n },\n {\n \"tag\": "Monopteridae",\n \"popularity\": 18363\n },\n {\n \"tag\": "commonable",\n \"popularity\": 18332\n },\n {\n \"tag\": "dreariment",\n \"popularity\": 18302\n },\n {\n \"tag\": "disbud",\n \"popularity\": 18272\n },\n {\n \"tag\": "monocled",\n \"popularity\": 18242\n },\n {\n \"tag\": "hurlbarrow",\n \"popularity\": 18212\n },\n {\n \"tag\": "opiateproof",\n \"popularity\": 18182\n },\n {\n \"tag\": "Fahrenheit",\n \"popularity\": 18152\n },\n {\n \"tag\": "writhed",\n \"popularity\": 18122\n },\n {\n \"tag\": "Volstead",\n \"popularity\": 18093\n },\n {\n \"tag\": "yesternight",\n \"popularity\": 18063\n },\n {\n \"tag\": "readmittance",\n \"popularity\": 18033\n },\n {\n \"tag\": "reiterable",\n \"popularity\": 18004\n },\n {\n \"tag\": "triquetral",\n \"popularity\": 17975\n },\n {\n \"tag\": "guillotinement",\n \"popularity\": 17945\n },\n {\n \"tag\": "repermission",\n \"popularity\": 17916\n },\n {\n \"tag\": "assishly",\n \"popularity\": 17887\n },\n {\n \"tag\": "daidle",\n \"popularity\": 17858\n },\n {\n \"tag\": "prismatoid",\n \"popularity\": 17829\n },\n {\n \"tag\": "irreptitious",\n \"popularity\": 17800\n },\n {\n \"tag\": "sourdeline",\n \"popularity\": 17771\n },\n {\n \"tag\": "Austrian",\n \"popularity\": 17742\n },\n {\n \"tag\": "psychorrhagic",\n \"popularity\": 17713\n },\n {\n \"tag\": "Monumbo",\n \"popularity\": 17685\n },\n {\n \"tag\": "cloiochoanitic",\n \"popularity\": 17656\n },\n {\n \"tag\": "hant",\n \"popularity\": 17628\n },\n {\n \"tag\": "roily pulldown",\n \"popularity\": 17599\n },\n {\n \"tag\": "recongratulation",\n \"popularity\": 17571\n },\n {\n \"tag\": "Peking",\n \"popularity\": 17543\n },\n {\n \"tag\": "erdvark",\n \"popularity\": 17514\n },\n {\n \"tag\": "antimnemonic",\n \"popularity\": 17486\n },\n {\n \"tag\": "noncapillarity",\n \"popularity\": 17458\n },\n {\n \"tag\": "irrepressive",\n \"popularity\": 17430\n },\n {\n \"tag\": "Petromyzontes",\n \"popularity\": 17402\n },\n {\n \"tag\": "piscatorially",\n \"popularity\": 17374\n },\n {\n \"tag\": "cholesterosis",\n \"popularity\": 17346\n },\n {\n \"tag\": "denunciate",\n \"popularity\": 17319\n },\n {\n \"tag\": "unmetalled",\n \"popularity\": 17291\n },\n {\n \"tag\": "Tigris enruin",\n \"popularity\": 17263\n },\n {\n \"tag\": "anaspalin",\n \"popularity\": 17236\n },\n {\n \"tag\": "monodromy",\n \"popularity\": 17208\n },\n {\n \"tag\": "Canichanan",\n \"popularity\": 17181\n },\n {\n \"tag\": "mesolabe",\n \"popularity\": 17154\n },\n {\n \"tag\": "trichothallic overcunningness",\n \"popularity\": 17127\n },\n {\n \"tag\": "spinsterishly",\n \"popularity\": 17099\n },\n {\n \"tag\": "sensilla",\n \"popularity\": 17072\n },\n {\n \"tag\": "wifelkin",\n \"popularity\": 17045\n },\n {\n \"tag\": "suppositionless",\n \"popularity\": 17018\n },\n {\n \"tag\": "irksomeness",\n \"popularity\": 16991\n },\n {\n \"tag\": "sanbenito",\n \"popularity\": 16964\n },\n {\n \"tag\": "nonstatement",\n \"popularity\": 16938\n },\n {\n \"tag\": "phenoloid",\n \"popularity\": 16911\n },\n {\n \"tag\": "Steinberger",\n \"popularity\": 16884\n },\n {\n \"tag\": "replicated boom",\n \"popularity\": 16858\n },\n {\n \"tag\": "sciomachiology",\n \"popularity\": 16831\n },\n {\n \"tag\": "starwise",\n \"popularity\": 16805\n },\n {\n \"tag\": "prerich",\n \"popularity\": 16778\n },\n {\n \"tag\": "unspawned",\n \"popularity\": 16752\n },\n {\n \"tag\": "unindentable",\n \"popularity\": 16726\n },\n {\n \"tag\": "stromatic",\n \"popularity\": 16700\n },\n {\n \"tag\": "fetishize",\n \"popularity\": 16673\n },\n {\n \"tag\": "dihydroxy",\n \"popularity\": 16647\n },\n {\n \"tag\": "precaudal",\n \"popularity\": 16621\n },\n {\n \"tag\": "Madagascar",\n \"popularity\": 16595\n },\n {\n \"tag\": "repinement",\n \"popularity\": 16570\n },\n {\n \"tag\": "noncathedral wenzel",\n \"popularity\": 16544\n },\n {\n \"tag\": "corollike",\n \"popularity\": 16518\n },\n {\n \"tag\": "pubes unamortization",\n \"popularity\": 16492\n },\n {\n \"tag\": "brickcroft",\n \"popularity\": 16467\n },\n {\n \"tag\": "intertrabecular",\n \"popularity\": 16441\n },\n {\n \"tag\": "formulaic",\n \"popularity\": 16416\n },\n {\n \"tag\": "arienzo",\n \"popularity\": 16390\n },\n {\n \"tag\": "Mazzinian",\n \"popularity\": 16365\n },\n {\n \"tag\": "wallowishly",\n \"popularity\": 16339\n },\n {\n \"tag\": "sysselman",\n \"popularity\": 16314\n },\n {\n \"tag\": "seligmannite",\n \"popularity\": 16289\n },\n {\n \"tag\": "harlequinery",\n \"popularity\": 16264\n },\n {\n \"tag\": "zucchetto",\n \"popularity\": 16239\n },\n {\n \"tag\": "malonyl",\n \"popularity\": 16214\n },\n {\n \"tag\": "patwari",\n \"popularity\": 16189\n },\n {\n \"tag\": "neoholmia venturesomeness",\n \"popularity\": 16164\n },\n {\n \"tag\": "Dehwar",\n \"popularity\": 16139\n },\n {\n \"tag\": "fetiferous",\n \"popularity\": 16114\n },\n {\n \"tag\": "chromatophore",\n \"popularity\": 16090\n },\n {\n \"tag\": "reregistration",\n \"popularity\": 16065\n },\n {\n \"tag\": "alienor",\n \"popularity\": 16040\n },\n {\n \"tag\": "Hexagynia",\n \"popularity\": 16016\n },\n {\n \"tag\": "cerebrotonia",\n \"popularity\": 15991\n },\n {\n \"tag\": "deedbox",\n \"popularity\": 15967\n },\n {\n \"tag\": "staab",\n \"popularity\": 15943\n },\n {\n \"tag\": "uratemia",\n \"popularity\": 15918\n },\n {\n \"tag\": "flaunt",\n \"popularity\": 15894\n },\n {\n \"tag\": "bogy",\n \"popularity\": 15870\n },\n {\n \"tag\": "subcartilaginous",\n \"popularity\": 15846\n },\n {\n \"tag\": "protonephridial",\n \"popularity\": 15822\n },\n {\n \"tag\": "Boswellia",\n \"popularity\": 15798\n },\n {\n \"tag\": "relaxant untiaraed protoepiphyte",\n \"popularity\": 15774\n },\n {\n \"tag\": "nesslerization",\n \"popularity\": 15750\n },\n {\n \"tag\": "precession",\n \"popularity\": 15726\n },\n {\n \"tag\": "peat",\n \"popularity\": 15702\n },\n {\n \"tag\": "unbit",\n \"popularity\": 15678\n },\n {\n \"tag\": "snailish",\n \"popularity\": 15655\n },\n {\n \"tag\": "porismatical",\n \"popularity\": 15631\n },\n {\n \"tag\": "hooflike",\n \"popularity\": 15608\n },\n {\n \"tag\": "resuppose phene cranic",\n \"popularity\": 15584\n },\n {\n \"tag\": "peptonization kipskin",\n \"popularity\": 15561\n },\n {\n \"tag\": "birdstone",\n \"popularity\": 15537\n },\n {\n \"tag\": "empty inferoanterior",\n \"popularity\": 15514\n },\n {\n \"tag\": "androtauric",\n \"popularity\": 15491\n },\n {\n \"tag\": "triamide",\n \"popularity\": 15467\n },\n {\n \"tag\": "showmanry",\n \"popularity\": 15444\n },\n {\n \"tag\": "doing",\n \"popularity\": 15421\n },\n {\n \"tag\": "bouchaleen",\n \"popularity\": 15398\n },\n {\n \"tag\": "precollude",\n \"popularity\": 15375\n },\n {\n \"tag\": "finger",\n \"popularity\": 15352\n },\n {\n \"tag\": "limnetic intermessenger",\n \"popularity\": 15329\n },\n {\n \"tag\": "uncharitable picrotoxic",\n \"popularity\": 15306\n },\n {\n \"tag\": "nationalizer Phasmidae",\n \"popularity\": 15283\n },\n {\n \"tag\": "laughingstock",\n \"popularity\": 15261\n },\n {\n \"tag\": "nondeferential",\n \"popularity\": 15238\n },\n {\n \"tag\": "uproariously",\n \"popularity\": 15215\n },\n {\n \"tag\": "manzanilla",\n \"popularity\": 15193\n },\n {\n \"tag\": "khahoon",\n \"popularity\": 15170\n },\n {\n \"tag\": "olericulturally longshanks",\n \"popularity\": 15148\n },\n {\n \"tag\": "enthusiastically methionic",\n \"popularity\": 15125\n },\n {\n \"tag\": "pobs",\n \"popularity\": 15103\n },\n {\n \"tag\": "tricarpellate",\n \"popularity\": 15081\n },\n {\n \"tag\": "souterrain",\n \"popularity\": 15058\n },\n {\n \"tag\": "tethelin",\n \"popularity\": 15036\n },\n {\n \"tag\": "tartle",\n \"popularity\": 15014\n },\n {\n \"tag\": "tidelike",\n \"popularity\": 14992\n },\n {\n \"tag\": "cosmoramic",\n \"popularity\": 14970\n },\n {\n \"tag\": "pretardiness",\n \"popularity\": 14948\n },\n {\n \"tag\": "insoul",\n \"popularity\": 14926\n },\n {\n \"tag\": "anthroxan",\n \"popularity\": 14904\n },\n {\n \"tag\": "jilter",\n \"popularity\": 14882\n },\n {\n \"tag\": "pectinibranchian trematode",\n \"popularity\": 14860\n },\n {\n \"tag\": "Renaissancist",\n \"popularity\": 14838\n },\n {\n \"tag\": "imaginant",\n \"popularity\": 14817\n },\n {\n \"tag\": "supercensure",\n \"popularity\": 14795\n },\n {\n \"tag\": "festilogy",\n \"popularity\": 14773\n },\n {\n \"tag\": "regression",\n \"popularity\": 14752\n },\n {\n \"tag\": "mesobregmate languorously",\n \"popularity\": 14730\n },\n {\n \"tag\": "unsupernaturalized",\n \"popularity\": 14709\n },\n {\n \"tag\": "boobyish",\n \"popularity\": 14687\n },\n {\n \"tag\": "scopolamine",\n \"popularity\": 14666\n },\n {\n \"tag\": "reamputation unchristianly",\n \"popularity\": 14645\n },\n {\n \"tag\": "cuneatic",\n \"popularity\": 14623\n },\n {\n \"tag\": "heathberry",\n \"popularity\": 14602\n },\n {\n \"tag\": "hate",\n \"popularity\": 14581\n },\n {\n \"tag\": "redeemableness",\n \"popularity\": 14560\n },\n {\n \"tag\": "damasse",\n \"popularity\": 14539\n },\n {\n \"tag\": "thrillsome",\n \"popularity\": 14518\n },\n {\n \"tag\": "disseverment",\n \"popularity\": 14497\n },\n {\n \"tag\": "underbishopric Ostyak",\n \"popularity\": 14476\n },\n {\n \"tag\": "Exoascales",\n \"popularity\": 14455\n },\n {\n \"tag\": "soiled",\n \"popularity\": 14434\n },\n {\n \"tag\": "Cain",\n \"popularity\": 14413\n },\n {\n \"tag\": "mismanageable arenae",\n \"popularity\": 14392\n },\n {\n \"tag\": "manducate unhinderably",\n \"popularity\": 14372\n },\n {\n \"tag\": "peregrin",\n \"popularity\": 14351\n },\n {\n \"tag\": "musicianly",\n \"popularity\": 14330\n },\n {\n \"tag\": "aln",\n \"popularity\": 14310\n },\n {\n \"tag\": "intercentrum",\n \"popularity\": 14289\n },\n {\n \"tag\": "roothold",\n \"popularity\": 14269\n },\n {\n \"tag\": "jane aneurism",\n \"popularity\": 14248\n },\n {\n \"tag\": "insinuatively forefeel phytolatrous",\n \"popularity\": 14228\n },\n {\n \"tag\": "kanchil",\n \"popularity\": 14208\n },\n {\n \"tag\": "Austrophile",\n \"popularity\": 14187\n },\n {\n \"tag\": "unterrorized",\n \"popularity\": 14167\n },\n {\n \"tag\": "admeasure",\n \"popularity\": 14147\n },\n {\n \"tag\": "electrodissolution",\n \"popularity\": 14127\n },\n {\n \"tag\": "unweddedly",\n \"popularity\": 14107\n },\n {\n \"tag\": "unannoying",\n \"popularity\": 14087\n },\n {\n \"tag\": "uningenuous",\n \"popularity\": 14067\n },\n {\n \"tag\": "omnibenevolent",\n \"popularity\": 14047\n },\n {\n \"tag\": "commissure",\n \"popularity\": 14027\n },\n {\n \"tag\": "tellureted",\n \"popularity\": 14007\n },\n {\n \"tag\": "suffragan",\n \"popularity\": 13987\n },\n {\n \"tag\": "sphaeriaceous",\n \"popularity\": 13967\n },\n {\n \"tag\": "unfearing",\n \"popularity\": 13947\n },\n {\n \"tag\": "stentoriousness precounsellor",\n \"popularity\": 13928\n },\n {\n \"tag\": "haemaspectroscope",\n \"popularity\": 13908\n },\n {\n \"tag\": "teras",\n \"popularity\": 13888\n },\n {\n \"tag\": "pulicine",\n \"popularity\": 13869\n },\n {\n \"tag\": "colicystopyelitis",\n \"popularity\": 13849\n },\n {\n \"tag\": "Physalia",\n \"popularity\": 13830\n },\n {\n \"tag\": "Saxicolidae",\n \"popularity\": 13810\n },\n {\n \"tag\": "peritonital",\n \"popularity\": 13791\n },\n {\n \"tag\": "dysphotic",\n \"popularity\": 13771\n },\n {\n \"tag\": "unabandoned",\n \"popularity\": 13752\n },\n {\n \"tag\": "rashful",\n \"popularity\": 13733\n },\n {\n \"tag\": "goodyness Manobo",\n \"popularity\": 13714\n },\n {\n \"tag\": "glaring",\n \"popularity\": 13694\n },\n {\n \"tag\": "horrorful",\n \"popularity\": 13675\n },\n {\n \"tag\": "intercepting",\n \"popularity\": 13656\n },\n {\n \"tag\": "semifine",\n \"popularity\": 13637\n },\n {\n \"tag\": "Gaypoo",\n \"popularity\": 13618\n },\n {\n \"tag\": "Metrosideros",\n \"popularity\": 13599\n },\n {\n \"tag\": "thoracicolumbar",\n \"popularity\": 13580\n },\n {\n \"tag\": "unserried",\n \"popularity\": 13561\n },\n {\n \"tag\": "keeperess cauterization",\n \"popularity\": 13542\n },\n {\n \"tag\": "administrant",\n \"popularity\": 13523\n },\n {\n \"tag\": "unpropitiatedness",\n \"popularity\": 13505\n },\n {\n \"tag\": "pensileness",\n \"popularity\": 13486\n },\n {\n \"tag\": "quinaldic unreceivable",\n \"popularity\": 13467\n },\n {\n \"tag\": "Carnaria",\n \"popularity\": 13448\n },\n {\n \"tag\": "azothionium wurrus",\n \"popularity\": 13430\n },\n {\n \"tag\": "mistresshood",\n \"popularity\": 13411\n },\n {\n \"tag\": "Savara",\n \"popularity\": 13393\n },\n {\n \"tag\": "dasyurine",\n \"popularity\": 13374\n },\n {\n \"tag\": "superideal",\n \"popularity\": 13356\n },\n {\n \"tag\": "Parisianize",\n \"popularity\": 13337\n },\n {\n \"tag\": "underearth",\n \"popularity\": 13319\n },\n {\n \"tag\": "athrogenic",\n \"popularity\": 13301\n },\n {\n \"tag\": "communicate",\n \"popularity\": 13282\n },\n {\n \"tag\": "denervation enworthed",\n \"popularity\": 13264\n },\n {\n \"tag\": "subbromide",\n \"popularity\": 13246\n },\n {\n \"tag\": "stenocoriasis",\n \"popularity\": 13228\n },\n {\n \"tag\": "facetiousness",\n \"popularity\": 13209\n },\n {\n \"tag\": "twaddling",\n \"popularity\": 13191\n },\n {\n \"tag\": "tetartoconid",\n \"popularity\": 13173\n },\n {\n \"tag\": "audiophile",\n \"popularity\": 13155\n },\n {\n \"tag\": "fustigate",\n \"popularity\": 13137\n },\n {\n \"tag\": "Sorbian cacophonia",\n \"popularity\": 13119\n },\n {\n \"tag\": "fondish",\n \"popularity\": 13101\n },\n {\n \"tag\": "endomastoiditis",\n \"popularity\": 13084\n },\n {\n \"tag\": "sniptious",\n \"popularity\": 13066\n },\n {\n \"tag\": "glochidiate",\n \"popularity\": 13048\n },\n {\n \"tag\": "polycarboxylic",\n \"popularity\": 13030\n },\n {\n \"tag\": "stamp",\n \"popularity\": 13012\n },\n {\n \"tag\": "tritonymph endotoxoid",\n \"popularity\": 12995\n },\n {\n \"tag\": "wolfskin",\n \"popularity\": 12977\n },\n {\n \"tag\": "oncosimeter",\n \"popularity\": 12959\n },\n {\n \"tag\": "outward",\n \"popularity\": 12942\n },\n {\n \"tag\": "circumscribed",\n \"popularity\": 12924\n },\n {\n \"tag\": "autohemolytic",\n \"popularity\": 12907\n },\n {\n \"tag\": "isorhamnose",\n \"popularity\": 12889\n },\n {\n \"tag\": "monarchomachic",\n \"popularity\": 12872\n },\n {\n \"tag\": "phaenomenon",\n \"popularity\": 12855\n },\n {\n \"tag\": "angiopressure",\n \"popularity\": 12837\n },\n {\n \"tag\": "similarize",\n \"popularity\": 12820\n },\n {\n \"tag\": "unseeable",\n \"popularity\": 12803\n },\n {\n \"tag\": "Toryize",\n \"popularity\": 12785\n },\n {\n \"tag\": "fruitling",\n \"popularity\": 12768\n },\n {\n \"tag\": "axle",\n \"popularity\": 12751\n },\n {\n \"tag\": "priestal cocked",\n \"popularity\": 12734\n },\n {\n \"tag\": "serotoxin",\n \"popularity\": 12717\n },\n {\n \"tag\": "unmovably",\n \"popularity\": 12700\n },\n {\n \"tag\": "darbha",\n \"popularity\": 12683\n },\n {\n \"tag\": "Mongolize",\n \"popularity\": 12666\n },\n {\n \"tag\": "clusteringly",\n \"popularity\": 12649\n },\n {\n \"tag\": "tendence",\n \"popularity\": 12632\n },\n {\n \"tag\": "foziness",\n \"popularity\": 12615\n },\n {\n \"tag\": "brickkiln lithify",\n \"popularity\": 12598\n },\n {\n \"tag\": "unpriest",\n \"popularity\": 12581\n },\n {\n \"tag\": "convincer",\n \"popularity\": 12564\n },\n {\n \"tag\": "mornlike",\n \"popularity\": 12548\n },\n {\n \"tag\": "overaddiction ostentatiousness",\n \"popularity\": 12531\n },\n {\n \"tag\": "diffusively moccasin pendom",\n \"popularity\": 12514\n },\n {\n \"tag\": "boose",\n \"popularity\": 12498\n },\n {\n \"tag\": "myonosus",\n \"popularity\": 12481\n },\n {\n \"tag\": "handsome",\n \"popularity\": 12464\n },\n {\n \"tag\": "paroxysmic",\n \"popularity\": 12448\n },\n {\n \"tag\": "Ulidian",\n \"popularity\": 12431\n },\n {\n \"tag\": "heartache",\n \"popularity\": 12415\n },\n {\n \"tag\": "torporize",\n \"popularity\": 12398\n },\n {\n \"tag\": "hippish",\n \"popularity\": 12382\n },\n {\n \"tag\": "stigmal militation",\n \"popularity\": 12366\n },\n {\n \"tag\": "matmaker",\n \"popularity\": 12349\n },\n {\n \"tag\": "marantaceous bivoluminous",\n \"popularity\": 12333\n },\n {\n \"tag\": "Uraniidae",\n \"popularity\": 12317\n },\n {\n \"tag\": "risper",\n \"popularity\": 12301\n },\n {\n \"tag\": "tintinnabulation",\n \"popularity\": 12284\n },\n {\n \"tag\": "tributorian",\n \"popularity\": 12268\n },\n {\n \"tag\": "ashamedly",\n \"popularity\": 12252\n },\n {\n \"tag\": "Macrourus",\n \"popularity\": 12236\n },\n {\n \"tag\": "Chora",\n \"popularity\": 12220\n },\n {\n \"tag\": "caul",\n \"popularity\": 12204\n },\n {\n \"tag\": "exsector",\n \"popularity\": 12188\n },\n {\n \"tag\": "acutish",\n \"popularity\": 12172\n },\n {\n \"tag\": "amphichrome",\n \"popularity\": 12156\n },\n {\n \"tag\": "guarder",\n \"popularity\": 12140\n },\n {\n \"tag\": "sculpturally",\n \"popularity\": 12124\n },\n {\n \"tag\": "benightmare",\n \"popularity\": 12108\n },\n {\n \"tag\": "chucky",\n \"popularity\": 12093\n },\n {\n \"tag\": "Venetian",\n \"popularity\": 12077\n },\n {\n \"tag\": "autotheater",\n \"popularity\": 12061\n },\n {\n \"tag\": "planarioid",\n \"popularity\": 12045\n },\n {\n \"tag\": "handkerchiefful",\n \"popularity\": 12030\n },\n {\n \"tag\": "fuliginousness potentize",\n \"popularity\": 12014\n },\n {\n \"tag\": "pantheum",\n \"popularity\": 11998\n },\n {\n \"tag\": "heavyweight",\n \"popularity\": 11983\n },\n {\n \"tag\": "unbrick",\n \"popularity\": 11967\n },\n {\n \"tag\": "duomachy",\n \"popularity\": 11952\n },\n {\n \"tag\": "polyphyodont",\n \"popularity\": 11936\n },\n {\n \"tag\": "hibernacle",\n \"popularity\": 11921\n },\n {\n \"tag\": "undistend",\n \"popularity\": 11905\n },\n {\n \"tag\": "hystericky",\n \"popularity\": 11890\n },\n {\n \"tag\": "paleolimnology",\n \"popularity\": 11875\n },\n {\n \"tag\": "cedarware",\n \"popularity\": 11859\n },\n {\n \"tag\": "overwrested",\n \"popularity\": 11844\n },\n {\n \"tag\": "Syriacism",\n \"popularity\": 11829\n },\n {\n \"tag\": "pretan",\n \"popularity\": 11813\n },\n {\n \"tag\": "formant",\n \"popularity\": 11798\n },\n {\n \"tag\": "pharmacopoeist Fedia",\n \"popularity\": 11783\n },\n {\n \"tag\": "exorcist eerisome",\n \"popularity\": 11768\n },\n {\n \"tag\": "separation",\n \"popularity\": 11753\n },\n {\n \"tag\": "infancy",\n \"popularity\": 11738\n },\n {\n \"tag\": "ecrasite",\n \"popularity\": 11723\n },\n {\n \"tag\": "propolize",\n \"popularity\": 11708\n },\n {\n \"tag\": "uncram phyllin",\n \"popularity\": 11693\n },\n {\n \"tag\": "thymopathy",\n \"popularity\": 11678\n },\n {\n \"tag\": "omniscient",\n \"popularity\": 11663\n },\n {\n \"tag\": "coussinet hazer",\n \"popularity\": 11648\n },\n {\n \"tag\": "contributiveness",\n \"popularity\": 11633\n },\n {\n \"tag\": "septifluous",\n \"popularity\": 11618\n },\n {\n \"tag\": "halfness",\n \"popularity\": 11603\n },\n {\n \"tag\": "tocher",\n \"popularity\": 11589\n },\n {\n \"tag\": "monotonist",\n \"popularity\": 11574\n },\n {\n \"tag\": "headchair",\n \"popularity\": 11559\n },\n {\n \"tag\": "everywhence",\n \"popularity\": 11544\n },\n {\n \"tag\": "gerate",\n \"popularity\": 11530\n },\n {\n \"tag\": "unrepellent",\n \"popularity\": 11515\n },\n {\n \"tag\": "inidoneous",\n \"popularity\": 11500\n },\n {\n \"tag\": "Rifi",\n \"popularity\": 11486\n },\n {\n \"tag\": "unstop",\n \"popularity\": 11471\n },\n {\n \"tag\": "conformer",\n \"popularity\": 11457\n },\n {\n \"tag\": "vivisectionally",\n \"popularity\": 11442\n },\n {\n \"tag\": "nonfinishing",\n \"popularity\": 11428\n },\n {\n \"tag\": "tyranness",\n \"popularity\": 11413\n },\n {\n \"tag\": "shepherdage havoc",\n \"popularity\": 11399\n },\n {\n \"tag\": "coronale",\n \"popularity\": 11385\n },\n {\n \"tag\": "airmarker",\n \"popularity\": 11370\n },\n {\n \"tag\": "subpanel",\n \"popularity\": 11356\n },\n {\n \"tag\": "conciliation",\n \"popularity\": 11342\n },\n {\n \"tag\": "supergun",\n \"popularity\": 11327\n },\n {\n \"tag\": "photoheliography",\n \"popularity\": 11313\n },\n {\n \"tag\": "cacosmia",\n \"popularity\": 11299\n },\n {\n \"tag\": "caressant",\n \"popularity\": 11285\n },\n {\n \"tag\": "swivet",\n \"popularity\": 11270\n },\n {\n \"tag\": "coddler",\n \"popularity\": 11256\n },\n {\n \"tag\": "rakehellish",\n \"popularity\": 11242\n },\n {\n \"tag\": "recohabitation",\n \"popularity\": 11228\n },\n {\n \"tag\": "postillator",\n \"popularity\": 11214\n },\n {\n \"tag\": "receipt",\n \"popularity\": 11200\n },\n {\n \"tag\": "nonconformistical",\n \"popularity\": 11186\n },\n {\n \"tag\": "unglorified",\n \"popularity\": 11172\n },\n {\n \"tag\": "unordinariness",\n \"popularity\": 11158\n },\n {\n \"tag\": "tetrahydroxy",\n \"popularity\": 11144\n },\n {\n \"tag\": "haploperistomic corporeity",\n \"popularity\": 11130\n },\n {\n \"tag\": "varical",\n \"popularity\": 11117\n },\n {\n \"tag\": "pilferment",\n \"popularity\": 11103\n },\n {\n \"tag\": "reverentially playcraft",\n \"popularity\": 11089\n },\n {\n \"tag\": "unretentive",\n \"popularity\": 11075\n },\n {\n \"tag\": "readiness",\n \"popularity\": 11061\n },\n {\n \"tag\": "thermomagnetism",\n \"popularity\": 11048\n },\n {\n \"tag\": "spotless",\n \"popularity\": 11034\n },\n {\n \"tag\": "semishrubby",\n \"popularity\": 11020\n },\n {\n \"tag\": "metrotomy",\n \"popularity\": 11007\n },\n {\n \"tag\": "hocker",\n \"popularity\": 10993\n },\n {\n \"tag\": "anecdotal",\n \"popularity\": 10979\n },\n {\n \"tag\": "tetrabelodont",\n \"popularity\": 10966\n },\n {\n \"tag\": "Ramillied",\n \"popularity\": 10952\n },\n {\n \"tag\": "sympatheticism",\n \"popularity\": 10939\n },\n {\n \"tag\": "kiskatom",\n \"popularity\": 10925\n },\n {\n \"tag\": "concyclically",\n \"popularity\": 10912\n },\n {\n \"tag\": "tunicless",\n \"popularity\": 10899\n },\n {\n \"tag\": "formalistic",\n \"popularity\": 10885\n },\n {\n \"tag\": "thermacogenesis",\n \"popularity\": 10872\n },\n {\n \"tag\": "multimotored",\n \"popularity\": 10858\n },\n {\n \"tag\": "inversive",\n \"popularity\": 10845\n },\n {\n \"tag\": "Jatki",\n \"popularity\": 10832\n },\n {\n \"tag\": "highest",\n \"popularity\": 10818\n },\n {\n \"tag\": "rubidic",\n \"popularity\": 10805\n },\n {\n \"tag\": "acranial",\n \"popularity\": 10792\n },\n {\n \"tag\": "pulvinulus",\n \"popularity\": 10779\n },\n {\n \"tag\": "nattiness",\n \"popularity\": 10766\n },\n {\n \"tag\": "antisimoniacal",\n \"popularity\": 10752\n },\n {\n \"tag\": "tetanize",\n \"popularity\": 10739\n },\n {\n \"tag\": "spectrophobia",\n \"popularity\": 10726\n },\n {\n \"tag\": "monopolitical",\n \"popularity\": 10713\n },\n {\n \"tag\": "teallite",\n \"popularity\": 10700\n },\n {\n \"tag\": "alicyclic interpellator",\n \"popularity\": 10687\n },\n {\n \"tag\": "nonsynthesized",\n \"popularity\": 10674\n },\n {\n \"tag\": "wheelwrighting",\n \"popularity\": 10661\n },\n {\n \"tag\": "pelliculate",\n \"popularity\": 10648\n },\n {\n \"tag\": "Euphyllopoda",\n \"popularity\": 10635\n },\n {\n \"tag\": "graver",\n \"popularity\": 10622\n },\n {\n \"tag\": "automorph",\n \"popularity\": 10609\n },\n {\n \"tag\": "underhanded",\n \"popularity\": 10597\n },\n {\n \"tag\": "causal",\n \"popularity\": 10584\n },\n {\n \"tag\": "odoom",\n \"popularity\": 10571\n },\n {\n \"tag\": "apodictical",\n \"popularity\": 10558\n },\n {\n \"tag\": "foundery",\n \"popularity\": 10545\n },\n {\n \"tag\": "unneighbored",\n \"popularity\": 10533\n },\n {\n \"tag\": "woolshearing",\n \"popularity\": 10520\n },\n {\n \"tag\": "boschveld",\n \"popularity\": 10507\n },\n {\n \"tag\": "unhardened lipopod",\n \"popularity\": 10495\n },\n {\n \"tag\": "unenriching",\n \"popularity\": 10482\n },\n {\n \"tag\": "spak",\n \"popularity\": 10469\n },\n {\n \"tag\": "yogasana",\n \"popularity\": 10457\n },\n {\n \"tag\": "depoetize",\n \"popularity\": 10444\n },\n {\n \"tag\": "parousiamania",\n \"popularity\": 10432\n },\n {\n \"tag\": "longlegs",\n \"popularity\": 10419\n },\n {\n \"tag\": "gelatinizability",\n \"popularity\": 10407\n },\n {\n \"tag\": "edeology",\n \"popularity\": 10394\n },\n {\n \"tag\": "sodwork",\n \"popularity\": 10382\n },\n {\n \"tag\": "somnambule",\n \"popularity\": 10369\n },\n {\n \"tag\": "antiquing",\n \"popularity\": 10357\n },\n {\n \"tag\": "intaker",\n \"popularity\": 10344\n },\n {\n \"tag\": "Gerberia",\n \"popularity\": 10332\n },\n {\n \"tag\": "preadmit",\n \"popularity\": 10320\n },\n {\n \"tag\": "bullhorn",\n \"popularity\": 10307\n },\n {\n \"tag\": "sororal",\n \"popularity\": 10295\n },\n {\n \"tag\": "phaeophyceous",\n \"popularity\": 10283\n },\n {\n \"tag\": "omphalopsychite",\n \"popularity\": 10271\n },\n {\n \"tag\": "substantious",\n \"popularity\": 10258\n },\n {\n \"tag\": "undemonstratively",\n \"popularity\": 10246\n },\n {\n \"tag\": "corallike blackit",\n \"popularity\": 10234\n },\n {\n \"tag\": "amoebous",\n \"popularity\": 10222\n },\n {\n \"tag\": "Polypodium",\n \"popularity\": 10210\n },\n {\n \"tag\": "blodite",\n \"popularity\": 10198\n },\n {\n \"tag\": "hordarian",\n \"popularity\": 10186\n },\n {\n \"tag\": "nonmoral",\n \"popularity\": 10174\n },\n {\n \"tag\": "dredgeful",\n \"popularity\": 10162\n },\n {\n \"tag\": "nourishingly",\n \"popularity\": 10150\n },\n {\n \"tag\": "seamy",\n \"popularity\": 10138\n },\n {\n \"tag\": "vara",\n \"popularity\": 10126\n },\n {\n \"tag\": "incorruptibleness",\n \"popularity\": 10114\n },\n {\n \"tag\": "manipulator",\n \"popularity\": 10102\n },\n {\n \"tag\": "chromodiascope uncountably",\n \"popularity\": 10090\n },\n {\n \"tag\": "typhemia",\n \"popularity\": 10078\n },\n {\n \"tag\": "Smalcaldic",\n \"popularity\": 10066\n },\n {\n \"tag\": "precontrive",\n \"popularity\": 10054\n },\n {\n \"tag\": "sowarry",\n \"popularity\": 10042\n },\n {\n \"tag\": "monopodic",\n \"popularity\": 10031\n },\n {\n \"tag\": "recodify",\n \"popularity\": 10019\n },\n {\n \"tag\": "phosphowolframic rimple",\n \"popularity\": 10007\n },\n {\n \"tag\": "triconch",\n \"popularity\": 9995\n },\n {\n \"tag\": "pycnodontoid",\n \"popularity\": 9984\n },\n {\n \"tag\": "bradyspermatism",\n \"popularity\": 9972\n },\n {\n \"tag\": "extensionist",\n \"popularity\": 9960\n },\n {\n \"tag\": "characterize",\n \"popularity\": 9949\n },\n {\n \"tag\": "anatreptic proteolytic",\n \"popularity\": 9937\n },\n {\n \"tag\": "waterboard",\n \"popularity\": 9925\n },\n {\n \"tag\": "allopathically",\n \"popularity\": 9914\n },\n {\n \"tag\": "arithmetician",\n \"popularity\": 9902\n },\n {\n \"tag\": "subsist",\n \"popularity\": 9891\n },\n {\n \"tag\": "Islamitish",\n \"popularity\": 9879\n },\n {\n \"tag\": "biddy",\n \"popularity\": 9868\n },\n {\n \"tag\": "reverberation",\n \"popularity\": 9856\n },\n {\n \"tag\": "Zaporogue",\n \"popularity\": 9845\n },\n {\n \"tag\": "soapberry",\n \"popularity\": 9833\n },\n {\n \"tag\": "physiognomics",\n \"popularity\": 9822\n },\n {\n \"tag\": "hospitalization",\n \"popularity\": 9810\n },\n {\n \"tag\": "dissembler",\n \"popularity\": 9799\n },\n {\n \"tag\": "festinate",\n \"popularity\": 9788\n },\n {\n \"tag\": "angiectopia",\n \"popularity\": 9776\n },\n {\n \"tag\": "Pulicidae",\n \"popularity\": 9765\n },\n {\n \"tag\": "beslimer",\n \"popularity\": 9754\n },\n {\n \"tag\": "nontreaty",\n \"popularity\": 9743\n },\n {\n \"tag\": "unhaggled",\n \"popularity\": 9731\n },\n {\n \"tag\": "catfall",\n \"popularity\": 9720\n },\n {\n \"tag\": "stola",\n \"popularity\": 9709\n },\n {\n \"tag\": "pataco",\n \"popularity\": 9698\n },\n {\n \"tag\": "ontologistic",\n \"popularity\": 9686\n },\n {\n \"tag\": "aerosphere",\n \"popularity\": 9675\n },\n {\n \"tag\": "deobstruent",\n \"popularity\": 9664\n },\n {\n \"tag\": "threepence",\n \"popularity\": 9653\n },\n {\n \"tag\": "cyprinoid",\n \"popularity\": 9642\n },\n {\n \"tag\": "overbank",\n \"popularity\": 9631\n },\n {\n \"tag\": "prostyle",\n \"popularity\": 9620\n },\n {\n \"tag\": "photoactivation",\n \"popularity\": 9609\n },\n {\n \"tag\": "homothetic",\n \"popularity\": 9598\n },\n {\n \"tag\": "roguedom",\n \"popularity\": 9587\n },\n {\n \"tag\": "underschool",\n \"popularity\": 9576\n },\n {\n \"tag\": "tractility",\n \"popularity\": 9565\n },\n {\n \"tag\": "gardenin",\n \"popularity\": 9554\n },\n {\n \"tag\": "Micromastictora",\n \"popularity\": 9543\n },\n {\n \"tag\": "gossypine",\n \"popularity\": 9532\n },\n {\n \"tag\": "amylodyspepsia",\n \"popularity\": 9521\n },\n {\n \"tag\": "Luciana",\n \"popularity\": 9510\n },\n {\n \"tag\": "meetly nonfisherman",\n \"popularity\": 9500\n },\n {\n \"tag\": "backhanded",\n \"popularity\": 9489\n },\n {\n \"tag\": "decrustation",\n \"popularity\": 9478\n },\n {\n \"tag\": "pinrail",\n \"popularity\": 9467\n },\n {\n \"tag\": "Mahori",\n \"popularity\": 9456\n },\n {\n \"tag\": "unsizable",\n \"popularity\": 9446\n },\n {\n \"tag\": "disawa",\n \"popularity\": 9435\n },\n {\n \"tag\": "launderability inconsidered",\n \"popularity\": 9424\n },\n {\n \"tag\": "unclassical",\n \"popularity\": 9414\n },\n {\n \"tag\": "inobtrusiveness",\n \"popularity\": 9403\n },\n {\n \"tag\": "sialogenous",\n \"popularity\": 9392\n },\n {\n \"tag\": "sulphonamide",\n \"popularity\": 9382\n },\n {\n \"tag\": "diluvion",\n \"popularity\": 9371\n },\n {\n \"tag\": "deuteranope",\n \"popularity\": 9361\n },\n {\n \"tag\": "addition",\n \"popularity\": 9350\n },\n {\n \"tag\": "bockeret",\n \"popularity\": 9339\n },\n {\n \"tag\": "unidentified",\n \"popularity\": 9329\n },\n {\n \"tag\": "caryatic",\n \"popularity\": 9318\n },\n {\n \"tag\": "misattribution",\n \"popularity\": 9308\n },\n {\n \"tag\": "outray",\n \"popularity\": 9297\n },\n {\n \"tag\": "areometrical",\n \"popularity\": 9287\n },\n {\n \"tag\": "antilogism",\n \"popularity\": 9277\n },\n {\n \"tag\": "inadjustable",\n \"popularity\": 9266\n },\n {\n \"tag\": "byssus",\n \"popularity\": 9256\n },\n {\n \"tag\": "trun",\n \"popularity\": 9245\n },\n {\n \"tag\": "thereology",\n \"popularity\": 9235\n },\n {\n \"tag\": "extort",\n \"popularity\": 9225\n },\n {\n \"tag\": "bumpkin",\n \"popularity\": 9214\n },\n {\n \"tag\": "sulphobenzide",\n \"popularity\": 9204\n },\n {\n \"tag\": "hydrogeology",\n \"popularity\": 9194\n },\n {\n \"tag\": "nidulariaceous",\n \"popularity\": 9183\n },\n {\n \"tag\": "propodiale",\n \"popularity\": 9173\n },\n {\n \"tag\": "fierily",\n \"popularity\": 9163\n },\n {\n \"tag\": "aerotonometry",\n \"popularity\": 9153\n },\n {\n \"tag\": "pelobatid oversuperstitious",\n \"popularity\": 9142\n },\n {\n \"tag\": "restringent",\n \"popularity\": 9132\n },\n {\n \"tag\": "tetrapodic",\n \"popularity\": 9122\n },\n {\n \"tag\": "heroicness Vendidad",\n \"popularity\": 9112\n },\n {\n \"tag\": "Sphingurus",\n \"popularity\": 9102\n },\n {\n \"tag\": "sclerote",\n \"popularity\": 9092\n },\n {\n \"tag\": "unkeyed",\n \"popularity\": 9082\n },\n {\n \"tag\": "superparliamentary",\n \"popularity\": 9072\n },\n {\n \"tag\": "hetericism",\n \"popularity\": 9061\n },\n {\n \"tag\": "hucklebone",\n \"popularity\": 9051\n },\n {\n \"tag\": "yojan",\n \"popularity\": 9041\n },\n {\n \"tag\": "bossed",\n \"popularity\": 9031\n },\n {\n \"tag\": "spiderwork",\n \"popularity\": 9021\n },\n {\n \"tag\": "millfeed dullery",\n \"popularity\": 9011\n },\n {\n \"tag\": "adnoun",\n \"popularity\": 9001\n },\n {\n \"tag\": "mesometric",\n \"popularity\": 8992\n },\n {\n \"tag\": "doublehandedness",\n \"popularity\": 8982\n },\n {\n \"tag\": "suppurant",\n \"popularity\": 8972\n },\n {\n \"tag\": "Berlinize",\n \"popularity\": 8962\n },\n {\n \"tag\": "sontag",\n \"popularity\": 8952\n },\n {\n \"tag\": "biplane",\n \"popularity\": 8942\n },\n {\n \"tag\": "insula",\n \"popularity\": 8932\n },\n {\n \"tag\": "unbrand",\n \"popularity\": 8922\n },\n {\n \"tag\": "Basilosaurus",\n \"popularity\": 8913\n },\n {\n \"tag\": "prenomination",\n \"popularity\": 8903\n },\n {\n \"tag\": "untextual",\n \"popularity\": 8893\n },\n {\n \"tag\": "coleslaw",\n \"popularity\": 8883\n },\n {\n \"tag\": "langsyne",\n \"popularity\": 8874\n },\n {\n \"tag\": "impede",\n \"popularity\": 8864\n },\n {\n \"tag\": "irrigator",\n \"popularity\": 8854\n },\n {\n \"tag\": "deflocculation",\n \"popularity\": 8844\n },\n {\n \"tag\": "narghile",\n \"popularity\": 8835\n },\n {\n \"tag\": "unguardedly ebenaceous",\n \"popularity\": 8825\n },\n {\n \"tag\": "conversantly subocular",\n \"popularity\": 8815\n },\n {\n \"tag\": "hydroponic",\n \"popularity\": 8806\n },\n {\n \"tag\": "anthropopsychism",\n \"popularity\": 8796\n },\n {\n \"tag\": "panoptic",\n \"popularity\": 8787\n },\n {\n \"tag\": "insufferable",\n \"popularity\": 8777\n },\n {\n \"tag\": "salema",\n \"popularity\": 8768\n },\n {\n \"tag\": "Myriapoda",\n \"popularity\": 8758\n },\n {\n \"tag\": "regarrison",\n \"popularity\": 8748\n },\n {\n \"tag\": "overlearned",\n \"popularity\": 8739\n },\n {\n \"tag\": "ultraroyalist conventical bureaucratical",\n \"popularity\": 8729\n },\n {\n \"tag\": "epicaridan",\n \"popularity\": 8720\n },\n {\n \"tag\": "poetastress",\n \"popularity\": 8711\n },\n {\n \"tag\": "monophthalmus",\n \"popularity\": 8701\n },\n {\n \"tag\": "simnel",\n \"popularity\": 8692\n },\n {\n \"tag\": "compotor",\n \"popularity\": 8682\n },\n {\n \"tag\": "hydrolase",\n \"popularity\": 8673\n },\n {\n \"tag\": "attemptless",\n \"popularity\": 8663\n },\n {\n \"tag\": "visceroptosis",\n \"popularity\": 8654\n },\n {\n \"tag\": "unpreparedly",\n \"popularity\": 8645\n },\n {\n \"tag\": "mastage",\n \"popularity\": 8635\n },\n {\n \"tag\": "preinfluence",\n \"popularity\": 8626\n },\n {\n \"tag\": "Siwan",\n \"popularity\": 8617\n },\n {\n \"tag\": "ceratotheca belvedere",\n \"popularity\": 8607\n },\n {\n \"tag\": "disenablement",\n \"popularity\": 8598\n },\n {\n \"tag\": "nine",\n \"popularity\": 8589\n },\n {\n \"tag\": "spellingdown abridgment",\n \"popularity\": 8580\n },\n {\n \"tag\": "twilightless",\n \"popularity\": 8571\n },\n {\n \"tag\": "overflow",\n \"popularity\": 8561\n },\n {\n \"tag\": "mismeasurement",\n \"popularity\": 8552\n },\n {\n \"tag\": "nawabship",\n \"popularity\": 8543\n },\n {\n \"tag\": "Phrynosoma",\n \"popularity\": 8534\n },\n {\n \"tag\": "unanticipatingly",\n \"popularity\": 8525\n },\n {\n \"tag\": "blankite",\n \"popularity\": 8516\n },\n {\n \"tag\": "role",\n \"popularity\": 8506\n },\n {\n \"tag\": "peperine edelweiss",\n \"popularity\": 8497\n },\n {\n \"tag\": "unhysterical",\n \"popularity\": 8488\n },\n {\n \"tag\": "attentiveness",\n \"popularity\": 8479\n },\n {\n \"tag\": "scintillant",\n \"popularity\": 8470\n },\n {\n \"tag\": "stenostomatous",\n \"popularity\": 8461\n },\n {\n \"tag\": "pectinite",\n \"popularity\": 8452\n },\n {\n \"tag\": "herring",\n \"popularity\": 8443\n },\n {\n \"tag\": "interroom",\n \"popularity\": 8434\n },\n {\n \"tag\": "laccol",\n \"popularity\": 8425\n },\n {\n \"tag\": "unpartably kylite",\n \"popularity\": 8416\n },\n {\n \"tag\": "spirivalve",\n \"popularity\": 8407\n },\n {\n \"tag\": "hoosegow",\n \"popularity\": 8398\n },\n {\n \"tag\": "doat",\n \"popularity\": 8389\n },\n {\n \"tag\": "amphibian",\n \"popularity\": 8380\n },\n {\n \"tag\": "exposit",\n \"popularity\": 8371\n },\n {\n \"tag\": "canopy",\n \"popularity\": 8363\n },\n {\n \"tag\": "houndlike",\n \"popularity\": 8354\n },\n {\n \"tag\": "spikebill",\n \"popularity\": 8345\n },\n {\n \"tag\": "wiseacre pyrotechnic",\n \"popularity\": 8336\n },\n {\n \"tag\": "confessingly woodman",\n \"popularity\": 8327\n },\n {\n \"tag\": "overside",\n \"popularity\": 8318\n },\n {\n \"tag\": "oftwhiles",\n \"popularity\": 8310\n },\n {\n \"tag\": "Musophagidae",\n \"popularity\": 8301\n },\n {\n \"tag\": "slumberer",\n \"popularity\": 8292\n },\n {\n \"tag\": "leiotrichy",\n \"popularity\": 8283\n },\n {\n \"tag\": "Mantispidae",\n \"popularity\": 8275\n },\n {\n \"tag\": "perceptually",\n \"popularity\": 8266\n },\n {\n \"tag\": "biller",\n \"popularity\": 8257\n },\n {\n \"tag\": "eudaemonical",\n \"popularity\": 8249\n },\n {\n \"tag\": "underfiend",\n \"popularity\": 8240\n },\n {\n \"tag\": "impartible",\n \"popularity\": 8231\n },\n {\n \"tag\": "saxicavous",\n \"popularity\": 8223\n },\n {\n \"tag\": "yapster",\n \"popularity\": 8214\n },\n {\n \"tag\": "aliseptal",\n \"popularity\": 8205\n },\n {\n \"tag\": "omniparient",\n \"popularity\": 8197\n },\n {\n \"tag\": "nishiki",\n \"popularity\": 8188\n },\n {\n \"tag\": "yuzluk",\n \"popularity\": 8180\n },\n {\n \"tag\": "solderer",\n \"popularity\": 8171\n },\n {\n \"tag\": "Pinna",\n \"popularity\": 8162\n },\n {\n \"tag\": "reinterfere",\n \"popularity\": 8154\n },\n {\n \"tag\": "superepic",\n \"popularity\": 8145\n },\n {\n \"tag\": "ronquil",\n \"popularity\": 8137\n },\n {\n \"tag\": "bratstvo",\n \"popularity\": 8128\n },\n {\n \"tag\": "Thea",\n \"popularity\": 8120\n },\n {\n \"tag\": "hermaphroditical",\n \"popularity\": 8111\n },\n {\n \"tag\": "enlief",\n \"popularity\": 8103\n },\n {\n \"tag\": "Jesuate",\n \"popularity\": 8095\n },\n {\n \"tag\": "gaysome",\n \"popularity\": 8086\n },\n {\n \"tag\": "iliohypogastric",\n \"popularity\": 8078\n },\n {\n \"tag\": "regardance",\n \"popularity\": 8069\n },\n {\n \"tag\": "cumulately",\n \"popularity\": 8061\n },\n {\n \"tag\": "haustorial nucleolocentrosome",\n \"popularity\": 8053\n },\n {\n \"tag\": "cosmocrat",\n \"popularity\": 8044\n },\n {\n \"tag\": "onyxitis",\n \"popularity\": 8036\n },\n {\n \"tag\": "Cabinda",\n \"popularity\": 8028\n },\n {\n \"tag\": "coresort",\n \"popularity\": 8019\n },\n {\n \"tag\": "drusy preformant",\n \"popularity\": 8011\n },\n {\n \"tag\": "piningly",\n \"popularity\": 8003\n },\n {\n \"tag\": "bootlessly",\n \"popularity\": 7994\n },\n {\n \"tag\": "talari",\n \"popularity\": 7986\n },\n {\n \"tag\": "amidoacetal",\n \"popularity\": 7978\n },\n {\n \"tag\": "pschent",\n \"popularity\": 7970\n },\n {\n \"tag\": "consumptional scarer titivate",\n \"popularity\": 7962\n },\n {\n \"tag\": "Anserinae",\n \"popularity\": 7953\n },\n {\n \"tag\": "flaunter",\n \"popularity\": 7945\n },\n {\n \"tag\": "reindeer",\n \"popularity\": 7937\n },\n {\n \"tag\": "disparage",\n \"popularity\": 7929\n },\n {\n \"tag\": "superheat",\n \"popularity\": 7921\n },\n {\n \"tag\": "Chromatium",\n \"popularity\": 7912\n },\n {\n \"tag\": "Tina",\n \"popularity\": 7904\n },\n {\n \"tag\": "rededicatory",\n \"popularity\": 7896\n },\n {\n \"tag\": "nontransient",\n \"popularity\": 7888\n },\n {\n \"tag\": "Phocaean brinkless",\n \"popularity\": 7880\n },\n {\n \"tag\": "ventriculose",\n \"popularity\": 7872\n },\n {\n \"tag\": "upplough",\n \"popularity\": 7864\n },\n {\n \"tag\": "succorless",\n \"popularity\": 7856\n },\n {\n \"tag\": "hayrake",\n \"popularity\": 7848\n },\n {\n \"tag\": "merriness amorphia",\n \"popularity\": 7840\n },\n {\n \"tag\": "merycism",\n \"popularity\": 7832\n },\n {\n \"tag\": "checkrow",\n \"popularity\": 7824\n },\n {\n \"tag\": "scry",\n \"popularity\": 7816\n },\n {\n \"tag\": "obvolve",\n \"popularity\": 7808\n },\n {\n \"tag\": "orchard",\n \"popularity\": 7800\n },\n {\n \"tag\": "isomerize",\n \"popularity\": 7792\n },\n {\n \"tag\": "competitrix",\n \"popularity\": 7784\n },\n {\n \"tag\": "unbannered",\n \"popularity\": 7776\n },\n {\n \"tag\": "undoctrined",\n \"popularity\": 7768\n },\n {\n \"tag\": "theologian",\n \"popularity\": 7760\n },\n {\n \"tag\": "nebby",\n \"popularity\": 7752\n },\n {\n \"tag\": "Cardiazol",\n \"popularity\": 7745\n },\n {\n \"tag\": "phagedenic",\n \"popularity\": 7737\n },\n {\n \"tag\": "nostalgic",\n \"popularity\": 7729\n },\n {\n \"tag\": "orthodoxy",\n \"popularity\": 7721\n },\n {\n \"tag\": "oversanguine",\n \"popularity\": 7713\n },\n {\n \"tag\": "lish",\n \"popularity\": 7705\n },\n {\n \"tag\": "ketogenic",\n \"popularity\": 7698\n },\n {\n \"tag\": "syndicalize",\n \"popularity\": 7690\n },\n {\n \"tag\": "leeftail",\n \"popularity\": 7682\n },\n {\n \"tag\": "bulbomedullary",\n \"popularity\": 7674\n },\n {\n \"tag\": "reletter",\n \"popularity\": 7667\n },\n {\n \"tag\": "bitterly",\n \"popularity\": 7659\n },\n {\n \"tag\": "participatory",\n \"popularity\": 7651\n },\n {\n \"tag\": "baldberry",\n \"popularity\": 7643\n },\n {\n \"tag\": "prowaterpower",\n \"popularity\": 7636\n },\n {\n \"tag\": "lexicographical",\n \"popularity\": 7628\n },\n {\n \"tag\": "Anisodactyli",\n \"popularity\": 7620\n },\n {\n \"tag\": "amphipodous",\n \"popularity\": 7613\n },\n {\n \"tag\": "triglandular",\n \"popularity\": 7605\n },\n {\n \"tag\": "xanthopsin",\n \"popularity\": 7597\n },\n {\n \"tag\": "indefinitude",\n \"popularity\": 7590\n },\n {\n \"tag\": "bookworm",\n \"popularity\": 7582\n },\n {\n \"tag\": "suffocative",\n \"popularity\": 7574\n },\n {\n \"tag\": "uncongested tyrant",\n \"popularity\": 7567\n },\n {\n \"tag\": "alow harmoniously Pamir",\n \"popularity\": 7559\n },\n {\n \"tag\": "monander",\n \"popularity\": 7552\n },\n {\n \"tag\": "bagatelle",\n \"popularity\": 7544\n },\n {\n \"tag\": "membranology",\n \"popularity\": 7537\n },\n {\n \"tag\": "parturifacient",\n \"popularity\": 7529\n },\n {\n \"tag\": "excitovascular",\n \"popularity\": 7522\n },\n {\n \"tag\": "homopolar",\n \"popularity\": 7514\n },\n {\n \"tag\": "phobiac",\n \"popularity\": 7507\n },\n {\n \"tag\": "clype",\n \"popularity\": 7499\n },\n {\n \"tag\": "unsubversive",\n \"popularity\": 7492\n },\n {\n \"tag\": "bostrychoidal scorpionwort",\n \"popularity\": 7484\n },\n {\n \"tag\": "biliteralism",\n \"popularity\": 7477\n },\n {\n \"tag\": "dentatocostate",\n \"popularity\": 7469\n },\n {\n \"tag\": "Pici",\n \"popularity\": 7462\n },\n {\n \"tag\": "sideritic",\n \"popularity\": 7454\n },\n {\n \"tag\": "syntaxis",\n \"popularity\": 7447\n },\n {\n \"tag\": "ingest",\n \"popularity\": 7440\n },\n {\n \"tag\": "rigmarolish",\n \"popularity\": 7432\n },\n {\n \"tag\": "ocreaceous",\n \"popularity\": 7425\n },\n {\n \"tag\": "hyperbrachyskelic",\n \"popularity\": 7418\n },\n {\n \"tag\": "basophobia",\n \"popularity\": 7410\n },\n {\n \"tag\": "substantialness",\n \"popularity\": 7403\n },\n {\n \"tag\": "agglutinoid",\n \"popularity\": 7396\n },\n {\n \"tag\": "longleaf",\n \"popularity\": 7388\n },\n {\n \"tag\": "electroengraving",\n \"popularity\": 7381\n },\n {\n \"tag\": "laparoenterotomy",\n \"popularity\": 7374\n },\n {\n \"tag\": "oxalylurea",\n \"popularity\": 7366\n },\n {\n \"tag\": "unattaintedly",\n \"popularity\": 7359\n },\n {\n \"tag\": "pennystone",\n \"popularity\": 7352\n },\n {\n \"tag\": "Plumbaginaceae",\n \"popularity\": 7345\n },\n {\n \"tag\": "horntip",\n \"popularity\": 7337\n },\n {\n \"tag\": "begrudge",\n \"popularity\": 7330\n },\n {\n \"tag\": "bechignoned",\n \"popularity\": 7323\n },\n {\n \"tag\": "hologonidium",\n \"popularity\": 7316\n },\n {\n \"tag\": "Pulian",\n \"popularity\": 7309\n },\n {\n \"tag\": "gratulation",\n \"popularity\": 7301\n },\n {\n \"tag\": "Sebright",\n \"popularity\": 7294\n },\n {\n \"tag\": "coinstantaneous emotionally",\n \"popularity\": 7287\n },\n {\n \"tag\": "thoracostracan",\n \"popularity\": 7280\n },\n {\n \"tag\": "saurodont",\n \"popularity\": 7273\n },\n {\n \"tag\": "coseat",\n \"popularity\": 7266\n },\n {\n \"tag\": "irascibility",\n \"popularity\": 7259\n },\n {\n \"tag\": "occlude",\n \"popularity\": 7251\n },\n {\n \"tag\": "metallurgist",\n \"popularity\": 7244\n },\n {\n \"tag\": "extraviolet",\n \"popularity\": 7237\n },\n {\n \"tag\": "clinic",\n \"popularity\": 7230\n },\n {\n \"tag\": "skater",\n \"popularity\": 7223\n },\n {\n \"tag\": "linguistic",\n \"popularity\": 7216\n },\n {\n \"tag\": "attacheship",\n \"popularity\": 7209\n },\n {\n \"tag\": "Rachianectes",\n \"popularity\": 7202\n },\n {\n \"tag\": "foliolose",\n \"popularity\": 7195\n },\n {\n \"tag\": "claudetite",\n \"popularity\": 7188\n },\n {\n \"tag\": "aphidian scratching",\n \"popularity\": 7181\n },\n {\n \"tag\": "Carida",\n \"popularity\": 7174\n },\n {\n \"tag\": "tiepin polymicroscope",\n \"popularity\": 7167\n },\n {\n \"tag\": "telpherage",\n \"popularity\": 7160\n },\n {\n \"tag\": "meek",\n \"popularity\": 7153\n },\n {\n \"tag\": "swiftness",\n \"popularity\": 7146\n },\n {\n \"tag\": "gentes",\n \"popularity\": 7139\n },\n {\n \"tag\": "uncommemorated",\n \"popularity\": 7132\n },\n {\n \"tag\": "Lazarus",\n \"popularity\": 7125\n },\n {\n \"tag\": "redivive",\n \"popularity\": 7119\n },\n {\n \"tag\": "nonfebrile",\n \"popularity\": 7112\n },\n {\n \"tag\": "nymphet",\n \"popularity\": 7105\n },\n {\n \"tag\": "areologically",\n \"popularity\": 7098\n },\n {\n \"tag\": "undonkey",\n \"popularity\": 7091\n },\n {\n \"tag\": "projecting",\n \"popularity\": 7084\n },\n {\n \"tag\": "pinnigrade",\n \"popularity\": 7077\n },\n {\n \"tag\": "butylation",\n \"popularity\": 7071\n },\n {\n \"tag\": "philologistic lenticle",\n \"popularity\": 7064\n },\n {\n \"tag\": "nooky",\n \"popularity\": 7057\n },\n {\n \"tag\": "incestuousness",\n \"popularity\": 7050\n },\n {\n \"tag\": "palingenetically",\n \"popularity\": 7043\n },\n {\n \"tag\": "mitochondria",\n \"popularity\": 7037\n },\n {\n \"tag\": "truthify",\n \"popularity\": 7030\n },\n {\n \"tag\": "titanyl",\n \"popularity\": 7023\n },\n {\n \"tag\": "bestride",\n \"popularity\": 7016\n },\n {\n \"tag\": "chende",\n \"popularity\": 7010\n },\n {\n \"tag\": "Chaucerian monophote",\n \"popularity\": 7003\n },\n {\n \"tag\": "cutback",\n \"popularity\": 6996\n },\n {\n \"tag\": "unpatiently",\n \"popularity\": 6989\n },\n {\n \"tag\": "subvitreous",\n \"popularity\": 6983\n },\n {\n \"tag\": "organizable",\n \"popularity\": 6976\n },\n {\n \"tag\": "anniverse uncomprehensible",\n \"popularity\": 6969\n },\n {\n \"tag\": "hyalescence",\n \"popularity\": 6963\n },\n {\n \"tag\": "amniochorial",\n \"popularity\": 6956\n },\n {\n \"tag\": "Corybantian",\n \"popularity\": 6949\n },\n {\n \"tag\": "genocide Scaphitidae",\n \"popularity\": 6943\n },\n {\n \"tag\": "accordionist",\n \"popularity\": 6936\n },\n {\n \"tag\": "becheck",\n \"popularity\": 6930\n },\n {\n \"tag\": "overproduce",\n \"popularity\": 6923\n },\n {\n \"tag\": "unmaniac frijolillo",\n \"popularity\": 6916\n },\n {\n \"tag\": "multisulcated",\n \"popularity\": 6910\n },\n {\n \"tag\": "wennebergite",\n \"popularity\": 6903\n },\n {\n \"tag\": "tautousious mowth",\n \"popularity\": 6897\n },\n {\n \"tag\": "marigold",\n \"popularity\": 6890\n },\n {\n \"tag\": "affray",\n \"popularity\": 6884\n },\n {\n \"tag\": "nonidolatrous",\n \"popularity\": 6877\n },\n {\n \"tag\": "aphrasia",\n \"popularity\": 6871\n },\n {\n \"tag\": "muddlingly",\n \"popularity\": 6864\n },\n {\n \"tag\": "clear",\n \"popularity\": 6858\n },\n {\n \"tag\": "Clitoria",\n \"popularity\": 6851\n },\n {\n \"tag\": "apportionment underwaist",\n \"popularity\": 6845\n },\n {\n \"tag\": "kodakist",\n \"popularity\": 6838\n },\n {\n \"tag\": "Momotidae",\n \"popularity\": 6832\n },\n {\n \"tag\": "cryptovalency",\n \"popularity\": 6825\n },\n {\n \"tag\": "floe",\n \"popularity\": 6819\n },\n {\n \"tag\": "aphagia",\n \"popularity\": 6812\n },\n {\n \"tag\": "brontograph",\n \"popularity\": 6806\n },\n {\n \"tag\": "tubulous",\n \"popularity\": 6799\n },\n {\n \"tag\": "unhorse",\n \"popularity\": 6793\n },\n {\n \"tag\": "chlordane",\n \"popularity\": 6787\n },\n {\n \"tag\": "colloquy brochan",\n \"popularity\": 6780\n },\n {\n \"tag\": "sloosh",\n \"popularity\": 6774\n },\n {\n \"tag\": "battered",\n \"popularity\": 6767\n },\n {\n \"tag\": "monocularity pluriguttulate",\n \"popularity\": 6761\n },\n {\n \"tag\": "chiastoneury",\n \"popularity\": 6755\n },\n {\n \"tag\": "Sanguinaria",\n \"popularity\": 6748\n },\n {\n \"tag\": "confessionary",\n \"popularity\": 6742\n },\n {\n \"tag\": "enzymic",\n \"popularity\": 6736\n },\n {\n \"tag\": "cord",\n \"popularity\": 6729\n },\n {\n \"tag\": "oviducal",\n \"popularity\": 6723\n },\n {\n \"tag\": "crozzle outsea",\n \"popularity\": 6717\n },\n {\n \"tag\": "balladical",\n \"popularity\": 6710\n },\n {\n \"tag\": "uncollectibleness",\n \"popularity\": 6704\n },\n {\n \"tag\": "predorsal",\n \"popularity\": 6698\n },\n {\n \"tag\": "reauthenticate",\n \"popularity\": 6692\n },\n {\n \"tag\": "ravissant",\n \"popularity\": 6685\n },\n {\n \"tag\": "advantageousness",\n \"popularity\": 6679\n },\n {\n \"tag\": "rung",\n \"popularity\": 6673\n },\n {\n \"tag\": "duncedom",\n \"popularity\": 6667\n },\n {\n \"tag\": "hematolite",\n \"popularity\": 6660\n },\n {\n \"tag\": "thisness",\n \"popularity\": 6654\n },\n {\n \"tag\": "mapau",\n \"popularity\": 6648\n },\n {\n \"tag\": "Hecatic",\n \"popularity\": 6642\n },\n {\n \"tag\": "meningoencephalocele",\n \"popularity\": 6636\n },\n {\n \"tag\": "confection sorra",\n \"popularity\": 6630\n },\n {\n \"tag\": "unsedate",\n \"popularity\": 6623\n },\n {\n \"tag\": "meningocerebritis",\n \"popularity\": 6617\n },\n {\n \"tag\": "biopsychological",\n \"popularity\": 6611\n },\n {\n \"tag\": "clavicithern",\n \"popularity\": 6605\n },\n {\n \"tag\": "resun",\n \"popularity\": 6599\n },\n {\n \"tag\": "bayamo",\n \"popularity\": 6593\n },\n {\n \"tag\": "seeableness",\n \"popularity\": 6587\n },\n {\n \"tag\": "hypsidolichocephalism",\n \"popularity\": 6581\n },\n {\n \"tag\": "salivous",\n \"popularity\": 6574\n },\n {\n \"tag\": "neumatize",\n \"popularity\": 6568\n },\n {\n \"tag\": "stree",\n \"popularity\": 6562\n },\n {\n \"tag\": "markshot",\n \"popularity\": 6556\n },\n {\n \"tag\": "phraseologically",\n \"popularity\": 6550\n },\n {\n \"tag\": "yealing",\n \"popularity\": 6544\n },\n {\n \"tag\": "puggy",\n \"popularity\": 6538\n },\n {\n \"tag\": "sexadecimal",\n \"popularity\": 6532\n },\n {\n \"tag\": "unofficerlike",\n \"popularity\": 6526\n },\n {\n \"tag\": "curiosa",\n \"popularity\": 6520\n },\n {\n \"tag\": "pedomotor",\n \"popularity\": 6514\n },\n {\n \"tag\": "astrally",\n \"popularity\": 6508\n },\n {\n \"tag\": "prosomatic",\n \"popularity\": 6502\n },\n {\n \"tag\": "bulletheaded",\n \"popularity\": 6496\n },\n {\n \"tag\": "fortuned",\n \"popularity\": 6490\n },\n {\n \"tag\": "pixy",\n \"popularity\": 6484\n },\n {\n \"tag\": "protectrix",\n \"popularity\": 6478\n },\n {\n \"tag\": "arthritical",\n \"popularity\": 6472\n },\n {\n \"tag\": "coction",\n \"popularity\": 6466\n },\n {\n \"tag\": "Anthropos",\n \"popularity\": 6460\n },\n {\n \"tag\": "runer",\n \"popularity\": 6454\n },\n {\n \"tag\": "prenotify",\n \"popularity\": 6449\n },\n {\n \"tag\": "microspheric gastroparalysis",\n \"popularity\": 6443\n },\n {\n \"tag\": "Jovicentrical",\n \"popularity\": 6437\n },\n {\n \"tag\": "ceratopsid",\n \"popularity\": 6431\n },\n {\n \"tag\": "Theodoric",\n \"popularity\": 6425\n },\n {\n \"tag\": "Pactolus",\n \"popularity\": 6419\n },\n {\n \"tag\": "spawning",\n \"popularity\": 6413\n },\n {\n \"tag\": "nonconfidential",\n \"popularity\": 6407\n },\n {\n \"tag\": "halotrichite infumate",\n \"popularity\": 6402\n },\n {\n \"tag\": "undiscriminatingly",\n \"popularity\": 6396\n },\n {\n \"tag\": "unexasperated",\n \"popularity\": 6390\n },\n {\n \"tag\": "isoeugenol",\n \"popularity\": 6384\n },\n {\n \"tag\": "pressboard",\n \"popularity\": 6378\n },\n {\n \"tag\": "unshrew",\n \"popularity\": 6372\n },\n {\n \"tag\": "huffingly",\n \"popularity\": 6367\n },\n {\n \"tag\": "wagaun",\n \"popularity\": 6361\n },\n {\n \"tag\": "squirt Philistine",\n \"popularity\": 6355\n },\n {\n \"tag\": "kryptic",\n \"popularity\": 6349\n },\n {\n \"tag\": "paraform",\n \"popularity\": 6344\n },\n {\n \"tag\": "preverify",\n \"popularity\": 6338\n },\n {\n \"tag\": "dalar",\n \"popularity\": 6332\n },\n {\n \"tag\": "interdictor appraisingly",\n \"popularity\": 6326\n },\n {\n \"tag\": "chipped",\n \"popularity\": 6321\n },\n {\n \"tag\": "Pteropoda",\n \"popularity\": 6315\n },\n {\n \"tag\": "Bohairic",\n \"popularity\": 6309\n },\n {\n \"tag\": "felting",\n \"popularity\": 6303\n },\n {\n \"tag\": "compurgatorial",\n \"popularity\": 6298\n },\n {\n \"tag\": "unclead",\n \"popularity\": 6292\n },\n {\n \"tag\": "stockish",\n \"popularity\": 6286\n },\n {\n \"tag\": "mulligatawny",\n \"popularity\": 6281\n },\n {\n \"tag\": "Monotheletism",\n \"popularity\": 6275\n },\n {\n \"tag\": "lutanist",\n \"popularity\": 6269\n },\n {\n \"tag\": "gluttonize",\n \"popularity\": 6264\n },\n {\n \"tag\": "hackneyed",\n \"popularity\": 6258\n },\n {\n \"tag\": "yield",\n \"popularity\": 6253\n },\n {\n \"tag\": "sulphonamido",\n \"popularity\": 6247\n },\n {\n \"tag\": "granulative",\n \"popularity\": 6241\n },\n {\n \"tag\": "swingy",\n \"popularity\": 6236\n },\n {\n \"tag\": "Desmidiales",\n \"popularity\": 6230\n },\n {\n \"tag\": "tootlish",\n \"popularity\": 6224\n },\n {\n \"tag\": "unsatisfiedly",\n \"popularity\": 6219\n },\n {\n \"tag\": "burucha",\n \"popularity\": 6213\n },\n {\n \"tag\": "premeditatingly",\n \"popularity\": 6208\n },\n {\n \"tag\": "cowrie",\n \"popularity\": 6202\n },\n {\n \"tag\": "pleurolysis",\n \"popularity\": 6197\n },\n {\n \"tag\": "nationalist",\n \"popularity\": 6191\n },\n {\n \"tag\": "Pholadacea",\n \"popularity\": 6186\n },\n {\n \"tag\": "anakrousis",\n \"popularity\": 6180\n },\n {\n \"tag\": "proctorial",\n \"popularity\": 6175\n },\n {\n \"tag\": "cavillation",\n \"popularity\": 6169\n },\n {\n \"tag\": "cervicobregmatic",\n \"popularity\": 6163\n },\n {\n \"tag\": "interspecific",\n \"popularity\": 6158\n },\n {\n \"tag\": "Teutonity",\n \"popularity\": 6152\n },\n {\n \"tag\": "snakeholing",\n \"popularity\": 6147\n },\n {\n \"tag\": "balcony",\n \"popularity\": 6142\n },\n {\n \"tag\": "latchless",\n \"popularity\": 6136\n },\n {\n \"tag\": "Mithraea",\n \"popularity\": 6131\n },\n {\n \"tag\": "pseudepigraph",\n \"popularity\": 6125\n },\n {\n \"tag\": "flosser",\n \"popularity\": 6120\n },\n {\n \"tag\": "kotyle",\n \"popularity\": 6114\n },\n {\n \"tag\": "outdo",\n \"popularity\": 6109\n },\n {\n \"tag\": "interclerical",\n \"popularity\": 6103\n },\n {\n \"tag\": "aurar",\n \"popularity\": 6098\n },\n {\n \"tag\": "apophyseal",\n \"popularity\": 6093\n },\n {\n \"tag\": "Miro",\n \"popularity\": 6087\n },\n {\n \"tag\": "Priscillian",\n \"popularity\": 6082\n },\n {\n \"tag\": "alluvia",\n \"popularity\": 6076\n },\n {\n \"tag\": "exordize",\n \"popularity\": 6071\n },\n {\n \"tag\": "breakage",\n \"popularity\": 6066\n },\n {\n \"tag\": "unclosable",\n \"popularity\": 6060\n },\n {\n \"tag\": "monocondylous",\n \"popularity\": 6055\n },\n {\n \"tag\": "dyarchy",\n \"popularity\": 6050\n },\n {\n \"tag\": "subchelate",\n \"popularity\": 6044\n },\n {\n \"tag\": "hearsay",\n \"popularity\": 6039\n },\n {\n \"tag\": "prestigiously",\n \"popularity\": 6034\n },\n {\n \"tag\": "unimuscular",\n \"popularity\": 6028\n },\n {\n \"tag\": "lingwort",\n \"popularity\": 6023\n },\n {\n \"tag\": "jealous",\n \"popularity\": 6018\n },\n {\n \"tag\": "artilleryman",\n \"popularity\": 6012\n },\n {\n \"tag\": "phantasmagorially",\n \"popularity\": 6007\n },\n {\n \"tag\": "stagnum",\n \"popularity\": 6002\n },\n {\n \"tag\": "organotropism shatteringly",\n \"popularity\": 5997\n },\n {\n \"tag\": "Mytilus Hebraist",\n \"popularity\": 5991\n },\n {\n \"tag\": "returf",\n \"popularity\": 5986\n },\n {\n \"tag\": "townfolk",\n \"popularity\": 5981\n },\n {\n \"tag\": "propitiative",\n \"popularity\": 5976\n },\n {\n \"tag\": "Anita unsullied",\n \"popularity\": 5970\n },\n {\n \"tag\": "bandoleered",\n \"popularity\": 5965\n },\n {\n \"tag\": "cubby",\n \"popularity\": 5960\n },\n {\n \"tag\": "Hexanchus",\n \"popularity\": 5955\n },\n {\n \"tag\": "circuminsular",\n \"popularity\": 5949\n },\n {\n \"tag\": "chamberletted eumycete",\n \"popularity\": 5944\n },\n {\n \"tag\": "secure",\n \"popularity\": 5939\n },\n {\n \"tag\": "Edwardean",\n \"popularity\": 5934\n },\n {\n \"tag\": "strenth",\n \"popularity\": 5929\n },\n {\n \"tag\": "exhaustless",\n \"popularity\": 5923\n },\n {\n \"tag\": "electioneerer",\n \"popularity\": 5918\n },\n {\n \"tag\": "estoile",\n \"popularity\": 5913\n },\n {\n \"tag\": "redden",\n \"popularity\": 5908\n },\n {\n \"tag\": "solicitee",\n \"popularity\": 5903\n },\n {\n \"tag\": "nonpatented",\n \"popularity\": 5898\n },\n {\n \"tag\": "lemming",\n \"popularity\": 5893\n },\n {\n \"tag\": "marled subalate",\n \"popularity\": 5887\n },\n {\n \"tag\": "premial horizonward",\n \"popularity\": 5882\n },\n {\n \"tag\": "nonrefueling",\n \"popularity\": 5877\n },\n {\n \"tag\": "rupturewort",\n \"popularity\": 5872\n },\n {\n \"tag\": "unfed",\n \"popularity\": 5867\n },\n {\n \"tag\": "empanelment",\n \"popularity\": 5862\n },\n {\n \"tag\": "isoosmosis",\n \"popularity\": 5857\n },\n {\n \"tag\": "jipijapa",\n \"popularity\": 5852\n },\n {\n \"tag\": "Fiji",\n \"popularity\": 5847\n },\n {\n \"tag\": "interferant",\n \"popularity\": 5842\n },\n {\n \"tag\": "reconstitution",\n \"popularity\": 5837\n },\n {\n \"tag\": "dockyardman",\n \"popularity\": 5832\n },\n {\n \"tag\": "dolichopodous",\n \"popularity\": 5826\n },\n {\n \"tag\": "whiteworm",\n \"popularity\": 5821\n },\n {\n \"tag\": "atheistically",\n \"popularity\": 5816\n },\n {\n \"tag\": "nonconcern",\n \"popularity\": 5811\n },\n {\n \"tag\": "scarabaeidoid",\n \"popularity\": 5806\n },\n {\n \"tag\": "triumviri",\n \"popularity\": 5801\n },\n {\n \"tag\": "rakit",\n \"popularity\": 5796\n },\n {\n \"tag\": "leecheater",\n \"popularity\": 5791\n },\n {\n \"tag\": "Arthrostraca",\n \"popularity\": 5786\n },\n {\n \"tag\": "upknit",\n \"popularity\": 5781\n },\n {\n \"tag\": "tymbalon",\n \"popularity\": 5776\n },\n {\n \"tag\": "inventurous",\n \"popularity\": 5771\n },\n {\n \"tag\": "perradiate",\n \"popularity\": 5766\n },\n {\n \"tag\": "seer",\n \"popularity\": 5762\n },\n {\n \"tag\": "Auricularia",\n \"popularity\": 5757\n },\n {\n \"tag\": "wettish exclusivity",\n \"popularity\": 5752\n },\n {\n \"tag\": "arteriosympathectomy",\n \"popularity\": 5747\n },\n {\n \"tag\": "tunlike",\n \"popularity\": 5742\n },\n {\n \"tag\": "cephalocercal",\n \"popularity\": 5737\n },\n {\n \"tag\": "meaninglessness",\n \"popularity\": 5732\n },\n {\n \"tag\": "fountful",\n \"popularity\": 5727\n },\n {\n \"tag\": "appraisement",\n \"popularity\": 5722\n },\n {\n \"tag\": "geniculated",\n \"popularity\": 5717\n },\n {\n \"tag\": "rotator",\n \"popularity\": 5712\n },\n {\n \"tag\": "foremarch biography",\n \"popularity\": 5707\n },\n {\n \"tag\": "arid",\n \"popularity\": 5703\n },\n {\n \"tag\": "inapprehensible",\n \"popularity\": 5698\n },\n {\n \"tag\": "chlorosulphonic",\n \"popularity\": 5693\n },\n {\n \"tag\": "braguette",\n \"popularity\": 5688\n },\n {\n \"tag\": "panophthalmitis",\n \"popularity\": 5683\n },\n {\n \"tag\": "pro objurgatorily",\n \"popularity\": 5678\n },\n {\n \"tag\": "zooplasty",\n \"popularity\": 5673\n },\n {\n \"tag\": "Terebratulidae",\n \"popularity\": 5669\n },\n {\n \"tag\": "Mahran",\n \"popularity\": 5664\n },\n {\n \"tag\": "anthologize merocele",\n \"popularity\": 5659\n },\n {\n \"tag\": "firecracker chiropractic",\n \"popularity\": 5654\n },\n {\n \"tag\": "tenorist",\n \"popularity\": 5649\n },\n {\n \"tag\": "amphitene",\n \"popularity\": 5645\n },\n {\n \"tag\": "silverbush toadstone",\n \"popularity\": 5640\n },\n {\n \"tag\": "entozoological",\n \"popularity\": 5635\n },\n {\n \"tag\": "trustlessness",\n \"popularity\": 5630\n },\n {\n \"tag\": "reassay",\n \"popularity\": 5625\n },\n {\n \"tag\": "chrysalides",\n \"popularity\": 5621\n },\n {\n \"tag\": "truncation",\n \"popularity\": 5616\n },\n {\n \"tag\": "unwavered mausoleal",\n \"popularity\": 5611\n },\n {\n \"tag\": "unserrated",\n \"popularity\": 5606\n },\n {\n \"tag\": "frampler",\n \"popularity\": 5602\n },\n {\n \"tag\": "celestial",\n \"popularity\": 5597\n },\n {\n \"tag\": "depreter",\n \"popularity\": 5592\n },\n {\n \"tag\": "retaliate",\n \"popularity\": 5588\n },\n {\n \"tag\": "decempunctate",\n \"popularity\": 5583\n },\n {\n \"tag\": "submitter",\n \"popularity\": 5578\n },\n {\n \"tag\": "phenothiazine",\n \"popularity\": 5573\n },\n {\n \"tag\": "hobbledehoyish",\n \"popularity\": 5569\n },\n {\n \"tag\": "erraticness",\n \"popularity\": 5564\n },\n {\n \"tag\": "ovariodysneuria",\n \"popularity\": 5559\n },\n {\n \"tag\": "puja",\n \"popularity\": 5555\n },\n {\n \"tag\": "cesspool",\n \"popularity\": 5550\n },\n {\n \"tag\": "sonation",\n \"popularity\": 5545\n },\n {\n \"tag\": "moggan",\n \"popularity\": 5541\n },\n {\n \"tag\": "overjutting",\n \"popularity\": 5536\n },\n {\n \"tag\": "cohobate",\n \"popularity\": 5531\n },\n {\n \"tag\": "Distoma",\n \"popularity\": 5527\n },\n {\n \"tag\": "Plectognathi",\n \"popularity\": 5522\n },\n {\n \"tag\": "dumple caliphate",\n \"popularity\": 5517\n },\n {\n \"tag\": "shiko",\n \"popularity\": 5513\n },\n {\n \"tag\": "downness",\n \"popularity\": 5508\n },\n {\n \"tag\": "whippletree",\n \"popularity\": 5504\n },\n {\n \"tag\": "nymphaeum",\n \"popularity\": 5499\n },\n {\n \"tag\": "there trest",\n \"popularity\": 5494\n },\n {\n \"tag\": "psychrometer",\n \"popularity\": 5490\n },\n {\n \"tag\": "pyelograph",\n \"popularity\": 5485\n },\n {\n \"tag\": "unsalvable",\n \"popularity\": 5481\n },\n {\n \"tag\": "bescreen",\n \"popularity\": 5476\n },\n {\n \"tag\": "cushy",\n \"popularity\": 5471\n },\n {\n \"tag\": "plicatolobate",\n \"popularity\": 5467\n },\n {\n \"tag\": "lakie",\n \"popularity\": 5462\n },\n {\n \"tag\": "anthropodeoxycholic",\n \"popularity\": 5458\n },\n {\n \"tag\": "resatisfaction",\n \"popularity\": 5453\n },\n {\n \"tag\": "unravelment unaccidental",\n \"popularity\": 5449\n },\n {\n \"tag\": "telewriter monogeneous",\n \"popularity\": 5444\n },\n {\n \"tag\": "unsabred",\n \"popularity\": 5440\n },\n {\n \"tag\": "startlingly",\n \"popularity\": 5435\n },\n {\n \"tag\": "Aralia",\n \"popularity\": 5431\n },\n {\n \"tag\": "alamonti",\n \"popularity\": 5426\n },\n {\n \"tag\": "Franklinization",\n \"popularity\": 5422\n },\n {\n \"tag\": "parliament",\n \"popularity\": 5417\n },\n {\n \"tag\": "schoolkeeper",\n \"popularity\": 5413\n },\n {\n \"tag\": "nonsociety",\n \"popularity\": 5408\n },\n {\n \"tag\": "parenthetic",\n \"popularity\": 5404\n },\n {\n \"tag\": "stog",\n \"popularity\": 5399\n },\n {\n \"tag\": "Pristipomidae",\n \"popularity\": 5395\n },\n {\n \"tag\": "exocarp",\n \"popularity\": 5390\n },\n {\n \"tag\": "monaxonial",\n \"popularity\": 5386\n },\n {\n \"tag\": "tramroad",\n \"popularity\": 5381\n },\n {\n \"tag\": "hookah",\n \"popularity\": 5377\n },\n {\n \"tag\": "saccharonic",\n \"popularity\": 5372\n },\n {\n \"tag\": "perimetrium",\n \"popularity\": 5368\n },\n {\n \"tag\": "libelluloid",\n \"popularity\": 5364\n },\n {\n \"tag\": "overrunningly",\n \"popularity\": 5359\n },\n {\n \"tag\": "untwister",\n \"popularity\": 5355\n },\n {\n \"tag\": "ninnyhammer",\n \"popularity\": 5350\n },\n {\n \"tag\": "metranate",\n \"popularity\": 5346\n },\n {\n \"tag\": "sarcoblast",\n \"popularity\": 5341\n },\n {\n \"tag\": "porkish",\n \"popularity\": 5337\n },\n {\n \"tag\": "chauvinistic",\n \"popularity\": 5333\n },\n {\n \"tag\": "sexagesimal",\n \"popularity\": 5328\n },\n {\n \"tag\": "hematogenic",\n \"popularity\": 5324\n },\n {\n \"tag\": "selfpreservatory",\n \"popularity\": 5320\n },\n {\n \"tag\": "myelauxe",\n \"popularity\": 5315\n },\n {\n \"tag\": "triply",\n \"popularity\": 5311\n },\n {\n \"tag\": "metaphysicous",\n \"popularity\": 5306\n },\n {\n \"tag\": "vitrinoid",\n \"popularity\": 5302\n },\n {\n \"tag\": "glabellae",\n \"popularity\": 5298\n },\n {\n \"tag\": "moonlighter",\n \"popularity\": 5293\n },\n {\n \"tag\": "monotheistically epexegetical",\n \"popularity\": 5289\n },\n {\n \"tag\": "pseudolateral",\n \"popularity\": 5285\n },\n {\n \"tag\": "heptamethylene",\n \"popularity\": 5280\n },\n {\n \"tag\": "salvadora",\n \"popularity\": 5276\n },\n {\n \"tag\": "unjovial diphenylthiourea",\n \"popularity\": 5272\n },\n {\n \"tag\": "thievishness",\n \"popularity\": 5268\n },\n {\n \"tag\": "unridable",\n \"popularity\": 5263\n },\n {\n \"tag\": "underhandedly",\n \"popularity\": 5259\n },\n {\n \"tag\": "fungiform",\n \"popularity\": 5255\n },\n {\n \"tag\": "scruffle",\n \"popularity\": 5250\n },\n {\n \"tag\": "preindisposition",\n \"popularity\": 5246\n },\n {\n \"tag\": "Amadis",\n \"popularity\": 5242\n },\n {\n \"tag\": "Culex",\n \"popularity\": 5238\n },\n {\n \"tag\": "churning",\n \"popularity\": 5233\n },\n {\n \"tag\": "imperite",\n \"popularity\": 5229\n },\n {\n \"tag\": "levorotation",\n \"popularity\": 5225\n },\n {\n \"tag\": "barbate",\n \"popularity\": 5221\n },\n {\n \"tag\": "knotwort",\n \"popularity\": 5216\n },\n {\n \"tag\": "gypsiferous",\n \"popularity\": 5212\n },\n {\n \"tag\": "tourmalinic",\n \"popularity\": 5208\n },\n {\n \"tag\": "helleboric",\n \"popularity\": 5204\n },\n {\n \"tag\": "pneumograph",\n \"popularity\": 5199\n },\n {\n \"tag\": "Peltigeraceae",\n \"popularity\": 5195\n },\n {\n \"tag\": "busine",\n \"popularity\": 5191\n },\n {\n \"tag\": "Ailuridae",\n \"popularity\": 5187\n },\n {\n \"tag\": "azotate",\n \"popularity\": 5183\n },\n {\n \"tag\": "unlikable",\n \"popularity\": 5178\n },\n {\n \"tag\": "sloyd",\n \"popularity\": 5174\n },\n {\n \"tag\": "biblioclasm",\n \"popularity\": 5170\n },\n {\n \"tag\": "Seres",\n \"popularity\": 5166\n },\n {\n \"tag\": "unaccurateness",\n \"popularity\": 5162\n },\n {\n \"tag\": "scrollwise",\n \"popularity\": 5157\n },\n {\n \"tag\": "flandowser",\n \"popularity\": 5153\n },\n {\n \"tag\": "unblackened",\n \"popularity\": 5149\n },\n {\n \"tag\": "schistosternia",\n \"popularity\": 5145\n },\n {\n \"tag\": "fuse",\n \"popularity\": 5141\n },\n {\n \"tag\": "narthecal",\n \"popularity\": 5137\n },\n {\n \"tag\": "Cueva",\n \"popularity\": 5133\n },\n {\n \"tag\": "appositeness",\n \"popularity\": 5128\n },\n {\n \"tag\": "proindustrial",\n \"popularity\": 5124\n },\n {\n \"tag\": "dermatorrhoea",\n \"popularity\": 5120\n },\n {\n \"tag\": "oxyurous tendential",\n \"popularity\": 5116\n },\n {\n \"tag\": "isopurpurin",\n \"popularity\": 5112\n },\n {\n \"tag\": "impose",\n \"popularity\": 5108\n },\n {\n \"tag\": "wordsmanship",\n \"popularity\": 5104\n },\n {\n \"tag\": "saturator",\n \"popularity\": 5100\n },\n {\n \"tag\": "Nordicity",\n \"popularity\": 5096\n },\n {\n \"tag\": "interaccuse",\n \"popularity\": 5092\n },\n {\n \"tag\": "acridinic",\n \"popularity\": 5087\n },\n {\n \"tag\": "scholion",\n \"popularity\": 5083\n },\n {\n \"tag\": "pseudoaconitine",\n \"popularity\": 5079\n },\n {\n \"tag\": "doctorial",\n \"popularity\": 5075\n },\n {\n \"tag\": "Etchimin",\n \"popularity\": 5071\n },\n {\n \"tag\": "oliviform",\n \"popularity\": 5067\n },\n {\n \"tag\": "Pele",\n \"popularity\": 5063\n },\n {\n \"tag\": "Chiromantis Progymnasium",\n \"popularity\": 5059\n },\n {\n \"tag\": "toxosis",\n \"popularity\": 5055\n },\n {\n \"tag\": "spadilla",\n \"popularity\": 5051\n },\n {\n \"tag\": "Actinopterygii",\n \"popularity\": 5047\n },\n {\n \"tag\": "untiring",\n \"popularity\": 5043\n },\n {\n \"tag\": "butyral",\n \"popularity\": 5039\n },\n {\n \"tag\": "Gymnoderinae",\n \"popularity\": 5035\n },\n {\n \"tag\": "testudo",\n \"popularity\": 5031\n },\n {\n \"tag\": "frigorify",\n \"popularity\": 5027\n },\n {\n \"tag\": "aliency",\n \"popularity\": 5023\n },\n {\n \"tag\": "jargon",\n \"popularity\": 5019\n },\n {\n \"tag\": "counterservice",\n \"popularity\": 5015\n },\n {\n \"tag\": "isostrychnine",\n \"popularity\": 5011\n },\n {\n \"tag\": "tellership",\n \"popularity\": 5007\n },\n {\n \"tag\": "miscegenetic",\n \"popularity\": 5003\n },\n {\n \"tag\": "sorcer",\n \"popularity\": 4999\n },\n {\n \"tag\": "tilewright",\n \"popularity\": 4995\n },\n {\n \"tag\": "cyanoplastid",\n \"popularity\": 4991\n },\n {\n \"tag\": "fluxionally",\n \"popularity\": 4987\n },\n {\n \"tag\": "proudhearted",\n \"popularity\": 4983\n },\n {\n \"tag\": "blithely",\n \"popularity\": 4979\n },\n {\n \"tag\": "jestproof",\n \"popularity\": 4975\n },\n {\n \"tag\": "jestwise",\n \"popularity\": 4971\n },\n {\n \"tag\": "nonassimilable",\n \"popularity\": 4967\n },\n {\n \"tag\": "compurgation",\n \"popularity\": 4964\n },\n {\n \"tag\": "unhate",\n \"popularity\": 4960\n },\n {\n \"tag\": "haplodonty",\n \"popularity\": 4956\n },\n {\n \"tag\": "cardholder",\n \"popularity\": 4952\n },\n {\n \"tag\": "rainlight megohmmeter overstout",\n \"popularity\": 4948\n },\n {\n \"tag\": "itchless",\n \"popularity\": 4944\n },\n {\n \"tag\": "begiggle",\n \"popularity\": 4940\n },\n {\n \"tag\": "chromatosphere",\n \"popularity\": 4936\n },\n {\n \"tag\": "typicality",\n \"popularity\": 4932\n },\n {\n \"tag\": "overgrown",\n \"popularity\": 4928\n },\n {\n \"tag\": "envolume",\n \"popularity\": 4925\n },\n {\n \"tag\": "pachycholia",\n \"popularity\": 4921\n },\n {\n \"tag\": "passageable",\n \"popularity\": 4917\n },\n {\n \"tag\": "pathopoiesis",\n \"popularity\": 4913\n },\n {\n \"tag\": "overbreak",\n \"popularity\": 4909\n },\n {\n \"tag\": "satyric",\n \"popularity\": 4905\n },\n {\n \"tag\": "unaudited",\n \"popularity\": 4901\n },\n {\n \"tag\": "whimble",\n \"popularity\": 4898\n },\n {\n \"tag\": "pressureless",\n \"popularity\": 4894\n },\n {\n \"tag\": "Selene",\n \"popularity\": 4890\n },\n {\n \"tag\": "slithery",\n \"popularity\": 4886\n },\n {\n \"tag\": "nondisfigurement",\n \"popularity\": 4882\n },\n {\n \"tag\": "overdelicious",\n \"popularity\": 4878\n },\n {\n \"tag\": "Perca",\n \"popularity\": 4875\n },\n {\n \"tag\": "Palladium",\n \"popularity\": 4871\n },\n {\n \"tag\": "insagacity",\n \"popularity\": 4867\n },\n {\n \"tag\": "peristoma",\n \"popularity\": 4863\n },\n {\n \"tag\": "uncreativeness",\n \"popularity\": 4859\n },\n {\n \"tag\": "incomparability surfboarding",\n \"popularity\": 4856\n },\n {\n \"tag\": "bacillar",\n \"popularity\": 4852\n },\n {\n \"tag\": "ulcerative",\n \"popularity\": 4848\n },\n {\n \"tag\": "stychomythia",\n \"popularity\": 4844\n },\n {\n \"tag\": "sesma somatics nonentry",\n \"popularity\": 4840\n },\n {\n \"tag\": "unsepulchred",\n \"popularity\": 4837\n },\n {\n \"tag\": "cephalanthium",\n \"popularity\": 4833\n },\n {\n \"tag\": "Asiaticization",\n \"popularity\": 4829\n },\n {\n \"tag\": "killeen",\n \"popularity\": 4825\n },\n {\n \"tag\": "Pseudococcus",\n \"popularity\": 4822\n },\n {\n \"tag\": "untractable",\n \"popularity\": 4818\n },\n {\n \"tag\": "apolegamic",\n \"popularity\": 4814\n },\n {\n \"tag\": "hyperpnea",\n \"popularity\": 4810\n },\n {\n \"tag\": "martyrolatry",\n \"popularity\": 4807\n },\n {\n \"tag\": "Sarmatic",\n \"popularity\": 4803\n },\n {\n \"tag\": "nonsurface",\n \"popularity\": 4799\n },\n {\n \"tag\": "adjoined",\n \"popularity\": 4796\n },\n {\n \"tag\": "vasiform",\n \"popularity\": 4792\n },\n {\n \"tag\": "tastelessness",\n \"popularity\": 4788\n },\n {\n \"tag\": "rumbo",\n \"popularity\": 4784\n },\n {\n \"tag\": "subdititious",\n \"popularity\": 4781\n },\n {\n \"tag\": "reparticipation",\n \"popularity\": 4777\n },\n {\n \"tag\": "Yorkshireism",\n \"popularity\": 4773\n },\n {\n \"tag\": "outcrow",\n \"popularity\": 4770\n },\n {\n \"tag\": "casserole",\n \"popularity\": 4766\n },\n {\n \"tag\": "semideltaic",\n \"popularity\": 4762\n },\n {\n \"tag\": "freemason",\n \"popularity\": 4759\n },\n {\n \"tag\": "catkin",\n \"popularity\": 4755\n },\n {\n \"tag\": "conscient",\n \"popularity\": 4751\n },\n {\n \"tag\": "reliably",\n \"popularity\": 4748\n },\n {\n \"tag\": "Telembi",\n \"popularity\": 4744\n },\n {\n \"tag\": "hide",\n \"popularity\": 4740\n },\n {\n \"tag\": "social",\n \"popularity\": 4737\n },\n {\n \"tag\": "ichneutic",\n \"popularity\": 4733\n },\n {\n \"tag\": "polypotome blouse pentagrammatic",\n \"popularity\": 4729\n },\n {\n \"tag\": "airdrome pesthole",\n \"popularity\": 4726\n },\n {\n \"tag\": "unportended",\n \"popularity\": 4722\n },\n {\n \"tag\": "sheerly",\n \"popularity\": 4719\n },\n {\n \"tag\": "acardiac",\n \"popularity\": 4715\n },\n {\n \"tag\": "fetor",\n \"popularity\": 4711\n },\n {\n \"tag\": "storax",\n \"popularity\": 4708\n },\n {\n \"tag\": "syndactylic",\n \"popularity\": 4704\n },\n {\n \"tag\": "otiatrics",\n \"popularity\": 4700\n },\n {\n \"tag\": "range",\n \"popularity\": 4697\n },\n {\n \"tag\": "branchway",\n \"popularity\": 4693\n },\n {\n \"tag\": "beatific",\n \"popularity\": 4690\n },\n {\n \"tag\": "Rugosa",\n \"popularity\": 4686\n },\n {\n \"tag\": "rafty",\n \"popularity\": 4682\n },\n {\n \"tag\": "gapy",\n \"popularity\": 4679\n },\n {\n \"tag\": "heterocercal",\n \"popularity\": 4675\n },\n {\n \"tag\": "actinopterygious",\n \"popularity\": 4672\n },\n {\n \"tag\": "glauconite",\n \"popularity\": 4668\n },\n {\n \"tag\": "limbless priest",\n \"popularity\": 4665\n },\n {\n \"tag\": "chrysene",\n \"popularity\": 4661\n },\n {\n \"tag\": "isentropic",\n \"popularity\": 4658\n },\n {\n \"tag\": "lairdess",\n \"popularity\": 4654\n },\n {\n \"tag\": "butterhead choliambic",\n \"popularity\": 4650\n },\n {\n \"tag\": "hexaseme",\n \"popularity\": 4647\n },\n {\n \"tag\": "treeify",\n \"popularity\": 4643\n },\n {\n \"tag\": "coronetted fructify",\n \"popularity\": 4640\n },\n {\n \"tag\": "admiralty",\n \"popularity\": 4636\n },\n {\n \"tag\": "Flosculariidae",\n \"popularity\": 4633\n },\n {\n \"tag\": "limaceous",\n \"popularity\": 4629\n },\n {\n \"tag\": "subterconscious",\n \"popularity\": 4626\n },\n {\n \"tag\": "stayless",\n \"popularity\": 4622\n },\n {\n \"tag\": "psha",\n \"popularity\": 4619\n },\n {\n \"tag\": "Mediterraneanize",\n \"popularity\": 4615\n },\n {\n \"tag\": "impenetrably",\n \"popularity\": 4612\n },\n {\n \"tag\": "Myrmeleonidae",\n \"popularity\": 4608\n },\n {\n \"tag\": "germander",\n \"popularity\": 4605\n },\n {\n \"tag\": "Buri",\n \"popularity\": 4601\n },\n {\n \"tag\": "papyrotamia",\n \"popularity\": 4598\n },\n {\n \"tag\": "Toxylon",\n \"popularity\": 4594\n },\n {\n \"tag\": "batatilla",\n \"popularity\": 4591\n },\n {\n \"tag\": "fabella assumer",\n \"popularity\": 4587\n },\n {\n \"tag\": "macromethod",\n \"popularity\": 4584\n },\n {\n \"tag\": "Blechnum",\n \"popularity\": 4580\n },\n {\n \"tag\": "pantography",\n \"popularity\": 4577\n },\n {\n \"tag\": "seminovel",\n \"popularity\": 4574\n },\n {\n \"tag\": "disembarrassment",\n \"popularity\": 4570\n },\n {\n \"tag\": "bushmaking",\n \"popularity\": 4567\n },\n {\n \"tag\": "neurosis",\n \"popularity\": 4563\n },\n {\n \"tag\": "Animalia",\n \"popularity\": 4560\n },\n {\n \"tag\": "Bernice",\n \"popularity\": 4556\n },\n {\n \"tag\": "wisen",\n \"popularity\": 4553\n },\n {\n \"tag\": "subhymenium",\n \"popularity\": 4549\n },\n {\n \"tag\": "esophagomycosis",\n \"popularity\": 4546\n },\n {\n \"tag\": "wireworks",\n \"popularity\": 4543\n },\n {\n \"tag\": "Sabellidae",\n \"popularity\": 4539\n },\n {\n \"tag\": "fustianish",\n \"popularity\": 4536\n },\n {\n \"tag\": "professively",\n \"popularity\": 4532\n },\n {\n \"tag\": "overcorruptly",\n \"popularity\": 4529\n },\n {\n \"tag\": "overcreep",\n \"popularity\": 4526\n },\n {\n \"tag\": "Castilloa",\n \"popularity\": 4522\n },\n {\n \"tag\": "forelady Georgie",\n \"popularity\": 4519\n },\n {\n \"tag\": "outsider",\n \"popularity\": 4515\n },\n {\n \"tag\": "Enukki",\n \"popularity\": 4512\n },\n {\n \"tag\": "gypsy",\n \"popularity\": 4509\n },\n {\n \"tag\": "Passamaquoddy",\n \"popularity\": 4505\n },\n {\n \"tag\": "reposit",\n \"popularity\": 4502\n },\n {\n \"tag\": "overtenderness",\n \"popularity\": 4499\n },\n {\n \"tag\": "keratome",\n \"popularity\": 4495\n },\n {\n \"tag\": "interclavicular hypermonosyllable Susanna",\n \"popularity\": 4492\n },\n {\n \"tag\": "mispropose",\n \"popularity\": 4489\n },\n {\n \"tag\": "Membranipora",\n \"popularity\": 4485\n },\n {\n \"tag\": "lampad",\n \"popularity\": 4482\n },\n {\n \"tag\": "header",\n \"popularity\": 4479\n },\n {\n \"tag\": "triseriate",\n \"popularity\": 4475\n },\n {\n \"tag\": "distrainment",\n \"popularity\": 4472\n },\n {\n \"tag\": "staphyloplastic",\n \"popularity\": 4469\n },\n {\n \"tag\": "outscour",\n \"popularity\": 4465\n },\n {\n \"tag\": "tallowmaking",\n \"popularity\": 4462\n },\n {\n \"tag\": "plugger",\n \"popularity\": 4459\n },\n {\n \"tag\": "fashionize",\n \"popularity\": 4455\n },\n {\n \"tag\": "puzzle",\n \"popularity\": 4452\n },\n {\n \"tag\": "imbrue",\n \"popularity\": 4449\n },\n {\n \"tag\": "osteoblast",\n \"popularity\": 4445\n },\n {\n \"tag\": "Hydrocores",\n \"popularity\": 4442\n },\n {\n \"tag\": "Lutra",\n \"popularity\": 4439\n },\n {\n \"tag\": "upridge scarfy",\n \"popularity\": 4435\n },\n {\n \"tag\": "ancon taffle",\n \"popularity\": 4432\n },\n {\n \"tag\": "impest",\n \"popularity\": 4429\n },\n {\n \"tag\": "uncollatedness",\n \"popularity\": 4426\n },\n {\n \"tag\": "hypersensitize",\n \"popularity\": 4422\n },\n {\n \"tag\": "autographically",\n \"popularity\": 4419\n },\n {\n \"tag\": "louther",\n \"popularity\": 4416\n },\n {\n \"tag\": "Ollie",\n \"popularity\": 4413\n },\n {\n \"tag\": "recompensate",\n \"popularity\": 4409\n },\n {\n \"tag\": "Shan",\n \"popularity\": 4406\n },\n {\n \"tag\": "brachycnemic",\n \"popularity\": 4403\n },\n {\n \"tag\": "Carinatae",\n \"popularity\": 4399\n },\n {\n \"tag\": "geotherm",\n \"popularity\": 4396\n },\n {\n \"tag\": "sawback",\n \"popularity\": 4393\n },\n {\n \"tag\": "Novatianist",\n \"popularity\": 4390\n },\n {\n \"tag\": "reapproach",\n \"popularity\": 4387\n },\n {\n \"tag\": "myelopoietic",\n \"popularity\": 4383\n },\n {\n \"tag\": "cyanin",\n \"popularity\": 4380\n },\n {\n \"tag\": "unsmutted",\n \"popularity\": 4377\n },\n {\n \"tag\": "nonpapist",\n \"popularity\": 4374\n },\n {\n \"tag\": "transbaikalian",\n \"popularity\": 4370\n },\n {\n \"tag\": "connately",\n \"popularity\": 4367\n },\n {\n \"tag\": "tenderize iterance",\n \"popularity\": 4364\n },\n {\n \"tag\": "hydrostatical",\n \"popularity\": 4361\n },\n {\n \"tag\": "unflag",\n \"popularity\": 4358\n },\n {\n \"tag\": "translate",\n \"popularity\": 4354\n },\n {\n \"tag\": "Scorzonera",\n \"popularity\": 4351\n },\n {\n \"tag\": "uncomforted",\n \"popularity\": 4348\n },\n {\n \"tag\": "risser varied",\n \"popularity\": 4345\n },\n {\n \"tag\": "plumbate",\n \"popularity\": 4342\n },\n {\n \"tag\": "Usneaceae",\n \"popularity\": 4338\n },\n {\n \"tag\": "fohat",\n \"popularity\": 4335\n },\n {\n \"tag\": "slagging",\n \"popularity\": 4332\n },\n {\n \"tag\": "superserious",\n \"popularity\": 4329\n },\n {\n \"tag\": "theocracy",\n \"popularity\": 4326\n },\n {\n \"tag\": "valonia",\n \"popularity\": 4323\n },\n {\n \"tag\": "Sapindales",\n \"popularity\": 4319\n },\n {\n \"tag\": "palaeozoologist",\n \"popularity\": 4316\n },\n {\n \"tag\": "yalb",\n \"popularity\": 4313\n },\n {\n \"tag\": "unviewed",\n \"popularity\": 4310\n },\n {\n \"tag\": "polyarteritis",\n \"popularity\": 4307\n },\n {\n \"tag\": "vectorial",\n \"popularity\": 4304\n },\n {\n \"tag\": "skimpingly",\n \"popularity\": 4301\n },\n {\n \"tag\": "athort",\n \"popularity\": 4297\n },\n {\n \"tag\": "tribofluorescence",\n \"popularity\": 4294\n },\n {\n \"tag\": "benzonitrol",\n \"popularity\": 4291\n },\n {\n \"tag\": "swiller subobtuse subjacency",\n \"popularity\": 4288\n },\n {\n \"tag\": "uncompassed",\n \"popularity\": 4285\n },\n {\n \"tag\": "cacochymia",\n \"popularity\": 4282\n },\n {\n \"tag\": "commensalist butadiene",\n \"popularity\": 4279\n },\n {\n \"tag\": "culpable",\n \"popularity\": 4276\n },\n {\n \"tag\": "contributive",\n \"popularity\": 4273\n },\n {\n \"tag\": "attemperately",\n \"popularity\": 4269\n },\n {\n \"tag\": "spelt",\n \"popularity\": 4266\n },\n {\n \"tag\": "exoneration",\n \"popularity\": 4263\n },\n {\n \"tag\": "antivivisectionist",\n \"popularity\": 4260\n },\n {\n \"tag\": "granitification",\n \"popularity\": 4257\n },\n {\n \"tag\": "palladize",\n \"popularity\": 4254\n },\n {\n \"tag\": "marksmanship",\n \"popularity\": 4251\n },\n {\n \"tag\": "bullydom",\n \"popularity\": 4248\n },\n {\n \"tag\": "spirality",\n \"popularity\": 4245\n },\n {\n \"tag\": "caliginous",\n \"popularity\": 4242\n },\n {\n \"tag\": "reportedly",\n \"popularity\": 4239\n },\n {\n \"tag\": "polyad",\n \"popularity\": 4236\n },\n {\n \"tag\": "arthroempyesis",\n \"popularity\": 4233\n },\n {\n \"tag\": "semibay facultatively",\n \"popularity\": 4229\n },\n {\n \"tag\": "metastatically",\n \"popularity\": 4226\n },\n {\n \"tag\": "prophetically",\n \"popularity\": 4223\n },\n {\n \"tag\": "Linguatula elapid",\n \"popularity\": 4220\n },\n {\n \"tag\": "pyknatom",\n \"popularity\": 4217\n },\n {\n \"tag\": "centimeter",\n \"popularity\": 4214\n },\n {\n \"tag\": "mensurate",\n \"popularity\": 4211\n },\n {\n \"tag\": "migraine",\n \"popularity\": 4208\n },\n {\n \"tag\": "pentagamist",\n \"popularity\": 4205\n },\n {\n \"tag\": "querken",\n \"popularity\": 4202\n },\n {\n \"tag\": "ambulance",\n \"popularity\": 4199\n },\n {\n \"tag\": "Stokavian",\n \"popularity\": 4196\n },\n {\n \"tag\": "malvasian",\n \"popularity\": 4193\n },\n {\n \"tag\": "uncouthsome",\n \"popularity\": 4190\n },\n {\n \"tag\": "readable",\n \"popularity\": 4187\n },\n {\n \"tag\": "enlodge",\n \"popularity\": 4184\n },\n {\n \"tag\": "plasterwise Appendiculariidae perspectograph",\n \"popularity\": 4181\n },\n {\n \"tag\": "inkweed",\n \"popularity\": 4178\n },\n {\n \"tag\": "streep",\n \"popularity\": 4175\n },\n {\n \"tag\": "diadelphian cultured",\n \"popularity\": 4172\n },\n {\n \"tag\": "hymenopterous",\n \"popularity\": 4169\n },\n {\n \"tag\": "unexorableness",\n \"popularity\": 4166\n },\n {\n \"tag\": "cascaron",\n \"popularity\": 4163\n },\n {\n \"tag\": "undaintiness",\n \"popularity\": 4160\n },\n {\n \"tag\": "Curtana",\n \"popularity\": 4157\n },\n {\n \"tag\": "scurvied",\n \"popularity\": 4154\n },\n {\n \"tag\": "molluscoidal",\n \"popularity\": 4151\n },\n {\n \"tag\": "yurt",\n \"popularity\": 4148\n },\n {\n \"tag\": "deciduitis",\n \"popularity\": 4145\n },\n {\n \"tag\": "creephole",\n \"popularity\": 4142\n },\n {\n \"tag\": "quatrefeuille",\n \"popularity\": 4139\n },\n {\n \"tag\": "bicapitate adenomatome",\n \"popularity\": 4136\n },\n {\n \"tag\": "damassin",\n \"popularity\": 4134\n },\n {\n \"tag\": "planching",\n \"popularity\": 4131\n },\n {\n \"tag\": "dashedly inferential",\n \"popularity\": 4128\n },\n {\n \"tag\": "lobe",\n \"popularity\": 4125\n },\n {\n \"tag\": "Hyrachyus",\n \"popularity\": 4122\n },\n {\n \"tag\": "knab",\n \"popularity\": 4119\n },\n {\n \"tag\": "discohexaster",\n \"popularity\": 4116\n },\n {\n \"tag\": "malign",\n \"popularity\": 4113\n },\n {\n \"tag\": "pedagoguism",\n \"popularity\": 4110\n },\n {\n \"tag\": "shrubbery",\n \"popularity\": 4107\n },\n {\n \"tag\": "undershrub",\n \"popularity\": 4104\n },\n {\n \"tag\": "bureaucrat",\n \"popularity\": 4101\n },\n {\n \"tag\": "pantaleon",\n \"popularity\": 4098\n },\n {\n \"tag\": "mesoventral",\n \"popularity\": 4096\n }]'; + +var log2 = Math.log(2); +var tagInfo = tagInfoJSON.parseJSON(function(a, b) { if (a == "popularity") { return Math.log(b) / log2; } else {return b; } }); + +function makeTagCloud(tagInfo) +{ + var output = '<div class="tagCloud" style="width: 100%">'; + + tagInfo.sort(function(a, b) { if (a.tag < b.tag) { return -1; } else if (a.tag == b.tag) { return 0; } else return 1; }); + + for (var i = 0; i < tagInfo.length; i++) { + var tag = tagInfo[i].tag; + + var validates = true; + for (var j = 0; j < tag.length; j++) { + var ch = tag.charCodeAt(j); + if (ch < 0x20 || ch >= 0x7f) { + validates = false; + break; + } + } + + if (!validates) + continue; + + var url = "http://example.com/tag/" + tag.replace(" ", "").toLowerCase(); + var popularity = tagInfo[i].popularity; + var color = 'rgb(' + Math.floor(255 * (popularity - 12) / 20) + ', 0, 255)'; + output += ' <a href="' + url + '" style="font-size: ' + popularity + 'px; color: ' + color + '">' + tag + '</a> \n'; + } + + output += '</div>'; + output.replace(" ", " "); + + return output; +} + +var tagcloud = makeTagCloud(tagInfo); +tagInfo = null; + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/string-unpack-code.html b/build/pgo/js-input/sunspider/string-unpack-code.html new file mode 100644 index 000000000..ba80c99ea --- /dev/null +++ b/build/pgo/js-input/sunspider/string-unpack-code.html @@ -0,0 +1,117 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-unpack-code</title> + +</head> + +<body> +<h3>string-unpack-code</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +// This test case unpacks the compressed code for the MochiKit, +// jQuery, Dojo and Prototype JavaScript libraries. + +/*** + MochiKit.MochiKit 1.3.1 : PACKED VERSION + THIS FILE IS AUTOMATICALLY GENERATED. If creating patches, please + diff against the source tree, not this file. + + See <http://mochikit.com/> for documentation, downloads, license, etc. + + (c) 2005 Bob Ippolito. All rights Reserved. +***/ + +for (var i = 0; i < 2; i++) { + +var decompressedMochiKit = function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(H(1q)!="L"){1q.2X("B.J")}if(H(B)=="L"){B={}}if(H(B.J)=="L"){B.J={}}B.J.1Y="1.3.1";B.J.1r="B.J";B.J.2l=G(7V,vR){if(7V===O){7V={}}R(u i=1;i<M.K;i++){u o=M[i];if(H(o)!="L"&&o!==O){R(u k in o){7V[k]=o[k]}}}F 7V};B.J.2l(B.J,{1K:G(){F"["+D.1r+" "+D.1Y+"]"},1l:G(){F D.1K()},4f:G(n){if(M.K===0){n=1}F G(){F n++}},4L:G(mw){u me=M.2U;if(M.K==1){me.1U=mw;F Y me()}},bg:G(vQ){u X=[];u m=B.J;u aw=m.1R(O,M);1M(aw.K){u o=aw.2P();if(o&&H(o)=="3n"&&H(o.K)=="2y"){R(u i=o.K-1;i>=0;i--){aw.e9(o[i])}}N{X.1c(o)}}F X},1R:G(7U,1i,av){if(!av){av=0}if(1i){u l=1i.K;if(H(l)!="2y"){if(H(B.15)!="L"){1i=B.15.2G(1i);l=1i.K}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}if(!7U){7U=[]}R(u i=av;i<l;i++){7U.1c(1i[i])}}F 7U},8Z:G(5g,1i){if(5g===O){5g={}}R(u i=1;i<M.K;i++){u o=M[i];if(H(o)!="L"&&o!==O){R(u k in o){u v=o[k];if(H(5g[k])=="3n"&&H(v)=="3n"){M.2U(5g[k],v)}N{5g[k]=v}}}}F 5g},lO:G(6c,1i){if(6c===O){6c={}}R(u i=1;i<M.K;i++){u o=M[i];R(u k in o){if(!(k in 6c)){6c[k]=o[k]}}}F 6c},lN:G(1i){u fj=[];R(u mv in 1i){fj.1c(mv)}F fj},lM:G(1i){u fh=[];u e;R(u fi in 1i){u v;1f{v=1i[fi]}1e(e){2V}fh.1c([fi,v])}F fh},jq:G(fg,ff,fe){fe.1U=Y B.J.5a(fg.1r+"."+ff);fg[ff]=fe},4i:{7L:G(a){F!!a},vP:G(a){F!a},eE:G(a){F a},2E:G(a){F~a},vO:G(a){F-a},vN:G(a,b){F a+b},vM:G(a,b){F a-b},4u:G(a,b){F a/b},vL:G(a,b){F a%b},vK:G(a,b){F a*b},3W:G(a,b){F a&b},or:G(a,b){F a|b},vJ:G(a,b){F a^b},vI:G(a,b){F a<<b},vH:G(a,b){F a>>b},vG:G(a,b){F a>>>b},eq:G(a,b){F a==b},ne:G(a,b){F a!=b},gt:G(a,b){F a>b},ge:G(a,b){F a>=b},lt:G(a,b){F a<b},le:G(a,b){F a<=b},vF:G(a,b){F B.J.2f(a,b)===0},vE:G(a,b){F B.J.2f(a,b)!==0},vD:G(a,b){F B.J.2f(a,b)==1},vC:G(a,b){F B.J.2f(a,b)!=-1},vB:G(a,b){F B.J.2f(a,b)==-1},vA:G(a,b){F B.J.2f(a,b)!=1},vz:G(a,b){F a&&b},vy:G(a,b){F a||b},vx:G(a,b){F b in a}},24:G(mu){F G(){F D[mu].1w(D,M)}},lL:G(mt){F G(a9){F a9[mt]}},66:G(){u fd={};R(u i=0;i<M.K;i++){u 6b=M[i];fd[6b]=6b}F G(){R(u i=0;i<M.K;i++){if(!(H(M[i])in fd)){F 1m}}F 1h}},lJ:G(){R(u i=0;i<M.K;i++){if(M[i]!==O){F 1m}}F 1h},lK:G(){R(u i=0;i<M.K;i++){u o=M[i];if(!(H(o)=="L"||o===O)){F 1m}}F 1h},lI:G(1i){F!B.J.7e.1w(D,M)},7e:G(1i){R(u i=0;i<M.K;i++){u o=M[i];if(!(o&&o.K)){F 1m}}F 1h},3A:G(){R(u i=0;i<M.K;i++){u o=M[i];u 6b=H(o);if((6b!="3n"&&!(6b=="G"&&H(o.vw)=="G"))||o===O||H(o.K)!="2y"){F 1m}}F 1h},eN:G(){R(u i=0;i<M.K;i++){u o=M[i];if(H(o)!="3n"||o===O||H(o.9P)!="G"){F 1m}}F 1h},lH:G(fn){if(fn===O){F B.J.1R(O,M,1)}u fc=[];R(u i=1;i<M.K;i++){fc.1c(fn(M[i]))}F fc},2r:G(fn,1g){u m=B.J;u 6a=B.15;u fb=m.3A;if(M.K<=2){if(!fb(1g)){if(6a){1g=6a.2G(1g);if(fn===O){F 1g}}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}if(fn===O){F m.1R(O,1g)}u 69=[];R(u i=0;i<1g.K;i++){69.1c(fn(1g[i]))}F 69}N{if(fn===O){fn=7o}u 7T=O;R(i=1;i<M.K;i++){if(!fb(M[i])){if(6a){F 6a.2G(6a.4c.1w(O,M))}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}u l=M[i].K;if(7T===O||7T>l){7T=l}}69=[];R(i=0;i<7T;i++){u fa=[];R(u j=1;j<M.K;j++){fa.1c(M[j][i])}69.1c(fn.1w(D,fa))}F 69}},lG:G(fn){u f9=[];if(fn===O){fn=B.J.4i.7L}R(u i=1;i<M.K;i++){u o=M[i];if(fn(o)){f9.1c(o)}}F f9},47:G(fn,1g,7S){u aq=[];u m=B.J;if(!m.3A(1g)){if(B.15){1g=B.15.2G(1g)}N{14 Y 3p("au 2E an at-as 3W B.15 2E ar")}}if(fn===O){fn=m.4i.7L}if(H(7o.1U.47)=="G"){F 7o.1U.47.cz(1g,fn,7S)}N{if(H(7S)=="L"||7S===O){R(u i=0;i<1g.K;i++){u o=1g[i];if(fn(o)){aq.1c(o)}}}N{R(i=0;i<1g.K;i++){o=1g[i];if(fn.cz(7S,o)){aq.1c(o)}}}}F aq},mq:G(7R){F G(){hd(M.K){3j 0:F 7R();3j 1:F 7R(M[0]);3j 2:F 7R(M[0],M[1]);3j 3:F 7R(M[0],M[1],M[2])}u f8=[];R(u i=0;i<M.K;i++){f8.1c("M["+i+"]")}F dB("(1A("+f8.2b(",")+"))")}},lv:G(mr,ms){u m=B.J;F m.1O.1w(D,m.1R([ms,mr],M,2))},1O:G(3c,4o){if(H(3c)=="1n"){3c=4o[3c]}u ao=3c.f5;u 5f=3c.am;u f6=3c.f7;u m=B.J;if(H(3c)=="G"&&H(3c.1w)=="L"){3c=m.mq(3c)}if(H(ao)!="G"){ao=3c}if(H(4o)!="L"){f6=4o}if(H(5f)=="L"){5f=[]}N{5f=5f.9T()}m.1R(5f,M,2);u 7Q=G(){u ap=M;u me=M.2U;if(me.am.K>0){ap=m.2o(me.am,ap)}u 4o=me.f7;if(!4o){4o=D}F me.f5.1w(4o,ap)};7Q.f7=f6;7Q.f5=ao;7Q.am=5f;F 7Q},lF:G(7P){u mp=B.J.1O;R(u k in 7P){u f4=7P[k];if(H(f4)=="G"){7P[k]=mp(f4,7P)}}},5u:G(mo,mn,ml,mk){B.J.ae.5M(mo,mn,ml,mk)},mj:{"5L":1h,"1n":1h,"2y":1h},2f:G(a,b){if(a==b){F 0}u f3=(H(a)=="L"||a===O);u f2=(H(b)=="L"||b===O);if(f3&&f2){F 0}N{if(f3){F-1}N{if(f2){F 1}}}u m=B.J;u f1=m.mj;if(!(H(a)in f1&&H(b)in f1)){1f{F m.ae.3C(a,b)}1e(e){if(e!=m.4d){14 e}}}if(a<b){F-1}N{if(a>b){F 1}}u f0=m.U;14 Y 3p(f0(a)+" 3W "+f0(b)+" 9v 2E be vv")},eM:G(a,b){F B.J.2f(a.9P(),b.9P())},eL:G(a,b){u mi=B.J.2f;u 7O=a.K;u al=0;if(7O>b.K){al=1;7O=b.K}N{if(7O<b.K){al=-1}}R(u i=0;i<7O;i++){u 4j=mi(a[i],b[i]);if(4j){F 4j}}F al},7M:G(mh,mg,mf,md){B.J.ad.5M(mh,mg,mf,md)},U:G(o){if(H(o)=="L"){F"L"}N{if(o===O){F"O"}}1f{if(H(o.1K)=="G"){F o.1K()}N{if(H(o.U)=="G"&&o.U!=M.2U){F o.U()}}F B.J.ad.3C(o)}1e(e){if(H(o.1r)=="1n"&&(o.1l==cZ.1U.1l||o.1l==vu.1U.1l)){F o.1r}}1f{u eZ=(o+"")}1e(e){F"["+H(o)+"]"}if(H(o)=="G"){o=eZ.23(/^\\s+/,"");u 5n=o.2A("{");if(5n!=-1){o=o.3H(0,5n)+"{...}"}}F eZ},eK:G(o){u m=B.J;F"["+m.2r(m.U,o).2b(", ")+"]"},ac:G(o){F("\\""+o.23(/(["\\\\])/g,"\\\\$1")+"\\"").23(/[\\f]/g,"\\\\f").23(/[\\b]/g,"\\\\b").23(/[\\n]/g,"\\\\n").23(/[\\t]/g,"\\\\t").23(/[\\r]/g,"\\\\r")},eJ:G(o){F o+""},ly:G(mc,mb,ma,m9){B.J.ab.5M(mc,mb,ma,m9)},lx:G(){F dB("("+M[0]+")")},lz:G(o){u 5e=H(o);if(5e=="L"){F"L"}N{if(5e=="2y"||5e=="5L"){F o+""}N{if(o===O){F"O"}}}u m=B.J;u eY=m.ac;if(5e=="1n"){F eY(o)}u me=M.2U;u 3S;if(H(o.m8)=="G"){3S=o.m8();if(o!==3S){F me(3S)}}if(H(o.m7)=="G"){3S=o.m7();if(o!==3S){F me(3S)}}if(5e!="G"&&H(o.K)=="2y"){u X=[];R(u i=0;i<o.K;i++){u 2i=me(o[i]);if(H(2i)!="1n"){2i="L"}X.1c(2i)}F"["+X.2b(", ")+"]"}1f{3S=m.ab.3C(o);F me(3S)}1e(e){if(e!=m.4d){14 e}}if(5e=="G"){F O}X=[];R(u k in o){u ak;if(H(k)=="2y"){ak="\\""+k+"\\""}N{if(H(k)=="1n"){ak=eY(k)}N{2V}}2i=me(o[k]);if(H(2i)!="1n"){2V}X.1c(ak+":"+2i)}F"{"+X.2b(", ")+"}"},lE:G(a,b){F(B.J.2f(a,b)===0)},lD:G(eX,4n){if(eX.K!=4n.K){F 1m}F(B.J.2f(eX,4n)===0)},2o:G(){u eW=[];u m6=B.J.1R;R(u i=0;i<M.K;i++){m6(eW,M[i])}F eW},eR:G(2h){u m=B.J;u eU=m.2f;if(M.K==1){F G(a,b){F eU(a[2h],b[2h])}}u eV=m.1R(O,M);F G(a,b){u aj=0;R(u i=0;(aj===0)&&(i<eV.K);i++){u 2h=eV[i];aj=eU(a[2h],b[2h])}F aj}},lC:G(2h){u m5=B.J.eR.1w(D,M);F G(a,b){F m5(b,a)}},2z:G(m4){u m=B.J;F m.1O.1w(D,m.1R([m4,L],M,1))},67:G(m0,1g){if(1g.K===0){F O}u ai=1g[0];u m3=B.J.2f;R(u i=1;i<1g.K;i++){u o=1g[i];if(m3(o,ai)==m0){ai=o}}F ai},lB:G(){F B.J.67(1,M)},lA:G(){F B.J.67(-1,M)},bi:G(1g,lY,lZ,3B){if(H(3B)=="L"||3B===O){3B=1g.K}R(u i=(lZ||0);i<3B;i++){if(1g[i]===lY){F i}}F-1},eO:G(1g,lW,lX,3B){if(H(3B)=="L"||3B===O){3B=1g.K}u 4j=B.J.2f;R(u i=(lX||0);i<3B;i++){if(4j(1g[i],lW)===0){F i}}F-1},d4:G(1j,lV){u ah=[1j];u lU=B.J.1R;1M(ah.K){u X=lV(ah.2P());if(X){lU(ah,X)}}},3f:G(ag){u 2w=ag.1r;if(H(2w)=="L"){2w=""}N{2w=2w+"."}R(u 1b in ag){u o=ag[1b];if(H(o)=="G"&&H(o.1r)=="L"){1f{o.1r=2w+1b}1e(e){}}}},dw:G(3s,68){if(H(B.S)!="L"&&M.K==1&&(H(3s)=="1n"||(H(3s.3T)!="L"&&3s.3T>0))){u kv=B.S.d5(3s);3s=kv[0];68=kv[1]}N{if(M.K==1){u o=3s;3s=[];68=[];R(u k in o){u v=o[k];if(H(v)!="G"){3s.1c(k);68.1c(v)}}}}u W=[];u lT=28.2a(3s.K,68.K);u eT=B.J.af;R(u i=0;i<lT;i++){v=68[i];if(H(v)!="L"&&v!==O){W.1c(eT(3s[i])+"="+eT(v))}}F W.2b("&")},lw:G(lS,lQ){u 7N=lS.23(/\\+/g,"%20").2R("&");u o={};u 5d;if(H(lR)!="L"){5d=lR}N{5d=vt}if(lQ){R(u i=0;i<7N.K;i++){u 2n=7N[i].2R("=");u 1b=5d(2n[0]);u 4n=o[1b];if(!(4n 2C 7o)){4n=[];o[1b]=4n}4n.1c(5d(2n[1]))}}N{R(i=0;i<7N.K;i++){2n=7N[i].2R("=");o[5d(2n[0])]=5d(2n[1])}}F o}});B.J.4a=G(){D.4m=[]};B.J.4a.1U={5M:G(1b,eS,3y,lP){if(lP){D.4m.e9([1b,eS,3y])}N{D.4m.1c([1b,eS,3y])}},3C:G(){R(u i=0;i<D.4m.K;i++){u 2n=D.4m[i];if(2n[1].1w(D,M)){F 2n[2].1w(D,M)}}14 B.J.4d},vs:G(1b){R(u i=0;i<D.4m.K;i++){u 2n=D.4m[i];if(2n[0]==1b){D.4m.4y(i,1);F 1h}}F 1m}};B.J.1z=["4f","4L","1R","2l","8Z","lO","lN","lM","5a","4i","24","lL","66","lo","ln","lK","lJ","lI","7e","3A","eN","lH","2r","lG","47","1O","lF","4d","4a","5u","2f","7M","U","lE","lD","2o","eR","lC","2z","lm","67","lp","eI","lB","lA","d4","ll","af","dw","lz","ly","lx","lw","eO","bi","bg","lv"];B.J.1W=["3f","ae","ad","ab","eM","eL","eK","ac","eJ"];B.J.2Y=G(lu,eP){if(H(B.eQ)=="L"){B.eQ=(B.3d||(H(1x)=="L"&&H(1q)=="L"))}if(!B.eQ){F}u 1p=eP.2k[":1p"];R(u i=0;i<1p.K;i++){lu[1p[i]]=eP[1p[i]]}};B.J.2d=G(){u m=D;m.vr=m.24;m.vq=m.eO;if(H(ls)!="L"){m.af=G(lr){F ls(lr).23(/\\\'/g,"%27")}}N{m.af=G(lq){F vp(lq).23(/\\+/g,"%2B").23(/\\"/g,"%22").W.23(/\\\'/g,"%27")}}m.5a=G(1b){D.43=1b;D.1b=1b};m.5a.1U=Y 2x();m.2l(m.5a.1U,{U:G(){if(D.43&&D.43!=D.1b){F D.1b+"("+m.U(D.43)+")"}N{F D.1b+"()"}},1l:m.24("U")});m.4d=Y m.5a("B.J.4d");m.lp=m.2z(m.67,1);m.eI=m.2z(m.67,-1);m.lo=m.66("G");m.ln=m.66("L");m.lm=m.2z(m.2l,O);m.ll=m.2z(m.2r,O);m.ae=Y m.4a();m.5u("vo",m.eN,m.eM);m.5u("ej",m.3A,m.eL);m.ad=Y m.4a();m.7M("ej",m.3A,m.eK);m.7M("1n",m.66("1n"),m.ac);m.7M("vn",m.66("2y","5L"),m.eJ);m.ab=Y m.4a();u 1p=m.2o(m.1z,m.1W);m.2k={":3e":m.2o(m.1W),":1p":1p};m.3f(D)};B.J.2d();if(!B.3d){2f=B.J.2f}B.J.2Y(D,B.J);if(H(1q)!="L"){1q.2X("B.15");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.15 3F on B.J!"}if(H(B.15)=="L"){B.15={}}B.15.1r="B.15";B.15.1Y="1.3.1";B.J.2l(B.15,{1K:G(){F"["+D.1r+" "+D.1Y+"]"},1l:G(){F D.1K()},9W:G(1b,lk,lj,lh){B.15.9Y.5M(1b,lk,lj,lh)},1Q:G(3R,lg){u I=B.15;if(M.K==2){F I.9Z(G(a){F a!=lg},3R)}if(H(3R.1a)=="G"){F 3R}N{if(H(3R.1Q)=="G"){F 3R.1Q()}}1f{F I.9Y.3C(3R)}1e(e){u m=B.J;if(e==m.4d){e=Y 3p(H(3R)+": "+m.U(3R)+" is 2E vm")}14 e}},eu:G(n){if(!n){n=0}u m=B.J;F{U:G(){F"eu("+n+")"},1l:m.24("U"),1a:m.4f(n)}},et:G(p){u I=B.15;u m=B.J;u 1g=[];u lf=I.1Q(p);F{U:G(){F"et(...)"},1l:m.24("U"),1a:G(){1f{u W=lf.1a();1g.1c(W);F W}1e(e){if(e!=I.25){14 e}if(1g.K===0){D.1a=G(){14 I.25}}N{u i=-1;D.1a=G(){i=(i+1)%1g.K;F 1g[i]}}F D.1a()}}}},7b:G(Q,n){u m=B.J;if(H(n)=="L"){F{U:G(){F"7b("+m.U(Q)+")"},1l:m.24("U"),1a:G(){F Q}}}F{U:G(){F"7b("+m.U(Q)+", "+n+")"},1l:m.24("U"),1a:G(){if(n<=0){14 B.15.25}n-=1;F Q}}},1a:G(ld){F ld.1a()},es:G(p,q){u m=B.J;u 1a=B.15.1a;u lc=m.2r(1Q,M);F{U:G(){F"es(...)"},1l:m.24("U"),1a:G(){F m.2r(1a,lc)}}},a1:G(3b,1V){u m=B.J;1V=B.15.1Q(1V);if(3b===O){3b=m.4i.7L}F{U:G(){F"a1(...)"},1l:m.24("U"),1a:G(){1M(1h){u W=1V.1a();if(3b(W)){F W}}F L}}},a0:G(3b,1V){u m=B.J;1V=B.15.1Q(1V);if(3b===O){3b=m.4i.7L}F{U:G(){F"a0(...)"},1l:m.24("U"),1a:G(){1M(1h){u W=1V.1a();if(!3b(W)){F W}}F L}}},er:G(1V){u I=B.15;u m=B.J;1V=I.1Q(1V);u 5c=0;u 2J=0;u 3a=1;u i=-1;if(M.K==2){2J=M[1]}N{if(M.K==3){5c=M[1];2J=M[2]}N{5c=M[1];2J=M[2];3a=M[3]}}F{U:G(){F"er("+["...",5c,2J,3a].2b(", ")+")"},1l:m.24("U"),1a:G(){u W;1M(i<5c){W=1V.1a();i++}if(5c>=2J){14 I.25}5c+=3a;F W}}},4c:G(aa,p,q){u m=B.J;u I=B.15;u lb=m.2r(I.1Q,m.1R(O,M,1));u 2r=m.2r;u 1a=I.1a;F{U:G(){F"4c(...)"},1l:m.24("U"),1a:G(){F aa.1w(D,2r(1a,lb))}}},ep:G(aa,1V,I){1V=B.15.1Q(1V);u m=B.J;F{U:G(){F"ep(...)"},1l:m.24("U"),1a:G(){F aa.1w(I,1V.1a())}}},55:G(p,q){u I=B.15;u m=B.J;if(M.K==1){F I.1Q(M[0])}u 64=m.2r(I.1Q,M);F{U:G(){F"55(...)"},1l:m.24("U"),1a:G(){1M(64.K>1){1f{F 64[0].1a()}1e(e){if(e!=I.25){14 e}64.2P()}}if(64.K==1){u a9=64.2P();D.1a=m.1O("1a",a9);F D.1a()}14 I.25}}},9Z:G(3b,1V){u I=B.15;1V=I.1Q(1V);F{U:G(){F"9Z(...)"},1l:B.J.24("U"),1a:G(){u W=1V.1a();if(!3b(W)){D.1a=G(){14 I.25};D.1a()}F W}}},eo:G(3b,1V){1V=B.15.1Q(1V);u m=B.J;u 1O=m.1O;F{"U":G(){F"eo(...)"},"1l":m.24("U"),"1a":G(){1M(1h){u W=1V.1a();if(!3b(W)){2K}}D.1a=1O("1a",1V);F W}}},a7:G(63,2u,la){2u.62[63]=-1;u m=B.J;u l9=m.eI;F{U:G(){F"en("+63+", ...)"},1l:m.24("U"),1a:G(){u W;u i=2u.62[63];if(i==2u.29){W=la.1a();2u.a8.1c(W);2u.29+=1;2u.62[63]+=1}N{W=2u.a8[i-2u.2a];2u.62[63]+=1;if(i==2u.2a&&l9(2u.62)!=2u.2a){2u.2a+=1;2u.a8.2P()}}F W}}},en:G(a6,n){u W=[];u 2u={"62":[],"a8":[],"29":-1,"2a":-1};if(M.K==1){n=2}u I=B.15;a6=I.1Q(a6);u a7=I.a7;R(u i=0;i<n;i++){W.1c(a7(i,2u,a6))}F W},2G:G(4l){u m=B.J;if(H(4l.9T)=="G"){F 4l.9T()}N{if(m.3A(4l)){F m.2o(4l)}}u I=B.15;4l=I.1Q(4l);u W=[];1f{1M(1h){W.1c(4l.1a())}}1e(e){if(e!=I.25){14 e}F W}F L},7H:G(fn,7K,l8){u i=0;u x=l8;u I=B.15;7K=I.1Q(7K);if(M.K<3){1f{x=7K.1a()}1e(e){if(e==I.25){e=Y 3p("7H() of vl vk vj no vi 3m")}14 e}i++}1f{1M(1h){x=fn(x,7K.1a())}}1e(e){if(e!=I.25){14 e}}F x},7I:G(){u 4k=0;u 2J=0;u 3a=1;if(M.K==1){2J=M[0]}N{if(M.K==2){4k=M[0];2J=M[1]}N{if(M.K==3){4k=M[0];2J=M[1];3a=M[2]}N{14 Y 3p("7I() vh 1, 2, or 3 M!")}}}if(3a===0){14 Y 3p("7I() 3a 5p 2E be 0")}F{1a:G(){if((3a>0&&4k>=2J)||(3a<0&&4k<=2J)){14 B.15.25}u W=4k;4k+=3a;F W},U:G(){F"7I("+[4k,2J,3a].2b(", ")+")"},1l:B.J.24("U")}},l0:G(a5,l7){u x=l7||0;u I=B.15;a5=I.1Q(a5);1f{1M(1h){x+=a5.1a()}}1e(e){if(e!=I.25){14 e}}F x},em:G(a4){u I=B.15;a4=I.1Q(a4);1f{1M(1h){a4.1a()}}1e(e){if(e!=I.25){14 e}}},9a:G(7J,1A,I){u m=B.J;if(M.K>2){1A=m.1O(1A,I)}if(m.3A(7J)){1f{R(u i=0;i<7J.K;i++){1A(7J[i])}}1e(e){if(e!=B.15.25){14 e}}}N{I=B.15;I.em(I.4c(1A,7J))}},kZ:G(l6,1A){u I=B.15;1f{I.a0(1A,l6).1a();F 1m}1e(e){if(e!=I.25){14 e}F 1h}},kY:G(l5,4j){u W=B.15.2G(l5);if(M.K==1){4j=B.J.2f}W.iz(4j);F W},kX:G(l4){u W=B.15.2G(l4);W.vg();F W},kW:G(l3,1A){u I=B.15;1f{I.a1(1A,l3).1a();F 1h}1e(e){if(e!=I.25){14 e}F 1m}},kV:G(1g,5b){if(B.J.3A(5b)){R(u i=0;i<5b.K;i++){1g.1c(5b[i])}}N{u I=B.15;5b=I.1Q(5b);1f{1M(1h){1g.1c(5b.1a())}}1e(e){if(e!=I.25){14 e}}}F 1g},ek:G(a3,eH){u m=B.J;u I=B.15;if(M.K<2){eH=m.4i.eE}a3=I.1Q(a3);u pk=L;u k=L;u v;G eF(){v=a3.1a();k=eH(v)}G l2(){u 7j=v;v=L;F 7j}u eG=1h;F{U:G(){F"ek(...)"},1a:G(){1M(k==pk){eF();if(eG){eG=1m;2K}}pk=k;F[k,{1a:G(){if(v==L){eF()}if(k!=pk){14 I.25}F l2()}}]}}},kU:G(a2,eD){u m=B.J;u I=B.15;if(M.K<2){eD=m.4i.eE}a2=I.1Q(a2);u ey=[];u eA=1h;u ez;1M(1h){1f{u eB=a2.1a();u 2h=eD(eB)}1e(e){if(e==I.25){2K}14 e}if(eA||2h!=ez){u eC=[];ey.1c([2h,eC])}eC.1c(eB);eA=1m;ez=2h}F ey},9X:G(ex){u i=0;F{U:G(){F"9X(...)"},1l:B.J.24("U"),1a:G(){if(i>=ex.K){14 B.15.25}F ex[i++]}}},eh:G(ew){F(ew&&H(ew.ei)=="G")},9V:G(l1){F{U:G(){F"9V(...)"},1l:B.J.24("U"),1a:G(){u W=l1.ei();if(W===O||W===L){14 B.15.25}F W}}}});B.15.1W=["9Y","9X","eh","9V",];B.15.1z=["25","9W","1Q","eu","et","7b","1a","es","a1","a0","er","4c","ep","55","9Z","eo","en","2G","7H","7I","l0","em","9a","kZ","kY","kX","kW","kV","ek","kU"];B.15.2d=G(){u m=B.J;D.25=Y m.5a("25");D.9Y=Y m.4a();D.9W("ej",m.3A,D.9X);D.9W("ei",D.eh,D.9V);D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.15.2d();if(!B.3d){7H=B.15.7H}B.J.2Y(D,B.15);if(H(1q)!="L"){1q.2X("B.1H");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.1H 3F on B.J!"}if(H(B.1H)=="L"){B.1H={}}B.1H.1r="B.1H";B.1H.1Y="1.3.1";B.1H.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1H.1l=G(){F D.1K()};B.1H.1z=["5C","49","7A","kR","2L","5Z","kG","ch","kE","kC"];B.1H.1W=["ef","e8","e7"];B.1H.49=G(1P,kT,3z){D.1P=1P;D.3N=kT;D.3z=3z;D.vf=Y 3Q()};B.1H.49.1U={U:G(){u m=B.J;F"49("+m.2r(m.U,[D.1P,D.3N,D.3z]).2b(", ")+")"},1l:B.J.24("U")};B.J.2l(B.1H,{ef:G(7F){u I=B.1H;if(H(7F)=="1n"){7F=I.5C[7F]}F G(1t){u 7G=1t.3N;if(H(7G)=="1n"){7G=I.5C[7G]}F 7G>=7F}},e8:G(){u kS=B.1H.49;R(u i=0;i<M.K;i++){if(!(M[i]2C kS)){F 1m}}F 1h},e7:G(a,b){F B.J.2f([a.3N,a.3z],[b.3N,b.3z])},kR:G(1t){cq("1P: "+1t.1P+"\\ve: "+1t.3N+"\\vd: "+1t.3z.2b(" "))}});B.1H.7A=G(7E){D.4f=0;if(H(7E)=="L"||7E===O){7E=-1}D.ec=7E;D.4h=[];D.7C={};D.e5=1m};B.1H.7A.1U={vc:G(){D.4h.4y(0,D.4h.K)},kK:G(1t){if(H(2O)!="L"&&2O.eg&&2O.eg.5Z){2O.eg.5Z(1t)}N{if(H(7h)!="L"&&7h.kQ){7h.kQ(1t)}N{if(H(5X)=="G"){5X(1t)}}}},kL:G(1t){R(u k in D.7C){u 2n=D.7C[k];if(2n.kO!=k||(2n[0]&&!2n[0](1t))){2V}2n[1](1t)}},hE:G(ee,7D,kP){if(H(7D)=="1n"){7D=B.1H.ef(7D)}u ed=[7D,kP];ed.kO=ee;D.7C[ee]=ed},c9:G(kN){gi D.7C[kN]},kH:G(kM,vb){u 1t=Y B.1H.49(D.4f,kM,B.J.1R(O,M,1));D.4h.1c(1t);D.kL(1t);if(D.e5){D.kK(1t.3N+": "+1t.3z.2b(" "))}D.4f+=1;1M(D.ec>=0&&D.4h.K>D.ec){D.4h.2P()}},c8:G(9U){u ea=0;if(!(H(9U)=="L"||9U===O)){ea=28.29(0,D.4h.K-9U)}F D.4h.9T(ea)},kJ:G(7B){if(H(7B)=="L"||7B===O){7B=30}u 9S=D.c8(7B);if(9S.K){u 1g=2r(G(m){F"\\n ["+m.1P+"] "+m.3N+": "+m.3z.2b(" ")},9S);1g.e9("va "+9S.K+" v9:");F 1g.2b("")}F""},v8:G(kI){if(H(B.1I)=="L"){cq(D.kJ())}N{B.1I.bY(kI||1m)}}};B.1H.2d=G(){D.5C={8M:40,8L:50,8K:30,8J:20,8I:10};u m=B.J;m.5u("49",D.e8,D.e7);u 61=m.2z;u e6=D.7A;u 60=e6.1U.kH;m.2l(D.7A.1U,{kF:61(60,"8I"),5Z:61(60,"8J"),dE:61(60,"8M"),kD:61(60,"8L"),kB:61(60,"8K")});u I=D;u 5Y=G(1b){F G(){I.2L[1b].1w(I.2L,M)}};D.5Z=5Y("5Z");D.kG=5Y("dE");D.ch=5Y("kF");D.kE=5Y("kD");D.kC=5Y("kB");D.2L=Y e6();D.2L.e5=1h;D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};if(H(5X)=="L"&&H(2v)!="L"&&2v.kA&&H(kz)!="L"){5X=G(){5X.3G=M;u ev=2v.kA("v7");ev.v6("5X",1m,1h);kz(ev)}}B.1H.2d();B.J.2Y(D,B.1H);if(H(1q)!="L"){1q.2X("B.1D")}if(H(B)=="L"){B={}}if(H(B.1D)=="L"){B.1D={}}B.1D.1r="B.1D";B.1D.1Y="1.3.1";B.1D.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1D.1l=G(){F D.1K()};B.1D.ks=G(1y){1y=1y+"";if(H(1y)!="1n"||1y.K===0){F O}u 7z=1y.2R("-");if(7z.K===0){F O}F Y 3Q(7z[0],7z[1]-1,7z[2])};B.1D.ky=/(\\d{4,})(?:-(\\d{1,2})(?:-(\\d{1,2})(?:[T ](\\d{1,2}):(\\d{1,2})(?::(\\d{1,2})(?:\\.(\\d+))?)?(?:(Z)|([+-])(\\d{1,2})(?::(\\d{1,2}))?)?)?)?)?/;B.1D.kr=G(1y){1y=1y+"";if(H(1y)!="1n"||1y.K===0){F O}u X=1y.3C(B.1D.ky);if(H(X)=="L"||X===O){F O}u 5W,7y,7x,9R,2a,9Q,7w;5W=3w(X[1],10);if(H(X[2])=="L"||X[2]===""){F Y 3Q(5W)}7y=3w(X[2],10)-1;7x=3w(X[3],10);if(H(X[4])=="L"||X[4]===""){F Y 3Q(5W,7y,7x)}9R=3w(X[4],10);2a=3w(X[5],10);9Q=(H(X[6])!="L"&&X[6]!=="")?3w(X[6],10):0;if(H(X[7])!="L"&&X[7]!==""){7w=28.ha(c5*4M("0."+X[7]))}N{7w=0}if((H(X[8])=="L"||X[8]==="")&&(H(X[9])=="L"||X[9]==="")){F Y 3Q(5W,7y,7x,9R,2a,9Q,7w)}u 58;if(H(X[9])!="L"&&X[9]!==""){58=3w(X[10],10)*v5;if(H(X[11])!="L"&&X[11]!==""){58+=3w(X[11],10)*kw}if(X[9]=="-"){58=-58}}N{58=0}F Y 3Q(3Q.v4(5W,7y,7x,9R,2a,9Q,7w)-58)};B.1D.dY=G(2g,kx){if(H(2g)=="L"||2g===O){F O}u hh=2g.v3();u mm=2g.v2();u ss=2g.v1();u 1g=[((kx&&(hh<10))?"0"+hh:hh),((mm<10)?"0"+mm:mm),((ss<10)?"0"+ss:ss)];F 1g.2b(":")};B.1D.kq=G(2g,7v){if(H(2g)=="L"||2g===O){F O}u ku=7v?"T":" ";u kt=7v?"Z":"";if(7v){2g=Y 3Q(2g.9P()+(2g.v0()*kw))}F B.1D.dX(2g)+ku+B.1D.dY(2g,7v)+kt};B.1D.dX=G(2g){if(H(2g)=="L"||2g===O){F O}u e4=B.1D.e3;F[2g.dZ(),e4(2g.e1()+1),e4(2g.e0())].2b("-")};B.1D.kp=G(d){d=d+"";if(H(d)!="1n"||d.K===0){F O}u a=d.2R("/");F Y 3Q(a[2],a[0]-1,a[1])};B.1D.e3=G(n){F(n>9)?n:"0"+n};B.1D.ko=G(d){if(H(d)=="L"||d===O){F O}u e2=B.1D.e3;F[e2(d.e1()+1),e2(d.e0()),d.dZ()].2b("/")};B.1D.kn=G(d){if(H(d)=="L"||d===O){F O}F[d.e1()+1,d.e0(),d.dZ()].2b("/")};B.1D.1z=["ks","kr","dY","kq","dX","kp","ko","kn"];B.1D.1W=[];B.1D.2k={":3e":B.1D.1z,":1p":B.1D.1z};B.1D.2d=G(){u 2w=D.1r+".";R(u k in D){u o=D[k];if(H(o)=="G"&&H(o.1r)=="L"){1f{o.1r=2w+k}1e(e){}}}};B.1D.2d();if(H(B.J)!="L"){B.J.2Y(D,B.1D)}N{(G(km,dW){if((H(1x)=="L"&&H(1q)=="L")||(H(B.3d)=="5L"&&B.3d)){u 1p=dW.2k[":1p"];R(u i=0;i<1p.K;i++){km[1p[i]]=dW[1p[i]]}}})(D,B.1D)}if(H(1q)!="L"){1q.2X("B.1s")}if(H(B)=="L"){B={}}if(H(B.1s)=="L"){B.1s={}}B.1s.1r="B.1s";B.1s.1Y="1.3.1";B.1s.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1s.1l=G(){F D.1K()};B.1s.ke=G(kl,kk,kj,ki,kh,dV,kg,9N,kf){F G(1P){1P=4M(1P);if(H(1P)=="L"||1P===O||k8(1P)){F kl}u 9L=kk;u 9K=kj;if(1P<0){1P=-1P}N{9L=9L.23(/-/,"")}u me=M.2U;u 9M=B.1s.dJ(ki);if(kh){1P=1P*3k;9K=9M.9y+9K}1P=B.1s.dK(1P,dV);u 9O=1P.2R(/\\./);u 3r=9O[0];u 3P=(9O.K==1)?"":9O[1];u X="";1M(3r.K<kg){3r="0"+3r}if(9N){1M(3r.K>9N){u i=3r.K-9N;X=9M.9A+3r.2W(i,3r.K)+X;3r=3r.2W(0,i)}}X=3r+X;if(dV>0){1M(3P.K<kf){3P=3P+"0"}X=X+9M.9z+3P}F 9L+X+9K}};B.1s.k5=G(9J,9H,9G){if(H(9H)=="L"){9H=""}u 3q=9J.3C(/((?:[0#]+,)?[0#]+)(?:\\.([0#]+))?(%)?/);if(!3q){14 3p("uZ uY")}u 7u=9J.3H(0,3q.c6);u kd=9J.3H(3q.c6+3q[0].K);if(7u.uX(/-/)==-1){7u=7u+"-"}u 9I=3q[1];u 3P=(H(3q[2])=="1n"&&3q[2]!="")?3q[2]:"";u kc=(H(3q[3])=="1n"&&3q[3]!="");u dU=9I.2R(/,/);u 9F;if(H(9G)=="L"){9G="dG"}if(dU.K==1){9F=O}N{9F=dU[1].K}u ka=9I.K-9I.23(/0/g,"").K;u k9=3P.K-3P.23(/0/g,"").K;u kb=3P.K;u W=B.1s.ke(9H,7u,kd,9G,kc,kb,ka,9F,k9);u m=B.J;if(m){u fn=M.2U;u 3G=m.2o(M);W.U=G(){F[I.1r,"(",2r(m.U,3G).2b(", "),")"].2b("")}}F W};B.1s.dJ=G(4g){if(H(4g)=="L"||4g===O){4g="dG"}if(H(4g)=="1n"){u W=B.1s.5V[4g];if(H(W)=="1n"){W=M.2U(W);B.1s.5V[4g]=W}F W}N{F 4g}};B.1s.k4=G(dT,9E){if(9E){u X=dT/9E;if(!k8(X)){F B.1s.9B(dT/9E)}}F"0"};B.1s.9B=G(dS){u dR=(dS<0?"-":"");u s=28.8B(28.uW(dS)*3k).1l();if(s=="0"){F s}if(s.K<3){1M(s.3Z(s.K-1)=="0"){s=s.2W(0,s.K-1)}F dR+"0."+s}u 5E=dR+s.2W(0,s.K-2);u 7t=s.2W(s.K-2,s.K);if(7t=="uV"){F 5E}N{if(7t.3Z(1)=="0"){F 5E+"."+7t.3Z(0)}N{F 5E+"."+7t}}};B.1s.dI=G(1y,dQ){1y=1y+"";if(H(1y)!="1n"){F O}if(!dQ){F 1y.23(/^\\s+/,"")}N{F 1y.23(Y 8V("^["+dQ+"]+"),"")}};B.1s.dH=G(1y,dP){1y=1y+"";if(H(1y)!="1n"){F O}if(!dP){F 1y.23(/\\s+$/,"")}N{F 1y.23(Y 8V("["+dP+"]+$"),"")}};B.1s.k2=G(1y,dO){u I=B.1s;F I.dH(I.dI(1y,dO),dO)};B.1s.dL=G(9D,9C){9D=28.8B(9D*28.dN(10,9C));u X=(9D*28.dN(10,-9C)).6I(9C);if(X.3Z(0)=="."){X="0"+X}F X};B.1s.dK=G(k7,dM){F B.1s.dL(k7+0.5*28.dN(10,-dM),dM)};B.1s.k3=G(k6){F B.1s.9B(3k*k6)+"%"};B.1s.1z=["dL","dK","k5","dJ","k4","9B","k3","dI","dH","k2"];B.1s.5V={k1:{9A:",",9z:".",9y:"%"},uU:{9A:".",9z:",",9y:"%"},uT:{9A:" ",9z:",",9y:"%"},"dG":"k1"};B.1s.1W=[];B.1s.2k={":1p":B.1s.1z,":3e":B.1s.1z};B.1s.2d=G(){u 2w=D.1r+".";u k,v,o;R(k in D.5V){o=D.5V[k];if(H(o)=="3n"){o.U=G(){F D.1r};o.1r=2w+"5V."+k}}R(k in D){o=D[k];if(H(o)=="G"&&H(o.1r)=="L"){1f{o.1r=2w+k}1e(e){}}}};B.1s.2d();if(H(B.J)!="L"){B.J.2Y(D,B.1s)}N{(G(k0,dF){if((H(1x)=="L"&&H(1q)=="L")||(H(B.3d)=="5L"&&B.3d)){u 1p=dF.2k[":1p"];R(u i=0;i<1p.K;i++){k0[1p[i]]=dF[1p[i]]}}})(D,B.1s)}if(H(1q)!="L"){1q.2X("B.1k");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.1k 3F on B.J!"}if(H(B.1k)=="L"){B.1k={}}B.1k.1r="B.1k";B.1k.1Y="1.3.1";B.1k.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1k.1l=G(){F D.1K()};B.1k.2t=G(jZ){D.55=[];D.id=D.7n();D.2H=-1;D.54=0;D.53=[O,O];D.7m=jZ;D.7l=1m;D.7r=1m};B.1k.2t.1U={U:G(){u 7s;if(D.2H==-1){7s="uS"}N{if(D.2H===0){7s="uR"}N{7s="dE"}}F"2t("+D.id+", "+7s+")"},1l:B.J.24("U"),7n:B.J.4f(),jY:G(){u I=B.1k;if(D.2H==-1){if(D.7m){D.7m(D)}N{D.7l=1h}if(D.2H==-1){D.52(Y I.di(D))}}N{if((D.2H===0)&&(D.53[0]2C I.2t)){D.53[0].jY()}}},jQ:G(){D.54++},jX:G(){D.54--;if((D.54===0)&&(D.2H>=0)){D.9u()}},jR:G(X){D.9x(X);D.jX()},9x:G(X){D.2H=((X 2C 2x)?1:0);D.53[D.2H]=X;D.9u()},dD:G(){if(D.2H!=-1){if(!D.7l){14 Y B.1k.dj(D)}D.7l=1m;F}},3o:G(X){D.dD();if(X 2C B.1k.2t){14 Y 2x("2t jW 9v aB be 7r if jV jU jT jS of a 3o")}D.9x(X)},52:G(X){D.dD();u I=B.1k;if(X 2C I.2t){14 Y 2x("2t jW 9v aB be 7r if jV jU jT jS of a 3o")}if(!(X 2C 2x)){X=Y I.9p(X)}D.9x(X)},jP:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(fn,fn)},5Q:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(fn,O)},jA:G(fn){if(M.K>1){fn=B.J.2z.1w(O,M)}F D.9w(O,fn)},9w:G(cb,eb){if(D.7r){14 Y 2x("uQ uP 9v 2E be re-uO")}D.55.1c([cb,eb]);if(D.2H>=0){D.9u()}F D},9u:G(){u dC=D.55;u 56=D.2H;u X=D.53[56];u I=D;u cb=O;1M(dC.K>0&&D.54===0){u 2n=dC.2P();u f=2n[56];if(f===O){2V}1f{X=f(X);56=((X 2C 2x)?1:0);if(X 2C B.1k.2t){cb=G(X){I.jR(X)};D.jQ()}}1e(3O){56=1;if(!(3O 2C 2x)){3O=Y B.1k.9p(3O)}X=3O}}D.2H=56;D.53[56]=X;if(cb&&D.54){X.jP(cb);X.7r=1h}}};B.J.2l(B.1k,{dk:G(){F dB("("+M[0].jN+")")},dp:G(uN){u d=Y B.1k.2t();d.3o.1w(d,M);F d},9q:G(uM){u d=Y B.1k.2t();d.52.1w(d,M);F d},do:G(){u I=M.2U;if(!I.7q){u dy=[G(){F Y 7q()},G(){F Y dA("jO.dz")},G(){F Y dA("uL.dz")},G(){F Y dA("jO.dz.4.0")},G(){14 Y B.1k.dh("uK uJ 2E uI 7q")}];R(u i=0;i<dy.K;i++){u 1A=dy[i];1f{I.7q=1A;F 1A()}1e(e){}}}F I.7q()},dx:G(){},jK:G(d){if(D.uH==4){1f{D.5T=O}1e(e){1f{D.5T=B.1k.dx}1e(e){}}u 5U=O;1f{5U=D.jm;if(!5U&&B.J.7e(D.jN)){5U=jM}}1e(e){}if(5U==hQ||5U==jM){d.3o(D)}N{u 3O=Y B.1k.dg(D,"uG uF");if(3O.2y){d.52(3O)}N{d.52(3O)}}}},jL:G(2s){1f{2s.5T=O}1e(e){1f{2s.5T=B.1k.dx}1e(e){}}2s.uE()},dl:G(2s,7p){if(H(7p)=="L"||7p===O){7p=""}u m=B.J;u I=B.1k;u d=Y I.2t(m.2z(I.jL,2s));1f{2s.5T=m.1O(I.jK,2s,d);2s.uD(7p)}1e(e){1f{2s.5T=O}1e(uC){}d.52(e)}F d},dn:G(5F){u I=B.1k;u 2s=I.do();if(M.K>1){u m=B.J;u qs=m.dw.1w(O,m.1R(O,M,1));if(qs){5F+="?"+qs}}2s.cp("uB",5F,1h);F I.dl(2s)},jv:G(5F){u I=B.1k;u d=I.dn.1w(I,M);d=d.5Q(I.dk);F d},dm:G(jJ,dv){u d=Y B.1k.2t();u m=B.J;if(H(dv)!="L"){d.5Q(G(){F dv})}u jI=uA(m.1O("3o",d),28.8B(jJ*c5));d.7m=G(){1f{uz(jI)}1e(e){}};F d},ju:G(jH,1A){u m=B.J;u jG=m.2z.1w(m,m.1R(O,M,1));F B.1k.dm(jH).5Q(G(X){F jG()})}});B.1k.5O=G(){D.5S=[];D.4e=1m;D.id=D.7n()};B.1k.5O.1U={bX:B.1k.5O,uy:G(){d=Y B.1k.2t();if(D.4e){D.5S.1c(d)}N{D.4e=1h;d.3o(D)}F d},jF:G(){if(!D.4e){14 3p("ux to jF an jE 5O")}D.4e=1m;if(D.5S.K>0){D.4e=1h;D.5S.2P().3o(D)}},7n:B.J.4f(),U:G(){u 9t;if(D.4e){9t="4e, "+D.5S.K+" 5S"}N{9t="jE"}F"5O("+D.id+", "+9t+")"},1l:B.J.24("U")};B.1k.7i=G(2G,du,jC,jB,jD){D.2G=2G;D.9r=Y 7o(D.2G.K);D.55=[];D.id=D.7n();D.2H=-1;D.54=0;D.53=[O,O];D.7m=jD;D.7l=1m;if(D.2G.K===0&&!du){D.3o(D.9r)}D.dr=0;D.jz=du;D.jy=jC;D.jx=jB;u 9s=0;B.J.2r(B.J.1O(G(d){d.5Q(B.J.1O(D.dt,D),9s,1h);d.jA(B.J.1O(D.dt,D),9s,1m);9s+=1},D),D.2G)};B.J.2l(B.1k.7i.1U,B.1k.2t.1U);B.J.2l(B.1k.7i.1U,{dt:G(ds,7k,5R){D.9r[ds]=[7k,5R];D.dr+=1;if(D.2H!==0){if(7k&&D.jz){D.3o([ds,5R])}N{if(!7k&&D.jy){D.52(5R)}N{if(D.dr==D.2G.K){D.3o(D.9r)}}}}if(!7k&&D.jx){5R=O}F 5R}});B.1k.jt=G(jw){u d=Y B.1k.7i(jw,1m,1h,1m);d.5Q(G(dq){u 7j=[];R(u i=0;i<dq.K;i++){7j.1c(dq[i][1])}F 7j});F d};B.1k.jr=G(1A){u I=B.1k;u 5P;1f{u r=1A.1w(O,B.J.1R([],M,1));if(r 2C I.2t){5P=r}N{if(r 2C 2x){5P=I.9q(r)}N{5P=I.dp(r)}}}1e(e){5P=I.9q(e)}F 5P};B.1k.1z=["dj","di","dh","9p","dg","2t","dp","9q","do","dn","jv","dm","ju","dl","5O","7i","jt","jr"];B.1k.1W=["dk"];B.1k.2d=G(){u m=B.J;u ne=m.2z(m.jq,D);ne("dj",G(jp){D.jo=jp});ne("di",G(jn){D.jo=jn});ne("dh",G(1t){D.43=1t});ne("9p",G(1t){D.43=1t});ne("dg",G(2s,1t){D.2s=2s;D.43=1t;1f{D.2y=2s.jm}1e(e){}});D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.1k.2d();B.J.2Y(D,B.1k);if(H(1q)!="L"){1q.2X("B.S");1q.2M("B.15")}if(H(1x)!="L"){1x.26("B.15",[])}1f{if(H(B.15)=="L"){14""}}1e(e){14"B.S 3F on B.15!"}if(H(B.S)=="L"){B.S={}}B.S.1r="B.S";B.S.1Y="1.3.1";B.S.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.S.1l=G(){F D.1K()};B.S.1z=["d5","cr","b9","95","94","j3","9k","cX","cw","iT","iV","4X","9j","iQ","hS","cs","ia","i9","i8","i7","i6","i5","i4","hV","i3","i2","i1","cu","hW","ct","i0","hZ","hY","hX","P","io","il","ik","ij","cm","ih","ii","ig","ie","ic","cv","8d","A","6m","ib","1E","$","4q","aH","cO","cN","iM","5G","iK","9d","9e","iH","iD","9c","iB","cG","97","hU","hT","iw","jh","jb","j6","j5","jk","jl"];B.S.1W=["9b"];B.S.5N=G(w,h){D.w=w;D.h=h};B.S.5N.1U.U=G(){u U=B.J.U;F"{w: "+U(D.w)+", h: "+U(D.h)+"}"};B.S.5t=G(x,y){D.x=x;D.y=y};B.S.5t.1U.U=G(){u U=B.J.U;F"{x: "+U(D.x)+", y: "+U(D.y)+"}"};B.S.5t.1U.1l=G(){F D.U()};B.J.2l(B.S,{jl:G(Q,o){Q=B.S.1E(Q);B.S.4X(Q,{"1T":{"9o":o,"-hL-9o":o,"-uw-9o":o,"47":" uv(9o="+(o*3k)+")"}})},jk:G(){u d=Y B.S.5N();u w=B.S.3X;u b=B.S.1Z.5s;if(w.jj){d.w=w.jj;d.h=w.uu}N{if(b.dd.9n){d.w=b.dd.9n;d.h=b.dd.ji}N{if(b&&b.9n){d.w=b.9n;d.h=b.ji}}}F d},jh:G(Q){u I=B.S;if(H(Q.w)=="2y"||H(Q.h)=="2y"){F Y I.5N(Q.w||0,Q.h||0)}Q=I.1E(Q);if(!Q){F L}if(I.4q(Q,"3u")!="98"){F Y I.5N(Q.jg||0,Q.ci||0)}u s=Q.1T;u je=s.dc;u jf=s.6P;s.dc="fR";s.6P="j8";s.3u="";u jd=Q.jg;u jc=Q.ci;s.3u="98";s.6P=jf;s.dc=je;F Y I.5N(jd,jc)},jb:G(Q,4Z){u I=B.S;Q=I.1E(Q);if(!Q){F L}u c=Y I.5t(0,0);if(Q.x&&Q.y){c.x+=Q.x||0;c.y+=Q.y||0;F c}N{if(Q.3t===O||I.4q(Q,"3u")=="98"){F L}}u 51=O;u 2j=O;u d=B.S.1Z;u de=d.7Z;u b=d.5s;if(Q.ja){51=Q.ja();c.x+=51.2I+(de.6y||b.6y)-(de.8q||b.8q);c.y+=51.3D+(de.4C||b.4C)-(de.8p||b.8p)}N{if(d.j9){51=d.j9(Q);c.x+=51.x;c.y+=51.y}N{if(Q.8g){c.x+=Q.db;c.y+=Q.da;2j=Q.8g;if(2j!=Q){1M(2j){c.x+=2j.db;c.y+=2j.da;2j=2j.8g}}u ua=ut.us.8G();if((H(7h)!="L"&&4M(7h.ur())<9)||(ua.2A("uq")!=-1&&I.4q(Q,"6P")=="j8")){c.x-=b.db;c.y-=b.da}}}}if(H(4Z)!="L"){4Z=M.2U(4Z);if(4Z){c.x-=(4Z.x||0);c.y-=(4Z.y||0)}}if(Q.3t){2j=Q.3t}N{2j=O}1M(2j&&2j.j7!="uo"&&2j.j7!="co"){c.x-=2j.6y;c.y-=2j.4C;if(2j.3t){2j=2j.3t}N{2j=O}}F c},j6:G(Q,d9,7g){Q=B.S.1E(Q);if(H(7g)=="L"){7g="px"}B.S.4X(Q,{"1T":{"5A":d9.w+7g,"3V":d9.h+7g}})},j5:G(Q,d8,7f){Q=B.S.1E(Q);if(H(7f)=="L"){7f="px"}B.S.4X(Q,{"1T":{"2I":d8.x+7f,"3D":d8.y+7f}})},cr:G(){F B.S.3X},b9:G(){F B.S.1Z},95:G(2m,1A){u I=B.S;u d6=I.1Z;u d7=I.un;u W;1f{I.3X=2m;I.1Z=2m.2v;W=1A()}1e(e){I.3X=d7;I.1Z=d6;14 e}I.3X=d7;I.1Z=d6;F W},d5:G(Q){u 7d=[];u 7c=[];u m=B.J;u I=B.S;if(H(Q)=="L"||Q===O){Q=I.1Z}N{Q=I.1E(Q)}m.d4(Q,G(Q){u 1b=Q.1b;if(m.7e(1b)){u 4Y=Q.cD;if(4Y=="cv"&&(Q.1J=="um"||Q.1J=="uk")&&!Q.ip){F O}if(4Y=="ct"){if(Q.j4>=0){u 9m=Q.1S[Q.j4];7d.1c(1b);7c.1c((9m.3m)?9m.3m:9m.7X);F O}7d.1c(1b);7c.1c("");F O}if(4Y=="cu"||4Y=="P"||4Y=="8d"||4Y=="6m"){F Q.5h}7d.1c(1b);7c.1c(Q.3m||"");F O}F Q.5h});F[7d,7c]},94:G(1N,1A){u I=B.S;u d3=I.1Z;u W;1f{I.1Z=1N;W=1A()}1e(e){I.1Z=d3;14 e}I.1Z=d3;F W},j3:G(1b,j2,3y,j1){B.S.9b.5M(1b,j2,3y,j1)},9k:G(1j,7a){u im=B.15;u I=B.S;u 1Q=im.1Q;u iY=im.7b;u 4c=im.4c;u iX=I.9b;u iZ=I.9k;u iW=B.J.4d;1M(1h){if(H(1j)=="L"||1j===O){F O}if(H(1j.3T)!="L"&&1j.3T>0){F 1j}if(H(1j)=="2y"||H(1j)=="5L"){1j=1j.1l()}if(H(1j)=="1n"){F I.1Z.4S(1j)}if(H(1j.j0)=="G"){1j=1j.j0(7a);2V}if(H(1j)=="G"){1j=1j(7a);2V}u 9l=O;1f{9l=1Q(1j)}1e(e){}if(9l){F 4c(iZ,9l,iY(7a))}1f{1j=iX.3C(1j,7a);2V}1e(e){if(e!=iW){14 e}}F I.1Z.4S(1j.1l())}F L},iV:G(1j,79,iU){u o={};o[79]=iU;1f{F B.S.4X(1j,o)}1e(e){}F O},iT:G(1j,79){u I=B.S;u d2=I.4U.99[79];1j=I.1E(1j);1f{if(d2){F 1j[d2]}F 1j.fm(79)}1e(e){}F O},4X:G(1j,5K){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j)}if(5K){u d0=B.J.8Z;if(I.4U.6X){R(u k in 5K){u v=5K[k];if(H(v)=="3n"&&H(Q[k])=="3n"){d0(Q[k],v)}N{if(k.2W(0,2)=="on"){if(H(v)=="1n"){v=Y cZ(v)}Q[k]=v}N{Q.4p(k,v)}}}}N{u iS=I.4U.99;R(k in 5K){v=5K[k];u d1=iS[k];if(k=="1T"&&H(v)=="1n"){Q.1T.3x=v}N{if(H(d1)=="1n"){Q[d1]=v}N{if(H(Q[k])=="3n"&&H(v)=="3n"){d0(Q[k],v)}N{if(k.2W(0,2)=="on"){if(H(v)=="1n"){v=Y cZ(v)}Q[k]=v}N{Q.4p(k,v)}}}}}}}F Q},9j:G(1j){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j)}u 78=[I.9k(B.J.1R(O,M,1),Q)];u iR=B.J.2o;1M(78.K){u n=78.2P();if(H(n)=="L"||n===O){}N{if(H(n.3T)=="2y"){Q.2c(n)}N{78=iR(n,78)}}}F Q},iQ:G(1j){u Q=1j;u I=B.S;if(H(1j)=="1n"){Q=I.1E(1j);M[0]=Q}u cY;1M((cY=Q.6n)){Q.6S(cY)}if(M.K<2){F Q}N{F I.9j.1w(D,M)}},cX:G(1b,4b){u Q;u I=B.S;u m=B.J;if(H(4b)=="1n"||H(4b)=="2y"){u 3G=m.1R([1b,O],M,1);F M.2U.1w(D,3G)}if(H(1b)=="1n"){if(4b&&"1b"in 4b&&!I.4U.6X){1b=("<"+1b+" 1b=\\""+I.9c(4b.1b)+"\\">")}Q=I.1Z.2S(1b)}N{Q=1b}if(4b){I.4X(Q,4b)}if(M.K<=2){F Q}N{u 3G=m.1R([Q],M,2);F I.9j.1w(D,3G)}},cw:G(){u m=B.J;F m.2z.1w(D,m.1R([B.S.cX],M))},cs:G(5J,1d){u I=B.S;5J=I.1E(5J);u cW=5J.3t;if(1d){1d=I.1E(1d);cW.uj(1d,5J)}N{cW.6S(5J)}F 1d},1E:G(id){u I=B.S;if(M.K==1){F((H(id)=="1n")?I.1Z.hN(id):id)}N{F B.J.2r(I.1E,M)}},4q:G(iP,cV,cU){if(M.K==2){cU=cV}u I=B.S;u el=I.1E(iP);u 77=I.1Z;if(!el||el==77){F L}if(el.iO){F el.iO[cV]}if(H(77.5k)=="L"){F L}if(77.5k===O){F L}u 9i=77.5k.g4(el,O);if(H(9i)=="L"||9i===O){F L}F 9i.6q(cU)},aH:G(76,9g,4W){u I=B.S;if(H(76)=="L"||76===O){76="*"}if(H(4W)=="L"||4W===O){4W=I.1Z}4W=I.1E(4W);u 9h=(4W.fr(76)||I.1Z.1p);if(H(9g)=="L"||9g===O){F B.J.1R(O,9h)}u cR=[];R(u i=0;i<9h.K;i++){u cS=9h[i];u cT=cS.3M.2R(" ");R(u j=0;j<cT.K;j++){if(cT[j]==9g){cR.1c(cS);2K}}}F cR},iN:G(5I,9f){u W=G(){u cQ=M.2U.5H;R(u i=0;i<cQ.K;i++){if(cQ[i].1w(D,M)===1m){2K}}if(9f){1f{D[5I]=O}1e(e){}}};W.5H=[];F W},cO:G(cP,5I,1A,9f){u I=B.S;u 4V=cP[5I];u 75=4V;if(!(H(4V)=="G"&&H(4V.5H)=="3n"&&4V.5H!==O)){75=I.iN(5I,9f);if(H(4V)=="G"){75.5H.1c(4V)}cP[5I]=75}75.5H.1c(1A)},cN:G(1A){u I=B.S;I.cO(I.3X,"gh",1A,1h)},iM:G(74){u I=B.S;I.cN(G(){74=I.1E(74);if(74){74.ui()}})},5G:G(iL,cM){u I=B.S;u 1i=I.1E(iL);if(I.4U.6X){1i.4p("iq",cM)}N{1i.4p("3M",cM)}},iK:G(cL){u I=B.S;R(u i=1;i<M.K;i++){u 1i=I.1E(M[i]);if(!I.9d(1i,cL)){I.9e(1i,cL)}}},9d:G(iJ,73){u I=B.S;u 1i=I.1E(iJ);u 2F=1i.3M;if(2F.K===0){I.5G(1i,73);F 1h}if(2F==73){F 1m}u cK=1i.3M.2R(" ");R(u i=0;i<cK.K;i++){if(cK[i]==73){F 1m}}I.5G(1i,2F+" "+73);F 1h},9e:G(iI,cJ){u I=B.S;u 1i=I.1E(iI);u 2F=1i.3M;if(2F.K===0){F 1m}if(2F==cJ){I.5G(1i,"");F 1h}u 72=1i.3M.2R(" ");R(u i=0;i<72.K;i++){if(72[i]==cJ){72.4y(i,1);I.5G(1i,72.2b(" "));F 1h}}F 1m},iH:G(iG,iF,iE){u 1i=B.S.1E(iG);u X=B.S.9e(1i,iF);if(X){B.S.9d(1i,iE)}F X},iD:G(iC,uh){u 1i=B.S.1E(iC);u cI=1i.3M.2R(" ");R(u i=1;i<M.K;i++){u cH=1m;R(u j=0;j<cI.K;j++){if(cI[j]==M[i]){cH=1h;2K}}if(!cH){F 1m}}F 1h},9c:G(s){F s.23(/&/g,"&ug;").23(/"/g,"&uf;").23(/</g,"<").23(/>/g,">")},iB:G(2q){F B.S.cG(2q).2b("")},cG:G(2q,1g){if(H(1g)=="L"||1g===O){1g=[]}u 70=[2q];u I=B.S;u cB=I.9c;u iA=I.4U;1M(70.K){2q=70.hP();if(H(2q)=="1n"){1g.1c(2q)}N{if(2q.3T==1){1g.1c("<"+2q.cD.8G());u 71=[];u cF=iA(2q);R(u i=0;i<cF.K;i++){u a=cF[i];71.1c([" ",a.1b,"=\\"",cB(a.3m),"\\""])}71.iz();R(i=0;i<71.K;i++){u cE=71[i];R(u j=0;j<cE.K;j++){1g.1c(cE[j])}}if(2q.ue()){1g.1c(">");70.1c("</"+2q.cD.8G()+">");u cC=2q.5h;R(i=cC.K-1;i>=0;i--){70.1c(cC[i])}}N{1g.1c("/>")}}N{if(2q.3T==3){1g.1c(cB(2q.iv))}}}}F 1g},97:G(ix,cA){u m=B.J;u iy=m.1R(O,M,1);B.15.9a(m.47(O,m.2r(B.S.1E,iy)),G(cA){cA.1T.3u=ix})},iw:G(1j,iu){u W=[];(G(1j){u cn=1j.5h;if(cn){R(u i=0;i<cn.K;i++){M.2U.cz(D,cn[i])}}u cy=1j.iv;if(H(cy)=="1n"){W.1c(cy)}})(B.S.1E(1j));if(iu){F W}N{F W.2b("")}},2d:G(2m){u m=B.J;D.1Z=2v;D.3X=2m;D.9b=Y m.4a();u 6Z=D.1Z.2S("cj");u 2T;if(6Z&&6Z.6Y&&6Z.6Y.K>0){u it=m.47;2T=G(1j){F it(2T.ir,1j.6Y)};2T.cx={};B.15.9a(6Z.6Y,G(a){2T.cx[a.1b]=a.3m});2T.ir=G(a){F(2T.cx[a.1b]!=a.3m)};2T.6X=1m;2T.99={"iq":"3M","ip":"ud","uc":"ub","R":"u9"}}N{2T=G(1j){F 1j.6Y};2T.6X=1h;2T.99={}}D.4U=2T;u 1C=D.cw;D.io=1C("ul");D.il=1C("ol");D.ik=1C("li");D.ij=1C("td");D.cm=1C("tr");D.ii=1C("u8");D.ih=1C("u7");D.ig=1C("u6");D.ie=1C("u5");D.ic=1C("th");D.cv=1C("ck");D.8d=1C("cj");D.A=1C("a");D.6m=1C("4u");D.ib=1C("u4");D.ia=1C("2e");D.i9=1C("tt");D.i8=1C("4O");D.i7=1C("h1");D.i6=1C("h2");D.i5=1C("h3");D.i4=1C("br");D.i3=1C("hr");D.i2=1C("u3");D.i1=1C("u2");D.cu=1C("u1");D.P=1C("p");D.ct=1C("u0");D.i0=1C("hJ");D.hZ=1C("tZ");D.hY=1C("tY");D.hX=1C("tX");D.hW=1C("tW");D.hV=1C("tV");D.hU=m.2z(D.97,"98");D.hT=m.2z(D.97,"8c");D.hS=D.cs;D.$=D.1E;D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)}});B.S.2d(((H(2O)=="L")?D:2O));if(!B.3d){95=B.S.95;94=B.S.94}B.J.2Y(D,B.S);if(H(1q)!="L"){1q.2X("B.1I");1q.2M("B.1H");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.1H",[]);1x.26("B.J",[])}1f{if(H(B.J)=="L"||H(B.1H)=="L"){14""}}1e(e){14"B.1I 3F on B.J 3W B.1H!"}if(H(B.1I)=="L"){B.1I={}}B.1I.1r="B.1I";B.1I.1Y="1.3.1";B.1I.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1I.1l=G(){F D.1K()};B.1I.bY=G(6W){u m=B.1I;6W=!(!6W);if(m.3l&&m.3l.8Q!=6W){m.3l.hA();m.3l=O}if(!m.3l||m.3l.8P){m.3l=Y m.1I(6W,B.1H.2L)}F m.3l};B.1I.1I=G(4R,6V){if(H(6V)=="L"||6V===O){6V=B.1H.2L}D.2L=6V;u tU=B.J.2l;u c3=B.J.8Z;u 1O=B.J.1O;u hM=B.J.4L;u 2m=2O;u 6U="tT";if(H(B.S)!="L"){2m=B.S.cr()}if(!4R){u 5F=2m.tS.tR.2R("?")[0].23(/[:\\/.><&]/g,"hR");u 1b=6U+"hR"+5F;u 5D=2m.cp("",1b,"tQ,tP,3V=hQ");if(!5D){cq("tO tN to cp tM 2O tL to hP-up tK.");F L}5D.2v.fl("<!tJ co tI \\"-//tH//tG co 4.0 tF//tE\\" "+"\\"fq://fp.tD.fo/cm/tC/tB.tA\\">"+"<hO><5E><8Y>[B.1I]</8Y></5E>"+"<5s></5s></hO>");5D.2v.hG();5D.2v.8Y+=" "+2m.2v.8Y;2m=5D}u 1N=2m.2v;D.1N=1N;u 21=1N.hN(6U);u c4=!!21;if(21&&H(21.5B)!="L"){21.5B.2L=D.2L;21.5B.6K();F 21.5B}if(c4){u cl;1M((cl=21.6n)){21.6S(cl)}}N{21=1N.2S("4u");21.id=6U}21.5B=D;u 8T=1N.2S("ck");u 8S=1N.2S("ck");u 6O=1N.2S("2e");u 6N=1N.2S("2e");u 6M=1N.2S("2e");u 6L=1N.2S("2e");u 3L=1N.2S("4u");u 42=1N.2S("4u");u 8U=6U+"tz";D.8N=hM(D.8N);u 4T=[];u 6R=O;u cf=G(1t){u 6T=1t.3N;if(H(6T)=="2y"){6T=B.1H.5C[6T]}F 6T};u cd=G(1t){F 1t.3z.2b(" ")};u ca=1O(G(1t){u 8W=cf(1t);u 7X=cd(1t);u c=D.8N[8W];u p=1N.2S("cj");p.3M="B-49 B-5C-"+8W;p.1T.3x="ty: 2N; 4F-8X: -hL-4O-3y; 4F-8X: -o-4O-3y; 4F-8X: 4O-3y; 4F-8X: 4O-tx; hK-3y: 2K-hK; 3y-hJ: tw; 3U: "+c;p.2c(1N.4S(8W+": "+7X));42.2c(p);42.2c(1N.2S("br"));if(3L.ci>3L.hI){3L.4C=0}N{3L.4C=3L.hI}},D);u hD=G(1t){4T[4T.K]=1t;ca(1t)};u hF=G(){u cg,ce;1f{cg=Y 8V(8T.3m);ce=Y 8V(8S.3m)}1e(e){ch("2x in 47 tv: "+e.43);F O}F G(1t){F(cg.hH(cf(1t))&&ce.hH(cd(1t)))}};u cc=G(){1M(42.6n){42.6S(42.6n)}};u hB=G(){4T=[];cc()};u bZ=1O(G(){if(D.8P){F}D.8P=1h;if(B.1I.3l==D){B.1I.3l=O}D.2L.c9(8U);21.5B=O;if(4R){21.3t.6S(21)}N{D.2m.hG()}},D);u c7=G(){cc();R(u i=0;i<4T.K;i++){u 1t=4T[i];if(6R===O||6R(1t)){ca(1t)}}};D.6K=G(){6R=hF();c7();D.2L.c9(8U);D.2L.hE(8U,6R,hD)};u c0=1O(G(){4T=D.2L.c8();c7()},D);u c2=1O(G(6Q){6Q=6Q||2O.6D;2h=6Q.6w||6Q.8t;if(2h==13){D.6K()}},D);u 31="3u: 8c; z-c6: c5; 2I: 2N; 6f: 2N; 6P: tu; 5A: 3k%; he-3U: 4F; c1: "+D.8O;if(4R){31+="; 3V: ts; 3E-3D: fO 8a 8y"}N{31+="; 3V: 3k%;"}21.1T.3x=31;if(!c4){1N.5s.2c(21)}31={"3x":"5A: 33%; 3u: 8Q; c1: "+D.8O};c3(8T,{"3m":"8L|8M|8K|8J|8I","hC":c2,"1T":31});21.2c(8T);c3(8S,{"3m":".*","hC":c2,"1T":31});21.2c(8S);31="5A: 8%; 3u:8Q; c1: "+D.8O;6O.2c(1N.4S("tq"));6O.8R=1O("6K",D);6O.1T.3x=31;21.2c(6O);6N.2c(1N.4S("tp"));6N.8R=c0;6N.1T.3x=31;21.2c(6N);6M.2c(1N.4S("tn"));6M.8R=hB;6M.1T.3x=31;21.2c(6M);6L.2c(1N.4S("tm"));6L.8R=bZ;6L.1T.3x=31;21.2c(6L);3L.1T.3x="fS: tk; 5A: 3k%";42.1T.3x="5A: 3k%; 3V: "+(4R?"tj":"3k%");3L.2c(42);21.2c(3L);D.6K();c0();if(4R){D.2m=L}N{D.2m=2m}D.8Q=4R;D.hA=bZ;D.8P=1m;F D};B.1I.1I.1U={"8O":"ti tg,tf-te","8N":{"8M":"1v","8L":"gU","8K":"1F","8J":"8y","8I":"bx"}};B.1I.1W=["1I"];B.1I.1z=["bY"];B.1I.2d=G(){D.2k={":3e":D.1z,":1p":B.J.2o(D.1z,D.1W)};B.J.3f(D);B.1I.3l=O};B.1I.2d();B.J.2Y(D,B.1I);if(H(1q)!="L"){1q.2X("B.V");1q.2M("B.J")}if(H(1x)!="L"){1x.26("B.J",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.V 3F on B.J"}if(H(B.V)=="L"){B.V={}}B.V.1r="B.V";B.V.1Y="1.3.1";B.V.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.V.1l=G(){F D.1K()};B.V.V=G(1v,hz,1F,6J){if(H(6J)=="L"||6J===O){6J=1}D.1B={r:1v,g:hz,b:1F,a:6J}};B.V.V.1U={bX:B.V.V,tc:G(hy){u 1B=D.1B;u m=B.V;F m.V.3Y(1B.r,1B.g,1B.b,hy)},tb:G(1o){u 1G=D.41();1G.h=1o;u m=B.V;F m.V.4H(1G)},ta:G(hx){u 1G=D.41();1G.s=hx;u m=B.V;F m.V.4H(1G)},t9:G(hw){u 1G=D.41();1G.l=hw;u m=B.V;F m.V.4H(1G)},t8:G(hv){u 1G=D.41();1G.l=28.29(1G.l-hv,0);u m=B.V;F m.V.4H(1G)},t7:G(hu){u 1G=D.41();1G.l=28.2a(1G.l+hu,1);u m=B.V;F m.V.4H(1G)},fJ:G(ht,5z){if(H(5z)=="L"||5z===O){5z=0.5}u sf=1-5z;u s=D.1B;u d=ht.1B;u df=5z;F B.V.V.3Y((s.r*sf)+(d.r*df),(s.g*sf)+(d.g*df),(s.b*sf)+(d.b*df),(s.a*sf)+(d.a*df))},h4:G(hs){u a=D.6r();u b=hs.6r();F B.J.2f([a.r,a.g,a.b,a.a],[b.r,b.g,b.b,b.a])},hq:G(){F D.41().b>0.5},t6:G(){F(!D.hq())},t5:G(){u c=D.41();u 2Z=B.V.6F;u W=D.ho;if(!W){u 5y=(2Z(c.h,bF).6I(0)+","+2Z(c.s,3k).hp(4)+"%"+","+2Z(c.l,3k).hp(4)+"%");u a=c.a;if(a>=1){a=1;W="1G("+5y+")"}N{if(a<=0){a=0}W="t4("+5y+","+a+")"}D.ho=W}F W},hl:G(){u c=D.1B;u 2Z=B.V.6F;u W=D.hn;if(!W){u 5y=(2Z(c.r,3h).6I(0)+","+2Z(c.g,3h).6I(0)+","+2Z(c.b,3h).6I(0));if(c.a!=1){W="t3("+5y+","+c.a+")"}N{W="1B("+5y+")"}D.hn=W}F W},6r:G(){F B.J.4L(D.1B)},t2:G(){u m=B.V;u c=D.1B;u 2Z=B.V.6F;u W=D.hm;if(!W){W=("#"+m.6E(2Z(c.r,3h))+m.6E(2Z(c.g,3h))+m.6E(2Z(c.b,3h)));D.hm=W}F W},t1:G(){u 2Q=D.2Q;u c=D.1B;if(H(2Q)=="L"||2Q===O){2Q=B.V.bA(D.1B);D.2Q=2Q}F B.J.4L(2Q)},41:G(){u 1G=D.1G;u c=D.1B;if(H(1G)=="L"||1G===O){1G=B.V.bC(D.1B);D.1G=1G}F B.J.4L(1G)},1l:G(){F D.hl()},U:G(){u c=D.1B;u hk=[c.r,c.g,c.b,c.a];F D.bX.1r+"("+hk.2b(", ")+")"}};B.J.2l(B.V.V,{3Y:G(1v,bW,1F,8H){u hj=B.V.V;if(M.K==1){u 1B=1v;1v=1B.r;bW=1B.g;1F=1B.b;if(H(1B.a)=="L"){8H=L}N{8H=1B.a}}F Y hj(1v,bW,1F,8H)},4H:G(1o,t0,sZ,sY){u m=B.V;F m.V.3Y(m.bB.1w(m,M))},sX:G(1o,sW,sV,sU){u m=B.V;F m.V.3Y(m.bz.1w(m,M))},hi:G(1b){u 8F=B.V.V;if(1b.3Z(0)=="\\""){1b=1b.3H(1,1b.K-2)}u bV=8F.by[1b.8G()];if(H(bV)=="1n"){F 8F.bT(bV)}N{if(1b=="aP"){F 8F.sT()}}F O},8f:G(4Q){u I=B.V.V;u bU=4Q.3H(0,3);if(bU=="1B"){F I.h9(4Q)}N{if(bU=="1G"){F I.h8(4Q)}N{if(4Q.3Z(0)=="#"){F I.bT(4Q)}}}F I.hi(4Q)},bT:G(4P){if(4P.3Z(0)=="#"){4P=4P.2W(1)}u 8E=[];u i,5x;if(4P.K==3){R(i=0;i<3;i++){5x=4P.3H(i,1);8E.1c(3w(5x+5x,16)/3h)}}N{R(i=0;i<6;i+=2){5x=4P.3H(i,2);8E.1c(3w(5x,16)/3h)}}u bS=B.V.V;F bS.3Y.1w(bS,8E)},bG:G(4O,hf,hg,4N){if(4N.2A(4O)===0){4N=4N.2W(4N.2A("(",3)+1,4N.K-1)}u bR=4N.2R(/\\s*,\\s*/);u bP=[];R(u i=0;i<bR.K;i++){u c=bR[i];u 2i;u bQ=c.2W(c.K-3);if(c.3Z(c.K-1)=="%"){2i=0.bE*4M(c.2W(0,c.K-1))}N{if(bQ=="sS"){2i=4M(c)/bF}N{if(bQ=="sR"){2i=4M(c)/(28.sQ*2)}N{2i=hg[i]*4M(c)}}}bP.1c(2i)}F D[hf].1w(D,bP)},bN:G(Q,sP,sO){u d=B.S;u 2F=B.V.V;R(Q=d.1E(Q);Q;Q=Q.3t){u bO=d.4q.1w(d,M);if(!bO){2V}u 8D=2F.8f(bO);if(!8D){2K}if(8D.6r().a>0){F 8D}}F O},ba:G(Q){u 2F=B.V.V;F 2F.bN(Q,"aZ","he-3U")||2F.sN()},sM:G(Q){u 2F=B.V.V;F 2F.bN(Q,"3U","3U")||2F.sL()},sK:G(){F B.J.4L(B.V.V.by)}});B.J.2l(B.V,{6F:G(v,8C){v*=8C;if(v<0){F 0}N{if(v>8C){F 8C}N{F v}}},hc:G(n1,n2,1o){if(1o>6){1o-=6}N{if(1o<0){1o+=6}}u 2i;if(1o<1){2i=n1+(n2-n1)*1o}N{if(1o<3){2i=n2}N{if(1o<4){2i=n1+(n2-n1)*(4-1o)}N{2i=n1}}}F 2i},bz:G(1o,5w,3i,bM){if(M.K==1){u 2Q=1o;1o=2Q.h;5w=2Q.s;3i=2Q.v;bM=2Q.a}u 1v;u 3K;u 1F;if(5w===0){1v=0;3K=0;1F=0}N{u i=28.8B(1o*6);u f=(1o*6)-i;u p=3i*(1-5w);u q=3i*(1-(5w*f));u t=3i*(1-(5w*(1-f)));hd(i){3j 1:1v=q;3K=3i;1F=p;2K;3j 2:1v=p;3K=3i;1F=t;2K;3j 3:1v=p;3K=q;1F=3i;2K;3j 4:1v=t;3K=p;1F=3i;2K;3j 5:1v=3i;3K=p;1F=q;2K;3j 6:3j 0:1v=3i;3K=t;1F=p;2K}}F{r:1v,g:3K,b:1F,a:bM}},bB:G(1o,5v,3v,bL){if(M.K==1){u 1G=1o;1o=1G.h;5v=1G.s;3v=1G.l;bL=1G.a}u 1v;u 8A;u 1F;if(5v===0){1v=3v;8A=3v;1F=3v}N{u m2;if(3v<=0.5){m2=3v*(1+5v)}N{m2=3v+5v-(3v*5v)}u m1=(2*3v)-m2;u f=B.V.hc;u h6=1o*6;1v=f(m1,m2,h6+2);8A=f(m1,m2,h6);1F=f(m1,m2,h6-2)}F{r:1v,g:8A,b:1F,a:bL}},bA:G(1v,4K,1F,bK){if(M.K==1){u 1B=1v;1v=1B.r;4K=1B.g;1F=1B.b;bK=1B.a}u 29=28.29(28.29(1v,4K),1F);u 2a=28.2a(28.2a(1v,4K),1F);u 1o;u 8z;u hb=29;if(2a==29){1o=0;8z=0}N{u 6H=(29-2a);8z=6H/29;if(1v==29){1o=(4K-1F)/6H}N{if(4K==29){1o=2+((1F-1v)/6H)}N{1o=4+((1v-4K)/6H)}}1o/=6;if(1o<0){1o+=1}if(1o>1){1o-=1}}F{h:1o,s:8z,v:hb,a:bK}},bC:G(1v,4J,1F,bI){if(M.K==1){u 1B=1v;1v=1B.r;4J=1B.g;1F=1B.b;bI=1B.a}u 29=28.29(1v,28.29(4J,1F));u 2a=28.2a(1v,28.2a(4J,1F));u 1o;u 6G;u bJ=(29+2a)/2;u 4I=29-2a;if(4I===0){1o=0;6G=0}N{if(bJ<=0.5){6G=4I/(29+2a)}N{6G=4I/(2-29-2a)}if(1v==29){1o=(4J-1F)/4I}N{if(4J==29){1o=2+((1F-1v)/4I)}N{1o=4+((1v-4J)/4I)}}1o/=6;if(1o<0){1o+=1}if(1o>1){1o-=1}}F{h:1o,s:6G,l:bJ,a:bI}},6E:G(1P){1P=28.ha(1P);u bH=1P.1l(16);if(1P<16){F"0"+bH}F bH},2d:G(){u m=B.J;D.V.h9=m.1O(D.V.bG,D.V,"1B","3Y",[1/3h,1/3h,1/3h,1]);D.V.h8=m.1O(D.V.bG,D.V,"1G","4H",[1/bF,0.bE,0.bE,1]);u 4G=1/3;u bD={8y:[0,0,0],1F:[0,0,1],gY:[0.6,0.4,0.2],gX:[0,1,1],sJ:[4G,4G,4G],gR:[0.5,0.5,0.5],bx:[0,1,0],sI:[2*4G,2*4G,2*4G],gN:[1,0,1],gL:[1,0.5,0],gK:[0.5,0,0.5],1v:[1,0,0],aP:[0,0,0,0],4F:[1,1,1],gI:[1,1,0]};u h7=G(1b,r,g,b,a){u W=D.3Y(r,g,b,a);D[1b]=G(){F W};F W};R(u k in bD){u 1b=k+"V";u h5=m.2o([h7,D.V,1b],bD[k]);D.V[1b]=m.1O.1w(O,h5)}u h0=G(){R(u i=0;i<M.K;i++){if(!(M[i]2C V)){F 1m}}F 1h};u gZ=G(a,b){F a.h4(b)};m.3f(D);m.5u(D.V.1r,h0,gZ);D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)}}});B.V.1z=["V"];B.V.1W=["6F","bC","bB","bA","bz","6E"];B.V.2d();B.J.2Y(D,B.V);B.V.V.by={sH:"#sG",sF:"#sE",sD:"#gW",sC:"#sB",sA:"#sz",sy:"#sx",sw:"#sv",8y:"#su",st:"#sr",1F:"#sq",sp:"#so",gY:"#sn",sm:"#sl",sk:"#sj",si:"#sh",sg:"#se",sd:"#sc",sb:"#sa",s9:"#s8",s7:"#s6",gX:"#gW",s5:"#s4",s3:"#s2",s1:"#s0",rZ:"#gV",rY:"#rX",rW:"#gV",rV:"#rU",rT:"#rS",rR:"#rQ",rP:"#rO",rN:"#rM",gU:"#rL",rK:"#rJ",rI:"#rH",rG:"#rF",rE:"#gT",rD:"#gT",rC:"#rB",rA:"#rz",ry:"#rx",rw:"#rv",ru:"#gS",rt:"#gS",rs:"#rr",rq:"#rp",ro:"#rn",rm:"#rl",rk:"#gM",rj:"#ri",rh:"#rg",rf:"#rd",rc:"#rb",gR:"#gQ",bx:"#ra",r9:"#r8",r7:"#gQ",r6:"#r5",r4:"#r3",r2:"#r1",r0:"#qZ",qY:"#qX",qW:"#qV",qU:"#qT",qS:"#qR",qQ:"#qP",qO:"#qN",qM:"#qL",qK:"#qJ",qI:"#qH",qG:"#qF",qE:"#gP",qD:"#qC",qB:"#gP",qA:"#qz",qy:"#qx",qw:"#qv",qu:"#qt",qr:"#gO",qq:"#gO",qp:"#qo",qn:"#qm",ql:"#qk",qj:"#qi",qh:"#qg",gN:"#gM",qf:"#qe",qd:"#qc",qb:"#qa",q9:"#q8",q7:"#q6",q5:"#q4",q3:"#q2",q1:"#q0",pZ:"#pY",pX:"#pW",pV:"#pU",pT:"#pS",pR:"#pQ",pP:"#pO",pN:"#pM",pL:"#pK",pJ:"#pI",pH:"#pG",pF:"#pE",gL:"#pD",pC:"#pB",pA:"#pz",py:"#pw",pv:"#pu",pt:"#ps",pr:"#pq",pp:"#po",pn:"#pm",pl:"#pj",pi:"#ph",pg:"#pf",pe:"#pd",gK:"#pc",1v:"#pb",pa:"#p9",p8:"#p7",p6:"#p5",p4:"#p3",p2:"#p1",p0:"#oZ",oY:"#oX",oW:"#oV",oU:"#oT",oS:"#oR",oQ:"#oP",oO:"#gJ",oN:"#gJ",oM:"#oL",oK:"#oJ",oI:"#oH",oG:"#oF",oE:"#oD",oC:"#oB",oA:"#oz",oy:"#ox",ow:"#ov",ou:"#ot",4F:"#os",oq:"#op",gI:"#oo",om:"#ok"};if(H(1q)!="L"){1q.2X("B.1u");1q.2M("B.J");1q.2M("B.S")}if(H(1x)!="L"){1x.26("B.J",[]);1x.26("B.S",[])}1f{if(H(B.J)=="L"){14""}}1e(e){14"B.1u 3F on B.J!"}1f{if(H(B.S)=="L"){14""}}1e(e){14"B.1u 3F on B.S!"}if(H(B.1u)=="L"){B.1u={}}B.1u.1r="B.1u";B.1u.1Y="1.3.1";B.1u.4x=[];B.1u.bq=G(1d,e){D.1L=e||2O.6D;D.gH=1d};B.J.2l(B.1u.bq.1U,{1K:G(){u U=B.J.U;u 1y="{6D(): "+U(D.6D())+", 1d(): "+U(D.1d())+", 1J(): "+U(D.1J())+", 8x(): "+U(D.8x())+", 4E(): "+"{8w: "+U(D.4E().8w)+", 8v: "+U(D.4E().8v)+", 8u: "+U(D.4E().8u)+", 2P: "+U(D.4E().2P)+", bw: "+U(D.4E().bw)+"}";if(D.1J()&&D.1J().2A("2h")===0){1y+=", 2h(): {3J: "+U(D.2h().3J)+", 1n: "+U(D.2h().1n)+"}"}if(D.1J()&&(D.1J().2A("3I")===0||D.1J().2A("gE")!=-1||D.1J()=="gD")){1y+=", 3I(): {4D: "+U(D.3I().4D)+", 6A: "+U(D.3I().6A);if(D.1J()!="gC"){1y+=", 2e: {2I: "+U(D.3I().2e.2I)+", 6v: "+U(D.3I().2e.6v)+", 3g: "+U(D.3I().2e.3g)+"}}"}N{1y+="}"}}if(D.1J()=="gG"||D.1J()=="gF"){1y+=", 6C(): "+U(D.6C())}1y+="}";F 1y},1l:G(){F D.1K()},1d:G(){F D.gH},6D:G(){F D.1L},1J:G(){F D.1L.1J||L},8x:G(){F D.1L.8x||D.1L.oj},6C:G(){if(D.1J()=="gG"){F(D.1L.6C||D.1L.aW)}N{if(D.1J()=="gF"){F(D.1L.6C||D.1L.oi)}}F L},4E:G(){u m={};m.8w=D.1L.oh;m.8v=D.1L.og;m.8u=D.1L.oe||1m;m.2P=D.1L.od;m.bw=m.8w||m.8v||m.2P||m.8u;F m},2h:G(){u k={};if(D.1J()&&D.1J().2A("2h")===0){if(D.1J()=="oc"||D.1J()=="ob"){k.3J=D.1L.8t;k.1n=(B.1u.5r[k.3J]||"oa");F k}N{if(D.1J()=="o9"){k.3J=0;k.1n="";if(H(D.1L.6B)!="L"&&D.1L.6B!==0&&!B.1u.bv[D.1L.6B]){k.3J=D.1L.6B;k.1n=bu.bt(k.3J)}N{if(D.1L.8t&&H(D.1L.6B)=="L"){k.3J=D.1L.8t;k.1n=bu.bt(k.3J)}}F k}}}F L},3I:G(){u m={};u e=D.1L;if(D.1J()&&(D.1J().2A("3I")===0||D.1J().2A("gE")!=-1||D.1J()=="gD")){m.6A=Y B.S.5t(0,0);if(e.6z||e.6x){m.6A.x=(!e.6z||e.6z<0)?0:e.6z;m.6A.y=(!e.6x||e.6x<0)?0:e.6x}m.4D=Y B.S.5t(0,0);if(e.8s||e.8r){m.4D.x=(!e.8s||e.8s<0)?0:e.8s;m.4D.y=(!e.8r||e.8r<0)?0:e.8r}N{u de=B.S.1Z.7Z;u b=B.S.1Z.5s;m.4D.x=e.6z+(de.6y||b.6y)-(de.8q||b.8q);m.4D.y=e.6x+(de.4C||b.4C)-(de.8p||b.8p)}if(D.1J()!="gC"){m.2e={};m.2e.2I=1m;m.2e.3g=1m;m.2e.6v=1m;if(e.6w){m.2e.2I=(e.6w==1);m.2e.6v=(e.6w==2);m.2e.3g=(e.6w==3)}N{m.2e.2I=!!(e.2e&1);m.2e.3g=!!(e.2e&2);m.2e.6v=!!(e.2e&4)}}F m}F L},2J:G(){D.8o();D.8n()},8o:G(){if(D.1L.8o){D.1L.8o()}N{D.1L.o8=1h}},8n:G(){if(D.1L.8n){D.1L.8n()}N{D.1L.o7=1m}}});B.1u.bv={3:"gz",o6:"gA",o5:"gy",o4:"gx",o3:"gw",o2:"gv",o1:"gu",o0:"gs",nZ:"gr",nY:"gq",nX:"gp",nW:"go"};R(i=gB;i<=nV;i++){B.1u.bv[i]="gk"+(i-gB+1)}B.1u.5r={8:"nU",9:"nT",12:"gA",13:"gz",16:"nS",17:"nR",18:"nQ",19:"nP",20:"nO",27:"nN",32:"nM",33:"gy",34:"gx",35:"gw",36:"gv",37:"gu",38:"gs",39:"gr",40:"gq",44:"nL",45:"gp",46:"go",59:"gn",91:"nK",92:"nJ",93:"nI",nH:"nG",nF:"nE",nD:"nC-gm",nB:"nA",nz:"ny",nx:"nw",nv:"nu",nt:"gn",ns:"nr",nq:"np",nn:"nm-gm",nl:"nk",nj:"ni",nh:"ng",nf:"nd",nc:"nb",na:"n9",n8:"n7"};R(u i=48;i<=57;i++){B.1u.5r[i]="gl"+(i-48)}R(i=65;i<=90;i++){B.1u.5r[i]="gl"+bu.bt(i)}R(i=96;i<=n6;i++){B.1u.5r[i]="n5"+(i-96)}R(i=gj;i<=n4;i++){B.1u.5r[i]="gk"+(i-gj+1)}B.J.2l(B.1u,{1K:G(){F"["+D.1r+" "+D.1Y+"]"},1l:G(){F D.1K()},g7:G(){u I=B.1u;u bs=I.4x;R(u i=0;i<bs.K;i++){I.6t(bs[i])}gi I.4x;1f{2O.gh=L}1e(e){}1f{2O.g8=L}1e(e){}},gb:G(1d,1A,1i,gg){u E=B.1u.bq;if(!gg){F B.J.1O(1A,1i)}1i=1i||1d;if(H(1A)=="1n"){F G(gf){1i[1A].1w(1i,[Y E(1d,gf)])}}N{F G(gd){1A.1w(1i,[Y E(1d,gd)])}}},6s:G(1d,2D,5q,4B){1d=B.S.1E(1d);u I=B.1u;if(H(2D)!="1n"){14 Y 2x("\'2D\' 5p be a 1n")}u 1i=O;u 1A=O;if(H(4B)!="L"){1i=5q;1A=4B;if(H(4B)=="1n"){if(H(5q[4B])!="G"){14 Y 2x("\'bp\' 5p be a G on \'gc\'")}}N{if(H(4B)!="G"){14 Y 2x("\'bp\' 5p be a G or 1n")}}}N{if(H(5q)!="G"){14 Y 2x("\'gc\' 5p be a G if \'bp\' is 2E n3")}N{1A=5q}}if(H(1i)=="L"||1i===O){1i=1d}u bm=!!(1d.bo||1d.bn);u 8m=I.gb(1d,1A,1i,bm);if(1d.bo){1d.bo(2D.3H(2),8m,1m)}N{if(1d.bn){1d.bn(2D,8m)}}u bk=[1d,2D,8m,bm,5q,4B];I.4x.1c(bk);F bk},6t:G(6u){if(!6u[3]){F}u 1d=6u[0];u 2D=6u[1];u bj=6u[2];if(1d.ga){1d.ga(2D.3H(2),bj,1m)}N{if(1d.g9){1d.g9(2D,bj)}N{14 Y 2x("\'1d\' 5p be a S n0")}}},8j:G(bh){u I=B.1u;u 5o=I.4x;u m=B.J;if(M.K>1){u 1d=B.S.1E(M[0]);u 2D=M[1];u 1i=M[2];u 1A=M[3];R(u i=5o.K-1;i>=0;i--){u o=5o[i];if(o[0]===1d&&o[1]===2D&&o[4]===1i&&o[5]===1A){I.6t(o);5o.4y(i,1);F 1h}}}N{u 5n=m.bi(5o,bh);if(5n>=0){I.6t(bh);5o.4y(5n,1);F 1h}}F 1m},8i:G(1d,2D){1d=B.S.1E(1d);u m=B.J;u 8l=m.bg(m.1R(O,M,1));u I=B.1u;u bd=I.6t;u 4z=I.4x;if(8l.K===0){R(u i=4z.K-1;i>=0;i--){u 4A=4z[i];if(4A[0]===1d){bd(4A);4z.4y(i,1)}}}N{u bf={};R(u i=0;i<8l.K;i++){bf[8l[i]]=1h}R(u i=4z.K-1;i>=0;i--){u 4A=4z[i];if(4A[0]===1d&&4A[1]in bf){bd(4A);4z.4y(i,1)}}}},8h:G(1d,2D){u bc=B.1u.4x;1d=B.S.1E(1d);u 3G=B.J.1R(O,M,2);u 5m=[];R(u i=0;i<bc.K;i++){u 8k=bc[i];if(8k[0]===1d&&8k[1]===2D){1f{8k[2].1w(1d,3G)}1e(e){5m.1c(e)}}}if(5m.K==1){14 5m[0]}N{if(5m.K>1){u e=Y 2x("mZ bb mY in mX \'2D\', mW bb mV");e.bb=5m;14 e}}}});B.1u.1W=[];B.1u.1z=["6s","8j","8h","8i"];B.1u.2d=G(2m){u m=B.J;D.1Z=2v;D.3X=2m;1f{D.6s(2O,"g8",D.g7)}1e(e){}D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)};m.3f(D)};B.1u.2d(D);if(!B.3d){6s=B.1u.6s;8j=B.1u.8j;8i=B.1u.8i;8h=B.1u.8h}B.J.2Y(D,B.1u);if(H(1q)!="L"){1q.2X("B.1X");1q.2M("B.J");1q.2M("B.S");1q.2M("B.V")}if(H(1x)!="L"){1x.26("B.J",[]);1x.26("B.S",[]);1x.26("B.V",[])}1f{if(H(B.J)=="L"||H(B.S)=="L"||H(B.V)=="L"){14""}}1e(e){14"B.1X 3F on B.J, B.S 3W B.V!"}if(H(B.1X)=="L"){B.1X={}}B.1X.1r="B.1X";B.1X.1Y="1.3.1";B.1X.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.1X.1l=G(){F D.1K()};B.1X.aI=G(e,g6){e=B.S.1E(e);D.fN(g6);if(D.1S.fL){e=D.g5(e)}u 4w=D.1S.3U;u C=B.V.V;if(D.1S.3U=="aW"){4w=C.ba(e)}N{if(!(4w 2C C)){4w=C.8f(4w)}}D.82=(4w.6r().a<=0);u 5l=D.1S.aV;if(D.1S.aV=="fM"){5l=C.ba(e.8g)}N{if(!(5l 2C C)){5l=C.8f(5l)}}D.g3(e,4w,5l)};B.1X.aI.1U={g5:G(e){u mU=e.3t;u 1N=B.S.b9();if(H(1N.5k)=="L"||1N.5k===O){F e}u 4v=1N.5k.g4(e,O);if(H(4v)=="L"||4v===O){F e}u b8=B.S.6m({"1T":{3u:"8c",mT:4v.6q("6p-3D"),85:4v.6q("6p-3g"),mS:4v.6q("6p-6f"),86:4v.6q("6p-2I"),6p:"2N"}});b8.6o=e.6o;e.6o="";e.2c(b8);F e},g3:G(e,b7,8e){if(D.1S.3E){D.g2(e,8e)}if(D.fy()){D.fX(e,b7,8e)}if(D.fx()){D.fV(e,b7,8e)}},g2:G(el,g1){u b6="6l 8a "+D.aQ(g1);u g0="3E-2I: "+b6;u fZ="3E-3g: "+b6;u fY="1T=\'"+g0+";"+fZ+"\'";el.6o="<4u "+fY+">"+el.6o+"</4u>"},fX:G(el,fW,b5){u b4=D.b1(b5);R(u i=0;i<D.1S.89;i++){b4.2c(D.b0(fW,b5,i,"3D"))}el.1T.mR=0;el.mQ(b4,el.6n)},fV:G(el,fU,b3){u b2=D.b1(b3);R(u i=(D.1S.89-1);i>=0;i--){b2.2c(D.b0(fU,b3,i,"6f"))}el.1T.mP=0;el.2c(b2)},b1:G(fT){u 2q=B.S;F 2q.6m({1T:{aZ:fT.1l()}})},b0:G(aY,fQ,n,aX){u 6k=B.S.8d();u 2p=6k.1T;2p.aZ=aY.1l();2p.3u="8c";2p.3V="6l";2p.fS="fR";2p.mO="6l";u 8b=D.aQ(aY,fQ);if(D.1S.3E&&n===0){2p.mN="8a";2p.mM="6l";2p.84="2N";2p.83="2N";2p.mL="2N";2p.3V="2N";2p.fP=8b.1l()}N{if(8b){2p.fP=8b.1l();2p.mK="8a";2p.mJ="2N 6l"}}if(!D.1S.4r&&(n==(D.1S.89-1))){2p.3V="fO"}D.fI(6k,n,aX);D.fG(6k,n,aX);F 6k},fN:G(fK){D.1S={6g:"1p",3U:"aW",aV:"fM",5j:1h,3E:1m,4r:1m,fL:1m};B.J.2l(D.1S,fK);D.1S.89=(D.1S.4r?2:4)},aL:G(){u 88=D.1S.6g;if(D.6h(88,"1p","3D")){F""}u aU=(88.2A("tl")!=-1);u aT=(88.2A("tr")!=-1);if(aU&&aT){F""}if(aU){F"2I"}if(aT){F"3g"}F""},aK:G(){u 87=D.1S.6g;if(D.6h(87,"1p","6f")){F""}u aS=(87.2A("bl")!=-1);u aR=(87.2A("br")!=-1);if(aS&&aR){F""}if(aS){F"2I"}if(aR){F"3g"}F""},aQ:G(aN,aO){if(aN=="aP"){F aO}N{if(D.1S.3E){F D.1S.3E}N{if(D.1S.5j){F aO.fJ(aN)}}}F""},fI:G(el,n,fH){u 6j=D.fE(n)+"px";u aM=(fH=="3D"?D.aL():D.aK());u 4t=el.1T;if(aM=="2I"){4t.86=6j;4t.85="2N"}N{if(aM=="3g"){4t.85=6j;4t.86="2N"}N{4t.86=6j;4t.85=6j}}},fG:G(el,n,fF){u 6i=D.fz(n)+"px";u aJ=(fF=="3D"?D.aL():D.aK());u 4s=el.1T;if(aJ=="2I"){4s.84=6i;4s.83="2N"}N{if(aJ=="3g"){4s.83=6i;4s.84="2N"}N{4s.84=6i;4s.83=6i}}},fE:G(n){if(D.82){F 0}u o=D.1S;if(o.4r&&o.5j){u fD=[1,0];F fD[n]}N{if(o.4r){u fC=[2,1];F fC[n]}N{if(o.5j){u fB=[3,2,1,0];F fB[n]}N{u fA=[5,3,2,1];F fA[n]}}}},fz:G(n){u o=D.1S;u 5i;if(o.4r&&(o.5j||D.82)){F 1}N{if(o.4r){5i=[1,0]}N{if(o.5j){5i=[2,1,1,1]}N{if(o.3E){5i=[0,2,0,0]}N{if(D.82){5i=[5,3,2,1]}N{F 0}}}}}F 5i[n]},6h:G(1y){R(u i=1;i<M.K;i++){if(1y.2A(M[i])!=-1){F 1h}}F 1m},fy:G(){F D.6h(D.1S.6g,"1p","3D","tl","tr")},fx:G(){F D.6h(D.1S.6g,"1p","6f","bl","br")},mI:G(el){F(el.5h.K==1&&el.5h[0].3T==3)}};B.1X.aF=G(e,fw){Y B.1X.aI(e,fw)};B.1X.fs=G(fv,fu,ft){u aG=B.S.aH(fv,fu);R(u i=0;i<aG.K;i++){B.1X.aF(aG[i],ft)}};B.1X.V=B.V.V;B.1X.mH=B.S.4q;B.1X.2d=G(){u m=B.J;m.3f(D);D.2k={":3e":D.1z,":1p":m.2o(D.1z,D.1W)}};B.1X.1z=["aF","fs"];B.1X.1W=[];B.1X.2d();B.J.2Y(D,B.1X);if(H(B)=="L"){B={}}if(H(B.B)=="L"){B.B={}}B.B.1r="B.B";B.B.1Y="1.3.1";B.B.1K=G(){F"["+D.1r+" "+D.1Y+"]"};B.B.1l=G(){F D.1K()};B.B.aA=["J","15","1H","1D","1s","1k","S","1I","V","1u","1X"];if(H(1x)!="L"||H(1q)!="L"){if(H(1q)!="L"){1q.2X("B.B");1q.2M("B.*")}if(H(1x)!="L"){1x.26("B.J",[]);1x.26("B.15",[]);1x.26("B.1H",[]);1x.26("B.1D",[]);1x.26("B.1s",[]);1x.26("B.1k",[]);1x.26("B.S",[]);1x.26("B.1I",[]);1x.26("B.V",[]);1x.26("B.1u",[]);1x.26("B.1X",[])}(G(){u 6e=B.J.1R;u I=B.B;u aE=I.aA;u aD=[];u aC=[];u 81={};u i,k,m,1p;R(i=0;i<aE.K;i++){m=B[aE[i]];6e(aD,m.1z);6e(aC,m.1W);R(k in m.2k){81[k]=6e(81[k],m.2k[k])}1p=m.2k[":1p"];if(!1p){1p=6e(O,m.1z,m.1W)}u j;R(j=0;j<1p.K;j++){k=1p[j];I[k]=m[k]}}I.1z=aD;I.1W=aC;I.2k=81}())}N{if(H(B.3d)=="L"){B.3d=1h}(G(){u 80=2v.fr("7W");u ay="fq://fp.mG.fo/mF/mE/mD.is.aB.mC";u 2w=O;u ax=O;u az={};u i;R(i=0;i<80.K;i++){u 1d=80[i].fm("1d");if(!1d){2V}az[1d]=1h;if(1d.3C(/B.js$/)){2w=1d.2W(0,1d.mB("B.js"));ax=80[i]}}if(2w===O){F}u 6d=B.B.aA;R(u i=0;i<6d.K;i++){if(B[6d[i]]){2V}u 7Y=2w+6d[i]+".js";if(7Y in az){2V}if(2v.7Z&&2v.7Z.mA==ay){u s=2v.mz(ay,"7W");s.4p("id","my"+2w+6d[i]);s.4p("1d",7Y);s.4p("1J","mx/x-fk");ax.3t.2c(s)}N{2v.fl("<7W 1d=\\""+7Y+"\\" 1J=\\"7X/fk\\"></7W>")}}})()}',62,1976,'||||||||||||||||||||||||||||||var|||||||MochiKit||this||return|function|typeof|self|Base|length|undefined|arguments|else|null||elem|for|DOM||repr|Color|rval|res|new||||||throw|Iter|||||next|name|push|src|catch|try|lst|true|obj|node|Async|toString|false|string|hue|all|dojo|NAME|Format|msg|Signal|red|apply|JSAN|str|EXPORT|func|rgb|_425|DateTime|getElement|blue|hsl|Logging|LoggingPane|type|__repr__|_event|while|doc|bind|num|iter|extend|options|style|prototype|seq|EXPORT_OK|Visual|VERSION|_document||_434||replace|forwardCall|StopIteration|use||Math|max|min|join|appendChild|__new__|button|compare|date|key|val|_329|EXPORT_TAGS|update|win|pair|concat|_596|dom|map|req|Deferred|sync|document|base|Error|number|partial|indexOf||instanceof|sig|not|cls|list|fired|left|stop|break|logger|require|0px|window|shift|hsv|split|createElement|_423|callee|continue|substring|provide|_exportSymbols|ccc||_464|||||||||step|pred|_51|__compat__|common|nameFunctions|right|255|_517|case|100|_loggingPane|value|object|callback|TypeError|_251|_246|_113|parentNode|display|_522|parseInt|cssText|wrap|info|isArrayLike|end|match|top|border|depends|args|substr|mouse|code|_519|_443|className|level|err|frac|Date|_135|_85|nodeType|color|height|and|_window|fromRGB|charAt||asHSL|_444|message||||filter||LogMessage|AdapterRegistry|_366|imap|NotFound|locked|counter|_262|_messages|operator|cmp|_165|_161|pairs|arr|_52|setAttribute|computedStyle|compact|_614|_610|div|_576|_572|_observers|splice|_565|_566|_555|scrollTop|page|modifier|white|_541|fromHSL|_539|_535|_528|clone|parseFloat|_505|pre|_499|_497|_427|createTextNode|_446|attributeArray|_388|_379|updateNodeAttributes|_341|_326||box|errback|results|paused|chain|_285||ofs||NamedError|_175|_147|_122|_83|_54|_17|childNodes|_619|blend|defaultView|_574|_569|idx|_562|must|_554|_specialKeys|body|Coordinates|registerComparator|_521|_516|hex|mid|_478|width|loggingPane|LogLevel|nwin|head|url|setElementClass|callStack|path|dest|_359|boolean|register|Dimensions|DeferredLock|_313|addCallback|_310|waiting|onreadystatechange|_290|LOCALE|year|printfire|_214|log|_213|_211|pos|_155|_153||typeMatcher|listMinMax|_114|_40|itr|typ|_19|_634|_625|bottom|corners|_hasString|_612|_608|_595|1px|DIV|firstChild|innerHTML|padding|getPropertyValue|asRGB|connect|_disconnect|_559|middle|which|clientY|scrollLeft|clientX|client|charCode|relatedTarget|event|toColorPart|clampColorComponent|_537|_534|toFixed|_468|buildAndApplyFilter|_442|_441|_440|_439|position|_463|_447|removeChild|_449|uid|_428|_426|compliant|attributes|_422|_409|_412|_400|_395|_390|_389|_377|_375|_363|attr|ctx|repeat|_340|_339|isNotEmpty|_335|_333|opera|DeferredList|ret|_309|silentlyCancelled|canceller|_nextId|Array|_293|XMLHttpRequest|chained|_281|tail|_252|_225|msec|day|month|iso|Logger|_208|listeners|_200|_198|_194|_196|reduce|range|_169|_162|truth|registerRepr|_121|_70|_58|_56|_47|_45|_41|_13|_1|script|text|uri|documentElement|_630|_629|isTransparent|borderRightWidth|borderLeftWidth|marginRight|marginLeft|_602|_599|numSlices|solid|_597|block|SPAN|_579|fromString|offsetParent|signal|disconnectAll|disconnect|_570|_563|_557|preventDefault|stopPropagation|clientTop|clientLeft|pageY|pageX|keyCode|meta|ctrl|alt|target|black|_532|_524|floor|_513|_512|_500|_495|toLowerCase|_487|DEBUG|INFO|WARNING|FATAL|ERROR|colorTable|logFont|closed|inline|onclick|_438|_437|_445|RegExp|_452|space|title|updatetree|||||withDocument|withWindow||setDisplayForElement|none|renames|forEach|domConverters|escapeHTML|addElementClass|removeElementClass|once|_378|_380|_376|appendChildNodes|coerceToDOM|_355|opt|clientWidth|opacity|GenericError|fail|resultList|_307|_301|_fire|can|addCallbacks|_resback|percent|decimal|separator|twoDigitFloat|_274|_273|_264|_257|_250|_249|_254|_248|_243|_242|fmt|_240|_245|getTime|sec|hour|_209|slice|_206|iterateNextIter|registerIteratorFactory|arrayLikeIter|iteratorRegistry|takewhile|ifilterfalse|ifilter|_181|_176|_168|_166|_159|_tee|deque|arg|fun|jsonRegistry|reprString|reprRegistry|comparatorRegistry|urlEncode|_110|_108|cur|_95|_87|_71|im_preargs||_53|_57|_46|present|like|array|Argument|_15|_12|_632|_631|_633|SUBMODULES|only|_628|_627|_626|roundElement|_624|getElementsByTagAndClassName|_RoundCorners|_613|_whichSideBottom|_whichSideTop|_609|_605|_606|transparent|_borderColor|_604|_603|_601|_600|bgColor|fromElement|_594|_592|backgroundColor|_createCornerSlice|_createCorner|_590|_589|_587|_586|_581|_578|_577|currentDocument|fromBackground|errors|_568|_564||sigs|flattenArguments|_561|findIdentical|_560|_558||_556|attachEvent|addEventListener|funcOrStr|Event||_548|fromCharCode|String|_specialMacKeys|any|green|_namedColors|hsvToRGB|rgbToHSV|hslToRGB|rgbToHSL|_542|01|360|_fromColorString|_540|_536|_538|_529|_523|_518|fromComputedStyle|_511|_507|_508|_506|_501|fromHexString|_498|_496|_486|__class__|createLoggingPane|_459|_461|font|_462|_430|_435|1000|index|_460|getMessages|removeListener|_451||_457|_450|infore|_448|_456|logDebug|offsetHeight|span|input|_436|TR||HTML|open|alert|currentWindow|swapDOM|SELECT|FORM|INPUT|createDOMFunc|ignoreAttr|_421|call|_417|_410|_415|nodeName|_414|_413|emitHTML|good|_406|_399|_397|_393|_392|addLoadEvent|addToCallStack|_387|_386|_381|_382|_383|_373|_372|_369|createDOM|_365|Function|_360|_362|_358|_344|nodeWalk|formContents|_337|_338|_334|_332|offsetTop|offsetLeft|visibility|parentElement|||XMLHttpRequestError|BrowserComplianceError|CancelledError|AlreadyCalledError|evalJSONRequest|sendXMLHttpRequest|wait|doSimpleXMLHttpRequest|getXMLHttpRequest|succeed|_312|finishedCount|_308|_cbDeferred|_303|_297|queryString|_nothing|_289|XMLHTTP|ActiveXObject|eval|_284|_check|error|_279|default|rstrip|lstrip|formatLocale|roundToFixed|truncToFixed|_276|pow|_272|_271|_270|sign|_265|_263|tmp|_238|_232|toISODate|toISOTime|getFullYear|getDate|getMonth|_230|_padTwo|_228|useNativeConsole|_212|compareLogMessage|isLogMessage|unshift|_207||maxSize|_202|_199|logLevelAtLeast|console|hasIterateNext|iterateNext|arrayLike|groupby||exhaust|tee|dropwhile|applymap||islice|izip|cycle|count||_189|_188|_183|_185|_184|_186|_187|_182|identity|fetch|_180|_177|listMin|reprNumber|reprArrayLike|compareArrayLike|compareDateLike|isDateLike|findValue|_128|__export__|keyComparator|_124|_118|_93|_94|_90|_88|_84|_77|_68|_67|_66|_65|_60|im_func|_55|im_self|_48|_44|_42|_39|_36|_33|_27|_26|_25|_22|_24|_20|javascript|write|getAttribute||org|www|http|getElementsByTagName|roundClass|_623|_622|_621|_620|_isBottomRounded|_isTopRounded|_borderSize|_618|_617|_616|_615|_marginSize|_611|_setBorder|_607|_setMargin|blendedColor|_598|__unstable__wrapElement|fromParent|_setOptions|2px|borderColor|_593|hidden|overflow|_591|_588|_roundBottomCorners|_585|_roundTopCorners|_584|_583|_582|_580|_renderBorder|_roundCornersImpl|getComputedStyle|_doWrap|_571|_unloadCache|onunload|detachEvent|removeEventListener|_listener|objOrFunc|_552||_551|_549|onload|delete|112|KEY_F|KEY_|MINUS|KEY_SEMICOLON|KEY_DELETE|KEY_INSERT|KEY_ARROW_DOWN|KEY_ARROW_RIGHT|KEY_ARROW_UP||KEY_ARROW_LEFT|KEY_HOME|KEY_END|KEY_PAGE_DOWN|KEY_PAGE_UP|KEY_ENTER|KEY_NUM_PAD_CLEAR|63236|mousemove|contextmenu|click|mouseout|mouseover|_src|yellow|708090|purple|orange|ff00ff|magenta|778899|d3d3d3|808080|gray|696969|2f4f4f|darkred|a9a9a9|00ffff|cyan|brown|_547|_546||||compareRGB|_545||_543|fromHSLString|fromRGBString|round|_533|_hslValue|switch|background|_503|_504||fromName|_488|col|toRGBString|_hexString|_rgbString|_hslString|toPrecision|isLight||_481|_477|_476|_475|_474|_473|_469|_466|closePane|_458|onkeypress|_454|addListener|_455|close|test|scrollHeight|option|word|moz|_431|getElementById|html|pop|200|_|removeElement|showElement|hideElement|CANVAS|STRONG|FIELDSET|LEGEND|OPTGROUP|OPTION|TEXTAREA|LABEL|HR|BR|H3|H2|H1|PRE|TT|BUTTON|IMG|TH||TABLE||TFOOT|THEAD|TBODY|TD|LI|OL|||UL|checked|class|ignoreAttrFilter||_424|_419|nodeValue|scrapeText|_416|_418|sort|_411|toHTML|_404|hasElementClass|_403|_402|_401|swapElementClass|_398|_394|toggleElementClass|_391|focusOnLoad|_newCallStack|currentStyle|_371|replaceChildNodes|_364|_361|getNodeAttribute|_357|setNodeAttribute|_354|_352|_350|_353|toDOM|_346|_345|registerDOMConverter|selectedIndex|setElementPosition|setElementDimensions|tagName|absolute|getBoxObjectFor|getBoundingClientRect|elementPosition|_325|_324|_322|_323|offsetWidth|elementDimensions|clientHeight|innerWidth|getViewportDimensions|setOpacity|status|_317|deferred|_316|_newNamedError|maybeDeferred||gatherResults|callLater|loadJSONDoc|_311|consumeErrors|fireOnOneErrback|fireOnOneCallback|addErrback|_305|_304|_306|unlocked|release|_300|_299|_298|_296|_xhr_onreadystatechange|_xhr_canceller|304|responseText|Msxml2|addBoth|_pause|_continue|result|the|are|they|instances|_unpause|cancel|_280|_278|en_US|strip|percentFormat|twoDigitAverage|numberFormatter|_277|_275|isNaN|_259|_258|_260|_255|_253|_numberFormatter|_241|_239|_237|_236|_235|_234|_233|_231|toAmericanDate|toPaddedAmericanDate|americanDate|toISOTimestamp|isoTimestamp|isoDate|foot|sep||60000|_221|_isoRegexp|dispatchEvent|createEvent|warning|logWarning|fatal|logFatal|debug|logError|baseLog|_210|getMessageText|logToConsole|dispatchListeners|_204|_203|ident|_201|postError|alertListener|_197|_192|groupby_as_array|iextend|some|reversed|sorted|every|sum|_190|eat|_174|_173|_172|_171|_167|_163|_158|_157|_151|_144|_141||_139|_136|_134||_133|_132|zip|merge|isUndefined|isCallable|listMax|_131|_130|encodeURIComponent||_127|method|parseQueryString|evalJSON|registerJSON|serializeJSON|objMin|objMax|reverseKeyComparator|arrayEqual|objEqual|bindMethods|xfilter|xmap|isEmpty|isNull|isUndefinedOrNull|itemgetter|items|keys|setdefault|_126|_120|decodeURIComponent|_119|len|_109|_107|_104|_105|_101|_102|_98|||_100|_97|_96|_91|json|__json__|_82|_81|_80|_79|_76||_75|_74|_73|_69|_primitives|_64|_63||_62|_61|_59|_wrapDumbFunction|_49|_50|_31|_30|_21|_7|application|MochiKit_|createElementNS|namespaceURI|lastIndexOf|xul|there|gatekeeper|keymaster|mozilla|getElementsComputedStyle|_hasSingleTextChild|borderWidth|borderStyle|borderBottomWidth|borderTopWidth|borderTopStyle|fontSize|paddingBottom|insertBefore|paddingTop|marginBottom|marginTop|_575|property|see|handling|thrown|Multiple|element|||given|123|KEY_NUM_PAD_|105|KEY_APOSTROPHE|222|KEY_RIGHT_SQUARE_BRACKET|221|KEY_REVERSE_SOLIDUS|220|KEY_LEFT_SQUARE_BRACKET||219|KEY_GRAVE_ACCENT|192|KEY_SOLIDUS|191|KEY_FULL_STOP|190|KEY_HYPHEN|189||KEY_COMMA|188|KEY_EQUALS_SIGN|187|186|KEY_SCROLL_LOCK|145|KEY_NUM_LOCK|144|KEY_NUM_PAD_SOLIDUS|111|KEY_NUM_PAD_FULL_STOP|110|KEY_NUM_PAD_HYPHEN|109|KEY_NUM_PAD_PLUS_SIGN|107|KEY_NUM_PAD_ASTERISK|106|KEY_SELECT|KEY_WINDOWS_RIGHT|KEY_WINDOWS_LEFT|KEY_PRINT_SCREEN|KEY_SPACEBAR|KEY_ESCAPE|KEY_CAPS_LOCK|KEY_PAUSE|KEY_ALT|KEY_CTRL|KEY_SHIFT|KEY_TAB|KEY_BACKSPACE|63242|63272|63302|63233|63235|63232|63234|63273|63275|63277|63276|63289|returnValue|cancelBubble|keypress|KEY_UNKNOWN|keyup|keydown|shiftKey|metaKey||ctrlKey|altKey|toElement|srcElement|9acd32||yellowgreen||ffff00|f5f5f5|whitesmoke||ffffff|f5deb3|wheat|ee82ee|violet|40e0d0|turquoise|ff6347|tomato|d8bfd8|thistle|008080|teal|d2b48c|tan|4682b4|steelblue|00ff7f|springgreen|fffafa|snow|slategrey|slategray|6a5acd|slateblue|87ceeb|skyblue|c0c0c0|silver|a0522d|sienna|fff5ee|seashell|2e8b57|seagreen|f4a460|sandybrown|fa8072|salmon|8b4513|saddlebrown|4169e1|royalblue|bc8f8f|rosybrown|ff0000|800080|b0e0e6|powderblue|dda0dd|plum|ffc0cb|pink|cd853f||peru|ffdab9|peachpuff|ffefd5|papayawhip|db7093|palevioletred|afeeee|paleturquoise|98fb98|palegreen|eee8aa||palegoldenrod|da70d6|orchid|ff4500|orangered|ffa500|6b8e23|olivedrab|808000|olive|fdf5e6|oldlace|000080|navy|ffdead|navajowhite|ffe4b5|moccasin|ffe4e1|mistyrose|f5fffa|mintcream|191970|midnightblue|c71585|mediumvioletred|48d1cc|mediumturquoise|00fa9a|mediumspringgreen|7b68ee|mediumslateblue|3cb371|mediumseagreen|9370db|mediumpurple|ba55d3|mediumorchid|0000cd|mediumblue|66cdaa|mediumaquamarine|800000|maroon|faf0e6|linen|32cd32|limegreen|00ff00|lime|ffffe0|lightyellow|b0c4de|lightsteelblue|lightslategrey|lightslategray||87cefa|lightskyblue|20b2aa|lightseagreen|ffa07a|lightsalmon|ffb6c1|lightpink|lightgrey|90ee90|lightgreen|lightgray|fafad2|lightgoldenrodyellow|e0ffff|lightcyan|f08080|lightcoral|add8e6|lightblue|fffacd|lemonchiffon|7cfc00|lawngreen|fff0f5|lavenderblush|e6e6fa|lavender|f0e68c|khaki|fffff0|ivory|4b0082|indigo|cd5c5c|indianred|ff69b4|hotpink|f0fff0|honeydew|grey|adff2f|greenyellow|008000|daa520|goldenrod|ffd700||gold|f8f8ff|ghostwhite|dcdcdc|gainsboro|fuchsia|228b22|forestgreen|fffaf0|floralwhite|b22222|firebrick|1e90ff|dodgerblue|dimgrey|dimgray|00bfff|deepskyblue|ff1493|deeppink|9400d3|darkviolet|00ced1|darkturquoise|darkslategrey|darkslategray|483d8b|darkslateblue|8fbc8f|darkseagreen|e9967a|darksalmon|8b0000|9932cc|darkorchid|ff8c00|darkorange|556b2f|darkolivegreen|8b008b|darkmagenta|bdb76b|darkkhaki|darkgrey|006400|darkgreen|darkgray|b8860b|darkgoldenrod|008b8b|darkcyan|00008b|darkblue|dc143c|crimson|fff8dc|cornsilk|6495ed|cornflowerblue|ff7f50|coral|d2691e||chocolate|7fff00|chartreuse|5f9ea0|cadetblue|deb887|burlywood|a52a2a|8a2be2|blueviolet|0000ff|ffebcd||blanchedalmond|000000|ffe4c4|bisque|f5f5dc|beige|f0ffff|azure|7fffd4|aquamarine|aqua|faebd7|antiquewhite|f0f8ff|aliceblue|lightGray|darkGray|namedColors|blackColor|fromText|whiteColor|_510|_509|PI|rad|deg|transparentColor|_494|_493|_492|fromHSV|_491|_490|_489|asHSV|toHexString|rgba|hsla|toHSLString|isDark|lighterColorWithLevel|darkerColorWithLevel|colorWithLightness|colorWithSaturation|colorWithHue|colorWithAlpha||serif|sans|Verdana||8pt|8em|auto||Close|Clear||Load|Filter||10em||fixed|regex|emergency|line|margin|_Listener|dtd|loose|html4|w3|EN|Transitional|DTD|W3C|PUBLIC|DOCTYPE|blocking|due|debugging|able|Not|resizable|dependent|href|location|_MochiKit_LoggingPane|_429|canvas|strong|fieldset|legend|optgroup|select|form|textarea|label|img|table|tfoot|thead|tbody|htmlFor||useMap|usemap|defaultChecked|hasChildNodes|quot|amp|_405|focus|replaceChild|checkbox||radio|_win|BODY||safari|version|userAgent|navigator|innerHeight|alpha|khtml|Tried|acquire|clearTimeout|setTimeout|GET|ignore|send|abort|failed|Request|readyState|support|does|Browser|Microsoft|_288|_287|used|Deferreds|Chained|success|unfired|fr_FR|de_DE|00|abs|search|pattern|Invalid|getTimezoneOffset|getSeconds|getMinutes|getHours|UTC|3600000|initEvent|Events|debuggingBookmarklet|MESSAGES|LAST|_205|clear|ninfo|nlevel|timestamp|reverse|takes|initial|with|sequence|empty|iterable|numbers|dateLike|escape|find|forward|unregister|unescape|Object|compared|item|contains|logor|logand|cle|clt|cge|cgt|cne|ceq|zrshift|rshift|lshift|xor|mul|mod|sub|add|neg|lognot|_9|_2'.split('|'),0,{}) + + +/* + * jQuery 1.2.1 - New Wave Javascript + * + * Copyright (c) 2007 John Resig (jquery.com) + * Dual licensed under the MIT (MIT-LICENSE.txt) + * and GPL (GPL-LICENSE.txt) licenses. + * + * $Date: 2007-09-16 23:42:06 -0400 (Sun, 16 Sep 2007) $ + * $Rev: 3353 $ + */ + +var decompressedJQuery = function(p,a,c,k,e,r){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('(G(){9(1m E!="W")H w=E;H E=18.15=G(a,b){I 6 7u E?6.5N(a,b):1u E(a,b)};9(1m $!="W")H D=$;18.$=E;H u=/^[^<]*(<(.|\\s)+>)[^>]*$|^#(\\w+)$/;E.1b=E.3A={5N:G(c,a){c=c||U;9(1m c=="1M"){H m=u.2S(c);9(m&&(m[1]||!a)){9(m[1])c=E.4D([m[1]],a);J{H b=U.3S(m[3]);9(b)9(b.22!=m[3])I E().1Y(c);J{6[0]=b;6.K=1;I 6}J c=[]}}J I 1u E(a).1Y(c)}J 9(E.1n(c))I 1u E(U)[E.1b.2d?"2d":"39"](c);I 6.6v(c.1c==1B&&c||(c.4c||c.K&&c!=18&&!c.1y&&c[0]!=W&&c[0].1y)&&E.2h(c)||[c])},4c:"1.2.1",7Y:G(){I 6.K},K:0,21:G(a){I a==W?E.2h(6):6[a]},2o:G(a){H b=E(a);b.4Y=6;I b},6v:G(a){6.K=0;1B.3A.1a.16(6,a);I 6},N:G(a,b){I E.N(6,a,b)},4I:G(a){H b=-1;6.N(G(i){9(6==a)b=i});I b},1x:G(f,d,e){H c=f;9(f.1c==3X)9(d==W)I 6.K&&E[e||"1x"](6[0],f)||W;J{c={};c[f]=d}I 6.N(G(a){L(H b 1i c)E.1x(e?6.R:6,b,E.1e(6,c[b],e,a,b))})},17:G(b,a){I 6.1x(b,a,"3C")},2g:G(e){9(1m e!="5i"&&e!=S)I 6.4n().3g(U.6F(e));H t="";E.N(e||6,G(){E.N(6.3j,G(){9(6.1y!=8)t+=6.1y!=1?6.6x:E.1b.2g([6])})});I t},5m:G(b){9(6[0])E(b,6[0].3H).6u().3d(6[0]).1X(G(){H a=6;1W(a.1w)a=a.1w;I a}).3g(6);I 6},8m:G(a){I 6.N(G(){E(6).6q().5m(a)})},8d:G(a){I 6.N(G(){E(6).5m(a)})},3g:G(){I 6.3z(1q,Q,1,G(a){6.58(a)})},6j:G(){I 6.3z(1q,Q,-1,G(a){6.3d(a,6.1w)})},6g:G(){I 6.3z(1q,P,1,G(a){6.12.3d(a,6)})},50:G(){I 6.3z(1q,P,-1,G(a){6.12.3d(a,6.2q)})},2D:G(){I 6.4Y||E([])},1Y:G(t){H b=E.1X(6,G(a){I E.1Y(t,a)});I 6.2o(/[^+>] [^+>]/.14(t)||t.1g("..")>-1?E.4V(b):b)},6u:G(e){H f=6.1X(G(){I 6.67?E(6.67)[0]:6.4R(Q)});H d=f.1Y("*").4O().N(G(){9(6[F]!=W)6[F]=S});9(e===Q)6.1Y("*").4O().N(G(i){H c=E.M(6,"2P");L(H a 1i c)L(H b 1i c[a])E.1j.1f(d[i],a,c[a][b],c[a][b].M)});I f},1E:G(t){I 6.2o(E.1n(t)&&E.2W(6,G(b,a){I t.16(b,[a])})||E.3m(t,6))},5V:G(t){I 6.2o(t.1c==3X&&E.3m(t,6,Q)||E.2W(6,G(a){I(t.1c==1B||t.4c)?E.2A(a,t)<0:a!=t}))},1f:G(t){I 6.2o(E.1R(6.21(),t.1c==3X?E(t).21():t.K!=W&&(!t.11||E.11(t,"2Y"))?t:[t]))},3t:G(a){I a?E.3m(a,6).K>0:P},7c:G(a){I 6.3t("."+a)},3i:G(b){9(b==W){9(6.K){H c=6[0];9(E.11(c,"24")){H e=c.4Z,a=[],Y=c.Y,2G=c.O=="24-2G";9(e<0)I S;L(H i=2G?e:0,33=2G?e+1:Y.K;i<33;i++){H d=Y[i];9(d.26){H b=E.V.1h&&!d.9V["1Q"].9L?d.2g:d.1Q;9(2G)I b;a.1a(b)}}I a}J I 6[0].1Q.1p(/\\r/g,"")}}J I 6.N(G(){9(b.1c==1B&&/4k|5j/.14(6.O))6.2Q=(E.2A(6.1Q,b)>=0||E.2A(6.2H,b)>=0);J 9(E.11(6,"24")){H a=b.1c==1B?b:[b];E("9h",6).N(G(){6.26=(E.2A(6.1Q,a)>=0||E.2A(6.2g,a)>=0)});9(!a.K)6.4Z=-1}J 6.1Q=b})},4o:G(a){I a==W?(6.K?6[0].3O:S):6.4n().3g(a)},6H:G(a){I 6.50(a).28()},6E:G(i){I 6.2J(i,i+1)},2J:G(){I 6.2o(1B.3A.2J.16(6,1q))},1X:G(b){I 6.2o(E.1X(6,G(a,i){I b.2O(a,i,a)}))},4O:G(){I 6.1f(6.4Y)},3z:G(f,d,g,e){H c=6.K>1,a;I 6.N(G(){9(!a){a=E.4D(f,6.3H);9(g<0)a.8U()}H b=6;9(d&&E.11(6,"1I")&&E.11(a[0],"4m"))b=6.4l("1K")[0]||6.58(U.5B("1K"));E.N(a,G(){H a=c?6.4R(Q):6;9(!5A(0,a))e.2O(b,a)})})}};G 5A(i,b){H a=E.11(b,"1J");9(a){9(b.3k)E.3G({1d:b.3k,3e:P,1V:"1J"});J E.5f(b.2g||b.6s||b.3O||"");9(b.12)b.12.3b(b)}J 9(b.1y==1)E("1J",b).N(5A);I a}E.1k=E.1b.1k=G(){H c=1q[0]||{},a=1,2c=1q.K,5e=P;9(c.1c==8o){5e=c;c=1q[1]||{}}9(2c==1){c=6;a=0}H b;L(;a<2c;a++)9((b=1q[a])!=S)L(H i 1i b){9(c==b[i])6r;9(5e&&1m b[i]==\'5i\'&&c[i])E.1k(c[i],b[i]);J 9(b[i]!=W)c[i]=b[i]}I c};H F="15"+(1u 3D()).3B(),6p=0,5c={};E.1k({8a:G(a){18.$=D;9(a)18.15=w;I E},1n:G(a){I!!a&&1m a!="1M"&&!a.11&&a.1c!=1B&&/G/i.14(a+"")},4a:G(a){I a.2V&&!a.1G||a.37&&a.3H&&!a.3H.1G},5f:G(a){a=E.36(a);9(a){9(18.6l)18.6l(a);J 9(E.V.1N)18.56(a,0);J 3w.2O(18,a)}},11:G(b,a){I b.11&&b.11.27()==a.27()},1L:{},M:G(c,d,b){c=c==18?5c:c;H a=c[F];9(!a)a=c[F]=++6p;9(d&&!E.1L[a])E.1L[a]={};9(b!=W)E.1L[a][d]=b;I d?E.1L[a][d]:a},30:G(c,b){c=c==18?5c:c;H a=c[F];9(b){9(E.1L[a]){2E E.1L[a][b];b="";L(b 1i E.1L[a])1T;9(!b)E.30(c)}}J{2a{2E c[F]}29(e){9(c.53)c.53(F)}2E E.1L[a]}},N:G(a,b,c){9(c){9(a.K==W)L(H i 1i a)b.16(a[i],c);J L(H i=0,48=a.K;i<48;i++)9(b.16(a[i],c)===P)1T}J{9(a.K==W)L(H i 1i a)b.2O(a[i],i,a[i]);J L(H i=0,48=a.K,3i=a[0];i<48&&b.2O(3i,i,3i)!==P;3i=a[++i]){}}I a},1e:G(c,b,d,e,a){9(E.1n(b))b=b.2O(c,[e]);H f=/z-?4I|7T-?7Q|1r|69|7P-?1H/i;I b&&b.1c==4W&&d=="3C"&&!f.14(a)?b+"2T":b},1o:{1f:G(b,c){E.N((c||"").2l(/\\s+/),G(i,a){9(!E.1o.3K(b.1o,a))b.1o+=(b.1o?" ":"")+a})},28:G(b,c){b.1o=c!=W?E.2W(b.1o.2l(/\\s+/),G(a){I!E.1o.3K(c,a)}).66(" "):""},3K:G(t,c){I E.2A(c,(t.1o||t).3s().2l(/\\s+/))>-1}},2k:G(e,o,f){L(H i 1i o){e.R["3r"+i]=e.R[i];e.R[i]=o[i]}f.16(e,[]);L(H i 1i o)e.R[i]=e.R["3r"+i]},17:G(e,p){9(p=="1H"||p=="2N"){H b={},42,41,d=["7J","7I","7G","7F"];E.N(d,G(){b["7C"+6]=0;b["7B"+6+"5Z"]=0});E.2k(e,b,G(){9(E(e).3t(\':3R\')){42=e.7A;41=e.7w}J{e=E(e.4R(Q)).1Y(":4k").5W("2Q").2D().17({4C:"1P",2X:"4F",19:"2Z",7o:"0",1S:"0"}).5R(e.12)[0];H a=E.17(e.12,"2X")||"3V";9(a=="3V")e.12.R.2X="7g";42=e.7e;41=e.7b;9(a=="3V")e.12.R.2X="3V";e.12.3b(e)}});I p=="1H"?42:41}I E.3C(e,p)},3C:G(h,j,i){H g,2w=[],2k=[];G 3n(a){9(!E.V.1N)I P;H b=U.3o.3Z(a,S);I!b||b.4y("3n")==""}9(j=="1r"&&E.V.1h){g=E.1x(h.R,"1r");I g==""?"1":g}9(j.1t(/4u/i))j=y;9(!i&&h.R[j])g=h.R[j];J 9(U.3o&&U.3o.3Z){9(j.1t(/4u/i))j="4u";j=j.1p(/([A-Z])/g,"-$1").2p();H d=U.3o.3Z(h,S);9(d&&!3n(h))g=d.4y(j);J{L(H a=h;a&&3n(a);a=a.12)2w.4w(a);L(a=0;a<2w.K;a++)9(3n(2w[a])){2k[a]=2w[a].R.19;2w[a].R.19="2Z"}g=j=="19"&&2k[2w.K-1]!=S?"2s":U.3o.3Z(h,S).4y(j)||"";L(a=0;a<2k.K;a++)9(2k[a]!=S)2w[a].R.19=2k[a]}9(j=="1r"&&g=="")g="1"}J 9(h.3Q){H f=j.1p(/\\-(\\w)/g,G(m,c){I c.27()});g=h.3Q[j]||h.3Q[f];9(!/^\\d+(2T)?$/i.14(g)&&/^\\d/.14(g)){H k=h.R.1S;H e=h.4v.1S;h.4v.1S=h.3Q.1S;h.R.1S=g||0;g=h.R.71+"2T";h.R.1S=k;h.4v.1S=e}}I g},4D:G(a,e){H r=[];e=e||U;E.N(a,G(i,d){9(!d)I;9(d.1c==4W)d=d.3s();9(1m d=="1M"){d=d.1p(/(<(\\w+)[^>]*?)\\/>/g,G(m,a,b){I b.1t(/^(70|6Z|6Y|9Q|4t|9N|9K|3a|9G|9E)$/i)?m:a+"></"+b+">"});H s=E.36(d).2p(),1s=e.5B("1s"),2x=[];H c=!s.1g("<9y")&&[1,"<24>","</24>"]||!s.1g("<9w")&&[1,"<6T>","</6T>"]||s.1t(/^<(9u|1K|9t|9r|9p)/)&&[1,"<1I>","</1I>"]||!s.1g("<4m")&&[2,"<1I><1K>","</1K></1I>"]||(!s.1g("<9m")||!s.1g("<9k"))&&[3,"<1I><1K><4m>","</4m></1K></1I>"]||!s.1g("<6Y")&&[2,"<1I><1K></1K><6L>","</6L></1I>"]||E.V.1h&&[1,"1s<1s>","</1s>"]||[0,"",""];1s.3O=c[1]+d+c[2];1W(c[0]--)1s=1s.5p;9(E.V.1h){9(!s.1g("<1I")&&s.1g("<1K")<0)2x=1s.1w&&1s.1w.3j;J 9(c[1]=="<1I>"&&s.1g("<1K")<0)2x=1s.3j;L(H n=2x.K-1;n>=0;--n)9(E.11(2x[n],"1K")&&!2x[n].3j.K)2x[n].12.3b(2x[n]);9(/^\\s/.14(d))1s.3d(e.6F(d.1t(/^\\s*/)[0]),1s.1w)}d=E.2h(1s.3j)}9(0===d.K&&(!E.11(d,"2Y")&&!E.11(d,"24")))I;9(d[0]==W||E.11(d,"2Y")||d.Y)r.1a(d);J r=E.1R(r,d)});I r},1x:G(c,d,a){H e=E.4a(c)?{}:E.5o;9(d=="26"&&E.V.1N)c.12.4Z;9(e[d]){9(a!=W)c[e[d]]=a;I c[e[d]]}J 9(E.V.1h&&d=="R")I E.1x(c.R,"9e",a);J 9(a==W&&E.V.1h&&E.11(c,"2Y")&&(d=="9d"||d=="9a"))I c.97(d).6x;J 9(c.37){9(a!=W){9(d=="O"&&E.11(c,"4t")&&c.12)6G"O 94 93\'t 92 91";c.90(d,a)}9(E.V.1h&&/6C|3k/.14(d)&&!E.4a(c))I c.4p(d,2);I c.4p(d)}J{9(d=="1r"&&E.V.1h){9(a!=W){c.69=1;c.1E=(c.1E||"").1p(/6O\\([^)]*\\)/,"")+(3I(a).3s()=="8S"?"":"6O(1r="+a*6A+")")}I c.1E?(3I(c.1E.1t(/1r=([^)]*)/)[1])/6A).3s():""}d=d.1p(/-([a-z])/8Q,G(z,b){I b.27()});9(a!=W)c[d]=a;I c[d]}},36:G(t){I(t||"").1p(/^\\s+|\\s+$/g,"")},2h:G(a){H r=[];9(1m a!="8P")L(H i=0,2c=a.K;i<2c;i++)r.1a(a[i]);J r=a.2J(0);I r},2A:G(b,a){L(H i=0,2c=a.K;i<2c;i++)9(a[i]==b)I i;I-1},1R:G(a,b){9(E.V.1h){L(H i=0;b[i];i++)9(b[i].1y!=8)a.1a(b[i])}J L(H i=0;b[i];i++)a.1a(b[i]);I a},4V:G(b){H r=[],2f={};2a{L(H i=0,6y=b.K;i<6y;i++){H a=E.M(b[i]);9(!2f[a]){2f[a]=Q;r.1a(b[i])}}}29(e){r=b}I r},2W:G(b,a,c){9(1m a=="1M")a=3w("P||G(a,i){I "+a+"}");H d=[];L(H i=0,4g=b.K;i<4g;i++)9(!c&&a(b[i],i)||c&&!a(b[i],i))d.1a(b[i]);I d},1X:G(c,b){9(1m b=="1M")b=3w("P||G(a){I "+b+"}");H d=[];L(H i=0,4g=c.K;i<4g;i++){H a=b(c[i],i);9(a!==S&&a!=W){9(a.1c!=1B)a=[a];d=d.8M(a)}}I d}});H v=8K.8I.2p();E.V={4s:(v.1t(/.+(?:8F|8E|8C|8B)[\\/: ]([\\d.]+)/)||[])[1],1N:/6w/.14(v),34:/34/.14(v),1h:/1h/.14(v)&&!/34/.14(v),35:/35/.14(v)&&!/(8z|6w)/.14(v)};H y=E.V.1h?"4h":"5h";E.1k({5g:!E.V.1h||U.8y=="8x",4h:E.V.1h?"4h":"5h",5o:{"L":"8w","8v":"1o","4u":y,5h:y,4h:y,3O:"3O",1o:"1o",1Q:"1Q",3c:"3c",2Q:"2Q",8u:"8t",26:"26",8s:"8r"}});E.N({1D:"a.12",8q:"15.4e(a,\'12\')",8p:"15.2I(a,2,\'2q\')",8n:"15.2I(a,2,\'4d\')",8l:"15.4e(a,\'2q\')",8k:"15.4e(a,\'4d\')",8j:"15.5d(a.12.1w,a)",8i:"15.5d(a.1w)",6q:"15.11(a,\'8h\')?a.8f||a.8e.U:15.2h(a.3j)"},G(i,n){E.1b[i]=G(a){H b=E.1X(6,n);9(a&&1m a=="1M")b=E.3m(a,b);I 6.2o(E.4V(b))}});E.N({5R:"3g",8c:"6j",3d:"6g",8b:"50",89:"6H"},G(i,n){E.1b[i]=G(){H a=1q;I 6.N(G(){L(H j=0,2c=a.K;j<2c;j++)E(a[j])[n](6)})}});E.N({5W:G(a){E.1x(6,a,"");6.53(a)},88:G(c){E.1o.1f(6,c)},87:G(c){E.1o.28(6,c)},86:G(c){E.1o[E.1o.3K(6,c)?"28":"1f"](6,c)},28:G(a){9(!a||E.1E(a,[6]).r.K){E.30(6);6.12.3b(6)}},4n:G(){E("*",6).N(G(){E.30(6)});1W(6.1w)6.3b(6.1w)}},G(i,n){E.1b[i]=G(){I 6.N(n,1q)}});E.N(["85","5Z"],G(i,a){H n=a.2p();E.1b[n]=G(h){I 6[0]==18?E.V.1N&&3y["84"+a]||E.5g&&38.33(U.2V["5a"+a],U.1G["5a"+a])||U.1G["5a"+a]:6[0]==U?38.33(U.1G["6n"+a],U.1G["6m"+a]):h==W?(6.K?E.17(6[0],n):S):6.17(n,h.1c==3X?h:h+"2T")}});H C=E.V.1N&&3x(E.V.4s)<83?"(?:[\\\\w*57-]|\\\\\\\\.)":"(?:[\\\\w\\82-\\81*57-]|\\\\\\\\.)",6k=1u 47("^>\\\\s*("+C+"+)"),6i=1u 47("^("+C+"+)(#)("+C+"+)"),6h=1u 47("^([#.]?)("+C+"*)");E.1k({55:{"":"m[2]==\'*\'||15.11(a,m[2])","#":"a.4p(\'22\')==m[2]",":":{80:"i<m[3]-0",7Z:"i>m[3]-0",2I:"m[3]-0==i",6E:"m[3]-0==i",3v:"i==0",3u:"i==r.K-1",6f:"i%2==0",6e:"i%2","3v-46":"a.12.4l(\'*\')[0]==a","3u-46":"15.2I(a.12.5p,1,\'4d\')==a","7X-46":"!15.2I(a.12.5p,2,\'4d\')",1D:"a.1w",4n:"!a.1w",7W:"(a.6s||a.7V||15(a).2g()||\'\').1g(m[3])>=0",3R:\'"1P"!=a.O&&15.17(a,"19")!="2s"&&15.17(a,"4C")!="1P"\',1P:\'"1P"==a.O||15.17(a,"19")=="2s"||15.17(a,"4C")=="1P"\',7U:"!a.3c",3c:"a.3c",2Q:"a.2Q",26:"a.26||15.1x(a,\'26\')",2g:"\'2g\'==a.O",4k:"\'4k\'==a.O",5j:"\'5j\'==a.O",54:"\'54\'==a.O",52:"\'52\'==a.O",51:"\'51\'==a.O",6d:"\'6d\'==a.O",6c:"\'6c\'==a.O",2r:\'"2r"==a.O||15.11(a,"2r")\',4t:"/4t|24|6b|2r/i.14(a.11)",3K:"15.1Y(m[3],a).K",7S:"/h\\\\d/i.14(a.11)",7R:"15.2W(15.32,G(1b){I a==1b.T;}).K"}},6a:[/^(\\[) *@?([\\w-]+) *([!*$^~=]*) *(\'?"?)(.*?)\\4 *\\]/,/^(:)([\\w-]+)\\("?\'?(.*?(\\(.*?\\))?[^(]*?)"?\'?\\)/,1u 47("^([:.#]*)("+C+"+)")],3m:G(a,c,b){H d,2b=[];1W(a&&a!=d){d=a;H f=E.1E(a,c,b);a=f.t.1p(/^\\s*,\\s*/,"");2b=b?c=f.r:E.1R(2b,f.r)}I 2b},1Y:G(t,o){9(1m t!="1M")I[t];9(o&&!o.1y)o=S;o=o||U;H d=[o],2f=[],3u;1W(t&&3u!=t){H r=[];3u=t;t=E.36(t);H l=P;H g=6k;H m=g.2S(t);9(m){H p=m[1].27();L(H i=0;d[i];i++)L(H c=d[i].1w;c;c=c.2q)9(c.1y==1&&(p=="*"||c.11.27()==p.27()))r.1a(c);d=r;t=t.1p(g,"");9(t.1g(" ")==0)6r;l=Q}J{g=/^([>+~])\\s*(\\w*)/i;9((m=g.2S(t))!=S){r=[];H p=m[2],1R={};m=m[1];L(H j=0,31=d.K;j<31;j++){H n=m=="~"||m=="+"?d[j].2q:d[j].1w;L(;n;n=n.2q)9(n.1y==1){H h=E.M(n);9(m=="~"&&1R[h])1T;9(!p||n.11.27()==p.27()){9(m=="~")1R[h]=Q;r.1a(n)}9(m=="+")1T}}d=r;t=E.36(t.1p(g,""));l=Q}}9(t&&!l){9(!t.1g(",")){9(o==d[0])d.44();2f=E.1R(2f,d);r=d=[o];t=" "+t.68(1,t.K)}J{H k=6i;H m=k.2S(t);9(m){m=[0,m[2],m[3],m[1]]}J{k=6h;m=k.2S(t)}m[2]=m[2].1p(/\\\\/g,"");H f=d[d.K-1];9(m[1]=="#"&&f&&f.3S&&!E.4a(f)){H q=f.3S(m[2]);9((E.V.1h||E.V.34)&&q&&1m q.22=="1M"&&q.22!=m[2])q=E(\'[@22="\'+m[2]+\'"]\',f)[0];d=r=q&&(!m[3]||E.11(q,m[3]))?[q]:[]}J{L(H i=0;d[i];i++){H a=m[1]=="#"&&m[3]?m[3]:m[1]!=""||m[0]==""?"*":m[2];9(a=="*"&&d[i].11.2p()=="5i")a="3a";r=E.1R(r,d[i].4l(a))}9(m[1]==".")r=E.4X(r,m[2]);9(m[1]=="#"){H e=[];L(H i=0;r[i];i++)9(r[i].4p("22")==m[2]){e=[r[i]];1T}r=e}d=r}t=t.1p(k,"")}}9(t){H b=E.1E(t,r);d=r=b.r;t=E.36(b.t)}}9(t)d=[];9(d&&o==d[0])d.44();2f=E.1R(2f,d);I 2f},4X:G(r,m,a){m=" "+m+" ";H c=[];L(H i=0;r[i];i++){H b=(" "+r[i].1o+" ").1g(m)>=0;9(!a&&b||a&&!b)c.1a(r[i])}I c},1E:G(t,r,h){H d;1W(t&&t!=d){d=t;H p=E.6a,m;L(H i=0;p[i];i++){m=p[i].2S(t);9(m){t=t.7O(m[0].K);m[2]=m[2].1p(/\\\\/g,"");1T}}9(!m)1T;9(m[1]==":"&&m[2]=="5V")r=E.1E(m[3],r,Q).r;J 9(m[1]==".")r=E.4X(r,m[2],h);J 9(m[1]=="["){H g=[],O=m[3];L(H i=0,31=r.K;i<31;i++){H a=r[i],z=a[E.5o[m[2]]||m[2]];9(z==S||/6C|3k|26/.14(m[2]))z=E.1x(a,m[2])||\'\';9((O==""&&!!z||O=="="&&z==m[5]||O=="!="&&z!=m[5]||O=="^="&&z&&!z.1g(m[5])||O=="$="&&z.68(z.K-m[5].K)==m[5]||(O=="*="||O=="~=")&&z.1g(m[5])>=0)^h)g.1a(a)}r=g}J 9(m[1]==":"&&m[2]=="2I-46"){H e={},g=[],14=/(\\d*)n\\+?(\\d*)/.2S(m[3]=="6f"&&"2n"||m[3]=="6e"&&"2n+1"||!/\\D/.14(m[3])&&"n+"+m[3]||m[3]),3v=(14[1]||1)-0,d=14[2]-0;L(H i=0,31=r.K;i<31;i++){H j=r[i],12=j.12,22=E.M(12);9(!e[22]){H c=1;L(H n=12.1w;n;n=n.2q)9(n.1y==1)n.4U=c++;e[22]=Q}H b=P;9(3v==1){9(d==0||j.4U==d)b=Q}J 9((j.4U+d)%3v==0)b=Q;9(b^h)g.1a(j)}r=g}J{H f=E.55[m[1]];9(1m f!="1M")f=E.55[m[1]][m[2]];f=3w("P||G(a,i){I "+f+"}");r=E.2W(r,f,h)}}I{r:r,t:t}},4e:G(b,c){H d=[];H a=b[c];1W(a&&a!=U){9(a.1y==1)d.1a(a);a=a[c]}I d},2I:G(a,e,c,b){e=e||1;H d=0;L(;a;a=a[c])9(a.1y==1&&++d==e)1T;I a},5d:G(n,a){H r=[];L(;n;n=n.2q){9(n.1y==1&&(!a||n!=a))r.1a(n)}I r}});E.1j={1f:G(g,e,c,h){9(E.V.1h&&g.4j!=W)g=18;9(!c.2u)c.2u=6.2u++;9(h!=W){H d=c;c=G(){I d.16(6,1q)};c.M=h;c.2u=d.2u}H i=e.2l(".");e=i[0];c.O=i[1];H b=E.M(g,"2P")||E.M(g,"2P",{});H f=E.M(g,"2t",G(){H a;9(1m E=="W"||E.1j.4T)I a;a=E.1j.2t.16(g,1q);I a});H j=b[e];9(!j){j=b[e]={};9(g.4S)g.4S(e,f,P);J g.7N("43"+e,f)}j[c.2u]=c;6.1Z[e]=Q},2u:1,1Z:{},28:G(d,c,b){H e=E.M(d,"2P"),2L,4I;9(1m c=="1M"){H a=c.2l(".");c=a[0]}9(e){9(c&&c.O){b=c.4Q;c=c.O}9(!c){L(c 1i e)6.28(d,c)}J 9(e[c]){9(b)2E e[c][b.2u];J L(b 1i e[c])9(!a[1]||e[c][b].O==a[1])2E e[c][b];L(2L 1i e[c])1T;9(!2L){9(d.4P)d.4P(c,E.M(d,"2t"),P);J d.7M("43"+c,E.M(d,"2t"));2L=S;2E e[c]}}L(2L 1i e)1T;9(!2L){E.30(d,"2P");E.30(d,"2t")}}},1F:G(d,b,e,c,f){b=E.2h(b||[]);9(!e){9(6.1Z[d])E("*").1f([18,U]).1F(d,b)}J{H a,2L,1b=E.1n(e[d]||S),4N=!b[0]||!b[0].2M;9(4N)b.4w(6.4M({O:d,2m:e}));b[0].O=d;9(E.1n(E.M(e,"2t")))a=E.M(e,"2t").16(e,b);9(!1b&&e["43"+d]&&e["43"+d].16(e,b)===P)a=P;9(4N)b.44();9(f&&f.16(e,b)===P)a=P;9(1b&&c!==P&&a!==P&&!(E.11(e,\'a\')&&d=="4L")){6.4T=Q;e[d]()}6.4T=P}I a},2t:G(d){H a;d=E.1j.4M(d||18.1j||{});H b=d.O.2l(".");d.O=b[0];H c=E.M(6,"2P")&&E.M(6,"2P")[d.O],3q=1B.3A.2J.2O(1q,1);3q.4w(d);L(H j 1i c){3q[0].4Q=c[j];3q[0].M=c[j].M;9(!b[1]||c[j].O==b[1]){H e=c[j].16(6,3q);9(a!==P)a=e;9(e===P){d.2M();d.3p()}}}9(E.V.1h)d.2m=d.2M=d.3p=d.4Q=d.M=S;I a},4M:G(c){H a=c;c=E.1k({},a);c.2M=G(){9(a.2M)a.2M();a.7L=P};c.3p=G(){9(a.3p)a.3p();a.7K=Q};9(!c.2m&&c.65)c.2m=c.65;9(E.V.1N&&c.2m.1y==3)c.2m=a.2m.12;9(!c.4K&&c.4J)c.4K=c.4J==c.2m?c.7H:c.4J;9(c.64==S&&c.63!=S){H e=U.2V,b=U.1G;c.64=c.63+(e&&e.2R||b.2R||0);c.7E=c.7D+(e&&e.2B||b.2B||0)}9(!c.3Y&&(c.61||c.60))c.3Y=c.61||c.60;9(!c.5F&&c.5D)c.5F=c.5D;9(!c.3Y&&c.2r)c.3Y=(c.2r&1?1:(c.2r&2?3:(c.2r&4?2:0)));I c}};E.1b.1k({3W:G(c,a,b){I c=="5Y"?6.2G(c,a,b):6.N(G(){E.1j.1f(6,c,b||a,b&&a)})},2G:G(d,b,c){I 6.N(G(){E.1j.1f(6,d,G(a){E(6).5X(a);I(c||b).16(6,1q)},c&&b)})},5X:G(a,b){I 6.N(G(){E.1j.28(6,a,b)})},1F:G(c,a,b){I 6.N(G(){E.1j.1F(c,a,6,Q,b)})},7x:G(c,a,b){9(6[0])I E.1j.1F(c,a,6[0],P,b)},25:G(){H a=1q;I 6.4L(G(e){6.4H=0==6.4H?1:0;e.2M();I a[6.4H].16(6,[e])||P})},7v:G(f,g){G 4G(e){H p=e.4K;1W(p&&p!=6)2a{p=p.12}29(e){p=6};9(p==6)I P;I(e.O=="4x"?f:g).16(6,[e])}I 6.4x(4G).5U(4G)},2d:G(f){5T();9(E.3T)f.16(U,[E]);J E.3l.1a(G(){I f.16(6,[E])});I 6}});E.1k({3T:P,3l:[],2d:G(){9(!E.3T){E.3T=Q;9(E.3l){E.N(E.3l,G(){6.16(U)});E.3l=S}9(E.V.35||E.V.34)U.4P("5S",E.2d,P);9(!18.7t.K)E(18).39(G(){E("#4E").28()})}}});E.N(("7s,7r,39,7q,6n,5Y,4L,7p,"+"7n,7m,7l,4x,5U,7k,24,"+"51,7j,7i,7h,3U").2l(","),G(i,o){E.1b[o]=G(f){I f?6.3W(o,f):6.1F(o)}});H x=P;G 5T(){9(x)I;x=Q;9(E.V.35||E.V.34)U.4S("5S",E.2d,P);J 9(E.V.1h){U.7f("<7d"+"7y 22=4E 7z=Q "+"3k=//:><\\/1J>");H a=U.3S("4E");9(a)a.62=G(){9(6.2C!="1l")I;E.2d()};a=S}J 9(E.V.1N)E.4B=4j(G(){9(U.2C=="5Q"||U.2C=="1l"){4A(E.4B);E.4B=S;E.2d()}},10);E.1j.1f(18,"39",E.2d)}E.1b.1k({39:G(g,d,c){9(E.1n(g))I 6.3W("39",g);H e=g.1g(" ");9(e>=0){H i=g.2J(e,g.K);g=g.2J(0,e)}c=c||G(){};H f="4z";9(d)9(E.1n(d)){c=d;d=S}J{d=E.3a(d);f="5P"}H h=6;E.3G({1d:g,O:f,M:d,1l:G(a,b){9(b=="1C"||b=="5O")h.4o(i?E("<1s/>").3g(a.40.1p(/<1J(.|\\s)*?\\/1J>/g,"")).1Y(i):a.40);56(G(){h.N(c,[a.40,b,a])},13)}});I 6},7a:G(){I E.3a(6.5M())},5M:G(){I 6.1X(G(){I E.11(6,"2Y")?E.2h(6.79):6}).1E(G(){I 6.2H&&!6.3c&&(6.2Q||/24|6b/i.14(6.11)||/2g|1P|52/i.14(6.O))}).1X(G(i,c){H b=E(6).3i();I b==S?S:b.1c==1B?E.1X(b,G(a,i){I{2H:c.2H,1Q:a}}):{2H:c.2H,1Q:b}}).21()}});E.N("5L,5K,6t,5J,5I,5H".2l(","),G(i,o){E.1b[o]=G(f){I 6.3W(o,f)}});H B=(1u 3D).3B();E.1k({21:G(d,b,a,c){9(E.1n(b)){a=b;b=S}I E.3G({O:"4z",1d:d,M:b,1C:a,1V:c})},78:G(b,a){I E.21(b,S,a,"1J")},77:G(c,b,a){I E.21(c,b,a,"45")},76:G(d,b,a,c){9(E.1n(b)){a=b;b={}}I E.3G({O:"5P",1d:d,M:b,1C:a,1V:c})},75:G(a){E.1k(E.59,a)},59:{1Z:Q,O:"4z",2z:0,5G:"74/x-73-2Y-72",6o:Q,3e:Q,M:S},49:{},3G:G(s){H f,2y=/=(\\?|%3F)/g,1v,M;s=E.1k(Q,s,E.1k(Q,{},E.59,s));9(s.M&&s.6o&&1m s.M!="1M")s.M=E.3a(s.M);9(s.1V=="4b"){9(s.O.2p()=="21"){9(!s.1d.1t(2y))s.1d+=(s.1d.1t(/\\?/)?"&":"?")+(s.4b||"5E")+"=?"}J 9(!s.M||!s.M.1t(2y))s.M=(s.M?s.M+"&":"")+(s.4b||"5E")+"=?";s.1V="45"}9(s.1V=="45"&&(s.M&&s.M.1t(2y)||s.1d.1t(2y))){f="4b"+B++;9(s.M)s.M=s.M.1p(2y,"="+f);s.1d=s.1d.1p(2y,"="+f);s.1V="1J";18[f]=G(a){M=a;1C();1l();18[f]=W;2a{2E 18[f]}29(e){}}}9(s.1V=="1J"&&s.1L==S)s.1L=P;9(s.1L===P&&s.O.2p()=="21")s.1d+=(s.1d.1t(/\\?/)?"&":"?")+"57="+(1u 3D()).3B();9(s.M&&s.O.2p()=="21"){s.1d+=(s.1d.1t(/\\?/)?"&":"?")+s.M;s.M=S}9(s.1Z&&!E.5b++)E.1j.1F("5L");9(!s.1d.1g("8g")&&s.1V=="1J"){H h=U.4l("9U")[0];H g=U.5B("1J");g.3k=s.1d;9(!f&&(s.1C||s.1l)){H j=P;g.9R=g.62=G(){9(!j&&(!6.2C||6.2C=="5Q"||6.2C=="1l")){j=Q;1C();1l();h.3b(g)}}}h.58(g);I}H k=P;H i=18.6X?1u 6X("9P.9O"):1u 6W();i.9M(s.O,s.1d,s.3e);9(s.M)i.5C("9J-9I",s.5G);9(s.5y)i.5C("9H-5x-9F",E.49[s.1d]||"9D, 9C 9B 9A 5v:5v:5v 9z");i.5C("X-9x-9v","6W");9(s.6U)s.6U(i);9(s.1Z)E.1j.1F("5H",[i,s]);H c=G(a){9(!k&&i&&(i.2C==4||a=="2z")){k=Q;9(d){4A(d);d=S}1v=a=="2z"&&"2z"||!E.6S(i)&&"3U"||s.5y&&E.6R(i,s.1d)&&"5O"||"1C";9(1v=="1C"){2a{M=E.6Q(i,s.1V)}29(e){1v="5k"}}9(1v=="1C"){H b;2a{b=i.5s("6P-5x")}29(e){}9(s.5y&&b)E.49[s.1d]=b;9(!f)1C()}J E.5r(s,i,1v);1l();9(s.3e)i=S}};9(s.3e){H d=4j(c,13);9(s.2z>0)56(G(){9(i){i.9q();9(!k)c("2z")}},s.2z)}2a{i.9o(s.M)}29(e){E.5r(s,i,S,e)}9(!s.3e)c();I i;G 1C(){9(s.1C)s.1C(M,1v);9(s.1Z)E.1j.1F("5I",[i,s])}G 1l(){9(s.1l)s.1l(i,1v);9(s.1Z)E.1j.1F("6t",[i,s]);9(s.1Z&&!--E.5b)E.1j.1F("5K")}},5r:G(s,a,b,e){9(s.3U)s.3U(a,b,e);9(s.1Z)E.1j.1F("5J",[a,s,e])},5b:0,6S:G(r){2a{I!r.1v&&9n.9l=="54:"||(r.1v>=6N&&r.1v<9j)||r.1v==6M||E.V.1N&&r.1v==W}29(e){}I P},6R:G(a,c){2a{H b=a.5s("6P-5x");I a.1v==6M||b==E.49[c]||E.V.1N&&a.1v==W}29(e){}I P},6Q:G(r,b){H c=r.5s("9i-O");H d=b=="6K"||!b&&c&&c.1g("6K")>=0;H a=d?r.9g:r.40;9(d&&a.2V.37=="5k")6G"5k";9(b=="1J")E.5f(a);9(b=="45")a=3w("("+a+")");I a},3a:G(a){H s=[];9(a.1c==1B||a.4c)E.N(a,G(){s.1a(3f(6.2H)+"="+3f(6.1Q))});J L(H j 1i a)9(a[j]&&a[j].1c==1B)E.N(a[j],G(){s.1a(3f(j)+"="+3f(6))});J s.1a(3f(j)+"="+3f(a[j]));I s.66("&").1p(/%20/g,"+")}});E.1b.1k({1A:G(b,a){I b?6.1U({1H:"1A",2N:"1A",1r:"1A"},b,a):6.1E(":1P").N(G(){6.R.19=6.3h?6.3h:"";9(E.17(6,"19")=="2s")6.R.19="2Z"}).2D()},1z:G(b,a){I b?6.1U({1H:"1z",2N:"1z",1r:"1z"},b,a):6.1E(":3R").N(G(){6.3h=6.3h||E.17(6,"19");9(6.3h=="2s")6.3h="2Z";6.R.19="2s"}).2D()},6J:E.1b.25,25:G(a,b){I E.1n(a)&&E.1n(b)?6.6J(a,b):a?6.1U({1H:"25",2N:"25",1r:"25"},a,b):6.N(G(){E(6)[E(6).3t(":1P")?"1A":"1z"]()})},9c:G(b,a){I 6.1U({1H:"1A"},b,a)},9b:G(b,a){I 6.1U({1H:"1z"},b,a)},99:G(b,a){I 6.1U({1H:"25"},b,a)},98:G(b,a){I 6.1U({1r:"1A"},b,a)},96:G(b,a){I 6.1U({1r:"1z"},b,a)},95:G(c,a,b){I 6.1U({1r:a},c,b)},1U:G(k,i,h,g){H j=E.6D(i,h,g);I 6[j.3L===P?"N":"3L"](G(){j=E.1k({},j);H f=E(6).3t(":1P"),3y=6;L(H p 1i k){9(k[p]=="1z"&&f||k[p]=="1A"&&!f)I E.1n(j.1l)&&j.1l.16(6);9(p=="1H"||p=="2N"){j.19=E.17(6,"19");j.2U=6.R.2U}}9(j.2U!=S)6.R.2U="1P";j.3M=E.1k({},k);E.N(k,G(c,a){H e=1u E.2j(3y,j,c);9(/25|1A|1z/.14(a))e[a=="25"?f?"1A":"1z":a](k);J{H b=a.3s().1t(/^([+-]=)?([\\d+-.]+)(.*)$/),1O=e.2b(Q)||0;9(b){H d=3I(b[2]),2i=b[3]||"2T";9(2i!="2T"){3y.R[c]=(d||1)+2i;1O=((d||1)/e.2b(Q))*1O;3y.R[c]=1O+2i}9(b[1])d=((b[1]=="-="?-1:1)*d)+1O;e.3N(1O,d,2i)}J e.3N(1O,a,"")}});I Q})},3L:G(a,b){9(E.1n(a)){b=a;a="2j"}9(!a||(1m a=="1M"&&!b))I A(6[0],a);I 6.N(G(){9(b.1c==1B)A(6,a,b);J{A(6,a).1a(b);9(A(6,a).K==1)b.16(6)}})},9f:G(){H a=E.32;I 6.N(G(){L(H i=0;i<a.K;i++)9(a[i].T==6)a.6I(i--,1)}).5n()}});H A=G(b,c,a){9(!b)I;H q=E.M(b,c+"3L");9(!q||a)q=E.M(b,c+"3L",a?E.2h(a):[]);I q};E.1b.5n=G(a){a=a||"2j";I 6.N(G(){H q=A(6,a);q.44();9(q.K)q[0].16(6)})};E.1k({6D:G(b,a,c){H d=b&&b.1c==8Z?b:{1l:c||!c&&a||E.1n(b)&&b,2e:b,3J:c&&a||a&&a.1c!=8Y&&a};d.2e=(d.2e&&d.2e.1c==4W?d.2e:{8X:8W,8V:6N}[d.2e])||8T;d.3r=d.1l;d.1l=G(){E(6).5n();9(E.1n(d.3r))d.3r.16(6)};I d},3J:{6B:G(p,n,b,a){I b+a*p},5q:G(p,n,b,a){I((-38.9s(p*38.8R)/2)+0.5)*a+b}},32:[],2j:G(b,c,a){6.Y=c;6.T=b;6.1e=a;9(!c.3P)c.3P={}}});E.2j.3A={4r:G(){9(6.Y.2F)6.Y.2F.16(6.T,[6.2v,6]);(E.2j.2F[6.1e]||E.2j.2F.6z)(6);9(6.1e=="1H"||6.1e=="2N")6.T.R.19="2Z"},2b:G(a){9(6.T[6.1e]!=S&&6.T.R[6.1e]==S)I 6.T[6.1e];H r=3I(E.3C(6.T,6.1e,a));I r&&r>-8O?r:3I(E.17(6.T,6.1e))||0},3N:G(c,b,e){6.5u=(1u 3D()).3B();6.1O=c;6.2D=b;6.2i=e||6.2i||"2T";6.2v=6.1O;6.4q=6.4i=0;6.4r();H f=6;G t(){I f.2F()}t.T=6.T;E.32.1a(t);9(E.32.K==1){H d=4j(G(){H a=E.32;L(H i=0;i<a.K;i++)9(!a[i]())a.6I(i--,1);9(!a.K)4A(d)},13)}},1A:G(){6.Y.3P[6.1e]=E.1x(6.T.R,6.1e);6.Y.1A=Q;6.3N(0,6.2b());9(6.1e=="2N"||6.1e=="1H")6.T.R[6.1e]="8N";E(6.T).1A()},1z:G(){6.Y.3P[6.1e]=E.1x(6.T.R,6.1e);6.Y.1z=Q;6.3N(6.2b(),0)},2F:G(){H t=(1u 3D()).3B();9(t>6.Y.2e+6.5u){6.2v=6.2D;6.4q=6.4i=1;6.4r();6.Y.3M[6.1e]=Q;H a=Q;L(H i 1i 6.Y.3M)9(6.Y.3M[i]!==Q)a=P;9(a){9(6.Y.19!=S){6.T.R.2U=6.Y.2U;6.T.R.19=6.Y.19;9(E.17(6.T,"19")=="2s")6.T.R.19="2Z"}9(6.Y.1z)6.T.R.19="2s";9(6.Y.1z||6.Y.1A)L(H p 1i 6.Y.3M)E.1x(6.T.R,p,6.Y.3P[p])}9(a&&E.1n(6.Y.1l))6.Y.1l.16(6.T);I P}J{H n=t-6.5u;6.4i=n/6.Y.2e;6.4q=E.3J[6.Y.3J||(E.3J.5q?"5q":"6B")](6.4i,n,0,1,6.Y.2e);6.2v=6.1O+((6.2D-6.1O)*6.4q);6.4r()}I Q}};E.2j.2F={2R:G(a){a.T.2R=a.2v},2B:G(a){a.T.2B=a.2v},1r:G(a){E.1x(a.T.R,"1r",a.2v)},6z:G(a){a.T.R[a.1e]=a.2v+a.2i}};E.1b.6m=G(){H c=0,3E=0,T=6[0],5t;9(T)8L(E.V){H b=E.17(T,"2X")=="4F",1D=T.12,23=T.23,2K=T.3H,4f=1N&&3x(4s)<8J;9(T.6V){5w=T.6V();1f(5w.1S+38.33(2K.2V.2R,2K.1G.2R),5w.3E+38.33(2K.2V.2B,2K.1G.2B));9(1h){H d=E("4o").17("8H");d=(d=="8G"||E.5g&&3x(4s)>=7)&&2||d;1f(-d,-d)}}J{1f(T.5l,T.5z);1W(23){1f(23.5l,23.5z);9(35&&/^t[d|h]$/i.14(1D.37)||!4f)d(23);9(4f&&!b&&E.17(23,"2X")=="4F")b=Q;23=23.23}1W(1D.37&&!/^1G|4o$/i.14(1D.37)){9(!/^8D|1I-9S.*$/i.14(E.17(1D,"19")))1f(-1D.2R,-1D.2B);9(35&&E.17(1D,"2U")!="3R")d(1D);1D=1D.12}9(4f&&b)1f(-2K.1G.5l,-2K.1G.5z)}5t={3E:3E,1S:c}}I 5t;G d(a){1f(E.17(a,"9T"),E.17(a,"8A"))}G 1f(l,t){c+=3x(l)||0;3E+=3x(t)||0}}})();',62,616,'||||||this|||if|||||||||||||||||||||||||||||||||function|var|return|else|length|for|data|each|type|false|true|style|null|elem|document|browser|undefined||options|||nodeName|parentNode||test|jQuery|apply|css|window|display|push|fn|constructor|url|prop|add|indexOf|msie|in|event|extend|complete|typeof|isFunction|className|replace|arguments|opacity|div|match|new|status|firstChild|attr|nodeType|hide|show|Array|success|parent|filter|trigger|body|height|table|script|tbody|cache|string|safari|start|hidden|value|merge|left|break|animate|dataType|while|map|find|global||get|id|offsetParent|select|toggle|selected|toUpperCase|remove|catch|try|cur|al|ready|duration|done|text|makeArray|unit|fx|swap|split|target||pushStack|toLowerCase|nextSibling|button|none|handle|guid|now|stack|tb|jsre|timeout|inArray|scrollTop|readyState|end|delete|step|one|name|nth|slice|doc|ret|preventDefault|width|call|events|checked|scrollLeft|exec|px|overflow|documentElement|grep|position|form|block|removeData|rl|timers|max|opera|mozilla|trim|tagName|Math|load|param|removeChild|disabled|insertBefore|async|encodeURIComponent|append|oldblock|val|childNodes|src|readyList|multiFilter|color|defaultView|stopPropagation|args|old|toString|is|last|first|eval|parseInt|self|domManip|prototype|getTime|curCSS|Date|top||ajax|ownerDocument|parseFloat|easing|has|queue|curAnim|custom|innerHTML|orig|currentStyle|visible|getElementById|isReady|error|static|bind|String|which|getComputedStyle|responseText|oWidth|oHeight|on|shift|json|child|RegExp|ol|lastModified|isXMLDoc|jsonp|jquery|previousSibling|dir|safari2|el|styleFloat|state|setInterval|radio|getElementsByTagName|tr|empty|html|getAttribute|pos|update|version|input|float|runtimeStyle|unshift|mouseover|getPropertyValue|GET|clearInterval|safariTimer|visibility|clean|__ie_init|absolute|handleHover|lastToggle|index|fromElement|relatedTarget|click|fix|evt|andSelf|removeEventListener|handler|cloneNode|addEventListener|triggered|nodeIndex|unique|Number|classFilter|prevObject|selectedIndex|after|submit|password|removeAttribute|file|expr|setTimeout|_|appendChild|ajaxSettings|client|active|win|sibling|deep|globalEval|boxModel|cssFloat|object|checkbox|parsererror|offsetLeft|wrapAll|dequeue|props|lastChild|swing|handleError|getResponseHeader|results|startTime|00|box|Modified|ifModified|offsetTop|evalScript|createElement|setRequestHeader|ctrlKey|callback|metaKey|contentType|ajaxSend|ajaxSuccess|ajaxError|ajaxStop|ajaxStart|serializeArray|init|notmodified|POST|loaded|appendTo|DOMContentLoaded|bindReady|mouseout|not|removeAttr|unbind|unload|Width|keyCode|charCode|onreadystatechange|clientX|pageX|srcElement|join|outerHTML|substr|zoom|parse|textarea|reset|image|odd|even|before|quickClass|quickID|prepend|quickChild|execScript|offset|scroll|processData|uuid|contents|continue|textContent|ajaxComplete|clone|setArray|webkit|nodeValue|fl|_default|100|linear|href|speed|eq|createTextNode|throw|replaceWith|splice|_toggle|xml|colgroup|304|200|alpha|Last|httpData|httpNotModified|httpSuccess|fieldset|beforeSend|getBoundingClientRect|XMLHttpRequest|ActiveXObject|col|br|abbr|pixelLeft|urlencoded|www|application|ajaxSetup|post|getJSON|getScript|elements|serialize|clientWidth|hasClass|scr|clientHeight|write|relative|keyup|keypress|keydown|change|mousemove|mouseup|mousedown|right|dblclick|resize|focus|blur|frames|instanceof|hover|offsetWidth|triggerHandler|ipt|defer|offsetHeight|border|padding|clientY|pageY|Left|Right|toElement|Bottom|Top|cancelBubble|returnValue|detachEvent|attachEvent|substring|line|weight|animated|header|font|enabled|innerText|contains|only|size|gt|lt|uFFFF|u0128|417|inner|Height|toggleClass|removeClass|addClass|replaceAll|noConflict|insertAfter|prependTo|wrap|contentWindow|contentDocument|http|iframe|children|siblings|prevAll|nextAll|wrapInner|prev|Boolean|next|parents|maxLength|maxlength|readOnly|readonly|class|htmlFor|CSS1Compat|compatMode|compatible|borderTopWidth|ie|ra|inline|it|rv|medium|borderWidth|userAgent|522|navigator|with|concat|1px|10000|array|ig|PI|NaN|400|reverse|fast|600|slow|Function|Object|setAttribute|changed|be|can|property|fadeTo|fadeOut|getAttributeNode|fadeIn|slideToggle|method|slideUp|slideDown|action|cssText|stop|responseXML|option|content|300|th|protocol|td|location|send|cap|abort|colg|cos|tfoot|thead|With|leg|Requested|opt|GMT|1970|Jan|01|Thu|area|Since|hr|If|Type|Content|meta|specified|open|link|XMLHTTP|Microsoft|img|onload|row|borderLeftWidth|head|attributes'.split('|'),0,{}); + +/* + Copyright (c) 2004-2007, The Dojo Foundation + All Rights Reserved. + + Licensed under the Academic Free License version 2.1 or above OR the + modified BSD license. For more information on Dojo licensing, see: + + http://dojotoolkit.org/community/licensing.shtml +*/ + +/* + This is a compiled version of Dojo, built for deployment and not for + development. To get an editable version, please visit: + + http://dojotoolkit.org + + for documentation and information on getting the source. +*/ + +var decompressedDojo = function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)d[e(c)]=k[c]||e(c);k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('if(V z=="1k"){(B(){if(V D["1o"]=="1k"){D.1o={}}if((!D["1z"])||(!1z["ca"])){D.1z={}}A cn=["rA","rz","1K","ry","rx","9f","rw","rv","ru","rt","rs","rr","rq","ro","rn","rm"];A i=0,24;1s(24=cn[i++]){if(!1z[24]){1z[24]=B(){}}}if(V D["z"]=="1k"){D.z={}}z.1W=D;A d3={im:U,rl:U,rk:"",rj:"",ri:"",rh:K,rg:U};R(A 8z in d3){if(V 1o[8z]=="1k"){1o[8z]=d3[8z]}}A jK=["rf","rd","rc","rb"];A t;1s(t=jK.3a()){z["is"+t]=U}})();z.8h=1o.8h;z.cY={jJ:0,jI:9,jH:0,jG:"",jF:2V("$ra: r9 $".1f(/[0-9]+/)[0]),2i:B(){4G(z.cY){C jJ+"."+jI+"."+jH+jG+" ("+jF+")"}}};z.d1=B(jE,jD,1V){A 2h=1V||z.1W;R(A i=0,p;2h&&(p=jE[i]);i++){2h=(p in 2h?2h[p]:(jD?2h[p]={}:1k))}C 2h};z.88=B(jC,jA,jB){A d2=jC.1A("."),p=d2.8q(),M=z.d1(d2,K,jB);C(M&&p?(M[p]=jA):1k)};z.6q=B(jz,jy,jx){C z.d1(jz.1A("."),jy,jx)};z.r8=B(jw,M){C!!z.6q(jw,U,M)};z["3u"]=B(d0){C z.1W.3u?z.1W.3u(d0):3u(d0)};z.ia=B(jv,cZ,cX){A 8y="r7: "+jv;if(cZ){8y+=" "+cZ}if(cX){8y+=" -- r6 be r5 in cY: "+cX}1z.1K(8y)};z.r4=B(ju,cW){A cV="r3: "+ju+" -- r2 r1 4F r0 qZ qY.";if(cW){cV+=" "+cW}1z.1K(cV)};(B(){A cR={53:{},6p:0,1h:{},8k:{z:{1p:"z",1Z:"."},cU:{1p:"cU",1Z:"../qX/cU"},cT:{1p:"cT",1Z:"cT"}},cN:B(cS){A mp=D.8k;C jp(mp[cS]&&mp[cS].1Z)},jk:B(8x){A mp=D.8k;if(D.cN(8x)){C mp[8x].1Z}C 8x},8v:[],6t:U,56:[],8t:[],8u:U};R(A cQ in cR){z[cQ]=cR[cQ]}})();z.jg=B(8w,cP,cb){A 1g=(((8w.2s(0)=="/"||8w.1f(/^\\w+:/)))?"":D.51)+8w;if(1o.jt&&z.c8){1g+="?"+67(1o.jt).2f(/\\W+/g,"")}1u{C!cP?D.cO(1g,cb):D.jq(1g,cP,cb)}1y(e){1z.1K(e);C U}};z.cO=B(1g,cb){if(D.8v[1g]){C K}A 6u=D.iR(1g,K);if(!6u){C U}D.8v[1g]=K;D.8v.Y(1g);if(cb){6u="("+6u+")"}A jr=z["3u"](6u+"\\r\\n//@ qW="+1g);if(cb){cb(jr)}C K};z.jq=B(1g,jo,cb){A ok=U;1u{ok=D.cO(1g,cb)}1y(e){1z.1K("qV je ",1g," 4G 9f: ",e)}C jp(ok&&D.53[jo])};z.6m=B(){D.8u=K;D.6t=K;A 57=D.56;D.56=[];R(A x=0;x<57.G;x++){57[x]()}D.8u=U;if(z.6t&&z.6p==0&&D.56.G>0){z.8s()}};z.ck=B(){A 57=D.8t;1s(57.G){(57.8q())()}};z.qU=B(M,jn){A d=z;if(P.G==1){d.56.Y(M)}I{if(P.G>1){d.56.Y(B(){M[jn]()})}}if(d.6t&&d.6p==0&&!d.8u){d.8s()}};z.dW=B(M,jm){A d=z;if(P.G==1){d.8t.Y(M)}I{if(P.G>1){d.8t.Y(B(){M[jm]()})}}};z.iM=B(){if(D.6t){C}if(D.6p>0){1z.1K("qT qS in qR!");C}z.8s()};z.8s=B(){if(V 5c=="8b"||(1o["qQ"]&&z.2M)){5c("z.6m();",0)}I{z.6m()}};z.cF=B(jl){A 4v=jl.1A(".");R(A i=4v.G;i>0;i--){A 8r=4v.2w(0,i).22(".");if((i==1)&&!D.cN(8r)){4v[0]="../"+4v[0]}I{A cM=D.jk(8r);if(cM!=8r){4v.3S(0,i,cM);3f}}}C 4v};z.jj=U;z.8m=B(2T,qP,55){55=D.jj||55;A 54=D.53[2T];if(54){C 54}A cL=2T.1A(".");A 3L=D.cF(2T);A jh=((3L[0].2s(0)!="/")&&!3L[0].1f(/^\\w+:/));A ji=3L[3L.G-1];A 3m;if(ji=="*"){2T=cL.2w(0,-1).22(".");3L.8q();3m=3L.22("/")+"/"+(1o["qO"]||"qN")+".js";if(jh&&3m.2s(0)=="/"){3m=3m.2w(1)}}I{3m=3L.22("/")+".js";2T=cL.22(".")}A jf=(!55)?2T:L;A ok=D.jg(3m,jf);if((!ok)&&(!55)){2m S 1O("qM 3O 4E \'"+2T+"\'; 72 qL \'"+3m+"\'")}if((!55)&&(!D["qK"])){54=D.53[2T];if(!54){2m S 1O("qJ \'"+2T+"\' is 3O qI a8 je \'"+3m+"\'")}}C 54};z.8c=z.8m;z.1Q=B(cK){A cJ=cK+"";A 8p=cJ;A 6s=cK.1A(/\\./);if(6s[6s.G-1]=="*"){6s.8q();8p=6s.22(".")}A 8o=z.6q(8p,K);D.53[cJ]=8o;D.53[8p]=8o;C 8o};z.qH=B(8n){A jd=8n["qG"]||[];A cI=jd.3U(8n[z.j4]||8n["aY"]||[]);R(A x=0;x<cI.G;x++){A 8l=cI[x];if(8l.1P==4e){z.8m.14(z,8l)}I{z.8m(8l)}}};z.jb=B(jc,qF){if(jc===K){A cH=[];R(A i=1;i<P.G;i++){cH.Y(P[i])}z.8c.14(z,cH)}};z.qE=z.jb;z.io=B(cG,ja){D.8k[cG]={1p:cG,1Z:ja}};z.qD=B(qC,qB,qA,qz){z.8c("z.j9");z.j9.qy.14(z.qx,P)};(B(){A j7=S 9G("^(([^:/?#]+):)?(//([^/?#]*))?([^?#]*)(\\\\?([^#]*))?(#(.*))?$");A j6=S 9G("^((([^:]+:)?([^@]+))@)?([^:]*)(:([0-9]+))?$");z.4r=B(){A n=L;A 1V=P;A 1g=1V[0];R(A i=1;i<1V.G;i++){if(!1V[i]){6c}A 1t=S z.4r(1V[i]+"");A 4u=S z.4r(1g+"");if((1t.28=="")&&(!1t.4t)&&(!1t.3l)&&(!1t.1r)){if(1t.52!=n){4u.52=1t.52}1t=4u}I{if(!1t.4t){1t.4t=4u.4t;if(!1t.3l){1t.3l=4u.3l;if(1t.28.2s(0)!="/"){A j8=4u.28.21(0,4u.28.31("/")+1)+1t.28;A 1X=j8.1A("/");R(A j=0;j<1X.G;j++){if(1X[j]=="."){if(j==1X.G-1){1X[j]=""}I{1X.3S(j,1);j--}}I{if(j>0&&!(j==1&&1X[0]=="")&&1X[j]==".."&&1X[j-1]!=".."){if(j==(1X.G-1)){1X.3S(j,1);1X[j-1]=""}I{1X.3S(j-1,2);j-=2}}}}1t.28=1X.22("/")}}}}1g="";if(1t.4t){1g+=1t.4t+":"}if(1t.3l){1g+="//"+1t.3l}1g+=1t.28;if(1t.1r){1g+="?"+1t.1r}if(1t.52){1g+="#"+1t.52}}D.1g=1g.2i();A r=D.1g.1f(j7);D.4t=r[2]||(r[1]?"":n);D.3l=r[4]||(r[3]?"":n);D.28=r[5];D.1r=r[7]||(r[6]?"":n);D.52=r[9]||(r[8]?"":n);if(D.3l!=n){r=D.3l.1f(j6);D.8X=r[3]||n;D.8W=r[4]||n;D.qw=r[5];D.qv=r[7]||n}};z.4r.1C.2i=B(){C D.1g}})();z.qu=B(j5,2E){A 2B=z.cF(j5).22("/");if(!2B){C L}if(2B.31("/")!=2B.G-1){2B+="/"}A cE=2B.T(":");if(2B.2s(0)!="/"&&(cE==-1||cE>2B.T("/"))){2B=z.51+2B}C S z.4r(2B,2E)};if(V 26!="1k"){z.c8=K;z.j4="qt";(B(){A d=z;if(1q&&1q.4I){A 8j=1q.4I("ak");A j3=/z(\\.qs)?\\.js([\\?\\.]|$)/i;R(A i=0;i<8j.G;i++){A 4X=8j[i].5t("4X");if(!4X){6c}A m=4X.1f(j3);if(m){if(!1o["51"]){1o["51"]=4X.21(0,m.hK)}A cD=8j[i].5t("1o");if(cD){A cC=3u("({ "+cD+" })");R(A x in cC){1o[x]=cC[x]}}3f}}}d.51=1o["51"];A n=cq;A 8i=n.iL;A 4Z=n.qr;A 6r=2k(4Z);d.2M=(8i.T("qq")>=0)?6r:0;d.6B=(4Z.T("qo")>=0)||(4Z.T("j2")>=0)?6r:0;d.3o=(4Z.T("j2")>=0)?6r:0;A j1=8i.T("qn");d.gu=d.7B=((j1>=0)&&(!d.6B))?6r:0;d.j0=0;d.1l=0;d.iV=0;1u{if(d.7B){d.j0=2k(8i.1A("qm/")[1].1A(" ")[0])}if((1q.gx)&&(!d.2M)){d.1l=2k(4Z.1A("qk ")[1].1A(";")[0])}}1y(e){}if(z.1l&&(26.8f.cu==="9q:")){1o.iT=K}d.iX=B(){A 2A;A qj;A cB=d.6q("cz.cy");if(cB){C cB}if(V iZ!="1k"){2A=S iZ()}I{if(d.1l){1u{2A=S 9j("qi.qh")}1y(e){}}I{if(cq.qg["8Z/x-iY"]){2A=1q.a9("8b");2A.cA("Z","8Z/x-iY");2A.cA("3n",0);2A.cA("58",0);2A.1c.gq="7C";1q.5K.4c(2A)}}}if(!2A){C L}z.88("cz.cy.qf",2A);C z.6q("cz.cy")};A iW=d.iX();if(iW){d.iV=K}A cm=1q["aX"];d.qe=(cm=="aW")||(cm=="gr")||(d.1l<6);d.8h=1o.8h||(d.1l?n.qd:n.qc).1M();d.qb=1z.1K;d.cx=["iU.8g","em.8g","iU.8g.4.0"];d.9b=B(){A 4s=L;A cv=L;if(!z.1l||!1o.iT){1u{4s=S qa()}1y(e){}}if(!4s){R(A i=0;i<3;++i){A cw=z.cx[i];1u{4s=S 9j(cw)}1y(e){cv=e}if(4s){z.cx=[cw];3f}}}if(!4s){2m S 1O("8g 3O q9: "+cv)}C 4s};d.8Y=B(iS){A 4Y=iS.3N||0;C((4Y>=q8)&&(4Y<q7))||(4Y==q6)||(4Y==q5)||(!4Y&&(8f.cu=="9q:"||8f.cu=="q4:"))};A cs=1q.4I("q3");A iQ=(cs&&cs.G>0);d.iR=B(1g,iP){A 3K=D.9b();if(!iQ&&z.4r){1g=(S z.4r(26.8f,1g)).2i()}3K.dL("dD",1g,U);1u{3K.dI(L);if(!d.8Y(3K)){A 1G=1O("q2 4F 4E "+1g+" 3N:"+3K.3N);1G.3N=3K.3N;1G.2G=3K.2G;2m 1G}}1y(e){if(iP){C L}2m e}C 3K.2G}})();z.iO=U;z.6o=B(e){z.iO=K;A cr=(e&&e.Z)?e.Z.1M():"4E";if(P.2O.iN||(cr!="q1"&&cr!="4E")){C}P.2O.iN=K;if(V z["8e"]!="1k"){dX(z.8e);63 z.8e}if(z.6p==0){z.iM()}};if(1q.66){if(z.2M||(z.7B&&(1o["q0"]===K))){1q.66("pZ",z.6o,L)}26.66("4E",z.6o,L)}if(/(pY|pX)/i.6Z(cq.iL)){z.8e=dN(B(){if(/6m|iJ/.6Z(1q.6F)){z.6o()}},10)}(B(){A 3g=26;A 8d=B(cp,fp){A iK=3g[cp]||B(){};3g[cp]=B(){fp.14(3g,P);iK.14(3g,P)}};if(z.1l){1q.fJ("<iI"+"iH pW 4X=\\"//:\\" "+"pV=\\"if(D.6F==\'iJ\'){z.6o();}\\">"+"</iI"+"iH>");A co=K;8d("iG",B(){3g.5c(B(){co=U},0)});8d("pU",B(){if(co){z.ck()}});1u{1q.pT.2P("v","pS:pR-pQ-pP:pO");1q.pN().pM("v\\\\:*","pL:2E(#aY#pK)")}1y(e){}}I{8d("iG",B(){z.ck()})}})();z.pJ=B(){};z.1e=26["1q"]||L;z.3E=B(){C z.1e.3E||z.1e.4I("3E")[0]};z.ch=B(iF,iE){z.1W=iF;z.1e=iE};z.cf=B(4q,6n,iD){if((6n)&&((V 4q=="3c")||(4q 1N 67))){4q=6n[4q]}C(6n?4q.14(6n,iD||[]):4q())};z.pI=B(cj,iC,iB,iA){A cg;A iz=z.1W;A iy=z.1e;1u{z.ch(cj,cj.1q);cg=z.cf(iC,iB,iA)}ir{z.ch(iz,iy)}C cg};z.pH=B(ix,iw,iv,iu){A ce;A ip=z.1e;1u{z.1e=ix;ce=z.cf(iw,iv,iu)}ir{z.1e=ip}C ce};if(1o["cd"]){R(A cc in 1o["cd"]){z.io(cc,1o["cd"][cc])}}}if(1o.im){if(!1z.ca){z.8c("z.pG.ca")}}}if(!z.1h["z.X.c9"]){z.1h["z.X.c9"]=K;z.1Q("z.X.c9");z.1R=B(it){C(V it=="3c"||it 1N 67)};z.2l=B(it){C(it&&it 1N 4e||V it=="6a"||((V z["1H"]!="1k")&&(it 1N z.1H)))};if(z.c8&&z.3o){z.1Y=B(it){if((V(it)=="B")&&(it=="[8b 1H]")){C U}C(V it=="B"||it 1N bI)}}I{z.1Y=B(it){C(V it=="B"||it 1N bI)}}z.ib=B(it){if(V it=="1k"){C U}C(it===L||V it=="8b"||z.2l(it)||z.1Y(it))};z.pF=B(it){A d=z;if((!it)||(V it=="1k")){C U}if(d.1R(it)){C U}if(d.1Y(it)){C U}if(d.2l(it)){C K}if((it.5w)&&(it.5w.1M()=="3R")){C U}if(pE(it.G)){C K}C U};z.pD=B(it){if(!it){C U}C!z.1Y(it)&&/\\{\\s*\\[il 5h\\]\\s*\\}/.6Z(67(it))};z.c7=B(M,4W){A 8a={};R(A x in 4W){if((V 8a[x]=="1k")||(8a[x]!=4W[x])){M[x]=4W[x]}}if(z.1l){A p=4W.2i;if((V(p)=="B")&&(p!=M.2i)&&(p!=8a.2i)&&(p!="\\pC 2i() {\\n [il 5h]\\n}\\n")){M.2i=4W.2i}}C M};z.1x=B(M,pB){R(A i=1,l=P.G;i<l;i++){z.c7(M,P[i])}C M};z.4M=B(c6,pA){R(A i=1,l=P.G;i<l;i++){z.c7(c6.1C,P[i])}C c6};z.ig=B(c5,89){A ij=z.4d(P,2);A ik=z.1R(89);C B(){A ih=z.4d(P);A f=(ik?(c5||z.1W)[89]:89);C(f)&&(f.14(c5||D,ij.3U(ih)))}};z.2p=B(2z,3k){if(P.G>2){C z.ig.14(z,P)}if(!3k){3k=2z;2z=L}if(z.1R(3k)){2z=2z||z.1W;if(!2z[3k]){2m(["z.2p: ie[\\"",3k,"\\"] is L (ie=\\"",2z,"\\")"].22(""))}C B(){C 2z[3k].14(2z,P||[])}}I{C(!2z?3k:B(){C 3k.14(2z,P||[])})}};z.6j=B(M,c3){B c4(){};c4.1C=M;A c2=S c4();if(c3){z.1x(c2,c3)}C c2};z.7X=B(pz){A Q=[L];C z.2p.14(z,Q.3U(z.4d(P)))};z.4d=B(M,ic){A Q=[];R(A x=ic||0;x<M.G;x++){Q.Y(M[x])}C Q};z.c1=B(o){if(!o){C o}if(z.2l(o)){A r=[];R(A i=0;i<o.G;++i){r.Y(z.c1(o[i]))}C r}I{if(z.ib(o)){if(o.2t&&o.a7){C o.a7(K)}I{A r=S o.1P();R(A i in o){if(!(i in r)||r[i]!=o[i]){r[i]=z.c1(o[i])}}C r}}}C o};z.7g=B(2H){C 2H.2f(/^\\s\\s*/,"").2f(/\\s\\s*$/,"")}}if(!z.1h["z.X.2r"]){z.1h["z.X.2r"]=K;z.1Q("z.X.2r");z.2r=B(6l,4p,3j){if(z.1Y(3j)||(P.G>3)){z.ia("z.2r: R 9P \'"+6l+"\' py pw B as \'1P\' pv pu of as a pt i3.","","1.0");A c=3j;3j=P[3]||{};3j.1P=c}A dd=P.2O,4V=L;if(z.2l(4p)){4V=4p;4p=4V.3a()}if(4V){R(A i=0,m;i<4V.G;i++){m=4V[i];if(!m){2m("ps #"+i+" 4F pr of "+6l+" is L. pq\'s pp a po pl is 3O 6m.")}4p=dd.6j(4p,m)}}A i9=(3j||0).1P,6k=dd.6j(4p),fn;R(A i in 3j){if(z.1Y(fn=3j[i])&&(!0[i])){fn.i4=i}}z.4M(6k,{4o:6l,bY:i9,bZ:L},3j||0);6k.1C.1P=6k;C z.88(6l,6k)};z.1x(z.2r,{6j:B(c0,i8){A bp=(c0||0).1C,mp=(i8||0).1C;A 2S=z.2r.i7();z.1x(2S,{84:bp,1x:mp});if(c0){2S.1C=z.6j(bp)}z.4M(2S,z.2r.i6,mp||0,{bY:L});2S.1C.1P=2S;2S.1C.4o=(bp||0).4o+"pk"+(mp||0).4o;z.88(2S.1C.4o,2S);C 2S},i7:B(){C B(){D.i5(P)}},i6:{i5:B(86){A c=86.2O,s=c.84,ct=s&&s.1P,m=c.1x,87=m&&m.1P,a=86,ii,fn;if(a[0]){if((fn=a[0]["bZ"])){a=fn.14(D,a)||a}}if(fn=c.1C.bZ){a=fn.14(D,a)||a}if(ct&&ct.14){ct.14(D,a)}if(87&&87.14){87.14(D,a)}if(ii=c.1C.bY){ii.14(D,86)}},bX:B(85){A c=D.1P,p,m;1s(c){p=c.84;m=c.1x;if(m==85||(m 1N 85.1P)){C p}if(m&&(m=m.bX(85))){C m}c=p&&p.1P}},6h:B(83,82,bW,6i){A p=bW,c,m,f;do{c=p.1P;m=c.1x;if(m&&(m=D.6h(83,82,m,6i))){C m}if((f=p[83])&&(6i==(f==82))){C p}p=c.84}1s(p);C!6i&&(p=D.bX(bW))&&D.6h(83,82,p,6i)},bU:B(2R,4U,bV){A a=P;if(!z.1R(a[0])){bV=4U;4U=2R;2R=4U.2O.i4}A c=4U.2O,p=D.1P.1C,a=bV||4U,fn,mp;if(D[2R]!=c||p[2R]==c){mp=D.6h(2R,c,p,K);if(!mp){2m(D.4o+": 1p i3 (\\""+2R+"\\") 4F bU pj 1f 2O (2r.js)")}p=D.6h(2R,c,mp,U)}fn=p&&p[2R];if(!fn){1z.1K(mp.4o+": no bU \\""+2R+"\\" ph pg (2r.js)");C}C fn.14(D,a)}}})}if(!z.1h["z.X.2c"]){z.1h["z.X.2c"]=K;z.1Q("z.X.2c");z.3i={i2:B(){C B(){A ap=4e.1C,c=P.2O,ls=c.2b,t=c.5V;A r=t&&t.14(D,P);R(A i in ls){if(!(i in ap)){ls[i].14(D,P)}}C r}},2P:B(6g,bT,i1){6g=6g||z.1W;A f=6g[bT];if(!f||!f.2b){A d=z.3i.i2();d.5V=f;d.2b=[];f=6g[bT]=d}C f.2b.Y(i1)},3J:B(i0,hZ,bS){A f=(i0||z.1W)[hZ];if(f&&f.2b&&bS--){63 f.2b[bS]}}};z.2c=B(M,pd,pc,pa,p9){A a=P,F=[],i=0;F.Y(z.1R(a[0])?L:a[i++],a[i++]);A a1=a[i+1];F.Y(z.1R(a1)||z.1Y(a1)?a[i++]:L,a[i++]);R(A l=a.G;i<l;i++){F.Y(a[i])}C z.by.14(D,F)};z.by=B(M,bR,hY,hX){A l=z.3i,h=l.2P(M,bR,z.2p(hY,hX));C[M,bR,h,l]};z.p8=B(6f){if(6f&&6f[0]!==1k){z.bv.14(D,6f);63 6f[0]}};z.bv=B(M,hV,hU,hW){hW.3J(M,hV,hU)};z.80={};z.p7=B(bQ,hT,hS){C[bQ,z.3i.2P(z.80,bQ,z.2p(hT,hS))]};z.p6=B(81){if(81){z.3i.3J(z.80,81[0],81[1])}};z.hQ=B(hR,F){A f=z.80[hR];(f)&&(f.14(D,F||[]))};z.p5=B(hP,M,bP){A pf=B(){z.hQ(hP,P)};C(bP)?z.2c(M,bP,pf):z.2c(M,pf)}}if(!z.1h["z.X.30"]){z.1h["z.X.30"]=K;z.1Q("z.X.30");z.30=B(hO){D.bM=[];D.id=D.hN();D.2y=-1;D.3M=0;D.4R=[L,L];D.bO=hO;D.7Z=U};z.4M(z.30,{hN:(B(){A n=1;C B(){C n++}})(),4C:B(){if(D.2y==-1){if(D.bO){D.bO(D)}I{D.7Z=K}if(D.2y==-1){A 1G=S 1O("30 p4");1G.dY="4C";D.5i(1G)}}I{if((D.2y==0)&&(D.4R[0]1N z.30)){D.4R[0].4C()}}},7V:B(1v){D.2y=((1v 1N 1O)?1:0);D.4R[D.2y]=1v;D.7U()},bN:B(){if(D.2y!=-1){if(!D.7Z){2m S 1O("p3 p2!")}D.7Z=U;C}},dM:B(1v){D.bN();D.7V(1v)},5i:B(1v){D.bN();if(!(1v 1N 1O)){1v=S 1O(1v)}D.7V(1v)},9e:B(cb,4T){A 6e=z.2p(cb,4T);if(P.G>2){6e=z.7X(6e,P,2)}C D.5k(6e,6e)},ef:B(cb,4T){A 7Y=z.2p(cb,4T);if(P.G>2){7Y=z.7X(7Y,P,2)}C D.5k(7Y,L)},ed:B(cb,4T){A 7W=z.2p(cb,4T);if(P.G>2){7W=z.7X(7W,P,2)}C D.5k(L,7W)},5k:B(cb,eb){D.bM.Y([cb,eb]);if(D.2y>=0){D.7U()}C D},7U:B(){A bL=D.bM;A 4n=D.2y;A 1v=D.4R[4n];A 4S=D;A cb=L;1s((bL.G>0)&&(D.3M==0)){A f=bL.3a()[4n];if(!f){6c}1u{1v=f(1v);4n=((1v 1N 1O)?1:0);if(1v 1N z.30){cb=B(1v){4S.7V(1v);4S.3M--;if((4S.3M==0)&&(4S.2y>=0)){4S.7U()}};D.3M++}}1y(1G){1z.1K(1G);4n=1;1v=1G}}D.2y=4n;D.4R[4n]=1v;if((cb)&&(D.3M)){1v.9e(cb)}}})}if(!z.1h["z.X.2e"]){z.1h["z.X.2e"]=K;z.1Q("z.X.2e");z.5m=B(2e){1u{C 3u("("+2e+")")}1y(e){1z.1K(e);C 2e}};z.bK=B(2H){C("\\""+2H.2f(/(["\\\\])/g,"\\\\$1")+"\\"").2f(/[\\f]/g,"\\\\f").2f(/[\\b]/g,"\\\\b").2f(/[\\n]/g,"\\\\n").2f(/[\\t]/g,"\\\\t").2f(/[\\r]/g,"\\\\r")};z.hM="\\t";z.eq=B(it,4l,4P){4P=4P||"";A 4k=(4l?4P+z.hM:"");A 6b=(4l?"\\n":"");A 4Q=V(it);if(4Q=="1k"){C"1k"}I{if((4Q=="4J")||(4Q=="p1")){C it+""}I{if(it===L){C"L"}}}if(4Q=="3c"){C z.bK(it)}A 6d=P.2O;A 4m;if(V it.hL=="B"){4m=it.hL();if(it!==4m){C 6d(4m,4l,4k)}}if(V it.2e=="B"){4m=it.2e();if(it!==4m){C 6d(4m,4l,4k)}}if(z.2l(it)){A 1v=[];R(A i=0;i<it.G;i++){A 1U=6d(it[i],4l,4k);if(V(1U)!="3c"){1U="1k"}1v.Y(6b+4k+1U)}C"["+1v.22(", ")+6b+4P+"]"}if(4Q=="B"){C L}A bJ=[];R(A 1i in it){A 7T;if(V(1i)=="4J"){7T="\\""+1i+"\\""}I{if(V(1i)=="3c"){7T=z.bK(1i)}I{6c}}1U=6d(it[1i],4l,4k);if(V(1U)!="3c"){6c}bJ.Y(6b+4k+7T+": "+1U)}C"{"+bJ.22(", ")+6b+4P+"}"}}if(!z.1h["z.X.6a"]){z.1h["z.X.6a"]=K;z.1Q("z.X.6a");(B(){A 69=B(Q,M,cb){C[(z.1R(Q)?Q.1A(""):Q),(M||z.1W),(z.1R(cb)?(S bI("1m","hK","6a",cb)):cb)]};z.1x(z,{T:B(bH,hH,hI,hJ){A i=0,2q=1,1d=bH.G;if(hJ){i=1d-1;2q=1d=-1}R(i=hI||i;i!=1d;i+=2q){if(bH[i]==hH){C i}}C-1},31:B(hG,hF,hE){C z.T(hG,hF,hE,K)},1n:B(Q,hD,M){if(!Q||!Q.G){C}A 1I=69(Q,M,hD);Q=1I[0];R(A i=0,l=1I[0].G;i<l;i++){1I[2].2d(1I[1],Q[i],i,Q)}},bE:B(bF,Q,hC,M){A 1I=69(Q,M,hC);Q=1I[0];R(A i=0,l=Q.G;i<l;i++){A bG=!!1I[2].2d(1I[1],Q[i],i,Q);if(bF^bG){C bG}}C bF},ah:B(Q,hB,hA){C D.bE(K,Q,hB,hA)},ag:B(Q,hz,hy){C D.bE(U,Q,hz,hy)},23:B(Q,7t,M){A 1I=69(Q,M,7t);Q=1I[0];A bD=((P[3])?(S P[3]()):[]);R(A i=0;i<Q.G;++i){bD.Y(1I[2].2d(1I[1],Q[i],i,Q))}C bD},3T:B(Q,hx,M){A 1I=69(Q,M,hx);Q=1I[0];A bC=[];R(A i=0;i<Q.G;i++){if(1I[2].2d(1I[1],Q[i],i,Q)){bC.Y(Q[i])}}C bC}})})()}if(!z.1h["z.X.1J"]){z.1h["z.X.1J"]=K;z.1Q("z.X.1J");z.1J=B(bB){if(bB){D.hw(bB)}};z.1J.hp={p0:[0,0,0],oZ:[60,60,60],oY:[2j,2j,2j],oX:[1T,1T,1T],oW:[2j,0,0],oV:[1T,0,0],oU:[2j,0,2j],oT:[1T,0,1T],oS:[0,2j,0],oR:[0,1T,0],oQ:[2j,2j,0],oP:[1T,1T,0],oO:[0,0,2j],oN:[0,0,1T],oM:[0,2j,2j],oL:[0,1T,1T]};z.4M(z.1J,{r:1T,g:1T,b:1T,a:1,bz:B(r,g,b,a){A t=D;t.r=r;t.g=g;t.b=b;t.a=a},hw:B(2Q){A d=z;if(d.1R(2Q)){d.hq(2Q,D)}I{if(d.2l(2Q)){d.7P(2Q,D)}I{D.bz(2Q.r,2Q.g,2Q.b,2Q.a);if(!(2Q 1N d.1J)){D.7Q()}}}C D},7Q:B(){C D},oK:B(){A t=D;C[t.r,t.g,t.b]},oJ:B(){A t=D;C[t.r,t.g,t.b,t.a]},oI:B(){A Q=z.23(["r","g","b"],B(x){A s=D[x].2i(16);C s.G<2?"0"+s:s},D);C"#"+Q.22("")},8F:B(hv){A t=D,7S=t.r+", "+t.g+", "+t.b;C(hv?"hs("+7S+", "+t.a:"7S("+7S)+")"},2i:B(){C D.8F(K)}});z.d8=B(bA,1d,hu,M){A d=z,t=M||S z.1J();d.1n(["r","g","b","a"],B(x){t[x]=bA[x]+(1d[x]-bA[x])*hu;if(x!="a"){t[x]=2Y.oH(t[x])}});C t.7Q()};z.ho=B(ht,M){A m=ht.1M().1f(/^hs?\\(([\\s\\.,0-9]+)\\)/);C m&&z.7P(m[1].1A(/\\s*,\\s*/),M)};z.hn=B(4j,M){A d=z,t=M||S d.1J(),7R=(4j.G==4)?4:8,hr=(1<<7R)-1;4j=2V("oG"+4j.3b(1));if(2L(4j)){C L}d.1n(["b","g","r"],B(x){A c=4j&hr;4j>>=7R;t[x]=7R==4?17*c:c});t.a=1;C t};z.7P=B(a,M){A t=M||S z.1J();t.bz(2V(a[0]),2V(a[1]),2V(a[2]),2V(a[3]));if(2L(t.a)){t.a=1}C t.7Q()};z.hq=B(2H,M){A a=z.1J.hp[2H];C a&&z.7P(a,M)||z.ho(2H,M)||z.hn(2H,M)}}if(!z.1h["z.X"]){z.1h["z.X"]=K;z.1Q("z.X")}if(!z.1h["z.X.5Z"]){z.1h["z.X.5Z"]=K;z.1Q("z.X.5Z");(B(){A 1j=z.b2={2P:B(E,68,fp){if(!E){C}68=1j.4O(68);fp=1j.7G(68,fp);E.66(68,fp,U);C fp},3J:B(E,hm,hl){(E)&&(E.oF(1j.4O(hm),hl,U))},4O:B(1p){C(1p.2w(0,2)=="on"?1p.2w(2):1p)},7G:B(1p,fp){C(1p!="4b"?fp:B(e){C fp.2d(D,1j.4i(e,D))})},4i:B(H,oE){4w(H.Z){2X"4b":1j.7K(H);3f}C H},7K:B(H){H.oD=(H.3h?67.oC(H.3h):"")}};z.oB=B(H,hk){C 1j.4i(H,hk)};z.gY=B(H){H.7J();H.7I()};A 7O=z.3i;z.by=B(M,bx,hh,hg,hi){A hj=M&&(M.2t||M.oA||M.66);A bw=!hj?0:(!hi?1:2),l=[z.3i,1j,7O][bw];A h=l.2P(M,bx,z.2p(hh,hg));C[M,bx,h,bw]};z.bv=B(M,he,hd,hf){([z.3i,1j,7O][hf]).3J(M,he,hd)};z.5W={oz:8,gV:9,oy:12,ox:13,ow:16,ov:17,ou:18,gG:19,ot:20,os:27,or:32,b5:33,b4:34,gE:35,gF:36,b7:37,b9:38,b6:39,b8:40,gD:45,8S:46,oq:47,oo:91,om:92,ol:93,oj:96,oi:97,oh:98,og:99,oe:6D,od:oc,ob:oa,o9:o8,o7:o6,o5:o4,o3:bi,o2:o1,o0:nZ,nY:nX,nW:nV,nU:bk,gS:nT,gR:nS,gQ:nR,gP:nQ,gO:nP,gN:nO,gM:nN,gL:nM,gK:nL,gJ:nK,gI:nJ,gH:nI,nH:nG,nF:nE,nD:nC,gB:nB,gC:nA};if(z.1l){bf=B(e,5h){1u{C(e.3I=5h)}1y(e){C 0}};A 61=z.3i;if(!1o.nz){7O=61=z.gy={b3:[],2P:B(64,bu,hc){64=64||z.1W;A f=64[bu];if(!f||!f.2b){A d=z.gz();d.5V=f&&(7M.Y(f)-1);d.2b=[];f=64[bu]=d}C f.2b.Y(7M.Y(hc)-1)},3J:B(hb,ha,7N){A f=(hb||z.1W)[ha],l=f&&f.2b;if(f&&l&&7N--){63 7M[l[7N]];63 l[7N]}}};A 7M=61.b3}z.1x(1j,{2P:B(E,62,fp){if(!E){C}62=1j.4O(62);if(62=="h3"){A kd=E.bs;if(!kd||!kd.2b||!kd.h9){1j.2P(E,"bs",1j.h4);E.bs.h9=K}}C 61.2P(E,62,1j.7G(fp))},3J:B(E,h8,h7){61.3J(E,1j.4O(h8),h7)},4O:B(7L){C(7L.2w(0,2)!="on"?"on"+7L:7L)},ny:B(){},4i:B(H,4N){if(!H){A w=(4N)&&((4N.aD||4N.1q||4N).nx)||26;H=w.5Z}if(!H){C(H)}H.5V=H.br;H.bh=(4N||H.br);H.nw=H.nv;H.nu=H.nr;A bq=H.br,1e=(bq&&bq.aD)||1q;A bn=((z.1l<6)||(1e["aX"]=="aW"))?1e.3E:1e.5K;A bm=z.aB();H.nq=H.np+z.aH(bn.5I||0)-bm.x;H.nn=H.nm+(bn.5G||0)-bm.y;if(H.Z=="fk"){H.h6=H.nl}if(H.Z=="fj"){H.h6=H.nk}H.7I=1j.bc;H.7J=1j.ba;C 1j.h5(H)},h5:B(H){4w(H.Z){2X"4b":A c=("3h"in H?H.3h:H.3I);if(c==10){c=0;H.3I=13}I{if(c==13||c==27){c=0}I{if(c==3){c=99}}}H.3h=c;1j.7K(H);3f}C H},gZ:{bi:42,bk:47,h2:59,nj:43,ni:44,nh:45,ng:46,nf:47,60:96,h1:91,nb:92,na:93,h0:39},h4:B(H){A kp=H.bh.h3;if(!kp||!kp.2b){C}A k=H.3I;A bj=(k!=13)&&(k!=32)&&(k!=27)&&(k<48||k>90)&&(k<96||k>bk)&&(k<h2||k>60)&&(k<h1||k>h0);if(bj||H.5Y){A c=(bj?0:k);if(H.5Y){if(k==3||k==13){C}I{if(c>95&&c<bi){c-=48}I{if((!H.5X)&&(c>=65&&c<=90)){c+=32}I{c=1j.gZ[c]||c}}}}A 2x=1j.7H(H,{Z:"4b",2x:K,3h:c});kp.2d(H.bh,2x);H.bg=2x.bg;H.bd=2x.bd;bf(H,2x.3I)}},bc:B(){D.bg=K},ba:B(){D.n9=D.3I;if(D.5Y){bf(D,0)}D.bd=U}});z.gY=B(H){H=H||26.5Z;1j.bc.2d(H);1j.ba.2d(H)}}1j.7H=B(H,gX){A 2x=z.1x({},H,gX);1j.7K(2x);2x.7J=B(){H.7J()};2x.7I=B(){H.7I()};C 2x};if(z.2M){z.1x(1j,{4i:B(H,n8){4w(H.Z){2X"4b":A c=H.n7;if(c==3){c=99}c=((c<41)&&(!H.5X)?0:c);if((H.5Y)&&(!H.5X)&&(c>=65)&&(c<=90)){c+=32}C 1j.7H(H,{3h:c})}C H}})}if(z.3o){z.1x(1j,{4i:B(H,n6){4w(H.Z){2X"4b":A c=H.3h,s=H.5X,k=H.3I;k=k||gA[H.gW]||0;if(H.gW=="n5"){c=0}I{if((H.5Y)&&(c>0)&&(c<27)){c+=96}I{if(c==z.5W.gU){c=z.5W.gV;s=K}I{c=(c>=32&&c<gT?c:0)}}}C 1j.7H(H,{3h:c,5X:s,3I:k})}C H}});z.1x(z.5W,{gU:25,b9:gT,b8:n4,b7:n3,b6:n2,gS:n1,gR:n0,gQ:mZ,gP:mY,gO:mX,gN:mW,gM:mV,gL:mU,gK:mT,gJ:mS,gI:mR,gH:mQ,gG:mP,8S:mO,gF:mN,gE:mM,b5:mL,b4:mK,gD:mJ,mI:mH,gC:mG,gB:mF});A dk=z.5W,gA={"mE":dk.b9,"mD":dk.b8,"mC":dk.b7,"mB":dk.b6,"mA":dk.b5,"mz":dk.b4}}})();if(z.1l){z.gz=B(){C B(){A ap=4e.1C,h=z.gy.b3,c=P.2O,ls=c.2b,t=h[c.5V];A r=t&&t.14(D,P);R(A i in ls){if(!(i in ap)){h[ls[i]].14(D,P)}}C r}};z.b2.7G=B(fp){A f=z.b2.4i;C B(e){C fp.2d(D,f(e,D))}}}}if(!z.1h["z.X.b1"]){z.1h["z.X.b1"]=K;z.1Q("z.X.b1");1u{1q.my("mx",U,K)}1y(e){}if(z.1l||z.2M){z.1D=B(id,1e){if(z.1R(id)){A b0=(1e||z.1e);A 11=b0.gv(id);if((11)&&(11.gw.id.1Z==id)){C 11}I{A 5U=b0.gx[id];if(!5U){C}if(!5U.G){C 5U}A i=0;1s(11=5U[i++]){if(11.gw.id.1Z==id){C 11}}}}I{C id}}}I{z.1D=B(id,1e){if(z.1R(id)){C(1e||z.1e).gv(id)}I{C id}}}(B(){A 5T=L;z.mw=B(E){E=z.1D(E);1u{if(!5T){5T=1q.a9("mv")}5T.4c(E.1L?E.1L.fs(E):E);5T.9L=""}1y(e){}};z.mu=B(E,7F){1u{E=z.1D(E);7F=z.1D(7F);1s(E){if(E===7F){C K}E=E.1L}}1y(e){}C U};z.mt=B(E,5S){E=z.1D(E);if(z.gu){E.1c.ms=(5S)?"dg":"7C"}I{if(z.6B){E.1c.mr=(5S)?"8K":"7C"}I{if(z.1l){E.gs=(5S)?"":"on";z.1r("*",E).1n(B(gt){gt.gs=(5S)?"":"on"})}}}};A 5R=B(E,4h){4h.1L.mq(E,4h);C K};A aZ=B(E,4h){A pn=4h.1L;if(4h==pn.fm){pn.4c(E)}I{C 5R(E,4h.71)}C K};z.5E=B(E,2a,3H){if((!E)||(!2a)||(V 3H=="1k")){C U}E=z.1D(E);2a=z.1D(2a);if(V 3H=="4J"){A cn=2a.3W;if(((3H==0)&&(cn.G==0))||(cn.G==3H)){2a.4c(E);C K}if(3H==0){C 5R(E,2a.5A)}C aZ(E,cn[3H-1])}4w(3H.1M()){2X"mo":C 5R(E,2a);2X"a8":C aZ(E,2a);2X"9M":if(2a.5A){C 5R(E,2a.5A)}I{2a.4c(E);C K}3f;aY:2a.4c(E);C K}};z.aP="5g-3G";if(z.1l){A aV=1q.aX;z.aP=(aV=="aW")||(aV=="gr")||(z.1l<6)?"g5-3G":"5g-3G"}A 1E,dv=1q.mn;if(z.3o){1E=B(E){A s=dv.3F(E,L);if(!s&&E.1c){E.1c.gq="";s=dv.3F(E,L)}C s||{}}}I{if(z.1l){1E=B(E){C E.gn}}I{1E=B(E){C dv.3F(E,L)}}}z.3F=1E;if(!z.1l){z.4g=B(mm,gp){C 2k(gp)||0}}I{z.4g=B(go,2N){if(!2N){C 0}if(2N=="ml"){C 4}if(2N.2w&&(2N.2w(-2)=="px")){C 2k(2N)}4G(go){A gm=1c.2g;A gl=aU.2g;aU.2g=gn.2g;1u{1c.2g=2N;2N=1c.mk}1y(e){2N=0}1c.2g=gm;aU.2g=gl}C 2N}}z.ge=(z.1l?B(E){1u{C(E.mj.mi.2W/6D)}1y(e){C 1}}:B(E){C z.3F(E).2W});z.gf=(z.1l?B(E,7D){if(7D==1){E.1c.7E=E.1c.7E.2f(/gk:[^;]*;/i,"");if(E.gj.1M()=="gi"){z.1r("> gh",E).1n(B(i){i.1c.7E=i.1c.7E.2f(/gk:[^;]*;/i,"")})}}I{A o="mh(mg="+(7D*6D)+")";E.1c.3T=o}if(E.gj.1M()=="gi"){z.1r("> gh",E).1n(B(i){i.1c.3T=o})}C 7D}:B(E,gg){C E.1c.2W=gg});A 5Q={3n:K,58:K,2g:K,5J:K};A gd=B(E,Z,5P){Z=Z.1M();if(5Q[Z]===K){C z.4g(E,5P)}I{if(5Q[Z]===U){C 5P}I{if((Z.T("mf")>=0)||(Z.T("md")>=0)||(Z.T("3n")>=0)||(Z.T("58")>=0)||(Z.T("5q")>=0)||(Z.T("mc")>=0)||(Z.T("ma")>=0)){5Q[Z]=K;C z.4g(E,5P)}I{5Q[Z]=U;C 5P}}}};z.1c=B(E,5O,aT){A n=z.1D(E),F=P.G,op=(5O=="2W");if(F==3){C op?z.gf(n,aT):n.1c[5O]=aT}if(F==2&&op){C z.ge(n)}A s=z.3F(n);C(F==1)?s:gd(n,5O,s[5O])};z.7A=B(n,gc){A s=gc||1E(n),px=z.4g,l=px(n,s.m9),t=px(n,s.m8);C{l:l,t:t,w:l+px(n,s.m7),h:t+px(n,s.m6)}};z.5N=B(n,gb){A ne="7C",px=z.4g,s=gb||1E(n),bl=(s.m5!=ne?px(n,s.m4):0),bt=(s.m3!=ne?px(n,s.m2):0);C{l:bl,t:bt,w:bl+(s.m1!=ne?px(n,s.m0):0),h:bt+(s.lZ!=ne?px(n,s.lY):0)}};z.aN=B(n,ga){A s=ga||1E(n),p=z.7A(n,s),b=z.5N(n,s);C{l:p.l+b.l,t:p.t+b.t,w:p.w+b.w,h:p.h+b.h}};z.aM=B(n,g9){A s=g9||1E(n),px=z.4g,l=px(n,s.lX),t=px(n,s.lW),r=px(n,s.lV),b=px(n,s.lU);if(z.3o&&(s.ax!="fU")){r=l}C{l:l,t:t,w:l+r,h:t+b}};z.au=B(E,g8){A s=g8||1E(E),me=z.aM(E,s);A l=E.fT-me.l,t=E.fS-me.t;if(z.7B){A aS=2k(s.2g),aR=2k(s.5J);if(!2L(aS)&&!2L(aR)){l=aS,t=aR}I{A p=E.1L;if(p&&p.1c){A aQ=1E(p);if(aQ.lT!="lS"){A be=z.5N(p,aQ);l+=be.l,t+=be.t}}}}I{if(z.2M){A p=E.1L;if(p){A be=z.5N(p);l-=be.l,t-=be.t}}}C{l:l,t:t,w:E.6v+me.w,h:E.8D+me.h}};z.aK=B(E,g7){A s=g7||1E(E),pe=z.7A(E,s),be=z.5N(E,s),w=E.aF,h;if(!w){w=E.6v,h=E.8D}I{h=E.lR,be.w=be.h=0}if(z.2M){pe.l+=be.l;pe.t+=be.t}C{l:pe.l,t:pe.t,w:w-pe.w-be.w,h:h-pe.h-be.h}};z.lQ=B(E,g6){A s=g6||1E(E),pe=z.7A(E,s),cb=z.aK(E,s);C{l:cb.l-pe.l,t:cb.t-pe.t,w:cb.w+pe.w,h:cb.h+pe.h}};z.aL=B(E,l,t,w,h,u){u=u||"px";4G(E.1c){if(!2L(l)){2g=l+u}if(!2L(t)){5J=t+u}if(w>=0){3n=w+u}if(h>=0){58=h+u}}};z.aO=B(E){A n=E.5w;C(z.aP=="g5-3G")||(n=="lP")||(n=="lO")};z.fX=B(E,7z,7y,g4){A bb=z.aO(E);if(bb){A pb=z.aN(E,g4);if(7z>=0){7z+=pb.w}if(7y>=0){7y+=pb.h}}z.aL(E,g3,g3,7z,7y)};z.fY=B(E,g1,g0,5M,5L,g2){A s=g2||z.3F(E);A bb=z.aO(E),pb=bb?fZ:z.aN(E,s),mb=z.aM(E,s);if(5M>=0){5M=2Y.5q(5M-pb.w-mb.w,0)}if(5L>=0){5L=2Y.5q(5L-pb.h-mb.h,0)}z.aL(E,g1,g0,5M,5L)};A fZ={l:0,t:0,w:0,h:0};z.lN=B(E,3G){A n=z.1D(E),s=1E(n),b=3G;C!b?z.au(n,s):z.fY(n,b.l,b.t,b.w,b.h,s)};z.lM=B(E,3G){A n=z.1D(E),s=1E(n),b=3G;C!b?z.aK(n,s):z.fX(n,b.w,b.h,s)};A 5H=B(E,1a){if(!(E=(E||0).1L)){C 0}A 1U,aJ=0,2h=z.3E();1s(E&&E.1c){if(1E(E).ax=="lL"){C 0}1U=E[1a];if(1U){aJ+=1U-0;if(E==2h){3f}}E=E.1L}C aJ};z.fQ=B(){A 2h=z.3E();A 3g=z.1W;A de=z.1e.5K;C{y:(3g.lK||de.5G||2h.5G||0),x:(3g.lJ||z.aH(de.5I)||2h.5I||0)}};z.aG=B(){C V z.aI=="1k"?(z.aI=z.3F(z.3E()).lI=="lH"):z.aI};z.aB=B(){A de=z.1e.5K;if(z.1l>=7){C{x:de.aC().2g,y:de.aC().5J}}I{C{x:z.aG()||26.am==26?de.fW:de.6v-de.aF-de.fW,y:de.lG}}};z.aH=B(aE){if(z.1l&&!z.aG()){A de=z.1e.5K;C aE+de.aF-de.lF}C aE};z.fP=B(E,aw){A ay=E.aD;A J={x:0,y:0};A 7w=U;A db=z.3E();if(z.1l){A aA=E.aC();A az=z.aB();J.x=aA.2g-az.x;J.y=aA.5J-az.y}I{if(ay["fV"]){A bo=ay.fV(E);J.x=bo.x-5H(E,"5I");J.y=bo.y-5H(E,"5G")}I{if(E["fR"]){7w=K;A 7x;if(z.3o&&(1E(E).ax=="fU")&&(E.1L==db)){7x=db}I{7x=db.1L}if(E.1L!=db){A nd=E;if(z.2M){nd=db}J.x-=5H(nd,"5I");J.y-=5H(nd,"5G")}A 4f=E;do{A n=4f["fT"];if(!z.2M||n>0){J.x+=2L(n)?0:n}A m=4f["fS"];J.y+=2L(m)?0:m;4f=4f.fR}1s((4f!=7x)&&4f)}I{if(E["x"]&&E["y"]){J.x+=2L(E.x)?0:E.x;J.y+=2L(E.y)?0:E.y}}}}if(7w||aw){A av=z.fQ();A m=7w?(!aw?-1:0):1;J.y+=m*av.y;J.x+=m*av.x}C J};z.af=B(E,fO){A n=z.1D(E),s=1E(n),mb=z.au(n,s);A at=z.fP(n,fO);mb.x=at.x;mb.y=at.y;C mb}})();z.fL=B(E,fN){C((" "+E.3A+" ").T(" "+fN+" ")>=0)};z.7s=B(E,ar){A 7v=E.3A;if((" "+7v+" ").T(" "+ar+" ")<0){E.3A=7v+(7v?" ":"")+ar}};z.7r=B(E,fM){A t=z.7g((" "+E.3A+" ").2f(" "+fM+" "," "));if(E.3A!=t){E.3A=t}};z.lE=B(E,aq,7u){if(V 7u=="1k"){7u=!z.fL(E,aq)}z[7u?"7s":"7r"](E,aq)}}if(!z.1h["z.X.1H"]){z.1h["z.X.1H"]=K;z.1Q("z.X.1H");(B(){A d=z;z.1H=B(){A F=P;if((F.G==1)&&(V F[0]=="4J")){D.G=eK(F[0])}I{if(F.G){d.1n(F,B(i){D.Y(i)},D)}}};z.1H.1C=S 4e;if(d.1l){A fK=B(al){C("A a2 = am."+al+"; "+"A ap = 4e.1C; "+"A ao = a2.1C; "+"R(A x in ao){ ap[x] = ao[x]; } "+"am."+al+" = 4e; ")};A fI=fK("z.1H");A aj=26.lD();aj.1q.fJ("<ak>"+fI+"</ak>");aj.lC(1,1,1,1)}z.4M(z.1H,{T:B(fH,fG){C d.T(D,fH,fG)},31:B(lB,lA){A aa=d.4d(P);aa.ae(D);C d.31.14(d,aa)},ah:B(fF,fE){C d.ah(D,fF,fE)},ag:B(fD,fC){C d.ag(D,fD,fC)},1n:B(fB,fA){d.1n(D,fB,fA);C D},23:B(7t,M){C d.23(D,7t,M,d.1H)},af:B(){C d.23(D,d.af)},1c:B(lz,ly){A aa=d.4d(P);aa.ae(D[0]);A s=d.1c.14(d,aa);C(P.G>1)?D:s},lx:B(lw,lv){A aa=d.4d(P);aa.ae(L);A s=D.23(B(i){aa[0]=i;C d.1c.14(d,aa)});C(P.G>1)?D:s},7s:B(fz){C D.1n(B(i){z.7s(i,fz)})},7r:B(fy){C D.1n(B(i){z.7r(i,fy)})},5E:B(fw,7q){A 1m=d.1r(fw)[0];7q=7q||"72";R(A x=0;x<D.G;x++){d.5E(D[x],1m,7q)}C D},2c:B(fv,fu,ft){D.1n(B(1m){d.2c(1m,fv,fu,ft)});C D},lu:B(ad){A ac=(ad)?d.9t(D,ad):D;ac.1n(B(1m){if(1m["1L"]){1m.1L.fs(1m)}});C ac},lt:B(fr,fq){A 1m=D[0];C d.1r(fr).1n(B(ai){d.5E(ai,1m,(fq||"72"))})},1r:B(7p){7p=7p||"";A J=S d.1H();D.1n(B(1m){d.1r(7p,1m).1n(B(ab){if(V ab!="1k"){J.Y(ab)}})});C J},3T:B(fo){A 5F=D;A 1V=P;A r=S d.1H();A rp=B(t){if(V t!="1k"){r.Y(t)}};if(d.1R(fo)){5F=d.9t(D,1V[0]);if(1V.G==1){C 5F}d.1n(d.3T(5F,1V[1],1V[2]),rp);C r}d.1n(d.3T(5F,1V[0],1V[1]),rp);C r},lr:B(7o,7n){A 1S=d.1e.a9("lq");if(d.1R(7o)){1S.9L=7o}I{1S.4c(7o)}A ct=((7n=="9M")||(7n=="a8"))?"fm":"5A";D.1n(B(1m){A 24=1S.a7(K);1s(24[ct]){d.5E(24[ct],1m,7n)}});C D},7m:B(fl,F){A a5=[];F=F||{};D.1n(B(1m){A a6={E:1m};d.1x(a6,F);a5.Y(d[fl](a6))});C d.fx.lp(a5)},8I:B(F){C D.7m("8I",F)},8H:B(F){C D.7m("8H",F)},6y:B(F){C D.7m("6y",F)}});z.1n(["fk","lo","fj","fi","ln","lm","ll","fi","lk","lj","4b"],B(H){A a4="on"+H;z.1H.1C[a4]=B(a,b){C D.2c(a4,a,b)}})})()}if(!z.1h["z.X.1r"]){z.1h["z.X.1r"]=K;z.1Q("z.X.1r");(B(){A d=z;A 2I=B(q){C[q.T("#"),q.T("."),q.T("["),q.T(":")]};A a0=B(a3,fh){A ql=a3.G;A i=2I(a3);A 1d=ql;R(A x=fh;x<i.G;x++){if(i[x]>=0){if(i[x]<1d){1d=i[x]}}}C(1d<0)?ql:1d};A 6X=B(7l){A i=2I(7l);if(i[0]!=-1){C 7l.21(i[0]+1,a0(7l,1))}I{C""}};A 5r=B(7k){A 5D;A i=2I(7k);if((i[0]==0)||(i[1]==0)){5D=0}I{5D=a0(7k,0)}C((5D>0)?7k.3b(0,5D).1M():"*")};A fg=B(Q){A J=-1;R(A x=0;x<Q.G;x++){A 1S=Q[x];if(1S>=0){if((1S>J)||(J==-1)){J=1S}}}C J};A 9H=B(7i){A i=2I(7i);if(-1==i[1]){C""}A di=i[1]+1;A 7j=fg(i.2w(2));if(di<7j){C 7i.21(di,7j)}I{if(-1==7j){C 7i.3b(di)}I{C""}}};A f3=[{1i:"|=",1f:B(15,fe){C"[5z(3U(\' \',@"+15+",\' \'), \' "+fe+"-\')]"}},{1i:"~=",1f:B(15,fd){C"[5z(3U(\' \',@"+15+",\' \'), \' "+fd+" \')]"}},{1i:"^=",1f:B(15,fb){C"[li-4G(@"+15+", \'"+fb+"\')]"}},{1i:"*=",1f:B(15,fa){C"[5z(@"+15+", \'"+fa+"\')]"}},{1i:"$=",1f:B(15,9Z){C"[21(@"+15+", 3c-G(@"+15+")-"+(9Z.G-1)+")=\'"+9Z+"\']"}},{1i:"!=",1f:B(15,f9){C"[3O(@"+15+"=\'"+f9+"\')]"}},{1i:"=",1f:B(15,f8){C"[@"+15+"=\'"+f8+"\']"}}];A 9C=B(9Y,3Z,f7,f6){A 49;A i=2I(3Z);if(i[2]>=0){A 4L=3Z.T("]",i[2]);A 29=3Z.21(i[2]+1,4L);1s(29&&29.G){if(29.2s(0)=="@"){29=29.2w(1)}49=L;R(A x=0;x<9Y.G;x++){A 1S=9Y[x];A 7h=29.T(1S.1i);if(7h>=0){A 15=29.21(0,7h);A 4a=29.21(7h+1S.1i.G);if((4a.2s(0)=="\\"")||(4a.2s(0)=="\'")){4a=4a.21(1,4a.G-1)}49=1S.1f(d.7g(15),d.7g(4a));3f}}if((!49)&&(29.G)){49=f7(29)}if(49){f6(49)}29=L;A 7f=3Z.T("[",4L);if(0<=7f){4L=3Z.T("]",7f);if(0<=4L){29=3Z.21(7f+1,4L)}}}}};A f0=B(f5){A 4K=".";A 7e=f5.1A(" ");1s(7e.G){A 2K=7e.3a();A 7d;if(2K==">"){7d="/";2K=7e.3a()}I{7d="//"}A f4=5r(2K);4K+=7d+f4;A id=6X(2K);if(id.G){4K+="[@id=\'"+id+"\'][1]"}A cn=9H(2K);if(cn.G){A 9X=" ";if(cn.2s(cn.G-1)=="*"){9X="";cn=cn.3b(0,cn.G-1)}4K+="[5z(3U(\' \',@9P,\' \'), \' "+cn+9X+"\')]"}9C(f3,2K,B(f2){C"[@"+f2+"]"},B(f1){4K+=f1})}C 4K};A 7a={};A eC=B(28){if(7a[28]){C 7a[28]}A 1e=d.1e;A 9W=f0(28);A 4H=B(9V){A J=[];A 7b;1u{7b=1e.9x(9W,9V,L,lh.lg,L)}1y(e){1z.1K("lf in le:",9W,"lc:",9V);1z.1K(e)}A 7c=7b.eZ();1s(7c){J.Y(7c);7c=7b.eZ()}C J};C 7a[28]=4H};A 5x={};A 9B={};A 3y=B(79,78){if(!79){C 78}if(!78){C 79}C B(){C 79.14(26,P)&&78.14(26,P)}};A 75=B(9U,3Y,5B,2J){A 2v=2J+1;A 76=(3Y.G==2v);A 2K=3Y[2J];if(2K==">"){A 77=9U.3W;if(!77.G){C}2v++;76=(3Y.G==2v);A 4H=6O(3Y[2J+1]);R(A x=0,11;x<77.G,11=77[x];x++){if(4H(11)){if(76){5B.Y(11)}I{75(11,3Y,5B,2v)}}}}A 5C=6U(2K)(9U);if(76){1s(5C.G){5B.Y(5C.3a())}}I{1s(5C.G){75(5C.3a(),3Y,5B,2v)}}};A eE=B(9T,eY){A J=[];A x=9T.G-1,11;1s(11=9T[x--]){75(11,eY,J,0)}C J};A 6O=B(3D){if(5x[3D]){C 5x[3D]}A ff=L;A 9S=5r(3D);if(9S!="*"){ff=3y(ff,B(N){C((N.2t==1)&&(9S==N.5w.1M()))})}A 9R=6X(3D);if(9R.G){ff=3y(ff,B(N){C((N.2t==1)&&(N.id==9R))})}if(2Y.5q.14(D,2I(3D).2w(1))>=0){ff=3y(ff,9z(3D))}C 5x[3D]=ff};A 5y=B(E){A pn=E.1L;A 9Q=pn.3W;A 2v=-1;A 3C=pn.5A;if(!3C){C 2v}A ci=E["eW"];A cl=pn["eX"];if(((V cl=="4J")&&(cl!=9Q.G))||(V ci!="4J")){pn["eX"]=9Q.G;A 2J=1;do{if(3C===E){2v=2J}if(3C.2t==1){3C["eW"]=2J;2J++}3C=3C.71}1s(3C)}I{2v=ci}C 2v};A lb=0;A 3X=B(N,15){A 74="";if(15=="9P"){C N.3A||74}if(15=="R"){C N.la||74}C N.5t(15,2)||74};A eH=[{1i:"|=",1f:B(15,9O){A eV=" "+9O+"-";C B(N){A ea=" "+(N.5t(15,2)||"");C((ea==9O)||(ea.T(eV)==0))}}},{1i:"^=",1f:B(15,eU){C B(N){C(3X(N,15).T(eU)==0)}}},{1i:"*=",1f:B(15,eT){C B(N){C(3X(N,15).T(eT)>=0)}}},{1i:"~=",1f:B(15,eS){A 9N=" "+eS+" ";C B(N){A ea=" "+3X(N,15)+" ";C(ea.T(9N)>=0)}}},{1i:"$=",1f:B(15,73){A 9N=" "+73;C B(N){A ea=" "+3X(N,15);C(ea.31(73)==(ea.G-73.G))}}},{1i:"!=",1f:B(15,eR){C B(N){C(3X(N,15)!=eR)}}},{1i:"=",1f:B(15,eQ){C B(N){C(3X(N,15)==eQ)}}}];A 9E=[{1i:"9M-9K",1f:B(1p,l9){C B(N){if(N.2t!=1){C U}A fc=N.eP;1s(fc&&(fc.2t!=1)){fc=fc.eP}C(!fc)}}},{1i:"72-9K",1f:B(1p,l8){C B(N){if(N.2t!=1){C U}A nc=N.71;1s(nc&&(nc.2t!=1)){nc=nc.71}C(!nc)}}},{1i:"l7",1f:B(1p,l6){C B(N){A cn=N.3W;A eO=N.3W.G;R(A x=eO-1;x>=0;x--){A nt=cn[x].2t;if((nt==1)||(nt==3)){C U}}C K}}},{1i:"5z",1f:B(1p,eN){C B(N){C(N.9L.T(eN)>=0)}}},{1i:"3O",1f:B(1p,eM){A eL=6O(eM);C B(N){C(!eL(N))}}},{1i:"l5-9K",1f:B(1p,2u){A pi=eK;if(2u=="l4"){C B(N){C(((5y(N))%2)==1)}}I{if((2u=="2n")||(2u=="l3")){C B(N){C((5y(N)%2)==0)}}I{if(2u.T("l2+")==0){A 70=pi(2u.3b(3));C B(N){C(N.1L.3W[70-1]===N)}}I{if((2u.T("n+")>0)&&(2u.G>3)){A 9J=2u.1A("n+",2);A eJ=pi(9J[0]);A 2J=pi(9J[1]);C B(N){C((5y(N)%eJ)==2J)}}I{if(2u.T("n")==-1){A 70=pi(2u);C B(N){C(5y(N)==70)}}}}}}}}];A 9z=B(3e){A 9I=(9B[3e]||5x[3e]);if(9I){C 9I}A ff=L;A i=2I(3e);if(i[0]>=0){A 24=5r(3e);if(24!="*"){ff=3y(ff,B(N){C(N.5w.1M()==24)})}}A 5u;A 3B=9H(3e);if(3B.G){A 9F=3B.2s(3B.G-1)=="*";if(9F){3B=3B.3b(0,3B.G-1)}A re=S 9G("(?:^|\\\\s)"+3B+(9F?".*":"")+"(?:\\\\s|$)");ff=3y(ff,B(N){C re.6Z(N.3A)})}if(i[3]>=0){A 3z=3e.3b(i[3]+1);A 9D="";A 5v=3z.T("(");A 6Y=3z.31(")");if((0<=5v)&&(0<=6Y)&&(6Y>5v)){9D=3z.21(5v+1,6Y);3z=3z.3b(0,5v)}5u=L;R(A x=0;x<9E.G;x++){A 1S=9E[x];if(1S.1i==3z){5u=1S.1f(3z,9D);3f}}if(5u){ff=3y(ff,5u)}}A eG=(d.1l)?B(5s){A eI=5s.1M();C B(N){C N[5s]||N[eI]}}:B(5s){C B(N){C(N&&N.5t&&N.l1(5s))}};9C(eH,3e,eG,B(eF){ff=3y(ff,eF)});if(!ff){ff=B(){C K}}C 9B[3e]=ff};A 6W={};A 6U=B(3d,1B){A 9A=6W[3d];if(9A){C 9A}A i=2I(3d);A id=6X(3d);if(i[0]==0){C 6W[3d]=B(1B){C[d.1D(id)]}}A 9y=9z(3d);A 5p;if(i[0]>=0){5p=B(1B){A 11=d.1D(id);if(9y(11)){C[11]}}}I{A 3V;A 24=5r(3d);if(2Y.5q.14(D,2I(3d))==-1){5p=B(1B){A J=[];A 11,x=0,3V=1B.4I(24);1s(11=3V[x++]){J.Y(11)}C J}}I{5p=B(1B){A J=[];A 11,x=0,3V=1B.4I(24);1s(11=3V[x++]){if(9y(11)){J.Y(11)}}C J}}}C 6W[3d]=5p};A l0={};A 5o={">":B(1B){A J=[];A 11,x=0,3V=1B.3W;1s(11=3V[x++]){if(11.2t==1){J.Y(11)}}C J}};A 9w=B(6V){if(0>6V.T(" ")){C 6U(6V)}A eD=B(1B){A 6S=6V.1A(" ");A 6T;if(6S[0]==">"){6T=[1B]}I{6T=6U(6S.3a())(1B)}C eE(6T,6S)};C eD};A 9v=((1q["9x"]&&!d.3o)?B(3x){A 6R=3x.1A(" ");if((1q["9x"])&&(3x.T(":")==-1)&&((K))){if(((6R.G>2)&&(3x.T(">")==-1))||(6R.G>3)||(3x.T("[")>=0)||((1==6R.G)&&(0<=3x.T(".")))){C eC(3x)}}C 9w(3x)}:9w);A ey=B(3w){if(5o[3w]){C 5o[3w]}if(0>3w.T(",")){C 5o[3w]=9v(3w)}I{A eB=3w.1A(/\\s*,\\s*/);A 4H=B(1B){A eA=0;A J=[];A 6Q;1s(6Q=eB[eA++]){J=J.3U(9v(6Q,6Q.T(" "))(1B))}C J};C 5o[3w]=4H}};A 5n=0;A ez=B(Q){A J=S d.1H();if(!Q){C J}if(Q[0]){J.Y(Q[0])}if(Q.G<2){C J}5n++;Q[0]["9u"]=5n;R(A x=1,11;11=Q[x];x++){if(Q[x]["9u"]!=5n){J.Y(11)}11["9u"]=5n}C J};d.1r=B(6P,1B){if(V 6P!="3c"){C S d.1H(6P)}if(V 1B=="3c"){1B=d.1D(1B)}C ez(ey(6P)(1B||d.1e))};d.9t=B(ex,9s){A 9r=S d.1H();A ff=(9s)?6O(9s):B(){C K};R(A x=0,11;11=ex[x];x++){if(ff(11)){9r.Y(11)}}C 9r}})()}if(!z.1h["z.X.1b"]){z.1h["z.X.1b"]=K;z.1Q("z.X.1b");z.6K=B(ew){A J={};A iq="kZ[Z!=9q][Z!=kY][Z!=et][Z!=kX][Z!=kW], kV, kU";z.1r(iq,ew).3T(B(E){C(!E.kT)}).1n(B(1m){A 3v=1m.1p;A Z=(1m.Z||"").1M();if((Z=="kS")||(Z=="kR")){if(1m.kQ){J[3v]=1m.1Z}}I{if(1m.kP){A ev=J[3v]=[];z.1r("kO[kN]",1m).1n(B(eu){ev.Y(eu.1Z)})}I{J[3v]=1m.1Z;if(Z=="et"){J[3v+".x"]=J[3v+".y"]=J[3v].x=J[3v].y=0}}}});C J};z.9h=B(23){A ec=kM;A J="";A es={};R(A x in 23){if(23[x]!=es[x]){if(z.2l(23[x])){R(A y=0;y<23[x].G;y++){J+=ec(x)+"="+ec(23[x][y])+"&"}}I{J+=ec(x)+"="+ec(23[x])+"&"}}}if((J.G)&&(J.2s(J.G-1)=="&")){J=J.3b(0,J.G-1)}C J};z.kL=B(er){C z.9h(z.6K(er))};z.kK=B(ep){C z.eq(z.6K(ep))};z.kJ=B(2H){A J={};A qp=2H.1A("&");A dc=kI;z.1n(qp,B(1m){if(1m.G){A 9p=1m.1A("=");A 1p=dc(9p.3a());A 1U=dc(9p.22("="));if(z.1R(J[1p])){J[1p]=[J[1p]]}if(z.2l(J[1p])){J[1p].Y(1U)}I{J[1p]=1U}}});C J};z.e1=U;z.e6={"9g":B(1b){C 1b.2G},"2e":B(1b){if(!1o.eo){1z.1K("kH kG kF a kE of 9g/2e-6M-9m"+" 4F kD kC kB kA 4G en kz"+" (ky 1o.eo=K 4F kx kw D kv)")}C z.5m(1b.2G)},"2e-6M-ku":B(1b){A 6N=1b.2G;A 9o=6N.T("/*");A 9n=6N.31("*/");if((9o==-1)||(9n==-1)){C z.5m(1b.2G)}C z.5m(6N.21(9o+2,9n))},"2e-6M-9m":B(1b){A 6L=1b.2G;A 9l=6L.T("/*");A 9k=6L.31("*/");if((9l==-1)||(9k==-1)){1z.1K("kt en ks\'t 6M 9m!");C""}C z.5m(6L.21(9l+2,9k))},"kr":B(1b){C z.3u(1b.2G)},"kq":B(1b){if(z.1l&&!1b.el){z.1n(["ko","em","kn","km"],B(i){1u{A 1e=S 9j(kl[i]+".kk");1e.kj=U;1e.ki(1b.2G);C 1e}1y(e){}})}I{C 1b.el}}};(B(){z.e5=B(F,ej,ei,eh){A 2F={};2F.F=F;A 6J=L;if(F.3R){A 3R=z.1D(F.3R);A 9i=3R.kh("kg");2F.2E=F.2E||(9i?9i.1Z:L);6J=z.6K(3R)}I{2F.2E=F.2E}A 5l=[{}];if(6J){5l.Y(6J)}if(F.5g){5l.Y(F.5g)}if(F.ek){5l.Y({"z.ek":S 5d().8O()})}2F.1r=z.9h(z.1x.14(L,5l));2F.9d=F.9d||"9g";A d=S z.30(ej);d.5k(ei,B(eg){C eh(eg,d)});A ld=F.4E;if(ld&&z.1Y(ld)){d.ef(B(ee){C ld.2d(F,ee,2F)})}A 1G=F.9f;if(1G&&z.1Y(1G)){d.ed(B(e9){C 1G.2d(F,e9,2F)})}A 6I=F.kf;if(6I&&z.1Y(6I)){d.9e(B(e8){C 6I.2d(F,e8,2F)})}d.1F=2F;C d};A e4=B(O){O.e0=K;A 1b=O.1F.1b;if(V 1b.e7=="B"){1b.e7()}};A e3=B(O){C z.e6[O.1F.9d](O.1F.1b)};A e2=B(9c,O){1z.1K(9c);C 9c};A 3Q=B(F){A O=z.e5(F,e4,e3,e2);O.1F.1b=z.9b(O.1F.F);C O};A 5j=L;A 3t=[];A 94=B(){A dZ=(S 5d()).dU();if(!z.e1){z.1n(3t,B(4D,6H){if(!4D){C}A O=4D.O;1u{if(!O||O.e0||!4D.dT(O)){3t.3S(6H,1);C}if(4D.dR(O)){3t.3S(6H,1);4D.dP(O)}I{if(O.9a){if(O.9a+(O.1F.F.6G||0)<dZ){3t.3S(6H,1);A 1G=S 1O("6G ke");1G.dY="6G";O.5i(1G);O.4C()}}}}1y(e){1z.1K(e);O.5i(S 1O("kc!"))}})}if(!3t.G){dX(5j);5j=L;C}};z.dV=B(){1u{z.1n(3t,B(i){i.O.4C()})}1y(e){}};if(z.1l){z.dW(z.dV)}z.dH=B(O,dS,dQ,dO){if(O.1F.F.6G){O.9a=(S 5d()).dU()}3t.Y({O:O,dT:dS,dR:dQ,dP:dO});if(!5j){5j=dN(94,50)}94()};A dJ="8Z/x-kb-3R-ka";A dG=B(O){C O.1F.1b.6F};A dF=B(O){C 4==O.1F.1b.6F};A dE=B(O){if(z.8Y(O.1F.1b)){O.dM(O)}I{O.5i(S 1O("k9 k8 k7 5h:"+O.1F.1b.3N))}};A 3P=B(Z,O){A 3s=O.1F;A F=3s.F;3s.1b.dL(Z,3s.2E,(F.k6!==K),(F.8X?F.8X:1k),(F.8W?F.8W:1k));if(F.6E){R(A 5f in F.6E){if(5f.1M()==="5g-Z"&&!F.8V){F.8V=F.6E[5f]}I{3s.1b.dK(5f,F.6E[5f])}}}3s.1b.dK("k5-k4",(F.8V||dJ));1u{3s.1b.dI(3s.1r)}1y(e){O.4C()}z.dH(O,dG,dF,dE);C O};z.8T=B(4B){if(4B.1r.G){4B.2E+=(4B.2E.T("?")==-1?"?":"&")+4B.1r;4B.1r=L}};z.k3=B(F){A O=3Q(F);z.8T(O.1F);C 3P("dD",O)};z.k2=B(F){C 3P("dC",3Q(F))};z.k1=B(F){A O=3Q(F);O.1F.1r=F.k0;C 3P("dC",O)};z.jZ=B(F){C 3P("dA",3Q(F))};z.jY=B(F){A O=3Q(F);A dB=O.1F;if(F["8U"]){dB.1r=F.8U;F.8U=L}C 3P("dA",O)};z.jX=B(F){A O=3Q(F);z.8T(O.1F);C 3P("8S",O)};z.dz=B(jW){2m S 1O("z.dz 3O jV jU")}})()}if(!z.1h["z.X.fx"]){z.1h["z.X.fx"]=K;z.1Q("z.X.fx");z.dx=B(dy,1d){D.1w=dy;D.1d=1d;D.4x=B(n){C((D.1d-D.1w)*n)+D.1w}};z.2r("z.d6",L,{1P:B(F){z.1x(D,F);if(z.2l(D.2C)){D.2C=S z.dx(D.2C[0],D.2C[1])}},2C:L,8Q:jT,5a:L,4z:0,dj:10,du:L,6x:L,dt:L,8B:L,dh:L,ds:L,dr:L,dm:L,2D:U,2Z:U,4A:L,8N:L,3r:L,2o:0,4y:0,3q:B(H,F){if(D[H]){D[H].14(D,F||[])}C D},5b:B(dw,8R){if(8R){5e(D.3r);D.2D=D.2Z=U;D.2o=0}I{if(D.2D&&!D.2Z){C D}}D.3q("6x");A d=dw||D.du;if(d>0){5c(z.2p(D,B(){D.5b(L,8R)}),d);C D}D.4A=S 5d().8O();if(D.2Z){D.4A-=D.8Q*D.2o}D.8N=D.4A+D.8Q;D.2D=K;D.2Z=U;A 8P=D.2C.4x(D.2o);if(!D.2o){if(!D.4y){D.4y=D.4z}D.3q("dt",[8P])}D.3q("ds",[8P]);D.8M();C D},jS:B(){5e(D.3r);if(!D.2D){C D}D.2Z=K;D.3q("dr",[D.2C.4x(D.2o)]);C D},jR:B(dq,dp){5e(D.3r);D.2D=D.2Z=K;D.2o=dq*6D;if(dp){D.5b()}C D},jQ:B(dn){if(!D.3r){C}5e(D.3r);if(dn){D.2o=1}D.3q("dm",[D.2C.4x(D.2o)]);D.2D=D.2Z=U;C D},3N:B(){if(D.2D){C D.2Z?"3M":"jP"}C"jO"},8M:B(){5e(D.3r);if(D.2D){A dl=S 5d().8O();A 2q=(dl-D.4A)/(D.8N-D.4A);if(2q>=1){2q=1}D.2o=2q;if(D.5a){2q=D.5a(2q)}D.3q("8B",[D.2C.4x(2q)]);if(2q<1){D.3r=5c(z.2p(D,"8M"),D.dj)}I{D.2D=U;if(D.4z>0){D.4z--;D.5b(L,K)}I{if(D.4z==-1){D.5b(L,K)}I{if(D.4y){D.4z=D.4y;D.4y=0}}}D.2o=0;D.3q("dh")}}C D}});(B(){A df=B(E){if(z.1l){A ns=E.1c;if(!ns.8L.G&&z.1c(E,"8L")=="dg"){ns.8L="1"}if(!ns.3n.G&&z.1c(E,"3n")=="8K"){ns.3n="8K"}}};z.6C=B(F){if(V F.1d=="1k"){2m S 1O("z.6C jN an 1d 1Z")}F.E=z.1D(F.E);A 3p=z.1x({6w:{}},F);A 8J=(3p.6w.2W={});8J.1w=(V 3p.1w=="1k")?B(){C 2V(z.1c(3p.E,"2W"))}:3p.1w;8J.1d=3p.1d;A 2U=z.6y(3p);z.2c(2U,"6x",L,B(){df(3p.E)});C 2U};z.8I=B(F){C z.6C(z.1x({1d:1},F))};z.8H=B(F){C z.6C(z.1x({1d:0},F))};if(z.6B&&!z.3o){z.8E=B(n){C 2k("0.5")+((2Y.da((n+2k("1.5"))*2Y.d9))/2)}}I{z.8E=B(n){C 0.5+((2Y.da((n+1.5)*2Y.d9))/2)}}A d4=B(6A){D.8G=6A;R(A p in 6A){A 1a=6A[p];if(1a.1w 1N z.1J){1a.d7=S z.1J()}}D.4x=B(r){A J={};R(A p in D.8G){A 1a=D.8G[p];A 6z=L;if(1a.1w 1N z.1J){6z=z.d8(1a.1w,1a.1d,r,1a.d7).8F()}I{if(!z.2l(1a.1w)){6z=((1a.1d-1a.1w)*r)+1a.1w+(p!="2W"?1a.jM||"px":"")}}J[p]=6z}C J}};z.6y=B(F){F.E=z.1D(F.E);if(!F.5a){F.5a=z.8E}A 2U=S z.d6(F);z.2c(2U,"6x",2U,B(){A pm={};R(A p in D.6w){A 1a=pm[p]=z.1x({},D.6w[p]);if(z.1Y(1a.1w)){1a.1w=1a.1w()}if(z.1Y(1a.1d)){1a.1d=1a.1d()}A d5=(p.1M().T("jL")>=0);B 8C(E,p){4w(p){2X"58":C E.8D;2X"3n":C E.6v}A v=z.1c(E,p);C(p=="2W")?2V(v):2k(v)};if(V 1a.1d=="1k"){1a.1d=8C(D.E,p)}I{if(V 1a.1w=="1k"){1a.1w=8C(D.E,p)}}if(d5){1a.1w=S z.1J(1a.1w);1a.1d=S z.1J(1a.1d)}I{1a.1w=(p=="2W")?2V(1a.1w):2k(1a.1w)}}D.2C=S d4(pm)});z.2c(2U,"8B",2U,B(8A){R(A s in 8A){z.1c(D.E,s,8A[s])}});C 2U}})()}',62,1711,'|||||||||||||||||||||||||||||||||||dojo|var|function|return|this|node|args|length|evt|else|ret|true|null|obj|elem|dfd|arguments|arr|for|new|indexOf|false|typeof||_base|push|type||te|||apply|attr|||||prop|xhr|style|end|doc|match|uri|_hasResource|key|del|undefined|isIE|item|forEach|djConfig|name|document|query|while|_66|try|res|start|mixin|catch|console|split|root|prototype|byId|gcs|ioArgs|err|NodeList|_p|Color|debug|parentNode|toLowerCase|instanceof|Error|constructor|provide|isString|ta|255|val|_a|global|_69|isFunction|value||substring|join|map|tn||window||path|_343|_220|_listeners|connect|call|json|replace|left|_b|toString|128|parseFloat|isArray|throw||_percent|hitch|step|declare|charAt|nodeType|_3c3|nidx|slice|faux|fired|_c4|_7e|loc|curve|_active|url|_44c|responseText|str|_312|idx|tqp|isNaN|isOpera|_22d|callee|add|_18b|_f8|_e2|_41|anim|Number|opacity|case|Math|_paused|Deferred|lastIndexOf|||||||||shift|substr|string|_3e7|_3ce|break|_w|charCode|_listener|_d5|_c5|authority|_49|width|isSafari|_49e|fire|_timer|_47b|_465|eval|_in|_40c|_409|_362|_3d9|className|_3d5|_386|_37a|body|getComputedStyle|box|_221|keyCode|remove|_8d|_46|paused|status|not|_478|_461|form|splice|filter|concat|tret|childNodes|_38b|_367|_33d||||||||||_340|_348|keypress|appendChild|_toArray|Array|_2b0|_toPixelValue|ref|_fixEvent|_19f|_14c|_14a|_150|_141|declaredClass|_d4|_99|_Url|_83|scheme|_67|_3d|switch|getValue|_startRepeatCount|repeat|_startTime|_47e|cancel|tif|load|to|with|tf|getElementsByTagName|number|_34c|_342|extend|_1e3|_normalizeEventName|_14b|_14e|results|self|cbfn|_f9|_d8|_b2|src|_88|dav||baseUrl|fragment|_loadedModules|_44|_43|_loaders|mll|height||easing|play|setTimeout|Date|clearTimeout|hdr|content|code|errback|_464|addCallbacks|_450|fromJson|_413|_3fc|_3ee|max|_31e|cond|getAttribute|_3d4|obi|tagName|_360|_381|contains|firstChild|_368|_372|_320|place|_2fa|scrollTop|_299|scrollLeft|top|documentElement|_288|_287|_getBorderExtents|_23f|_23d|_239|_218|_216|_211|eles|target|keys|shiftKey|ctrlKey|event|192|iel|_1db|delete|_1cf||addEventListener|String|_1af|_157|array|_14d|continue|_14f|_137|_11f|_106|_findMethod|has|_delegate|_dc|_d3|loaded|_9a|_loadInit|_inFlightCount|getObject|tv|_4f|_postLoad|_2d|offsetWidth|properties|beforeBegin|animateProperty|_4ad|_4a6|isKhtml|_fade|100|headers|readyState|timeout|_469|_457|_44d|formToObject|_441|comment|_43d|_36f|_419|tp|_40a|_406|_407|_373|_403|_3e6|_31b|cbi|test|_3c7|nextSibling|last|_3a1|_38e|_365|_36b|ecn|_364|_363|_356|_35e|_35f|_34f|_34d|_349|trim|tci|_328|_32b|_31f|_31c|_anim|_300|_2ff|_2f5|_2e7|removeClass|addClass|func|_2c4|cls|_2a9|_2ae|_280|_27f|_getPadExtents|isMoz|none|_233|cssText|_214|_fixCallback|_synthesizeEvent|stopPropagation|preventDefault|_setKeyChar|_1e1|ieh|_1d7|_1be|colorFromArray|sanitize|bits|rgb|_156|_fire|_resback|_13d|partial|_13a|silentlyCancelled|_topics|_127|_f1|_f0|superclass|_ec|_e3|mct|setObject|_bf|_b3|object|require|_92|_khtmlTimer|location|XMLHTTP|locale|dua|_71|_modulePrefixes|_55|_loadModule|_51|_50|_4e|pop|_3f|_callLoaded|_unloaders|_loadNotifying|_loadedUrls|_27|_24|_1d|_5|_4b7|onAnimate|getStyle|offsetHeight|_defaultEasing|toCss|_properties|fadeOut|fadeIn|_49f|auto|zoom|_cycle|_endTime|valueOf|_494|duration|_492|DELETE|_ioAddQueryToUrl|putData|contentType|password|user|_isDocumentOk|application|||||_466||||||startTime|_xhrObj|_45f|handleAs|addBoth|error|text|objectToQuery|_44f|ActiveXObject|_443|_442|filtered|_43f|_43e|_437|file|tnl|_41c|_filterQueryResult|_zipIdx|_408|_402|evaluate|_3ed|_380|fHit|_361|_33b|_3da|_3ab|_3d6|RegExp|_327|_3cf|_3c9|child|innerHTML|first|tval|_391|class|pnc|_37e|_37c|_375|_366|_35c|_35a|_353|_33c|_336|_314|||_315|_oe|_307|_309|cloneNode|after|createElement||_2f8|_2ef|_2ee|unshift|coords|some|every||_2cb|script|_2c9|parent||a2p||_2c3|_2bd||abs|_getMarginBox|_2b3|_2a6|position|_2a7|_2ac|_2ab|_getIeDocumentElementOffset|getBoundingClientRect|ownerDocument|_2a3|clientWidth|_isBodyLtr|_fixIeBiDiScrollLeft|_bodyLtr|_29d|_getContentBox|_setBox|_getMarginExtents|_getPadBorderExtents|_usesBorderBox|boxModel|pcs|st|sl|_240|runtimeStyle|_dcm|BackCompat|compatMode|default|_21b|_d|html|_event_listener|handlers|PAGE_DOWN|PAGE_UP|RIGHT_ARROW|LEFT_ARROW|DOWN_ARROW|UP_ARROW|_preventDefault||_stopPropagation|returnValue||_trySetKeyCode|cancelBubble|currentTarget|106|_1ee|111||_1e8|_1e7|||se|srcElement|onkeydown||_1d0|_disconnect|lid|_1c0|_connect|_set|_195|_185|_183|_17d|_everyOrSome|_16b|_172|_15b|Function|_154|_escapeString|_140|chain|_check|canceller|_12d|_124|_11a|_10d|_107|inherited|_fa|_f2|_findMixin|_constructor|preamble|_de|clone|tmp|_c7|TMP|_be|_ba|_mixin|isBrowser|lang|firebug||param|modulePaths|_a7|_fireCallback|_a0|setContext||_9c|unloaded||||_96|_93|navigator|_90|_89||protocol|_84|_86|_XMLHTTP_PROGIDS|gears|google|setAttribute|_80|_77|cfg|_6f|_getModuleSymbols|_5a|_58|_53|_4d|_4c|_45|_40|_moduleHasPrefix|_loadUri|_28|_26|_21|_22|tests|doh|_20|_1f|_1c|version|_1b|_19|_getProp|_11|_4|_4a5|_4b3|_Animation|tempColor|blendColors|PI|sin|||||_49a|normal|onEnd||rate||curr|onStop|_497||_496|pct|onPause|onPlay|onBegin|delay||_491|_Line|_48b|wrapForm|PUT|_487|POST|GET|_476|_474|_472|_ioWatch|send|_471|setRequestHeader|open|callback|setInterval|_470|resHandle|_46f|ioCheck|_46e|validCheck|getTime|_ioCancelAll|addOnUnload|clearInterval|dojoType|now|canceled|_blockAsync|_45e|_45c|_459|_ioSetArgs|_contentHandlers|abort|_458|_456||||addErrback|_454|addCallback|_452|_44b|_44a|_449|preventCache|responseXML|Microsoft|JSON|usePlainJson|_431|toJson|_430|_42d|image|opt|ria|_421|_41b|_40b|_zip|_410|_40d|_357|sqf|_374|_3e5|_3df|_38f|clc|pred|parseInt|ntf|_3bf|_3bc|cnl|previousSibling|_3a9|_3a6|_39c|_399|_396|_392|__cachedIndex|__cachedLength|_376|iterateNext|_34a|_355|_354|_32c|_350|_34b|_33f|_33e|_33a|_338|_334|_332||_330|_32e||_322|_316|mousemove|mouseout|mouseover|_305|lastChild||_2f9||_2f2|_2f1|removeChild|_2ec|_2eb|_2ea|_2e6||_2e4|_2e2|_2d6|_2d5|_2d4|_2d3|_2d2|_2d1|_2cd|_2cc|scs|write|_2c8|hasClass|_2c0|_2bb|_2b5|_abs|_docScroll|offsetParent|offsetTop|offsetLeft|absolute|getBoxObjectFor|clientLeft|_setContentSize|_setMarginBox|_28d|_286|_285|_289|NaN|_281|border|_272|_26b|_260|_258|_253|_24c|_246|_23a|_getOpacity|_setOpacity|_238|td|tr|nodeName|FILTER|_22f|_22e|currentStyle|_22c|_22b|display|QuirksMode|unselectable|_217|isMozilla|getElementById|attributes|all|_ie_listener|_getIeDispatcher|_1fd|NUM_LOCK|SCROLL_LOCK|INSERT|END|HOME|PAUSE|F12|F11|F10|F9|F8|F7|F6|F5|F4|F3|F2|F1|63232|SHIFT_TAB|TAB|keyIdentifier|_1f3|stopEvent|_punctMap|222|219|186|onkeypress|_stealthKeyDown|_fixKeys|relatedTarget|_1e0|_1df|_stealthKeydown|_1d6|_1d5|_1d1|_1ca|_1c9|_1cb|_1c2|_1c1|_1c3|_1c4|_1bc|_1b3|_1b2|colorFromHex|colorFromRgb|named|colorFromString|mask|rgba|_19c|_197|_192|setColor|_180|_178|_177|_175|_174|_16d|_166|_164|_163|_162|_15c|_15d|_15e|index|__json__|toJsonIndentStr|_nextId|_12f|_12b|publish|_128|_126|_125|_122|_121|_123|_11c|_11b|_10c|_10b|_108|getDispatcher|argument|nom|_construct|_core|_makeCtor|_df|_db|deprecated|isObject|_cc||scope||_hitchArgs|_c2||pre|_c1|native|isDebug||registerModulePath|_a8||finally|||_a6|_a5|_a4|_a3|_a2|_a1|_9f|_9e|_9d|_9b|_98|_97|onbeforeunload|ipt|scr|complete|_95|userAgent|_modulesLoaded|initialized|_initFired|_8c|_8a|_getText|_87|ieForceActiveXXhr|Msxml2|isGears|_81|_gearsObject|googlegears|GearsFactory|isFF|_7d|Safari|_72|_name|_6c|ire|ore|_68|i18n|_5b|requireIf|_56|_52|loading|_4a|_loadPath|_47|_48|_global_omit_module_check|_getModulePrefix|_3c|_3a|_37|_30|Boolean|_loadUriAndCheck|_2e||cacheBust|_1e|_1a|_17|_16|_15|_14|_f|_10|_e|_9|_8|revision|flag|patch|minor|major|_6|color|units|needs|stopped|playing|stop|gotoPercent|pause|1000|implemented|yet|_48a|xhrDelete|rawXhrPut|xhrPut|postData|rawXhrPost|xhrPost|xhrGet|Type|Content|sync|response|http|bad|urlencoded|www|_watchInFlightError||exceeded|handle|action|getAttributeNode|loadXML|async|XMLDOM|prefixes|MSXML3|MSXML|MSXML2||xml|javascript|wasn|your|optional|message|off|turn|use|endpoints|issues|security|potential|avoid|mimetype|using|consider|please|decodeURIComponent|queryToObject|formToJson|formToQuery|encodeURIComponent|selected|option|multiple|checked|checkbox|radio|disabled|textarea|select|button|reset|submit|input|_3fb|hasAttribute|0n|even|odd|nth|_3b5|empty|_3b1|_3ad|htmlFor|_38a|under||exprssion|failure|ANY_TYPE|XPathResult|starts|keyup|keydown|mouseup|mousedown|blur|click|combine|span|addContent||adopt|orphan|_2de|_2dd|styles|_2da|_2d9|_2cf|_2ce|show|createPopup|toggleClass|scrollWidth|clientTop|ltr|direction|pageXOffset|pageYOffset|fixed|contentBox|marginBox|BUTTON|TABLE|_getBorderBox|clientHeight|visible|overflow|marginBottom|marginRight|marginTop|marginLeft|borderBottomWidth|borderBottomStyle|borderRightWidth|borderRightStyle|borderTopWidth|borderTopStyle|borderLeftWidth|borderLeftStyle|paddingBottom|paddingRight|paddingTop|paddingLeft|offset||min|padding||margin|Opacity|Alpha|alpha|filters|pixelLeft|medium|_22a|defaultView|before||insertBefore|KhtmlUserSelect|MozUserSelect|setSelectable|isDescendant|div|_destroyElement|BackgroundImageCache|execCommand|PageDown|PageUp|Right|Left|Down|Up|63289|63249|63248|PRINT_SCREEN|63302|63277|63276|63275|63273|63272|63250|63247|63246|63245|63244|63243|63242|63241|63240|63239|63238|63237|63236|63235|63234|63233|Enter|_1f9|which|_1f6|bubbledKeyCode|221|220||||191|190|189|188|187|toElement|fromElement|clientY|pageY||clientX|pageX|offsetY|||layerY|offsetX|layerX|parentWindow|_nop|_allow_leaks|145|144|126|F15|125|F14|124|F13|123|122|121|120|119|118|117|116|115|114|113|112|NUMPAD_DIVIDE|110|NUMPAD_PERIOD|109|NUMPAD_MINUS|108|NUMPAD_ENTER|107|NUMPAD_PLUS|NUMPAD_MULTIPLY|105|NUMPAD_9|104|NUMPAD_8|103|NUMPAD_7|102|NUMPAD_6|101|NUMPAD_5|NUMPAD_4||NUMPAD_3|NUMPAD_2|NUMPAD_1|NUMPAD_0||SELECT|RIGHT_WINDOW||LEFT_WINDOW||HELP|SPACE|ESCAPE|CAPS_LOCK|ALT|CTRL|SHIFT|ENTER|CLEAR|BACKSPACE|attachEvent|fixEvent|fromCharCode|keyChar|_1b9|removeEventListener|0x|round|toHex|toRgba|toRgb|aqua|teal|blue|navy|yellow|olive|lime|green|fuchsia|purple|red|maroon|white|gray|silver|black|boolean|called|already|Cancelled|connectPublisher|unsubscribe|subscribe|disconnect|_113|_112||_111|_110|||found|was||must|_|module|||required|likely|It|declaration|Mixin|separate|instead|property|initializer||pass|_c9|_bb|_b7|nfunction|isAlien|isFinite|isArrayLike|_firebug|withDoc|withGlobal|_writeIncludes|VML|behavior|addRule|createStyleSheet|vml|com|microsoft|schemas|urn|namespaces|onunload|onreadystatechange|defer|khtml|WebKit|DOMContentLoaded|enableMozDomContentLoaded|domcontentloaded|Unable|base|chrome|1223|304|300|200|available|XMLHttpRequest|_println|language|userLanguage|isQuirks|factory|mimeTypes|Factory|Gears|_7f|MSIE||Firefox|Gecko|Konqueror||Opera|appVersion|xd|browser|moduleUrl|port|host|hostenv|_requireLocalization|_5f|_5e|_5d|_5c|requireLocalization|requireAfterIf|_57|common|platformRequire|defined|symbol|_isXDomain|tried|Could|__package__|packageFileName|_42|useXDomain|flight|still|files|addOnLoad|failed|sourceURL|util|notice|without|change|subject|APIs|EXPERIMENTAL|experimental|removed|will|DEPRECATED|exists|10315|Rev|Mobile|Spidermonkey|Rhino||Browser|delayMozLoadingFix|preventBackButtonFix|libraryScriptUri|baseRelativePath|baseScriptUri|allowQueryConfig|warn|trace|timeEnd||time|profileEnd|profile|log|info|groupEnd|group|dirxml|dir|count|assert'.split('|'),0,{}); + + +/* + +Prototype 1.5 rc0 + - Adapted from Ruby on Rails - http://dev.rubyonrails.org/browser/spinoffs/prototype/src + - By Lunarmedia, 06 August, 2006 + - Available at (and packed with) JavascriptCompressor.com + +Please note this version is missing the selector.js component of the full Prototype library. +You can get the compressed version of selector at JavascriptCompressor.com + +*/ + +var decompressedPrototype = function(p,a,c,k,e,d){e=function(c){return(c<a?"":e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[(function(e){return d[e]})];e=(function(){return'\\w+'});c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('d T={4l:\'1.5.8P\',3E:\'(?:<3G.*?>)((\\n|\\r|.)*?)(?:<\\/3G>)\',2v:7(){},K:7(x){c x}};d 1b={17:7(){c 7(){6.1I.2n(6,N)}}};d 1e=z q();q.u=7(5d,O){G(d 1G 2M O){5d[1G]=O[1G]}c 5d};q.1U=7(U){1j{f(U==1v)c\'1v\';f(U==1L)c\'1L\';c U.1U?U.1U():U.2C()}1s(e){f(e 8R 9l)c\'...\';25 e}};7j.v.1d=7(){d 43=6,23=$A(N),U=23.8S();c 7(){c 43.2n(U,23.3s($A(N)))}};7j.v.8U=7(U){d 43=6;c 7(C){c 43.8V(U,C||1W.C)}};q.u(8Q.v,{8W:7(){d 4Z=6.2C(16);f(6<16)c\'0\'+4Z;c 4Z},5j:7(){c 6+1},8Y:7(o){$R(0,6,11).V(o);c 6}});d 6s={6j:7(){d 48;G(d i=0;i<N.t;i++){d 6L=N[i];1j{48=6L();1y}1s(e){}}c 48}};d 6Q=1b.17();6Q.v={1I:7(1a,1J){6.1a=1a;6.1J=1J;6.41=Y;6.2A()},2A:7(){5Z(6.2D.1d(6),6.1J*4z)},2D:7(){f(!6.41){1j{6.41=11;6.1a()}8Z{6.41=Y}}}};q.u(4b.v,{2T:7(1A,1z){d L=\'\',O=6,I;1z=N.90.52(1z);1H(O.t>0){f(I=O.I(1A)){L+=O.47(0,I.w);L+=(1z(I)||\'\').2C();O=O.47(I.w+I[0].t)}1D{L+=O,O=\'\'}}c L},92:7(1A,1z,3i){1z=6.2T.52(1z);3i=3i===1v?1:3i;c 6.2T(1A,7(I){f(--3i<0)c I[0];c 1z(I)})},93:7(1A,o){6.2T(1A,o);c 6},94:7(t,2S){t=t||30;2S=2S===1v?\'...\':2S;c 6.t>t?6.47(0,t-2S.t)+2S:6},9F:7(){c 6.2y(/^\\s+/,\'\').2y(/\\s+$/,\'\')},71:7(){c 6.2y(/<\\/?[^>]+>/7Y,\'\')},2Q:7(){c 6.2y(z 3O(T.3E,\'5P\'),\'\')},70:7(){d 6Y=z 3O(T.3E,\'5P\');d 5p=z 3O(T.3E,\'98\');c(6.I(6Y)||[]).1C(7(5o){c(5o.I(5p)||[\'\',\'\'])[1]})},3q:7(){c 6.70().1C(7(3G){c 4q(3G)})},9E:7(){d 1q=J.4Y(\'1q\');d 1Y=J.9D(6);1q.75(1Y);c 1q.3h},9c:7(){d 1q=J.4Y(\'1q\');1q.3h=6.71();c 1q.2z[0]?1q.2z[0].6q:\'\'},78:7(){d 7i=6.I(/^\\??(.*)$/)[1].3j(\'&\');c 7i.36({},7(5b,72){d 1i=72.3j(\'=\');5b[1i[0]]=1i[1];c 5b})},1Z:7(){c 6.3j(\'\')},3P:7(){d 2l=6.3j(\'-\');f(2l.t==1)c 2l[0];d 54=6.5g(\'-\')==0?2l[0].7e(0).3Y()+2l[0].7g(1):2l[0];G(d i=1,73=2l.t;i<73;i++){d s=2l[i];54+=s.7e(0).3Y()+s.7g(1)}c 54},1U:7(){c"\'"+6.2y(/\\\\/g,\'\\\\\\\\\').2y(/\'/g,\'\\\\\\\'\')+"\'"}});4b.v.2T.52=7(1z){f(2i 1z==\'7\')c 1z;d 2U=z 3n(1z);c 7(I){c 2U.7a(I)}};4b.v.9h=4b.v.78;d 3n=1b.17();3n.79=/(^|.|\\r|\\n)(#\\{(.*?)\\})/;3n.v={1I:7(2U,1A){6.2U=2U.2C();6.1A=1A||3n.79},7a:7(U){c 6.2U.2T(6.1A,7(I){d 53=I[1];f(53==\'\\\\\')c I[2];c 53+(U[I[3]]||\'\').2C()})}};d $1y=z q();d $49=z q();d 1p={V:7(o){d w=0;1j{6.2m(7(h){1j{o(h,w++)}1s(e){f(e!=$49)25 e}})}1s(e){f(e!=$1y)25 e}},9n:7(o){d L=11;6.V(7(h,w){L=L&&!!(o||T.K)(h,w);f(!L)25 $1y});c L},9o:7(o){d L=11;6.V(7(h,w){f(L=!!(o||T.K)(h,w))25 $1y});c L},3e:7(o){d P=[];6.V(7(h,w){P.W(o(h,w))});c P},7n:7(o){d L;6.V(7(h,w){f(o(h,w)){L=h;25 $1y}});c L},7o:7(o){d P=[];6.V(7(h,w){f(o(h,w))P.W(h)});c P},9p:7(1A,o){d P=[];6.V(7(h,w){d 7c=h.2C();f(7c.I(1A))P.W((o||T.K)(h,w))});c P},1M:7(U){d 51=Y;6.V(7(h){f(h==U){51=11;25 $1y}});c 51},36:7(45,o){6.V(7(h,w){45=o(45,h,w)});c 45},9q:7(1F){d 23=$A(N).47(1);c 6.3e(7(h){c h[1F].2n(h,23)})},9s:7(o){d L;6.V(7(h,w){h=(o||T.K)(h,w);f(L==1v||h>=L)L=h});c L},9u:7(o){d L;6.V(7(h,w){h=(o||T.K)(h,w);f(L==1v||h<L)L=h});c L},9v:7(o){d 50=[],58=[];6.V(7(h,w){((o||T.K)(h,w)?50:58).W(h)});c[50,58]},3r:7(1G){d P=[];6.V(7(h,w){P.W(h[1G])});c P},9x:7(o){d P=[];6.V(7(h,w){f(!o(h,w))P.W(h)});c P},9y:7(o){c 6.3e(7(h,w){c{h:h,59:o(h,w)}}).9z(7(18,3U){d a=18.59,b=3U.59;c a<b?-1:a>b?1:0}).3r(\'h\')},1Z:7(){c 6.3e(T.K)},9B:7(){d o=T.K,23=$A(N);f(2i 23.5e()==\'7\')o=23.9C();d 7l=[6].3s(23).1C($A);c 6.1C(7(h,w){c o(7l.3r(w))})},1U:7(){c\'#<1p:\'+6.1Z().1U()+\'>\'}};q.u(1p,{1C:1p.3e,5v:1p.7n,1k:1p.7o,8M:1p.1M,7p:1p.1Z});d $A=1E.7q=7(2R){f(!2R)c[];f(2R.1Z){c 2R.1Z()}1D{d P=[];G(d i=0;i<2R.t;i++)P.W(2R[i]);c P}};q.u(1E.v,1p);f(!1E.v.4d)1E.v.4d=1E.v.4m;q.u(1E.v,{2m:7(o){G(d i=0;i<6.t;i++)o(6[i])},5i:7(){6.t=0;c 6},7r:7(){c 6[0]},5e:7(){c 6[6.t-1]},7s:7(){c 6.1k(7(h){c h!=1v||h!=1L})},6J:7(){c 6.36([],7(6H,h){c 6H.3s(h&&h.5D==1E?h.6J():[h])})},5s:7(){d 4N=$A(N);c 6.1k(7(h){c!4N.1M(h)})},5g:7(U){G(d i=0;i<6.t;i++)f(6[i]==U)c i;c-1},4m:7(5h){c(5h!==Y?6:6.1Z()).4d()},1U:7(){c\'[\'+6.1C(q.1U).1N(\', \')+\']\'}});d 4h={2m:7(o){G(d 1O 2M 6){d h=6[1O];f(2i h==\'7\')49;d 1i=[1O,h];1i.1O=1O;1i.h=h;o(1i)}},7t:7(){c 6.3r(\'1O\')},4N:7(){c 6.3r(\'h\')},7u:7(2N){c $H(2N).36($H(6),7(4Q,1i){4Q[1i.1O]=1i.h;c 4Q})},7w:7(){c 6.1C(7(1i){c 1i.1C(4n).1N(\'=\')}).1N(\'&\')},1U:7(){c\'#<4h:{\'+6.1C(7(1i){c 1i.1C(q.1U).1N(\': \')}).1N(\', \')+\'}>\'}};7 $H(U){d 2N=q.u({},U||{});q.u(2N,1p);q.u(2N,4h);c 2N};3L=1b.17();q.u(3L.v,1p);q.u(3L.v,{1I:7(22,2x,2H){6.22=22;6.2x=2x;6.2H=2H},2m:7(o){d h=6.22;2q{o(h);h=h.5j()}1H(6.1M(h))},1M:7(h){f(h<6.22)c Y;f(6.2H)c h<6.2x;c h<=6.2x}});d $R=7(22,2x,2H){c z 3L(22,2x,2H)};d M={4w:7(){c 6s.6j(7(){c z 5C()},7(){c z 5n(\'7y.6d\')},7(){c z 5n(\'7z.6d\')})||Y},4s:0};M.2W={3b:[],2m:7(o){6.3b.2m(o)},69:7(4F){f(!6.1M(4F))6.3b.W(4F)},7A:7(5t){6.3b=6.3b.5s(5t)},3y:7(1a,26,E,2Z){6.V(7(3o){f(3o[1a]&&2i 3o[1a]==\'7\'){1j{3o[1a].2n(3o,[26,E,2Z])}1s(e){}}})}};q.u(M.2W,1p);M.2W.69({5G:7(){M.4s++},1B:7(){M.4s--}});M.44=7(){};M.44.v={4a:7(m){6.m={1F:\'4j\',4p:11,5H:\'5E/x-86-Q-7C\',28:\'\'};q.u(6.m,m||{})},3l:7(){c 6.E.32==1v||6.E.32==0||(6.E.32>=84&&6.E.32<7E)},7G:7(){c!6.3l()}};M.3t=1b.17();M.3t.5L=[\'7H\',\'80\',\'7I\',\'7J\',\'4t\'];M.3t.v=q.u(z M.44(),{1I:7(1l,m){6.E=M.4w();6.4a(m);6.26(1l)},26:7(1l){d 28=6.m.28||\'\';f(28.t>0)28+=\'&7K=\';1j{6.1l=1l;f(6.m.1F==\'7L\'&&28.t>0)6.1l+=(6.1l.I(/\\?/)?\'&\':\'?\')+28;M.2W.3y(\'5G\',6,6.E);6.E.7N(6.m.1F,6.1l,6.m.4p);f(6.m.4p){6.E.5T=6.5J.1d(6);2Y((7(){6.4r(1)}).1d(6),10)}6.5A();d 1c=6.m.5V?6.m.5V:28;6.E.7O(6.m.1F==\'4j\'?1c:1L)}1s(e){6.3p(e)}},5A:7(){d 1P=[\'X-7P-7Q\',\'5C\',\'X-T-4l\',T.4l,\'7R\',\'1Y/7m, 1Y/2e, 5E/5F, 1Y/5F, */*\'];f(6.m.1F==\'4j\'){1P.W(\'5Q-2g\',6.m.5H);f(6.E.7S)1P.W(\'7T\',\'7U\')}f(6.m.1P)1P.W.2n(1P,6.m.1P);G(d i=0;i<1P.t;i+=2)6.E.7V(1P[i],1P[i+1])},5J:7(){d 2F=6.E.2F;f(2F!=1)6.4r(6.E.2F)},4A:7(B){1j{c 6.E.7W(B)}1s(e){}},5M:7(){1j{c 4q(\'(\'+6.4A(\'X-7X\')+\')\')}1s(e){}},5R:7(){1j{c 4q(6.E.3F)}1s(e){6.3p(e)}},4r:7(2F){d C=M.3t.5L[2F];d E=6.E,2Z=6.5M();f(C==\'4t\'){1j{(6.m[\'2I\'+6.E.32]||6.m[\'2I\'+(6.3l()?\'81\':\'82\')]||T.2v)(E,2Z)}1s(e){6.3p(e)}f((6.4A(\'5Q-2g\')||\'\').I(/^1Y\\/7m/i))6.5R()}1j{(6.m[\'2I\'+C]||T.2v)(E,2Z);M.2W.3y(\'2I\'+C,6,E,2Z)}1s(e){6.3p(e)}f(C==\'4t\')6.E.5T=T.2v},3p:7(57){(6.m.5W||T.2v)(6,57);M.2W.3y(\'5W\',6,57)}});M.4C=1b.17();q.u(q.u(M.4C.v,M.3t.v),{1I:7(1w,1l,m){6.4x={3m:1w.3m?$(1w.3m):$(1w),3z:1w.3z?$(1w.3z):(1w.3m?1L:$(1w))};6.E=M.4w();6.4a(m);d 1B=6.m.1B||T.2v;6.m.1B=(7(E,U){6.5Y();1B(E,U)}).1d(6);6.26(1l)},5Y:7(){d 3A=6.3l()?6.4x.3m:6.4x.3z;d 3k=6.E.3F;f(!6.m.3q)3k=3k.2Q();f(3A){f(6.m.60){z 6.m.60(3A,3k)}1D{k.6h(3A,3k)}}f(6.3l()){f(6.1B)2Y(6.1B.1d(6),10)}}});M.61=1b.17();M.61.v=q.u(z M.44(),{1I:7(1w,1l,m){6.4a(m);6.1B=6.m.1B;6.1J=(6.m.1J||2);6.2s=(6.m.2s||1);6.4B={};6.1w=1w;6.1l=1l;6.22()},22:7(){6.m.1B=6.63.1d(6);6.2D()},7b:7(){6.4B.1B=1v;89(6.65);(6.1B||T.2v).2n(6,N)},63:7(26){f(6.m.2s){6.2s=(26.3F==6.64?6.2s*6.m.2s:1);6.64=26.3F}6.65=2Y(6.2D.1d(6),6.2s*6.1J*4z)},2D:7(){6.4B=z M.4C(6.1w,6.1l,6.m)}});7 $(){d P=[],4;G(d i=0;i<N.t;i++){4=N[i];f(2i 4==\'8c\')4=J.8d(4);P.W(k.u(4))}c P.t<2?P[0]:P};J.8f=7(1f,6a){d 6b=($(6a)||J.1c).4D(\'*\');c $A(6b).36([],7(12,4E){f(4E.1f.I(z 3O("(^|\\\\s)"+1f+"(\\\\s|$)")))12.W(k.u(4E));c 12})};f(!1W.k)d k=z q();k.u=7(4){f(!4)c;f(4X)c 4;f(!4.6e&&4.1h&&4!=1W){d 2a=k.3d,2r=k.u.2r;G(d 1G 2M 2a){d h=2a[1G];f(2i h==\'7\')4[1G]=2r.4W(h)}}4.6e=11;c 4};k.u.2r={4W:7(h){c 6[h]=6[h]||7(){c h.2n(1L,[6].3s($A(N)))}}};k.3d={4U:7(4){c $(4).l.2B!=\'3Q\'},6N:7(){G(d i=0;i<N.t;i++){d 4=$(N[i]);k[k.4U(4)?\'6f\':\'6w\'](4)}},6f:7(){G(d i=0;i<N.t;i++){d 4=$(N[i]);4.l.2B=\'3Q\'}},6w:7(){G(d i=0;i<N.t;i++){d 4=$(N[i]);4.l.2B=\'\'}},42:7(4){4=$(4);4.1X.8h(4)},6h:7(4,2e){$(4).3h=2e.2Q();2Y(7(){2e.3q()},10)},2y:7(4,2e){4=$(4);f(4.6k){4.6k=2e.2Q()}1D{d 1K=4.6R.6S();1K.56(4);4.1X.8i(1K.6T(2e.2Q()),4)}2Y(7(){2e.3q()},10)},8k:7(4){4=$(4);c 4.2k},3K:7(4){c z k.3S(4)},8l:7(4,1f){f(!(4=$(4)))c;c k.3K(4).1M(1f)},8m:7(4,1f){f(!(4=$(4)))c;c k.3K(4).7k(1f)},8n:7(4,1f){f(!(4=$(4)))c;c k.3K(4).42(1f)},8p:7(4){4=$(4);G(d i=0;i<4.2z.t;i++){d 3M=4.2z[i];f(3M.8q==3&&!/\\S/.4v(3M.6q))k.42(3M)}},8r:7(4){c $(4).3h.I(/^\\s*$/)},8s:7(4,3I){4=$(4),3I=$(3I);1H(4=4.1X)f(4==3I)c 11;c Y},6t:7(4){4=$(4);d x=4.x?4.x:4.2f,y=4.y?4.y:4.29;1W.6t(x,y)},1R:7(4,l){4=$(4);d h=4.l[l.3P()];f(!h){f(J.4J&&J.4J.6v){d 4L=J.4J.6v(4,1L);h=4L?4L.8v(l):1L}1D f(4.6x){h=4.6x[l.3P()]}}f(1W.6E&&[\'18\',\'1n\',\'3U\',\'6G\'].1M(l))f(k.1R(4,\'14\')==\'4G\')h=\'6y\';c h==\'6y\'?1L:h},8x:7(4,l){4=$(4);G(d B 2M l)4.l[B.3P()]=l[B]},8y:7(4){4=$(4);f(k.1R(4,\'2B\')!=\'3Q\')c{21:4.2p,24:4.2k};d 20=4.l;d 6B=20.4O;d 6A=20.14;20.4O=\'31\';20.14=\'2o\';20.2B=\'\';d 6C=4.6m;d 6D=4.6p;20.2B=\'3Q\';20.14=6A;20.4O=6B;c{21:6C,24:6D}},8z:7(4){4=$(4);d 4R=k.1R(4,\'14\');f(4R==\'4G\'||!4R){4.4T=11;4.l.14=\'3T\';f(1W.6E){4.l.1n=0;4.l.18=0}}},8A:7(4){4=$(4);f(4.4T){4.4T=1v;4.l.14=4.l.1n=4.l.18=4.l.6G=4.l.3U=\'\'}},8B:7(4){4=$(4);f(4.3c)c;4.3c=4.l.3V;f((k.1R(4,\'3V\')||\'4U\')!=\'31\')4.l.3V=\'31\'},8D:7(4){4=$(4);f(4.3c)c;4.l.3V=4.3c;4.3c=1v}};q.u(k,k.3d);d 4X=Y;f(!3W&&/3x|3w|3u/.4v(33.62)){d 3W={}};k.6K=7(2a){q.u(k.3d,2a||{});f(2i 3W!=\'1v\'){d 2a=k.3d,2r=k.u.2r;G(d 1G 2M 2a){d h=2a[1G];f(2i h==\'7\')3W.v[1G]=2r.4W(h)}4X=11}};k.6K();d 6M=z q();6M.2B=k.6N;1e.1g=7(3f){6.3f=3f};1e.1g.v={1I:7(4,2t){6.4=$(4);6.2t=2t.2Q();f(6.3f&&6.4.6O){1j{6.4.6O(6.3f,6.2t)}1s(e){d 1h=6.4.1h.2w();f(1h==\'4V\'||1h==\'8N\'){6.2X(6.6U())}1D{25 e}}}1D{6.1K=6.4.6R.6S();f(6.2V)6.2V();6.2X([6.1K.6T(6.2t)])}2Y(7(){2t.3q()},10)},6U:7(){d 1q=J.4Y(\'1q\');1q.3h=\'<6V><4V>\'+6.2t+\'</4V></6V>\';c $A(1q.2z[0].2z[0].2z)}};d 1g=z q();1g.6W=1b.17();1g.6W.v=q.u(z 1e.1g(\'96\'),{2V:7(){6.1K.97(6.4)},2X:7(2h){2h.V((7(2j){6.4.1X.55(2j,6.4)}).1d(6))}});1g.5m=1b.17();1g.5m.v=q.u(z 1e.1g(\'99\'),{2V:7(){6.1K.56(6.4);6.1K.74(11)},2X:7(2h){2h.4m(Y).V((7(2j){6.4.55(2j,6.4.9a)}).1d(6))}});1g.7h=1b.17();1g.7h.v=q.u(z 1e.1g(\'9d\'),{2V:7(){6.1K.56(6.4);6.1K.74(6.4)},2X:7(2h){2h.V((7(2j){6.4.75(2j)}).1d(6))}});1g.76=1b.17();1g.76.v=q.u(z 1e.1g(\'9i\'),{2V:7(){6.1K.9m(6.4)},2X:7(2h){2h.V((7(2j){6.4.1X.55(2j,6.4.9t)}).1d(6))}});k.3S=1b.17();k.3S.v={1I:7(4){6.4=$(4)},2m:7(o){6.4.1f.3j(/\\s+/).1k(7(B){c B.t>0}).2m(o)},5c:7(1f){6.4.1f=1f},7k:7(5a){f(6.1M(5a))c;6.5c(6.1Z().3s(5a).1N(\' \'))},42:7(4c){f(!6.1M(4c))c;6.5c(6.1k(7(1f){c 1f!=4c}).1N(\' \'))},2C:7(){c 6.1Z().1N(\' \')}};q.u(k.3S.v,1p);d 5I={5i:7(){G(d i=0;i<N.t;i++)$(N[i]).h=\'\'},4f:7(4){$(4).4f()},7v:7(){G(d i=0;i<N.t;i++)f($(N[i]).h==\'\')c Y;c 11},1k:7(4){$(4).1k()},5y:7(4){4=$(4);4.4f();f(4.1k)4.1k()}};d D={3a:7(Q){d 12=D.2L($(Q));d 4I=z 1E();G(d i=0;i<12.t;i++){d 4g=D.k.3a(12[i]);f(4g)4I.W(4g)}c 4I.1N(\'&\')},2L:7(Q){Q=$(Q);d 12=z 1E();G(d 1h 2M D.k.2E){d 4H=Q.4D(1h);G(d j=0;j<4H.t;j++)12.W(4H[j])}c 12},7x:7(Q,3N,B){Q=$(Q);d 3H=Q.4D(\'2u\');f(!3N&&!B)c 3H;d 4y=z 1E();G(d i=0;i<3H.t;i++){d 2u=3H[i];f((3N&&2u.2g!=3N)||(B&&2u.B!=B))49;4y.W(2u)}c 4y},7B:7(Q){d 12=D.2L(Q);G(d i=0;i<12.t;i++){d 4=12[i];4.7D();4.4o=\'11\'}},7F:7(Q){d 12=D.2L(Q);G(d i=0;i<12.t;i++){d 4=12[i];4.4o=\'\'}},5z:7(Q){c D.2L(Q).5v(7(4){c 4.2g!=\'31\'&&!4.4o&&[\'2u\',\'1k\',\'3J\'].1M(4.1h.2w())})},7M:7(Q){5I.5y(D.5z(Q))},5w:7(Q){$(Q).5w()}};D.k={3a:7(4){4=$(4);d 1F=4.1h.2w();d 1S=D.k.2E[1F](4);f(1S){d 1O=4n(1S[0]);f(1O.t==0)c;f(1S[1].5D!=1E)1S[1]=[1S[1]];c 1S[1].1C(7(h){c 1O+\'=\'+4n(h)}).1N(\'&\')}},1x:7(4){4=$(4);d 1F=4.1h.2w();d 1S=D.k.2E[1F](4);f(1S)c 1S[1]}};D.k.2E={2u:7(4){6c(4.2g.2w()){1r\'7Z\':1r\'31\':1r\'6l\':1r\'1Y\':c D.k.2E.3J(4);1r\'6g\':1r\'6i\':c D.k.2E.5O(4)}c Y},5O:7(4){f(4.83)c[4.B,4.h]},3J:7(4){c[4.B,4.h]},1k:7(4){c D.k.2E[4.2g==\'1k-6n\'?\'5S\':\'5X\'](4)},5S:7(4){d h=\'\',2b,w=4.85;f(w>=0){2b=4.m[w];h=2b.h||2b.1Y}c[4.B,h]},5X:7(4){d h=[];G(d i=0;i<4.t;i++){d 2b=4.m[i];f(2b.87)h.W(2b.h||2b.1Y)}c[4.B,h]}};d $F=D.k.1x;1e.3D=7(){};1e.3D.v={1I:7(4,1J,1a){6.1J=1J;6.4=$(4);6.1a=1a;6.2K=6.1x();6.2A()},2A:7(){5Z(6.2D.1d(6),6.1J*4z)},2D:7(){d h=6.1x();f(6.2K!=h){6.1a(6.4,h);6.2K=h}}};D.k.3C=1b.17();D.k.3C.v=q.u(z 1e.3D(),{1x:7(){c D.k.1x(6.4)}});D.3C=1b.17();D.3C.v=q.u(z 1e.3D(),{1x:7(){c D.3a(6.4)}});1e.2c=7(){};1e.2c.v={1I:7(4,1a){6.4=$(4);6.1a=1a;6.2K=6.1x();f(6.4.1h.2w()==\'Q\')6.67();1D 6.2A(6.4)},4K:7(){d h=6.1x();f(6.2K!=h){6.1a(6.4,h);6.2K=h}},67:7(){d 12=D.2L(6.4);G(d i=0;i<12.t;i++)6.2A(12[i])},2A:7(4){f(4.2g){6c(4.2g.2w()){1r\'6g\':1r\'6i\':1o.3B(4,\'8j\',6.4K.1d(6));1y;1r\'6l\':1r\'1Y\':1r\'3J\':1r\'1k-6n\':1r\'1k-8t\':1o.3B(4,\'8u\',6.4K.1d(6));1y}}}};D.k.2c=1b.17();D.k.2c.v=q.u(z 1e.2c(),{1x:7(){c D.k.1x(6.4)}});D.2c=1b.17();D.2c.v=q.u(z 1e.2c(),{1x:7(){c D.3a(6.4)}});f(!1W.1o){d 1o=z q()}q.u(1o,{8C:8,8F:9,8H:13,8I:27,8J:37,8L:38,8O:39,8T:40,8X:46,4:7(C){c C.Z||C.91},95:7(C){c(((C.6X)&&(C.6X==1))||((C.6Z)&&(C.6Z==1)))},9b:7(C){c C.9e||(C.9f+(J.3R.2G||J.1c.2G))},9g:7(C){c C.9j||(C.9k+(J.3R.2O||J.1c.2O))},7b:7(C){f(C.7d){C.7d();C.9r()}1D{C.48=Y;C.9w=11}},9A:7(C,1h){d 4=1o.4(C);1H(4.1X&&(!4.1h||(4.1h.3Y()!=1h.3Y())))4=4.1X;c 4},1T:Y,5u:7(4,B,1V,1u){f(!6.1T)6.1T=[];f(4.5f){6.1T.W([4,B,1V,1u]);4.5f(B,1V,1u)}1D f(4.4i){6.1T.W([4,B,1V,1u]);4.4i(\'2I\'+B,1V)}},66:7(){f(!1o.1T)c;G(d i=0;i<1o.1T.t;i++){1o.5N.2n(6,1o.1T[i]);1o.1T[i][0]=1L}1o.1T=Y},3B:7(4,B,1V,1u){d 4=$(4);1u=1u||Y;f(B==\'5U\'&&(33.4u.I(/3x|3w|3u/)||4.4i))B=\'5K\';6.5u(4,B,1V,1u)},5N:7(4,B,1V,1u){d 4=$(4);1u=1u||Y;f(B==\'5U\'&&(33.4u.I(/3x|3w|3u/)||4.4k))B=\'5K\';f(4.5x){4.5x(B,1V,1u)}1D f(4.4k){1j{4.4k(\'2I\'+B,1V)}1s(e){}}}});f(33.4u.I(/\\88\\b/))1o.3B(1W,\'8a\',1o.66,Y);d 2d={6o:Y,4P:7(){6.6z=1W.8e||J.3R.2G||J.1c.2G||0;6.6F=1W.8g||J.3R.2O||J.1c.2O||0},6u:7(4){d 19=0,15=0;2q{19+=4.2O||0;15+=4.2G||0;4=4.1X}1H(4);c[15,19]},35:7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;4=4.1Q}1H(4);c[15,19]},68:7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;4=4.1Q;f(4){p=k.1R(4,\'14\');f(p==\'3T\'||p==\'2o\')1y}}1H(4);c[15,19]},1Q:7(4){f(4.1Q)c 4.1Q;f(4==J.1c)c 4;1H((4=4.1X)&&4!=J.1c)f(k.1R(4,\'14\')!=\'4G\')c 4;c J.1c},8o:7(4,x,y){f(6.6o)c 6.6r(4,x,y);6.3g=x;6.34=y;6.1t=6.35(4);c(y>=6.1t[1]&&y<6.1t[1]+4.2k&&x>=6.1t[0]&&x<6.1t[0]+4.2p)},6r:7(4,x,y){d 4S=6.6u(4);6.3g=x+4S[0]-6.6z;6.34=y+4S[1]-6.6F;6.1t=6.35(4);c(6.34>=6.1t[1]&&6.34<6.1t[1]+4.2k&&6.3g>=6.1t[0]&&6.3g<6.1t[0]+4.2p)},8E:7(3Z,4){f(!3Z)c 0;f(3Z==\'8G\')c((6.1t[1]+4.2k)-6.34)/4.2k;f(3Z==\'8K\')c((6.1t[0]+4.2p)-6.3g)/4.2p},77:7(O,Z){O=$(O);Z=$(Z);Z.l.14=\'2o\';d 2P=6.35(O);Z.l.1n=2P[1]+\'1m\';Z.l.18=2P[0]+\'1m\';Z.l.21=O.2p+\'1m\';Z.l.24=O.2k+\'1m\'},4e:7(4M){d 19=0,15=0;d 4=4M;2q{19+=4.29||0;15+=4.2f||0;f(4.1Q==J.1c)f(k.1R(4,\'14\')==\'2o\')1y}1H(4=4.1Q);4=4M;2q{19-=4.2O||0;15-=4.2G||0}1H(4=4.1X);c[15,19]},77:7(O,Z){d m=q.u({5l:11,5r:11,5B:11,5q:11,29:0,2f:0},N[2]||{});O=$(O);d p=2d.4e(O);Z=$(Z);d 2J=[0,0];d 3v=1L;f(k.1R(Z,\'14\')==\'2o\'){3v=2d.1Q(Z);2J=2d.4e(3v)}f(3v==J.1c){2J[0]-=J.1c.2f;2J[1]-=J.1c.29}f(m.5l)Z.l.18=(p[0]-2J[0]+m.2f)+\'1m\';f(m.5r)Z.l.1n=(p[1]-2J[1]+m.29)+\'1m\';f(m.5B)Z.l.21=O.2p+\'1m\';f(m.5q)Z.l.24=O.2k+\'1m\'},8b:7(4){4=$(4);f(4.l.14==\'2o\')c;2d.4P();d 2P=2d.68(4);d 1n=2P[1];d 18=2P[0];d 21=4.6m;d 24=4.6p;4.6P=18-3X(4.l.18||0);4.6I=1n-3X(4.l.1n||0);4.5k=4.l.21;4.7f=4.l.24;4.l.14=\'2o\';4.l.1n=1n+\'1m\';4.l.18=18+\'1m\';4.l.21=21+\'1m\';4.l.24=24+\'1m\'},8w:7(4){4=$(4);f(4.l.14==\'3T\')c;2d.4P();4.l.14=\'3T\';d 1n=3X(4.l.1n||0)-(4.6I||0);d 18=3X(4.l.18||0)-(4.6P||0);4.l.1n=1n+\'1m\';4.l.18=18+\'1m\';4.l.24=4.7f;4.l.21=4.5k}};f(/3x|3w|3u/.4v(33.62)){2d.35=7(4){d 19=0,15=0;2q{19+=4.29||0;15+=4.2f||0;f(4.1Q==J.1c)f(k.1R(4,\'14\')==\'2o\')1y;4=4.1Q}1H(4);c[15,19]}};',62,600,'||||element||this|function|||||return|var||if||value|||Element|style|options||iterator||Object|||length|extend|prototype|index|||new||name|event|Form|transport||for||match|document||result|Ajax|arguments|source|results|form|||Prototype|object|each|push||false|target||true|elements||position|valueL||create|left|valueT|callback|Class|body|bind|Abstract|className|Insertion|tagName|pair|try|select|url|px|top|Event|Enumerable|div|case|catch|offset|useCapture|undefined|container|getValue|break|replacement|pattern|onComplete|map|else|Array|method|property|while|initialize|frequency|range|null|include|join|key|requestHeaders|offsetParent|getStyle|parameter|observers|inspect|observer|window|parentNode|text|toArray|els|width|start|args|height|throw|request||parameters|offsetTop|methods|opt|EventObserver|Position|html|offsetLeft|type|fragments|typeof|fragment|offsetHeight|oStringList|_each|apply|absolute|offsetWidth|do|cache|decay|content|input|emptyFunction|toLowerCase|end|replace|childNodes|registerCallback|display|toString|onTimerEvent|Serializers|readyState|scrollLeft|exclusive|on|delta|lastValue|getElements|in|hash|scrollTop|offsets|stripScripts|iterable|truncation|gsub|template|initializeRange|Responders|insertContent|setTimeout|json||hidden|status|navigator|ycomp|cumulativeOffset|inject||||serialize|responders|_overflow|Methods|collect|adjacency|xcomp|innerHTML|count|split|response|responseIsSuccess|success|Template|responder|dispatchException|evalScripts|pluck|concat|Request|KHTML|parent|Safari|Konqueror|dispatch|failure|receiver|observe|Observer|TimedObserver|ScriptFragment|responseText|script|inputs|ancestor|textarea|classNames|ObjectRange|node|typeName|RegExp|camelize|none|documentElement|ClassNames|relative|right|overflow|HTMLElement|parseFloat|toUpperCase|mode||currentlyExecuting|remove|__method|Base|memo||slice|returnValue|continue|setOptions|String|classNameToRemove|_reverse|page|focus|queryComponent|Hash|attachEvent|post|detachEvent|Version|reverse|encodeURIComponent|disabled|asynchronous|eval|respondToReadyState|activeRequestCount|Complete|appVersion|test|getTransport|containers|matchingInputs|1000|header|updater|Updater|getElementsByTagName|child|responderToAdd|static|tagElements|queryComponents|defaultView|onElementEvent|css|forElement|values|visibility|prepare|mergedHash|pos|offsetcache|_madePositioned|visible|tbody|findOrStore|_nativeExtensions|createElement|digits|trues|found|prepareReplacement|before|camelizedString|insertBefore|selectNodeContents|exception|falses|criteria|classNameToAdd|params|set|destination|last|addEventListener|indexOf|inline|clear|succ|_originalWidth|setLeft|Top|ActiveXObject|scriptTag|matchOne|setHeight|setTop|without|responderToRemove|_observeAndCache|find|reset|removeEventListener|activate|findFirstElement|setRequestHeaders|setWidth|XMLHttpRequest|constructor|application|xml|onCreate|contentType|Field|onStateChange|keydown|Events|evalJSON|stopObserving|inputSelector|img|Content|evalResponse|selectOne|onreadystatechange|keypress|postBody|onException|selectMany|updateContent|setInterval|insertion|PeriodicalUpdater|userAgent|updateComplete|lastText|timer|unloadCache|registerFormCallbacks|positionedOffset|register|parentElement|children|switch|XMLHTTP|_extended|hide|checkbox|update|radio|these|outerHTML|password|clientWidth|one|includeScrollOffsets|clientHeight|nodeValue|withinIncludingScrolloffsets|Try|scrollTo|realOffset|getComputedStyle|show|currentStyle|auto|deltaX|originalPosition|originalVisibility|originalWidth|originalHeight|opera|deltaY|bottom|array|_originalTop|flatten|addMethods|lambda|Toggle|toggle|insertAdjacentHTML|_originalLeft|PeriodicalExecuter|ownerDocument|createRange|createContextualFragment|contentFromAnonymousTable|table|Before|which|matchAll|button|extractScripts|stripTags|pairString|len|collapse|appendChild|After|clone|toQueryParams|Pattern|evaluate|stop|stringValue|preventDefault|charAt|_originalHeight|substring|Bottom|pairs|Function|add|collections|javascript|detect|findAll|entries|from|first|compact|keys|merge|present|toQueryString|getInputs|Msxml2|Microsoft|unregister|disable|urlencoded|blur|300|enable|responseIsFailure|Uninitialized|Loaded|Interactive|_|get|focusFirstElement|open|send|Requested|With|Accept|overrideMimeType|Connection|close|setRequestHeader|getResponseHeader|JSON|gi|submit|Loading|Success|Failure|checked|200|selectedIndex|www|selected|bMSIE|clearTimeout|unload|absolutize|string|getElementById|pageXOffset|getElementsByClassName|pageYOffset|removeChild|replaceChild|click|getHeight|hasClassName|addClassName|removeClassName|within|cleanWhitespace|nodeType|empty|childOf|multiple|change|getPropertyValue|relativize|setStyle|getDimensions|makePositioned|undoPositioned|makeClipping|KEY_BACKSPACE|undoClipping|overlap|KEY_TAB|vertical|KEY_RETURN|KEY_ESC|KEY_LEFT|horizontal|KEY_UP|member|tr|KEY_RIGHT|0_RC_0|Number|instanceof|shift|KEY_DOWN|bindAsEventListener|call|toColorPart|KEY_DELETE|times|finally|callee|srcElement|sub|scan|truncate|isLeftClick|beforeBegin|setStartBefore|im|afterBegin|firstChild|pointerX|unescapeHTML|beforeEnd|pageX|clientX|pointerY|parseQuery|afterEnd|pageY|clientY|RangeError|setStartAfter|all|any|grep|invoke|stopPropagation|max|nextSibling|min|partition|cancelBubble|reject|sortBy|sort|findElement|zip|pop|createTextNode|escapeHTML|strip'.split('|'),0,{}) + +} + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/sunspider/string-validate-input.html b/build/pgo/js-input/sunspider/string-validate-input.html new file mode 100644 index 000000000..72cf920b2 --- /dev/null +++ b/build/pgo/js-input/sunspider/string-validate-input.html @@ -0,0 +1,139 @@ +<!DOCTYPE html> +<head> +<!-- + Copyright (C) 2007 Apple Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--> + +<title>SunSpider string-validate-input</title> + +</head> + +<body> +<h3>string-validate-input</h3> +<div id="console"> +</div> + +<script> + +var _sunSpiderStartDate = new Date(); + +letters = new Array("a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z"); +numbers = new Array(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26); +colors = new Array("FF","CC","99","66","33","00"); + +var endResult; + +function doTest() +{ + endResult = ""; + + // make up email address + for (var k=0;k<4000;k++) + { + name = makeName(6); + (k%2)?email=name+"@mac.com":email=name+"(at)mac.com"; + + // validate the email address + var pattern = /^[a-zA-Z0-9\-\._]+@[a-zA-Z0-9\-_]+(\.?[a-zA-Z0-9\-_]*)\.[a-zA-Z]{2,3}$/; + + if(pattern.test(email)) + { + var r = email + " appears to be a valid email address."; + addResult(r); + } + else + { + r = email + " does NOT appear to be a valid email address."; + addResult(r); + } + } + + // make up ZIP codes + for (var s=0;s<4000;s++) + { + var zipGood = true; + var zip = makeNumber(4); + (s%2)?zip=zip+"xyz":zip=zip.concat("7"); + + // validate the zip code + for (var i = 0; i < zip.length; i++) { + var ch = zip.charAt(i); + if (ch < "0" || ch > "9") { + zipGood = false; + r = zip + " contains letters."; + addResult(r); + } + } + if (zipGood && zip.length>5) + { + zipGood = false; + r = zip + " is longer than five characters."; + addResult(r); + } + if (zipGood) + { + r = zip + " appears to be a valid ZIP code."; + addResult(r); + } + } +} + +function makeName(n) +{ + var tmp = ""; + for (var i=0;i<n;i++) + { + var l = Math.floor(26*Math.random()); + tmp += letters[l]; + } + return tmp; +} + +function makeNumber(n) +{ + var tmp = ""; + for (var i=0;i<n;i++) + { + var l = Math.floor(9*Math.random()); + tmp = tmp.concat(l); + } + return tmp; +} + +function addResult(r) +{ + endResult += "\n" + r; +} + +doTest(); + + +var _sunSpiderInterval = new Date() - _sunSpiderStartDate; + +document.getElementById("console").innerHTML = _sunSpiderInterval; +</script> + + +</body> +</html> diff --git a/build/pgo/js-input/valid-xhtml10.png b/build/pgo/js-input/valid-xhtml10.png Binary files differnew file mode 100644 index 000000000..2275ee6ea --- /dev/null +++ b/build/pgo/js-input/valid-xhtml10.png diff --git a/build/pgo/profileserver.py b/build/pgo/profileserver.py new file mode 100644 index 000000000..3e5a870c3 --- /dev/null +++ b/build/pgo/profileserver.py @@ -0,0 +1,92 @@ +#!/usr/bin/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/. + +from mozprofile import FirefoxProfile, Profile, Preferences +from mozprofile.permissions import ServerLocations +from mozrunner import FirefoxRunner, CLI +from mozhttpd import MozHttpd +import json +import socket +import threading +import os +import sys +import shutil +import tempfile +from datetime import datetime +from mozbuild.base import MozbuildObject +from buildconfig import substs + +PORT = 8888 + +if __name__ == '__main__': + cli = CLI() + debug_args, interactive = cli.debugger_arguments() + + build = MozbuildObject.from_environment() + httpd = MozHttpd(port=PORT, + docroot=os.path.join(build.topsrcdir, "build", "pgo")) + httpd.start(block=False) + + locations = ServerLocations() + locations.add_host(host='127.0.0.1', + port=PORT, + options='primary,privileged') + + #TODO: mozfile.TemporaryDirectory + profilePath = tempfile.mkdtemp() + try: + #TODO: refactor this into mozprofile + prefpath = os.path.join(build.topsrcdir, "testing", "profiles", "prefs_general.js") + prefs = {} + prefs.update(Preferences.read_prefs(prefpath)) + interpolation = { "server": "%s:%d" % httpd.httpd.server_address, + "OOP": "false"} + prefs = json.loads(json.dumps(prefs) % interpolation) + for pref in prefs: + prefs[pref] = Preferences.cast(prefs[pref]) + profile = FirefoxProfile(profile=profilePath, + preferences=prefs, + addons=[os.path.join(build.topsrcdir, 'tools', 'quitter', 'quitter@mozilla.org.xpi')], + locations=locations) + + env = os.environ.copy() + env["MOZ_CRASHREPORTER_NO_REPORT"] = "1" + env["XPCOM_DEBUG_BREAK"] = "warn" + + # For VC12+, make sure we can find the right bitness of pgort1x0.dll + if not substs.get('HAVE_64BIT_BUILD'): + for e in ('VS140COMNTOOLS', 'VS120COMNTOOLS'): + if e not in env: + continue + + vcdir = os.path.abspath(os.path.join(env[e], '../../VC/bin')) + if os.path.exists(vcdir): + env['PATH'] = '%s;%s' % (vcdir, env['PATH']) + break + + # Run Firefox a first time to initialize its profile + runner = FirefoxRunner(profile=profile, + binary=build.get_binary_path(where="staged-package"), + cmdargs=['javascript:Quitter.quit()'], + env=env) + runner.start() + runner.wait() + + jarlog = os.getenv("JARLOG_FILE") + if jarlog: + env["MOZ_JAR_LOG_FILE"] = os.path.abspath(jarlog) + print "jarlog: %s" % env["MOZ_JAR_LOG_FILE"] + + cmdargs = ["http://localhost:%d/index.html" % PORT] + runner = FirefoxRunner(profile=profile, + binary=build.get_binary_path(where="staged-package"), + cmdargs=cmdargs, + env=env) + runner.start(debug_args=debug_args, interactive=interactive) + runner.wait() + httpd.stop() + finally: + shutil.rmtree(profilePath) diff --git a/build/pgo/server-locations.txt b/build/pgo/server-locations.txt new file mode 100644 index 000000000..194cbe9ea --- /dev/null +++ b/build/pgo/server-locations.txt @@ -0,0 +1,269 @@ +# +# 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 defines the locations at which this HTTP server may be accessed. +# It is referred to by the following page, so if this file moves, that page must +# be modified accordingly: +# +# https://developer.mozilla.org/en/docs/Mochitest#How_do_I_test_issues_which_only_show_up_when_tests_are_run_across_domains.3F +# +# Empty lines and lines which begin with "#" are ignored and may be used for +# storing comments. All other lines consist of an origin followed by whitespace +# and a comma-separated list of options (if indeed any options are needed). +# +# The format of an origin is, referring to RFC 2396, a scheme (either "http" or +# "https"), followed by "://", followed by a host, followed by ":", followed by +# a port number. The colon and port number must be present even if the port +# number is the default for the protocol. +# +# Unrecognized options are ignored. Recognized options are "primary" and +# "privileged", "nocert", "cert=some_cert_nickname", "redir=hostname" and +# "failHandshake". +# +# "primary" denotes a location which is the canonical location of +# the server; this location is the one assumed for requests which don't +# otherwise identify a particular origin (e.g. HTTP/1.0 requests). +# +# "privileged" denotes a location which should have the ability to request +# elevated privileges; the default is no privileges. +# +# "nocert" makes sense only for https:// hosts and means there is not +# any certificate automatically generated for this host. +# +# "failHandshake" causes the tls handshake to fail (by sending a client hello to +# the client). +# +# "cert=nickname" tells the pgo server to use a particular certificate +# for this host. The certificate is referenced by its nickname that must +# not contain any spaces. The certificate key files (PKCS12 modules) +# for custom certification are loaded from build/pgo/certs +# directory. When new certificate is added to this dir pgo/ssltunnel +# must be built then. This is only necessary for cases where we really do +# want specific certs. +# +# "redir=hostname" tells the pgo server is only used for https:// +# hosts while processing the CONNECT tunnel request. It responds +# to the CONNECT with a 302 and redirection to the hostname instead +# of connecting to the real back end and replying with a 200. This +# mode exists primarily to ensure we don't allow a proxy to do that. +# + +# +# This is the primary location from which tests run. +# +http://mochi.test:8888 primary,privileged + +# +# These are a common set of prefixes scattered across one TLD with two ports and +# another TLD on a single port. +# +http://127.0.0.1:80 privileged +http://127.0.0.1:8888 privileged +http://test:80 privileged +http://mochi.test:8888 privileged +http://test1.mochi.test:8888 +http://sub1.test1.mochi.test:8888 +http://sub2.xn--lt-uia.mochi.test:8888 +http://test2.mochi.test:8888 +http://example.org:80 privileged +http://test1.example.org:80 privileged +http://test2.example.org:80 privileged +http://sub1.test1.example.org:80 privileged +http://sub1.test2.example.org:80 privileged +http://sub2.test1.example.org:80 privileged +http://sub2.test2.example.org:80 privileged +http://example.org:8000 privileged +http://test1.example.org:8000 privileged +http://test2.example.org:8000 privileged +http://sub1.test1.example.org:8000 privileged +http://sub1.test2.example.org:8000 privileged +http://sub2.test1.example.org:8000 privileged +http://sub2.test2.example.org:8000 privileged +http://example.com:80 privileged +http://www.example.com:80 privileged +http://test1.example.com:80 privileged +http://test2.example.com:80 privileged +http://sub1.test1.example.com:80 privileged +http://sub1.test2.example.com:80 privileged +http://sub2.test1.example.com:80 privileged +http://sub2.test2.example.com:80 privileged +http://noxul.example.com:80 privileged,noxul +http://example.net:80 privileged +# Used to test that clearing Service Workers for domain example.com, does not clear prefixexample.com +http://prefixexample.com:80 + +# The first HTTPS location is used to generate the Common Name (CN) value of the +# certificate's Issued To field. +https://example.com:443 privileged +https://test1.example.com:443 privileged +https://test2.example.com:443 privileged +https://example.org:443 privileged +https://test1.example.org:443 privileged +https://test2.example.org:443 privileged +https://sub1.test1.example.com:443 privileged +https://sub1.test2.example.com:443 privileged +https://sub2.test1.example.com:443 privileged +https://sub2.test2.example.com:443 privileged +https://nocert.example.com:443 privileged,nocert +https://self-signed.example.com:443 privileged,cert=selfsigned +https://untrusted.example.com:443 privileged,cert=untrusted +https://expired.example.com:443 privileged,cert=expired +https://requestclientcert.example.com:443 privileged,clientauth=request +https://requireclientcert.example.com:443 privileged,clientauth=require +https://mismatch.expired.example.com:443 privileged,cert=expired +https://mismatch.untrusted.example.com:443 privileged,cert=untrusted +https://untrusted-expired.example.com:443 privileged,cert=untrustedandexpired +https://mismatch.untrusted-expired.example.com:443 privileged,cert=untrustedandexpired + +# This is here so that we don't load the default live bookmark over +# the network in every test suite. +http://fxfeeds.mozilla.com:80 + +# Prevent safebrowsing tests from hitting the network for its-a-trap.html and +# its-an-attack.html. +http://www.itisatrap.org:80 + +# +# These are subdomains of <ält.example.org>. +# +http://sub1.xn--lt-uia.example.org:8000 privileged +http://sub2.xn--lt-uia.example.org:80 privileged +http://xn--exmple-cua.test:80 privileged +http://sub1.xn--exmple-cua.test:80 privileged +http://xn--exaple-kqf.test:80 privileged +http://sub1.xn--exaple-kqf.test:80 privileged + +https://xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged +https://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged + +# +# These are subdomains of <παράδειγμα.δοκιμή>, the Greek IDN for example.test. +# +http://xn--hxajbheg2az3al.xn--jxalpdlp:80 privileged +http://sub1.xn--hxajbheg2az3al.xn--jxalpdlp:80 privileged + +# Bug 413909 test host +https://bug413909.xn--hxajbheg2az3al.xn--jxalpdlp:443 privileged,cert=bug413909cert + +# +# These hosts are used in tests which exercise privilege-granting functionality; +# we could reuse some of the names above, but specific names make it easier to +# distinguish one from the other in tests (as well as what functionality is +# being tested). +# +http://sectest1.example.org:80 privileged +http://sub.sectest2.example.org:80 privileged +http://sectest2.example.org:80 +http://sub.sectest1.example.org:80 + +https://sectest1.example.org:443 privileged +https://sub.sectest2.example.org:443 privileged +https://sectest2.example.org:443 +https://sub.sectest1.example.org:443 + +# +# Used while testing the url-classifier +# +http://malware.example.com:80 +http://unwanted.example.com:80 +http://tracking.example.com:80 +http://not-tracking.example.com:80 +http://tracking.example.org:80 +http://itisatracker.org:80 +http://trackertest.org:80 + +https://malware.example.com:443 +https://unwanted.example.com:443 +https://tracking.example.com:443 +https://not-tracking.example.com:443 +https://tracking.example.org:443 + +# Bug 1281083 +http://bug1281083.example.com:80 + +# Bug 483437, 484111 +https://www.bank1.com:443 privileged,cert=escapeattack1 + +# +# CONNECT for redirproxy results in a 302 redirect to +# test1.example.com +# +https://redirproxy.example.com:443 privileged,redir=test1.example.com + +# Host used for IndexedDB Quota testing +http://bug704464-1.example.com:80 privileged +http://bug704464-2.example.com:80 privileged +http://bug704464-3.example.com:80 privileged +http://bug702292.example.com:80 privileged + +# W3C hosts. +# See http://www.w3.org/wiki/Testing/Requirements#The_Web_test_server_must_be_available_through_different_domain_names +http://w3c-test.org:80 +http://w3c-test.org:81 +http://w3c-test.org:82 +http://w3c-test.org:83 +http://www.w3c-test.org:80 +http://www.w3c-test.org:81 +http://www.w3c-test.org:82 +http://www.w3c-test.org:83 +http://www1.w3c-test.org:80 +http://www1.w3c-test.org:81 +http://www1.w3c-test.org:82 +http://www1.w3c-test.org:83 +http://www2.w3c-test.org:80 +http://www2.w3c-test.org:81 +http://www2.w3c-test.org:82 +http://www2.w3c-test.org:83 +# http://天気の良い日.w3c-test.org +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:80 +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:81 +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:82 +http://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:83 +# http://élève.w3c-test.org +http://xn--lve-6lad.w3c-test.org:80 +http://xn--lve-6lad.w3c-test.org:81 +http://xn--lve-6lad.w3c-test.org:82 +http://xn--lve-6lad.w3c-test.org:83 +# HTTPS versions of the above +https://w3c-test.org:443 +https://www.w3c-test.org:443 +https://www1.w3c-test.org:443 +https://www2.w3c-test.org:443 +https://xn--n8j6ds53lwwkrqhv28a.w3c-test.org:443 +https://xn--lve-6lad.w3c-test.org:443 +http://test.w3.org:80 + +# Hosts for testing TLD-based fallback encoding +http://example.tw:80 privileged +http://example.cn:80 privileged +http://example.co.jp:80 privileged +http://example.fi:80 privileged + +# Hosts for testing marketplace apps installations +https://marketplace.firefox.com:443 privileged +https://marketplace-dev.allizom.org:443 privileged +https://marketplace.allizom.org:443 privileged + +# Host for HPKP +https://include-subdomains.pinning-dynamic.example.com:443 privileged,cert=dynamicPinningGood +https://bad.include-subdomains.pinning-dynamic.example.com:443 privileged,cert=dynamicPinningBad + +# Host for static pin tests +https://badchain.include-subdomains.pinning.example.com:443 privileged,cert=staticPinningBad +https://fail-handshake.example.com:443 privileged,failHandshake + +# Hosts for sha1 console warning tests +https://sha1ee.example.com:443 privileged,cert=sha1_end_entity +https://sha256ee.example.com:443 privileged,cert=sha256_end_entity + +# Hosts for ssl3/rc4 console warning tests +https://ssl3.example.com:443 privileged,ssl3 +https://rc4.example.com:443 privileged,rc4 +https://ssl3rc4.example.com:443 privileged,ssl3,rc4 +https://tls1.example.com:443 privileged,tls1 + +# Hosts for youtube rewrite tests +https://mochitest.youtube.com:443 diff --git a/build/prebuilt-interfaces.manifest b/build/prebuilt-interfaces.manifest new file mode 100644 index 000000000..d63941e8c --- /dev/null +++ b/build/prebuilt-interfaces.manifest @@ -0,0 +1 @@ +interfaces interfaces.xpt diff --git a/build/pymake/LICENSE b/build/pymake/LICENSE new file mode 100644 index 000000000..04a7d641d --- /dev/null +++ b/build/pymake/LICENSE @@ -0,0 +1,21 @@ +The MIT License + +Copyright (c) 2009 The Mozilla Foundation <http://www.mozilla.org/> + +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. diff --git a/build/pymake/README b/build/pymake/README new file mode 100644 index 000000000..4f0fdfea4 --- /dev/null +++ b/build/pymake/README @@ -0,0 +1,64 @@ +INTRODUCTION + +make.py (and the pymake modules that support it) are an implementation of the make tool +which are mostly compatible with makefiles written for GNU make. + +PURPOSE + +The Mozilla project inspired this tool with several goals: + +* Improve build speeds, especially on Windows. This can be done by reducing the total number + of processes that are launched, especially MSYS shell processes which are expensive. + +* Allow writing some complicated build logic directly in Python instead of in shell. + +* Allow computing dependencies for special targets, such as members within ZIP files. + +* Enable experiments with build system. By writing a makefile parser, we can experiment + with converting in-tree makefiles to another build system, such as SCons, waf, ant, ...insert + your favorite build tool here. Or we could experiment along the lines of makepp, keeping + our existing makefiles, but change the engine to build a global dependency graph. + +KNOWN INCOMPATIBILITIES + +* Order-only prerequisites are not yet supported + +* Secondary expansion is not yet supported. + +* Target-specific variables behave differently than in GNU make: in pymake, the target-specific + variable only applies to the specific target that is mentioned, and does not apply recursively + to all dependencies which are remade. This is an intentional change: the behavior of GNU make + is neither deterministic nor intuitive. + +* $(eval) is only supported during the parse phase. Any attempt to recursively expand + an $(eval) function during command execution will fail. This is an intentional incompatibility. + +* There is a subtle difference in execution order that can cause unexpected changes in the + following circumstance: +** A file `foo.c` exists on the VPATH +** A rule for `foo.c` exists with a dependency on `tool` and no commands +** `tool` is remade for some other reason earlier in the file + In this case, pymake resets the VPATH of `foo.c`, while GNU make does not. This shouldn't + happen in the real world, since a target found on the VPATH without commands is silly. But + mozilla/js/src happens to have a rule, which I'm patching. + +* pymake does not implement any of the builtin implicit rules or the related variables. Mozilla + only cares because pymake doesn't implicitly define $(RM), which I'm also fixing in the Mozilla + code. + +ISSUES + +* Speed is a problem. + +FUTURE WORK + +* implement a new type of command which is implemented in python. This would allow us +to replace the current `nsinstall` binary (and execution costs for the shell and binary) with an +in-process python solution. + +AUTHOR + +Initial code was written by Benjamin Smedberg <benjamin@smedbergs.us>. For future releases see +http://benjamin.smedbergs.us/pymake/ + +See the LICENSE file for license information (MIT license) diff --git a/build/pymake/make.py b/build/pymake/make.py new file mode 100755 index 000000000..0857f3f8c --- /dev/null +++ b/build/pymake/make.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +""" +make.py + +A drop-in or mostly drop-in replacement for GNU make. +""" + +import sys, os +import pymake.command, pymake.process + +import gc + +if __name__ == '__main__': + 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. + import subprocess + mozmake = os.path.join(os.path.dirname(__file__), '..', '..', + 'mozmake.exe') + 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)) + + sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) + sys.stderr = os.fdopen(sys.stderr.fileno(), 'w', 0) + + gc.disable() + + pymake.command.main(sys.argv[1:], os.environ, os.getcwd(), cb=sys.exit) + pymake.process.ParallelContext.spin() + assert False, "Not reached" diff --git a/build/pymake/mkformat.py b/build/pymake/mkformat.py new file mode 100755 index 000000000..41dd761b2 --- /dev/null +++ b/build/pymake/mkformat.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python + +import sys +import pymake.parser + +filename = sys.argv[1] +source = None + +with open(filename, 'rU') as fh: + source = fh.read() + +statements = pymake.parser.parsestring(source, filename) +print statements.to_source() diff --git a/build/pymake/mkparse.py b/build/pymake/mkparse.py new file mode 100755 index 000000000..253683948 --- /dev/null +++ b/build/pymake/mkparse.py @@ -0,0 +1,12 @@ +#!/usr/bin/env python + +import sys +import pymake.parser + +for f in sys.argv[1:]: + print "Parsing %s" % f + fd = open(f, 'rU') + s = fd.read() + fd.close() + stmts = pymake.parser.parsestring(s, f) + print stmts diff --git a/build/pymake/pymake/__init__.py b/build/pymake/pymake/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/pymake/pymake/__init__.py diff --git a/build/pymake/pymake/builtins.py b/build/pymake/pymake/builtins.py new file mode 100644 index 000000000..eb6f2e11b --- /dev/null +++ b/build/pymake/pymake/builtins.py @@ -0,0 +1,120 @@ +# Basic commands implemented in Python +import errno, sys, os, shutil, time +from getopt import getopt, GetoptError + +from process import PythonException + +__all__ = ["mkdir", "rm", "sleep", "touch"] + +def mkdir(args): + """ + Emulate some of the behavior of mkdir(1). + Only supports the -p (--parents) argument. + """ + try: + opts, args = getopt(args, "p", ["parents"]) + except GetoptError, e: + raise PythonException, ("mkdir: %s" % e, 1) + parents = False + for o, a in opts: + if o in ('-p', '--parents'): + parents = True + for f in args: + try: + if parents: + os.makedirs(f) + else: + os.mkdir(f) + except OSError, e: + if e.errno == errno.EEXIST and parents: + pass + else: + raise PythonException, ("mkdir: %s" % e, 1) + +def rm(args): + """ + Emulate most of the behavior of rm(1). + Only supports the -r (--recursive) and -f (--force) arguments. + """ + try: + opts, args = getopt(args, "rRf", ["force", "recursive"]) + except GetoptError, e: + raise PythonException, ("rm: %s" % e, 1) + force = False + recursive = False + for o, a in opts: + if o in ('-f', '--force'): + force = True + elif o in ('-r', '-R', '--recursive'): + recursive = True + for f in args: + if os.path.isdir(f): + if not recursive: + raise PythonException, ("rm: cannot remove '%s': Is a directory" % f, 1) + else: + shutil.rmtree(f, force) + elif os.path.exists(f): + try: + os.unlink(f) + except: + if not force: + raise PythonException, ("rm: failed to remove '%s': %s" % (f, sys.exc_info()[0]), 1) + elif not force: + raise PythonException, ("rm: cannot remove '%s': No such file or directory" % f, 1) + +def sleep(args): + """ + Emulate the behavior of sleep(1). + """ + total = 0 + values = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400} + for a in args: + multiplier = 1 + for k, v in values.iteritems(): + if a.endswith(k): + a = a[:-1] + multiplier = v + break + try: + f = float(a) + total += f * multiplier + except ValueError: + raise PythonException, ("sleep: invalid time interval '%s'" % a, 1) + time.sleep(total) + +def touch(args): + """ + Emulate the behavior of touch(1). + """ + try: + opts, args = getopt(args, "t:") + except GetoptError, e: + raise PythonException, ("touch: %s" % e, 1) + opts = dict(opts) + times = None + if '-t' in opts: + import re + from time import mktime, localtime + m = re.match('^(?P<Y>(?:\d\d)?\d\d)?(?P<M>\d\d)(?P<D>\d\d)(?P<h>\d\d)(?P<m>\d\d)(?:\.(?P<s>\d\d))?$', opts['-t']) + if not m: + raise PythonException, ("touch: invalid date format '%s'" % opts['-t'], 1) + def normalized_field(m, f): + if f == 'Y': + if m.group(f) is None: + return localtime()[0] + y = int(m.group(f)) + if y < 69: + y += 2000 + elif y < 100: + y += 1900 + return y + if m.group(f) is None: + return localtime()[0] if f == 'Y' else 0 + return int(m.group(f)) + time = [normalized_field(m, f) for f in ['Y', 'M', 'D', 'h', 'm', 's']] + [0, 0, -1] + time = mktime(time) + times = (time, time) + for f in args: + if not os.path.exists(f): + open(f, 'a').close() + os.utime(f, times) diff --git a/build/pymake/pymake/command.py b/build/pymake/pymake/command.py new file mode 100644 index 000000000..cd68e4fdb --- /dev/null +++ b/build/pymake/pymake/command.py @@ -0,0 +1,278 @@ +# 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/. +""" +Makefile execution. + +Multiple `makes` can be run within the same process. Each one has an entirely data.Makefile and .Target +structure, environment, and working directory. Typically they will all share a parallel execution context, +except when a submake specifies -j1 when the parent make is building in parallel. +""" + +import os, subprocess, sys, logging, time, traceback, re +from optparse import OptionParser +import data, parserdata, process, util + +# TODO: If this ever goes from relocatable package to system-installed, this may need to be +# a configured-in path. + +makepypath = util.normaljoin(os.path.dirname(__file__), '../make.py') + +_simpleopts = re.compile(r'^[a-zA-Z]+(\s|$)') +def parsemakeflags(env): + """ + Parse MAKEFLAGS from the environment into a sequence of command-line arguments. + """ + + makeflags = env.get('MAKEFLAGS', '') + makeflags = makeflags.strip() + + if makeflags == '': + return [] + + if _simpleopts.match(makeflags): + makeflags = '-' + makeflags + + opts = [] + curopt = '' + + i = 0 + while i < len(makeflags): + c = makeflags[i] + if c.isspace(): + opts.append(curopt) + curopt = '' + i += 1 + while i < len(makeflags) and makeflags[i].isspace(): + i += 1 + continue + + if c == '\\': + i += 1 + if i == len(makeflags): + raise data.DataError("MAKEFLAGS has trailing backslash") + c = makeflags[i] + + curopt += c + i += 1 + + if curopt != '': + opts.append(curopt) + + return opts + +def _version(*args): + print """pymake: GNU-compatible make program +Copyright (C) 2009 The Mozilla Foundation <http://www.mozilla.org/> +This is free software; see the source for copying conditions. +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.""" + +_log = logging.getLogger('pymake.execution') + +class _MakeContext(object): + def __init__(self, makeflags, makelevel, workdir, context, env, targets, options, ostmts, overrides, cb): + self.makeflags = makeflags + self.makelevel = makelevel + + self.workdir = workdir + self.context = context + self.env = env + self.targets = targets + self.options = options + self.ostmts = ostmts + self.overrides = overrides + self.cb = cb + + self.restarts = 0 + + self.remakecb(True) + + def remakecb(self, remade, error=None): + if error is not None: + print error + self.context.defer(self.cb, 2) + return + + if remade: + if self.restarts > 0: + _log.info("make.py[%i]: Restarting makefile parsing", self.makelevel) + + self.makefile = data.Makefile(restarts=self.restarts, + make='%s %s' % (sys.executable.replace('\\', '/'), makepypath.replace('\\', '/')), + makeflags=self.makeflags, + makeoverrides=self.overrides, + workdir=self.workdir, + context=self.context, + env=self.env, + makelevel=self.makelevel, + targets=self.targets, + keepgoing=self.options.keepgoing, + silent=self.options.silent, + justprint=self.options.justprint) + + self.restarts += 1 + + try: + self.ostmts.execute(self.makefile) + for f in self.options.makefiles: + self.makefile.include(f) + self.makefile.finishparsing() + self.makefile.remakemakefiles(self.remakecb) + except util.MakeError, e: + print e + self.context.defer(self.cb, 2) + + return + + if len(self.targets) == 0: + if self.makefile.defaulttarget is None: + print "No target specified and no default target found." + self.context.defer(self.cb, 2) + return + + _log.info("Making default target %s", self.makefile.defaulttarget) + self.realtargets = [self.makefile.defaulttarget] + self.tstack = ['<default-target>'] + else: + self.realtargets = self.targets + self.tstack = ['<command-line>'] + + self.makefile.gettarget(self.realtargets.pop(0)).make(self.makefile, self.tstack, cb=self.makecb) + + def makecb(self, error, didanything): + assert error in (True, False) + + if error: + self.context.defer(self.cb, 2) + return + + if not len(self.realtargets): + if self.options.printdir: + print "make.py[%i]: Leaving directory '%s'" % (self.makelevel, self.workdir) + sys.stdout.flush() + + self.context.defer(self.cb, 0) + else: + self.makefile.gettarget(self.realtargets.pop(0)).make(self.makefile, self.tstack, self.makecb) + +def main(args, env, cwd, cb): + """ + Start a single makefile execution, given a command line, working directory, and environment. + + @param cb a callback to notify with an exit code when make execution is finished. + """ + + try: + makelevel = int(env.get('MAKELEVEL', '0')) + + op = OptionParser() + op.add_option('-f', '--file', '--makefile', + action='append', + dest='makefiles', + default=[]) + op.add_option('-d', + action="store_true", + dest="verbose", default=False) + op.add_option('-k', '--keep-going', + action="store_true", + dest="keepgoing", default=False) + op.add_option('--debug-log', + dest="debuglog", default=None) + op.add_option('-C', '--directory', + dest="directory", default=None) + op.add_option('-v', '--version', action="store_true", + dest="printversion", default=False) + op.add_option('-j', '--jobs', type="int", + dest="jobcount", default=1) + op.add_option('-w', '--print-directory', action="store_true", + dest="printdir") + op.add_option('--no-print-directory', action="store_false", + dest="printdir", default=True) + op.add_option('-s', '--silent', action="store_true", + dest="silent", default=False) + op.add_option('-n', '--just-print', '--dry-run', '--recon', + action="store_true", + dest="justprint", default=False) + + options, arguments1 = op.parse_args(parsemakeflags(env)) + options, arguments2 = op.parse_args(args, values=options) + + op.destroy() + + arguments = arguments1 + arguments2 + + if options.printversion: + _version() + cb(0) + return + + shortflags = [] + longflags = [] + + if options.keepgoing: + shortflags.append('k') + + if options.printdir: + shortflags.append('w') + + if options.silent: + shortflags.append('s') + options.printdir = False + + if options.justprint: + shortflags.append('n') + + loglevel = logging.WARNING + if options.verbose: + loglevel = logging.DEBUG + shortflags.append('d') + + logkwargs = {} + if options.debuglog: + logkwargs['filename'] = options.debuglog + longflags.append('--debug-log=%s' % options.debuglog) + + if options.directory is None: + workdir = cwd + else: + workdir = util.normaljoin(cwd, options.directory) + + if options.jobcount != 1: + longflags.append('-j%i' % (options.jobcount,)) + + makeflags = ''.join(shortflags) + if len(longflags): + makeflags += ' ' + ' '.join(longflags) + + logging.basicConfig(level=loglevel, **logkwargs) + + context = process.getcontext(options.jobcount) + + if options.printdir: + print "make.py[%i]: Entering directory '%s'" % (makelevel, workdir) + sys.stdout.flush() + + if len(options.makefiles) == 0: + if os.path.exists(util.normaljoin(workdir, 'Makefile')): + options.makefiles.append('Makefile') + else: + print "No makefile found" + cb(2) + return + + ostmts, targets, overrides = parserdata.parsecommandlineargs(arguments) + + _MakeContext(makeflags, makelevel, workdir, context, env, targets, options, ostmts, overrides, cb) + except (util.MakeError), e: + print e + if options.printdir: + print "make.py[%i]: Leaving directory '%s'" % (makelevel, workdir) + sys.stdout.flush() + cb(2) + return diff --git a/build/pymake/pymake/data.py b/build/pymake/pymake/data.py new file mode 100644 index 000000000..dcd7e9225 --- /dev/null +++ b/build/pymake/pymake/data.py @@ -0,0 +1,1842 @@ +""" +A representation of makefile data structures. +""" + +import logging, re, os, sys +import parserdata, parser, functions, process, util, implicit +from cStringIO import StringIO + +if sys.version_info[0] < 3: + str_type = basestring +else: + str_type = str + +_log = logging.getLogger('pymake.data') + +class DataError(util.MakeError): + pass + +class ResolutionError(DataError): + """ + Raised when dependency resolution fails, either due to recursion or to missing + prerequisites.This is separately catchable so that implicit rule search can try things + without having to commit. + """ + pass + +def withoutdups(it): + r = set() + for i in it: + if not i in r: + r.add(i) + yield i + +def mtimeislater(deptime, targettime): + """ + Is the mtime of the dependency later than the target? + """ + + if deptime is None: + return True + if targettime is None: + return False + # int(1000*x) because of http://bugs.python.org/issue10148 + return int(1000 * deptime) > int(1000 * targettime) + +def getmtime(path): + try: + s = os.stat(path) + return s.st_mtime + except OSError: + return None + +def stripdotslash(s): + if s.startswith('./'): + st = s[2:] + return st if st != '' else '.' + return s + +def stripdotslashes(sl): + for s in sl: + yield stripdotslash(s) + +def getindent(stack): + return ''.ljust(len(stack) - 1) + +def _if_else(c, t, f): + if c: + return t() + return f() + + +class BaseExpansion(object): + """Base class for expansions. + + A make expansion is the parsed representation of a string, which may + contain references to other elements. + """ + + @property + def is_static_string(self): + """Returns whether the expansion is composed of static string content. + + This is always True for StringExpansion. It will be True for Expansion + only if all elements of that Expansion are static strings. + """ + raise Exception('Must be implemented in child class.') + + def functions(self, descend=False): + """Obtain all functions inside this expansion. + + This is a generator for pymake.functions.Function instances. + + By default, this only returns functions existing as the primary + elements of this expansion. If `descend` is True, it will descend into + child expansions and extract all functions in the tree. + """ + # An empty generator. Yeah, it's weird. + for x in []: + yield x + + def variable_references(self, descend=False): + """Obtain all variable references in this expansion. + + This is a generator for pymake.functionsVariableRef instances. + + To retrieve the names of variables, simply query the `vname` field on + the returned instances. Most of the time these will be StringExpansion + instances. + """ + for f in self.functions(descend=descend): + if not isinstance(f, functions.VariableRef): + continue + + yield f + + @property + def is_filesystem_dependent(self): + """Whether this expansion may query the filesystem for evaluation. + + This effectively asks "is any function in this expansion dependent on + the filesystem. + """ + for f in self.functions(descend=True): + if f.is_filesystem_dependent: + return True + + return False + + @property + def is_shell_dependent(self): + """Whether this expansion may invoke a shell for evaluation.""" + + for f in self.functions(descend=True): + if isinstance(f, functions.ShellFunction): + return True + + return False + + +class StringExpansion(BaseExpansion): + """An Expansion representing a static string. + + This essentially wraps a single str instance. + """ + + __slots__ = ('loc', 's',) + simple = True + + def __init__(self, s, loc): + assert isinstance(s, str_type) + self.s = s + self.loc = loc + + def lstrip(self): + self.s = self.s.lstrip() + + def rstrip(self): + self.s = self.s.rstrip() + + def isempty(self): + return self.s == '' + + def resolve(self, i, j, fd, k=None): + fd.write(self.s) + + def resolvestr(self, i, j, k=None): + return self.s + + def resolvesplit(self, i, j, k=None): + return self.s.split() + + def clone(self): + e = Expansion(self.loc) + e.appendstr(self.s) + return e + + @property + def is_static_string(self): + return True + + def __len__(self): + return 1 + + def __getitem__(self, i): + assert i == 0 + return self.s, False + + def __repr__(self): + return "Exp<%s>(%r)" % (self.loc, self.s) + + def __eq__(self, other): + """We only compare the string contents.""" + return self.s == other + + def __ne__(self, other): + return not self.__eq__(other) + + def to_source(self, escape_variables=False, escape_comments=False): + s = self.s + + if escape_comments: + s = s.replace('#', '\\#') + + if escape_variables: + return s.replace('$', '$$') + + return s + + +class Expansion(BaseExpansion, list): + """A representation of expanded data. + + This is effectively an ordered list of StringExpansion and + pymake.function.Function instances. Every item in the collection appears in + the same context in a make file. + """ + + __slots__ = ('loc',) + simple = False + + def __init__(self, loc=None): + # A list of (element, isfunc) tuples + # element is either a string or a function + self.loc = loc + + @staticmethod + def fromstring(s, path): + return StringExpansion(s, parserdata.Location(path, 1, 0)) + + def clone(self): + e = Expansion() + e.extend(self) + return e + + def appendstr(self, s): + assert isinstance(s, str_type) + if s == '': + return + + self.append((s, False)) + + def appendfunc(self, func): + assert isinstance(func, functions.Function) + self.append((func, True)) + + def concat(self, o): + """Concatenate the other expansion on to this one.""" + if o.simple: + self.appendstr(o.s) + else: + self.extend(o) + + def isempty(self): + return (not len(self)) or self[0] == ('', False) + + def lstrip(self): + """Strip leading literal whitespace from this expansion.""" + while True: + i, isfunc = self[0] + if isfunc: + return + + i = i.lstrip() + if i != '': + self[0] = i, False + return + + del self[0] + + def rstrip(self): + """Strip trailing literal whitespace from this expansion.""" + while True: + i, isfunc = self[-1] + if isfunc: + return + + i = i.rstrip() + if i != '': + self[-1] = i, False + return + + del self[-1] + + def finish(self): + # Merge any adjacent literal strings: + strings = [] + elements = [] + for (e, isfunc) in self: + if isfunc: + if strings: + s = ''.join(strings) + if s: + elements.append((s, False)) + strings = [] + elements.append((e, True)) + else: + strings.append(e) + + if not elements: + # This can only happen if there were no function elements. + return StringExpansion(''.join(strings), self.loc) + + if strings: + s = ''.join(strings) + if s: + elements.append((s, False)) + + if len(elements) < len(self): + self[:] = elements + + return self + + def resolve(self, makefile, variables, fd, setting=[]): + """ + Resolve this variable into a value, by interpolating the value + of other variables. + + @param setting (Variable instance) the variable currently + being set, if any. Setting variables must avoid self-referential + loops. + """ + assert isinstance(makefile, Makefile) + assert isinstance(variables, Variables) + assert isinstance(setting, list) + + for e, isfunc in self: + if isfunc: + e.resolve(makefile, variables, fd, setting) + else: + assert isinstance(e, str_type) + fd.write(e) + + def resolvestr(self, makefile, variables, setting=[]): + fd = StringIO() + self.resolve(makefile, variables, fd, setting) + return fd.getvalue() + + def resolvesplit(self, makefile, variables, setting=[]): + return self.resolvestr(makefile, variables, setting).split() + + @property + def is_static_string(self): + """An Expansion is static if all its components are strings, not + functions.""" + for e, is_func in self: + if is_func: + return False + + return True + + def functions(self, descend=False): + for e, is_func in self: + if is_func: + yield e + + if descend: + for exp in e.expansions(descend=True): + for f in exp.functions(descend=True): + yield f + + def __repr__(self): + return "<Expansion with elements: %r>" % ([e for e, isfunc in self],) + + def to_source(self, escape_variables=False, escape_comments=False): + parts = [] + for e, is_func in self: + if is_func: + parts.append(e.to_source()) + continue + + if escape_variables: + parts.append(e.replace('$', '$$')) + continue + + parts.append(e) + + return ''.join(parts) + + def __eq__(self, other): + if not isinstance(other, (Expansion, StringExpansion)): + return False + + # Expansions are equivalent if adjacent string literals normalize to + # the same value. So, we must normalize before any comparisons are + # made. + a = self.clone().finish() + + if isinstance(other, StringExpansion): + if isinstance(a, StringExpansion): + return a == other + + # A normalized Expansion != StringExpansion. + return False + + b = other.clone().finish() + + # b could be a StringExpansion now. + if isinstance(b, StringExpansion): + if isinstance(a, StringExpansion): + return a == b + + # Our normalized Expansion != normalized StringExpansion. + return False + + if len(a) != len(b): + return False + + for i in xrange(len(self)): + e1, is_func1 = a[i] + e2, is_func2 = b[i] + + if is_func1 != is_func2: + return False + + if type(e1) != type(e2): + return False + + if e1 != e2: + return False + + return True + + def __ne__(self, other): + return not self.__eq__(other) + +class Variables(object): + """ + A mapping from variable names to variables. Variables have flavor, source, and value. The value is an + expansion object. + """ + + __slots__ = ('parent', '_map') + + FLAVOR_RECURSIVE = 0 + FLAVOR_SIMPLE = 1 + FLAVOR_APPEND = 2 + + SOURCE_OVERRIDE = 0 + SOURCE_COMMANDLINE = 1 + SOURCE_MAKEFILE = 2 + SOURCE_ENVIRONMENT = 3 + SOURCE_AUTOMATIC = 4 + SOURCE_IMPLICIT = 5 + + def __init__(self, parent=None): + self._map = {} # vname -> flavor, source, valuestr, valueexp + self.parent = parent + + def readfromenvironment(self, env): + for k, v in env.iteritems(): + self.set(k, self.FLAVOR_RECURSIVE, self.SOURCE_ENVIRONMENT, v) + + def get(self, name, expand=True): + """ + Get the value of a named variable. Returns a tuple (flavor, source, value) + + If the variable is not present, returns (None, None, None) + + @param expand If true, the value will be returned as an expansion. If false, + it will be returned as an unexpanded string. + """ + flavor, source, valuestr, valueexp = self._map.get(name, (None, None, None, None)) + if flavor is not None: + if expand and flavor != self.FLAVOR_SIMPLE and valueexp is None: + d = parser.Data.fromstring(valuestr, parserdata.Location("Expansion of variables '%s'" % (name,), 1, 0)) + valueexp, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata) + self._map[name] = flavor, source, valuestr, valueexp + + if flavor == self.FLAVOR_APPEND: + if self.parent: + pflavor, psource, pvalue = self.parent.get(name, expand) + else: + pflavor, psource, pvalue = None, None, None + + if pvalue is None: + flavor = self.FLAVOR_RECURSIVE + # fall through + else: + if source > psource: + # TODO: log a warning? + return pflavor, psource, pvalue + + if not expand: + return pflavor, psource, pvalue + ' ' + valuestr + + pvalue = pvalue.clone() + pvalue.appendstr(' ') + pvalue.concat(valueexp) + + return pflavor, psource, pvalue + + if not expand: + return flavor, source, valuestr + + if flavor == self.FLAVOR_RECURSIVE: + val = valueexp + else: + val = Expansion.fromstring(valuestr, "Expansion of variable '%s'" % (name,)) + + return flavor, source, val + + if self.parent is not None: + return self.parent.get(name, expand) + + return (None, None, None) + + def set(self, name, flavor, source, value, force=False): + assert flavor in (self.FLAVOR_RECURSIVE, self.FLAVOR_SIMPLE) + assert source in (self.SOURCE_OVERRIDE, self.SOURCE_COMMANDLINE, self.SOURCE_MAKEFILE, self.SOURCE_ENVIRONMENT, self.SOURCE_AUTOMATIC, self.SOURCE_IMPLICIT) + assert isinstance(value, str_type), "expected str, got %s" % type(value) + + prevflavor, prevsource, prevvalue = self.get(name) + if prevsource is not None and source > prevsource and not force: + # TODO: give a location for this warning + _log.info("not setting variable '%s', set by higher-priority source to value '%s'" % (name, prevvalue)) + return + + self._map[name] = flavor, source, value, None + + def append(self, name, source, value, variables, makefile): + assert source in (self.SOURCE_OVERRIDE, self.SOURCE_MAKEFILE, self.SOURCE_AUTOMATIC) + assert isinstance(value, str_type) + + if name not in self._map: + self._map[name] = self.FLAVOR_APPEND, source, value, None + return + + prevflavor, prevsource, prevvalue, valueexp = self._map[name] + if source > prevsource: + # TODO: log a warning? + return + + if prevflavor == self.FLAVOR_SIMPLE: + d = parser.Data.fromstring(value, parserdata.Location("Expansion of variables '%s'" % (name,), 1, 0)) + valueexp, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata) + + val = valueexp.resolvestr(makefile, variables, [name]) + self._map[name] = prevflavor, prevsource, prevvalue + ' ' + val, None + return + + newvalue = prevvalue + ' ' + value + self._map[name] = prevflavor, prevsource, newvalue, None + + def merge(self, other): + assert isinstance(other, Variables) + for k, flavor, source, value in other: + self.set(k, flavor, source, value) + + def __iter__(self): + for k, (flavor, source, value, valueexp) in self._map.iteritems(): + yield k, flavor, source, value + + def __contains__(self, item): + return item in self._map + +class Pattern(object): + """ + A pattern is a string, possibly with a % substitution character. From the GNU make manual: + + '%' characters in pattern rules can be quoted with precending backslashes ('\'). Backslashes that + would otherwise quote '%' charcters can be quoted with more backslashes. Backslashes that + quote '%' characters or other backslashes are removed from the pattern before it is compared t + file names or has a stem substituted into it. Backslashes that are not in danger of quoting '%' + characters go unmolested. For example, the pattern the\%weird\\%pattern\\ has `the%weird\' preceding + the operative '%' character, and 'pattern\\' following it. The final two backslashes are left alone + because they cannot affect any '%' character. + + This insane behavior probably doesn't matter, but we're compatible just for shits and giggles. + """ + + __slots__ = ('data') + + def __init__(self, s): + r = [] + i = 0 + slen = len(s) + while i < slen: + c = s[i] + if c == '\\': + nc = s[i + 1] + if nc == '%': + r.append('%') + i += 1 + elif nc == '\\': + r.append('\\') + i += 1 + else: + r.append(c) + elif c == '%': + self.data = (''.join(r), s[i+1:]) + return + else: + r.append(c) + i += 1 + + # This is different than (s,) because \% and \\ have been unescaped. Parsing patterns is + # context-sensitive! + self.data = (''.join(r),) + + def ismatchany(self): + return self.data == ('','') + + def ispattern(self): + return len(self.data) == 2 + + def __hash__(self): + return self.data.__hash__() + + def __eq__(self, o): + assert isinstance(o, Pattern) + return self.data == o.data + + def gettarget(self): + assert not self.ispattern() + return self.data[0] + + def hasslash(self): + return self.data[0].find('/') != -1 or self.data[1].find('/') != -1 + + def match(self, word): + """ + Match this search pattern against a word (string). + + @returns None if the word doesn't match, or the matching stem. + If this is a %-less pattern, the stem will always be '' + """ + d = self.data + if len(d) == 1: + if word == d[0]: + return word + return None + + d0, d1 = d + l1 = len(d0) + l2 = len(d1) + if len(word) >= l1 + l2 and word.startswith(d0) and word.endswith(d1): + if l2 == 0: + return word[l1:] + return word[l1:-l2] + + return None + + def resolve(self, dir, stem): + if self.ispattern(): + return dir + self.data[0] + stem + self.data[1] + + return self.data[0] + + def subst(self, replacement, word, mustmatch): + """ + Given a word, replace the current pattern with the replacement pattern, a la 'patsubst' + + @param mustmatch If true and this pattern doesn't match the word, throw a DataError. Otherwise + return word unchanged. + """ + assert isinstance(replacement, str_type) + + stem = self.match(word) + if stem is None: + if mustmatch: + raise DataError("target '%s' doesn't match pattern" % (word,)) + return word + + if not self.ispattern(): + # if we're not a pattern, the replacement is not parsed as a pattern either + return replacement + + return Pattern(replacement).resolve('', stem) + + def __repr__(self): + return "<Pattern with data %r>" % (self.data,) + + _backre = re.compile(r'[%\\]') + def __str__(self): + if not self.ispattern(): + return self._backre.sub(r'\\\1', self.data[0]) + + return self._backre.sub(r'\\\1', self.data[0]) + '%' + self.data[1] + +class RemakeTargetSerially(object): + __slots__ = ('target', 'makefile', 'indent', 'rlist') + + def __init__(self, target, makefile, indent, rlist): + self.target = target + self.makefile = makefile + self.indent = indent + self.rlist = rlist + self.commandscb(False) + + def resolvecb(self, error, didanything): + assert error in (True, False) + + if didanything: + self.target.didanything = True + + if error: + self.target.error = True + self.makefile.error = True + if not self.makefile.keepgoing: + self.target.notifydone(self.makefile) + return + else: + # don't run the commands! + del self.rlist[0] + self.commandscb(error=False) + else: + self.rlist.pop(0).runcommands(self.indent, self.commandscb) + + def commandscb(self, error): + assert error in (True, False) + + if error: + self.target.error = True + self.makefile.error = True + + if self.target.error and not self.makefile.keepgoing: + self.target.notifydone(self.makefile) + return + + if not len(self.rlist): + self.target.notifydone(self.makefile) + else: + self.rlist[0].resolvedeps(True, self.resolvecb) + +class RemakeTargetParallel(object): + __slots__ = ('target', 'makefile', 'indent', 'rlist', 'rulesremaining', 'currunning') + + def __init__(self, target, makefile, indent, rlist): + self.target = target + self.makefile = makefile + self.indent = indent + self.rlist = rlist + + self.rulesremaining = len(rlist) + self.currunning = False + + for r in rlist: + makefile.context.defer(self.doresolve, r) + + def doresolve(self, r): + if self.makefile.error and not self.makefile.keepgoing: + r.error = True + self.resolvecb(True, False) + else: + r.resolvedeps(False, self.resolvecb) + + def resolvecb(self, error, didanything): + assert error in (True, False) + + if error: + self.target.error = True + + if didanything: + self.target.didanything = True + + self.rulesremaining -= 1 + + # commandscb takes care of the details if we're currently building + # something + if self.currunning: + return + + self.runnext() + + def runnext(self): + assert not self.currunning + + if self.makefile.error and not self.makefile.keepgoing: + self.rlist = [] + else: + while len(self.rlist) and self.rlist[0].error: + del self.rlist[0] + + if not len(self.rlist): + if not self.rulesremaining: + self.target.notifydone(self.makefile) + return + + if self.rlist[0].depsremaining != 0: + return + + self.currunning = True + rule = self.rlist.pop(0) + self.makefile.context.defer(rule.runcommands, self.indent, self.commandscb) + + def commandscb(self, error): + assert error in (True, False) + if error: + self.target.error = True + self.makefile.error = True + + assert self.currunning + self.currunning = False + self.runnext() + +class RemakeRuleContext(object): + def __init__(self, target, makefile, rule, deps, + targetstack, avoidremakeloop): + self.target = target + self.makefile = makefile + self.rule = rule + self.deps = deps + self.targetstack = targetstack + self.avoidremakeloop = avoidremakeloop + + self.running = False + self.error = False + self.depsremaining = len(deps) + 1 + self.remake = False + + def resolvedeps(self, serial, cb): + self.resolvecb = cb + self.didanything = False + if serial: + self._resolvedepsserial() + else: + self._resolvedepsparallel() + + def _weakdepfinishedserial(self, error, didanything): + if error: + self.remake = True + self._depfinishedserial(False, didanything) + + def _depfinishedserial(self, error, didanything): + assert error in (True, False) + + if didanything: + self.didanything = True + + if error: + self.error = True + if not self.makefile.keepgoing: + self.resolvecb(error=True, didanything=self.didanything) + return + + if len(self.resolvelist): + dep, weak = self.resolvelist.pop(0) + self.makefile.context.defer(dep.make, + self.makefile, self.targetstack, weak and self._weakdepfinishedserial or self._depfinishedserial) + else: + self.resolvecb(error=self.error, didanything=self.didanything) + + def _resolvedepsserial(self): + self.resolvelist = list(self.deps) + self._depfinishedserial(False, False) + + def _startdepparallel(self, d): + dep, weak = d + if weak: + depfinished = self._weakdepfinishedparallel + else: + depfinished = self._depfinishedparallel + if self.makefile.error: + depfinished(True, False) + else: + dep.make(self.makefile, self.targetstack, depfinished) + + def _weakdepfinishedparallel(self, error, didanything): + if error: + self.remake = True + self._depfinishedparallel(False, didanything) + + def _depfinishedparallel(self, error, didanything): + assert error in (True, False) + + if error: + print "<%s>: Found error" % self.target.target + self.error = True + if didanything: + self.didanything = True + + self.depsremaining -= 1 + if self.depsremaining == 0: + self.resolvecb(error=self.error, didanything=self.didanything) + + def _resolvedepsparallel(self): + self.depsremaining -= 1 + if self.depsremaining == 0: + self.resolvecb(error=self.error, didanything=self.didanything) + return + + self.didanything = False + + for d in self.deps: + self.makefile.context.defer(self._startdepparallel, d) + + def _commandcb(self, error): + assert error in (True, False) + + if error: + self.runcb(error=True) + return + + if len(self.commands): + self.commands.pop(0)(self._commandcb) + else: + self.runcb(error=False) + + def runcommands(self, indent, cb): + assert not self.running + self.running = True + + self.runcb = cb + + if self.rule is None or not len(self.rule.commands): + if self.target.mtime is None: + self.target.beingremade() + else: + for d, weak in self.deps: + if mtimeislater(d.mtime, self.target.mtime): + if d.mtime is None: + self.target.beingremade() + else: + _log.info("%sNot remaking %s ubecause it would have no effect, even though %s is newer.", indent, self.target.target, d.target) + break + cb(error=False) + return + + if self.rule.doublecolon: + if len(self.deps) == 0: + if self.avoidremakeloop: + _log.info("%sNot remaking %s using rule at %s because it would introduce an infinite loop.", indent, self.target.target, self.rule.loc) + cb(error=False) + return + + remake = self.remake + if remake: + _log.info("%sRemaking %s using rule at %s: weak dependency was not found.", indent, self.target.target, self.rule.loc) + else: + if self.target.mtime is None: + remake = True + _log.info("%sRemaking %s using rule at %s: target doesn't exist or is a forced target", indent, self.target.target, self.rule.loc) + + if not remake: + if self.rule.doublecolon: + if len(self.deps) == 0: + _log.info("%sRemaking %s using rule at %s because there are no prerequisites listed for a double-colon rule.", indent, self.target.target, self.rule.loc) + remake = True + + if not remake: + for d, weak in self.deps: + if mtimeislater(d.mtime, self.target.mtime): + _log.info("%sRemaking %s using rule at %s because %s is newer.", indent, self.target.target, self.rule.loc, d.target) + remake = True + break + + if remake: + self.target.beingremade() + self.target.didanything = True + try: + self.commands = [c for c in self.rule.getcommands(self.target, self.makefile)] + except util.MakeError, e: + print e + sys.stdout.flush() + cb(error=True) + return + + self._commandcb(False) + else: + cb(error=False) + +MAKESTATE_NONE = 0 +MAKESTATE_FINISHED = 1 +MAKESTATE_WORKING = 2 + +class Target(object): + """ + An actual (non-pattern) target. + + It holds target-specific variables and a list of rules. It may also point to a parent + PatternTarget, if this target is being created by an implicit rule. + + The rules associated with this target may be Rule instances or, in the case of static pattern + rules, PatternRule instances. + """ + + wasremade = False + + def __init__(self, target, makefile): + assert isinstance(target, str_type) + self.target = target + self.vpathtarget = None + self.rules = [] + self.variables = Variables(makefile.variables) + self.explicit = False + self._state = MAKESTATE_NONE + + def addrule(self, rule): + assert isinstance(rule, (Rule, PatternRuleInstance)) + if len(self.rules) and rule.doublecolon != self.rules[0].doublecolon: + raise DataError("Cannot have single- and double-colon rules for the same target. Prior rule location: %s" % self.rules[0].loc, rule.loc) + + if isinstance(rule, PatternRuleInstance): + if len(rule.prule.targetpatterns) != 1: + raise DataError("Static pattern rules must only have one target pattern", rule.prule.loc) + if rule.prule.targetpatterns[0].match(self.target) is None: + raise DataError("Static pattern rule doesn't match target '%s'" % self.target, rule.loc) + + self.rules.append(rule) + + def isdoublecolon(self): + return self.rules[0].doublecolon + + def isphony(self, makefile): + """Is this a phony target? We don't check for existence of phony targets.""" + return makefile.gettarget('.PHONY').hasdependency(self.target) + + def hasdependency(self, t): + for rule in self.rules: + if t in rule.prerequisites: + return True + + return False + + def resolveimplicitrule(self, makefile, targetstack, rulestack): + """ + Try to resolve an implicit rule to build this target. + """ + # The steps in the GNU make manual Implicit-Rule-Search.html are very detailed. I hope they can be trusted. + + indent = getindent(targetstack) + + _log.info("%sSearching for implicit rule to make '%s'", indent, self.target) + + dir, s, file = util.strrpartition(self.target, '/') + dir = dir + s + + candidates = [] # list of PatternRuleInstance + + hasmatch = util.any((r.hasspecificmatch(file) for r in makefile.implicitrules)) + + for r in makefile.implicitrules: + if r in rulestack: + _log.info("%s %s: Avoiding implicit rule recursion", indent, r.loc) + continue + + if not len(r.commands): + continue + + for ri in r.matchesfor(dir, file, hasmatch): + candidates.append(ri) + + newcandidates = [] + + for r in candidates: + depfailed = None + for p in r.prerequisites: + t = makefile.gettarget(p) + t.resolvevpath(makefile) + if not t.explicit and t.mtime is None: + depfailed = p + break + + if depfailed is not None: + if r.doublecolon: + _log.info("%s Terminal rule at %s doesn't match: prerequisite '%s' not mentioned and doesn't exist.", indent, r.loc, depfailed) + else: + newcandidates.append(r) + continue + + _log.info("%sFound implicit rule at %s for target '%s'", indent, r.loc, self.target) + self.rules.append(r) + return + + # Try again, but this time with chaining and without terminal (double-colon) rules + + for r in newcandidates: + newrulestack = rulestack + [r.prule] + + depfailed = None + for p in r.prerequisites: + t = makefile.gettarget(p) + try: + t.resolvedeps(makefile, targetstack, newrulestack, True) + except ResolutionError: + depfailed = p + break + + if depfailed is not None: + _log.info("%s Rule at %s doesn't match: prerequisite '%s' could not be made.", indent, r.loc, depfailed) + continue + + _log.info("%sFound implicit rule at %s for target '%s'", indent, r.loc, self.target) + self.rules.append(r) + return + + _log.info("%sCouldn't find implicit rule to remake '%s'", indent, self.target) + + def ruleswithcommands(self): + "The number of rules with commands" + return reduce(lambda i, rule: i + (len(rule.commands) > 0), self.rules, 0) + + def resolvedeps(self, makefile, targetstack, rulestack, recursive): + """ + Resolve the actual path of this target, using vpath if necessary. + + Recursively resolve dependencies of this target. This means finding implicit + rules which match the target, if appropriate. + + Figure out whether this target needs to be rebuild, and set self.outofdate + appropriately. + + @param targetstack is the current stack of dependencies being resolved. If + this target is already in targetstack, bail to prevent infinite + recursion. + @param rulestack is the current stack of implicit rules being used to resolve + dependencies. A rule chain cannot use the same implicit rule twice. + """ + assert makefile.parsingfinished + + if self.target in targetstack: + raise ResolutionError("Recursive dependency: %s -> %s" % ( + " -> ".join(targetstack), self.target)) + + targetstack = targetstack + [self.target] + + indent = getindent(targetstack) + + _log.info("%sConsidering target '%s'", indent, self.target) + + self.resolvevpath(makefile) + + # Sanity-check our rules. If we're single-colon, only one rule should have commands + ruleswithcommands = self.ruleswithcommands() + if len(self.rules) and not self.isdoublecolon(): + if ruleswithcommands > 1: + # In GNU make this is a warning, not an error. I'm going to be stricter. + # TODO: provide locations + raise DataError("Target '%s' has multiple rules with commands." % self.target) + + if ruleswithcommands == 0: + self.resolveimplicitrule(makefile, targetstack, rulestack) + + # If a target is mentioned, but doesn't exist, has no commands and no + # prerequisites, it is special and exists just to say that targets which + # depend on it are always out of date. This is like .FORCE but more + # compatible with other makes. + # Otherwise, we don't know how to make it. + if not len(self.rules) and self.mtime is None and not util.any((len(rule.prerequisites) > 0 + for rule in self.rules)): + raise ResolutionError("No rule to make target '%s' needed by %r" % (self.target, + targetstack)) + + if recursive: + for r in self.rules: + newrulestack = rulestack + [r] + for d in r.prerequisites: + dt = makefile.gettarget(d) + if dt.explicit: + continue + + dt.resolvedeps(makefile, targetstack, newrulestack, True) + + for v in makefile.getpatternvariablesfor(self.target): + self.variables.merge(v) + + def resolvevpath(self, makefile): + if self.vpathtarget is not None: + return + + if self.isphony(makefile): + self.vpathtarget = self.target + self.mtime = None + return + + if self.target.startswith('-l'): + stem = self.target[2:] + f, s, e = makefile.variables.get('.LIBPATTERNS') + if e is not None: + libpatterns = [Pattern(stripdotslash(s)) for s in e.resolvesplit(makefile, makefile.variables)] + if len(libpatterns): + searchdirs = [''] + searchdirs.extend(makefile.getvpath(self.target)) + + for lp in libpatterns: + if not lp.ispattern(): + raise DataError('.LIBPATTERNS contains a non-pattern') + + libname = lp.resolve('', stem) + + for dir in searchdirs: + libpath = util.normaljoin(dir, libname).replace('\\', '/') + fspath = util.normaljoin(makefile.workdir, libpath) + mtime = getmtime(fspath) + if mtime is not None: + self.vpathtarget = libpath + self.mtime = mtime + return + + self.vpathtarget = self.target + self.mtime = None + return + + search = [self.target] + if not os.path.isabs(self.target): + search += [util.normaljoin(dir, self.target).replace('\\', '/') + for dir in makefile.getvpath(self.target)] + + targetandtime = self.searchinlocs(makefile, search) + if targetandtime is not None: + (self.vpathtarget, self.mtime) = targetandtime + return + + self.vpathtarget = self.target + self.mtime = None + + def searchinlocs(self, makefile, locs): + """ + Look in the given locations relative to the makefile working directory + for a file. Return a pair of the target and the mtime if found, None + if not. + """ + for t in locs: + fspath = util.normaljoin(makefile.workdir, t).replace('\\', '/') + mtime = getmtime(fspath) +# _log.info("Searching %s ... checking %s ... mtime %r" % (t, fspath, mtime)) + if mtime is not None: + return (t, mtime) + + return None + + def beingremade(self): + """ + When we remake ourself, we have to drop any vpath prefixes. + """ + self.vpathtarget = self.target + self.wasremade = True + + def notifydone(self, makefile): + assert self._state == MAKESTATE_WORKING, "State was %s" % self._state + # If we were remade then resolve mtime again + if self.wasremade: + targetandtime = self.searchinlocs(makefile, [self.target]) + if targetandtime is not None: + (_, self.mtime) = targetandtime + else: + self.mtime = None + + self._state = MAKESTATE_FINISHED + for cb in self._callbacks: + makefile.context.defer(cb, error=self.error, didanything=self.didanything) + del self._callbacks + + def make(self, makefile, targetstack, cb, avoidremakeloop=False, printerror=True): + """ + If we are out of date, asynchronously make ourself. This is a multi-stage process, mostly handled + by the helper objects RemakeTargetSerially, RemakeTargetParallel, + RemakeRuleContext. These helper objects should keep us from developing + any cyclical dependencies. + + * resolve dependencies (synchronous) + * gather a list of rules to execute and related dependencies (synchronous) + * for each rule (in parallel) + ** remake dependencies (asynchronous) + ** build list of commands to execute (synchronous) + ** execute each command (asynchronous) + * asynchronously notify when all rules are complete + + @param cb A callback function to notify when remaking is finished. It is called + thusly: callback(error=True/False, didanything=True/False) + If there is no asynchronous activity to perform, the callback may be called directly. + """ + + serial = makefile.context.jcount == 1 + + if self._state == MAKESTATE_FINISHED: + cb(error=self.error, didanything=self.didanything) + return + + if self._state == MAKESTATE_WORKING: + assert not serial + self._callbacks.append(cb) + return + + assert self._state == MAKESTATE_NONE + + self._state = MAKESTATE_WORKING + self._callbacks = [cb] + self.error = False + self.didanything = False + + indent = getindent(targetstack) + + try: + self.resolvedeps(makefile, targetstack, [], False) + except util.MakeError, e: + if printerror: + print e + self.error = True + self.notifydone(makefile) + return + + assert self.vpathtarget is not None, "Target was never resolved!" + if not len(self.rules): + self.notifydone(makefile) + return + + if self.isdoublecolon(): + rulelist = [RemakeRuleContext(self, makefile, r, [(makefile.gettarget(p), False) for p in r.prerequisites], targetstack, avoidremakeloop) for r in self.rules] + else: + alldeps = [] + + commandrule = None + for r in self.rules: + rdeps = [(makefile.gettarget(p), r.weakdeps) for p in r.prerequisites] + if len(r.commands): + assert commandrule is None + commandrule = r + # The dependencies of the command rule are resolved before other dependencies, + # no matter the ordering of the other no-command rules + alldeps[0:0] = rdeps + else: + alldeps.extend(rdeps) + + rulelist = [RemakeRuleContext(self, makefile, commandrule, alldeps, targetstack, avoidremakeloop)] + + targetstack = targetstack + [self.target] + + if serial: + RemakeTargetSerially(self, makefile, indent, rulelist) + else: + RemakeTargetParallel(self, makefile, indent, rulelist) + +def dirpart(p): + d, s, f = util.strrpartition(p, '/') + if d == '': + return '.' + + return d + +def filepart(p): + d, s, f = util.strrpartition(p, '/') + return f + +def setautomatic(v, name, plist): + v.set(name, Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join(plist)) + v.set(name + 'D', Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join((dirpart(p) for p in plist))) + v.set(name + 'F', Variables.FLAVOR_SIMPLE, Variables.SOURCE_AUTOMATIC, ' '.join((filepart(p) for p in plist))) + +def setautomaticvariables(v, makefile, target, prerequisites): + prtargets = [makefile.gettarget(p) for p in prerequisites] + prall = [pt.vpathtarget for pt in prtargets] + proutofdate = [pt.vpathtarget for pt in withoutdups(prtargets) + if target.mtime is None or mtimeislater(pt.mtime, target.mtime)] + + setautomatic(v, '@', [target.vpathtarget]) + if len(prall): + setautomatic(v, '<', [prall[0]]) + + setautomatic(v, '?', proutofdate) + setautomatic(v, '^', list(withoutdups(prall))) + setautomatic(v, '+', prall) + +def splitcommand(command): + """ + Using the esoteric rules, split command lines by unescaped newlines. + """ + start = 0 + i = 0 + while i < len(command): + c = command[i] + if c == '\\': + i += 1 + elif c == '\n': + yield command[start:i] + i += 1 + start = i + continue + + i += 1 + + if i > start: + yield command[start:i] + +def findmodifiers(command): + """ + Find any of +-@% prefixed on the command. + @returns (command, isHidden, isRecursive, ignoreErrors, isNative) + """ + + isHidden = False + isRecursive = False + ignoreErrors = False + isNative = False + + realcommand = command.lstrip(' \t\n@+-%') + modset = set(command[:-len(realcommand)]) + return realcommand, '@' in modset, '+' in modset, '-' in modset, '%' in modset + +class _CommandWrapper(object): + def __init__(self, cline, ignoreErrors, loc, context, **kwargs): + self.ignoreErrors = ignoreErrors + self.loc = loc + self.cline = cline + self.kwargs = kwargs + self.context = context + + def _cb(self, res): + if res != 0 and not self.ignoreErrors: + print "%s: command '%s' failed, return code %i" % (self.loc, self.cline, res) + self.usercb(error=True) + else: + self.usercb(error=False) + + def __call__(self, cb): + self.usercb = cb + process.call(self.cline, loc=self.loc, cb=self._cb, context=self.context, **self.kwargs) + +class _NativeWrapper(_CommandWrapper): + def __init__(self, cline, ignoreErrors, loc, context, + pycommandpath, **kwargs): + _CommandWrapper.__init__(self, cline, ignoreErrors, loc, context, + **kwargs) + if pycommandpath: + self.pycommandpath = re.split('[%s\s]+' % os.pathsep, + pycommandpath) + else: + self.pycommandpath = None + + def __call__(self, cb): + # get the module and method to call + parts, badchar = process.clinetoargv(self.cline, self.kwargs['cwd']) + if parts is None: + raise DataError("native command '%s': shell metacharacter '%s' in command line" % (self.cline, badchar), self.loc) + if len(parts) < 2: + raise DataError("native command '%s': no method name specified" % self.cline, self.loc) + module = parts[0] + method = parts[1] + cline_list = parts[2:] + self.usercb = cb + process.call_native(module, method, cline_list, + loc=self.loc, cb=self._cb, context=self.context, + pycommandpath=self.pycommandpath, **self.kwargs) + +def getcommandsforrule(rule, target, makefile, prerequisites, stem): + v = Variables(parent=target.variables) + setautomaticvariables(v, makefile, target, prerequisites) + if stem is not None: + setautomatic(v, '*', [stem]) + + env = makefile.getsubenvironment(v) + + for c in rule.commands: + cstring = c.resolvestr(makefile, v) + for cline in splitcommand(cstring): + cline, isHidden, isRecursive, ignoreErrors, isNative = findmodifiers(cline) + if (isHidden or makefile.silent) and not makefile.justprint: + echo = None + else: + echo = "%s$ %s" % (c.loc, cline) + if not isNative: + yield _CommandWrapper(cline, ignoreErrors=ignoreErrors, env=env, cwd=makefile.workdir, loc=c.loc, context=makefile.context, + echo=echo, justprint=makefile.justprint) + else: + f, s, e = v.get("PYCOMMANDPATH", True) + if e: + e = e.resolvestr(makefile, v, ["PYCOMMANDPATH"]) + yield _NativeWrapper(cline, ignoreErrors=ignoreErrors, + env=env, cwd=makefile.workdir, + loc=c.loc, context=makefile.context, + echo=echo, justprint=makefile.justprint, + pycommandpath=e) + +class Rule(object): + """ + A rule contains a list of prerequisites and a list of commands. It may also + contain rule-specific variables. This rule may be associated with multiple targets. + """ + + def __init__(self, prereqs, doublecolon, loc, weakdeps): + self.prerequisites = prereqs + self.doublecolon = doublecolon + self.commands = [] + self.loc = loc + self.weakdeps = weakdeps + + def addcommand(self, c): + assert isinstance(c, (Expansion, StringExpansion)) + self.commands.append(c) + + def getcommands(self, target, makefile): + assert isinstance(target, Target) + # Prerequisites are merged if the target contains multiple rules and is + # not a terminal (double colon) rule. See + # https://www.gnu.org/software/make/manual/make.html#Multiple-Targets. + prereqs = [] + prereqs.extend(self.prerequisites) + + if not self.doublecolon: + for rule in target.rules: + # The current rule comes first, which is already in prereqs so + # we don't need to add it again. + if rule != self: + prereqs.extend(rule.prerequisites) + + return getcommandsforrule(self, target, makefile, prereqs, stem=None) + # TODO: $* in non-pattern rules? + +class PatternRuleInstance(object): + weakdeps = False + + """ + A pattern rule instantiated for a particular target. It has the same API as Rule, but + different internals, forwarding most information on to the PatternRule. + """ + def __init__(self, prule, dir, stem, ismatchany): + assert isinstance(prule, PatternRule) + + self.dir = dir + self.stem = stem + self.prule = prule + self.prerequisites = prule.prerequisitesforstem(dir, stem) + self.doublecolon = prule.doublecolon + self.loc = prule.loc + self.ismatchany = ismatchany + self.commands = prule.commands + + def getcommands(self, target, makefile): + assert isinstance(target, Target) + return getcommandsforrule(self, target, makefile, self.prerequisites, stem=self.dir + self.stem) + + def __str__(self): + return "Pattern rule at %s with stem '%s', matchany: %s doublecolon: %s" % (self.loc, + self.dir + self.stem, + self.ismatchany, + self.doublecolon) + +class PatternRule(object): + """ + An implicit rule or static pattern rule containing target patterns, prerequisite patterns, + and a list of commands. + """ + + def __init__(self, targetpatterns, prerequisites, doublecolon, loc): + self.targetpatterns = targetpatterns + self.prerequisites = prerequisites + self.doublecolon = doublecolon + self.loc = loc + self.commands = [] + + def addcommand(self, c): + assert isinstance(c, (Expansion, StringExpansion)) + self.commands.append(c) + + def ismatchany(self): + return util.any((t.ismatchany() for t in self.targetpatterns)) + + def hasspecificmatch(self, file): + for p in self.targetpatterns: + if not p.ismatchany() and p.match(file) is not None: + return True + + return False + + def matchesfor(self, dir, file, skipsinglecolonmatchany): + """ + Determine all the target patterns of this rule that might match target t. + @yields a PatternRuleInstance for each. + """ + + for p in self.targetpatterns: + matchany = p.ismatchany() + if matchany: + if skipsinglecolonmatchany and not self.doublecolon: + continue + + yield PatternRuleInstance(self, dir, file, True) + else: + stem = p.match(dir + file) + if stem is not None: + yield PatternRuleInstance(self, '', stem, False) + else: + stem = p.match(file) + if stem is not None: + yield PatternRuleInstance(self, dir, stem, False) + + def prerequisitesforstem(self, dir, stem): + return [p.resolve(dir, stem) for p in self.prerequisites] + +class _RemakeContext(object): + def __init__(self, makefile, cb): + self.makefile = makefile + self.included = [(makefile.gettarget(f), required) + for f, required in makefile.included] + self.toremake = list(self.included) + self.cb = cb + + self.remakecb(error=False, didanything=False) + + def remakecb(self, error, didanything): + assert error in (True, False) + + if error and self.required: + print "Error remaking makefiles (ignored)" + + if len(self.toremake): + target, self.required = self.toremake.pop(0) + target.make(self.makefile, [], avoidremakeloop=True, cb=self.remakecb, printerror=False) + else: + for t, required in self.included: + if t.wasremade: + _log.info("Included file %s was remade, restarting make", t.target) + self.cb(remade=True) + return + elif required and t.mtime is None: + self.cb(remade=False, error=DataError("No rule to remake missing include file %s" % t.target)) + return + + self.cb(remade=False) + +class Makefile(object): + """ + The top-level data structure for makefile execution. It holds Targets, implicit rules, and other + state data. + """ + + def __init__(self, workdir=None, env=None, restarts=0, make=None, + makeflags='', makeoverrides='', + makelevel=0, context=None, targets=(), keepgoing=False, + silent=False, justprint=False): + self.defaulttarget = None + + if env is None: + env = os.environ + self.env = env + + self.variables = Variables() + self.variables.readfromenvironment(env) + + self.context = context + self.exportedvars = {} + self._targets = {} + self.keepgoing = keepgoing + self.silent = silent + self.justprint = justprint + self._patternvariables = [] # of (pattern, variables) + self.implicitrules = [] + self.parsingfinished = False + + self._patternvpaths = [] # of (pattern, [dir, ...]) + + if workdir is None: + workdir = os.getcwd() + workdir = os.path.realpath(workdir) + self.workdir = workdir + self.variables.set('CURDIR', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, workdir.replace('\\','/')) + + # the list of included makefiles, whether or not they existed + self.included = [] + + self.variables.set('MAKE_RESTARTS', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, restarts > 0 and str(restarts) or '') + + self.variables.set('.PYMAKE', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_MAKEFILE, "1") + if make is not None: + self.variables.set('MAKE', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_MAKEFILE, make) + + if makeoverrides != '': + self.variables.set('-*-command-variables-*-', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, makeoverrides) + makeflags += ' -- $(MAKEOVERRIDES)' + + self.variables.set('MAKEOVERRIDES', Variables.FLAVOR_RECURSIVE, + Variables.SOURCE_ENVIRONMENT, + '${-*-command-variables-*-}') + + self.variables.set('MAKEFLAGS', Variables.FLAVOR_RECURSIVE, + Variables.SOURCE_MAKEFILE, makeflags) + self.exportedvars['MAKEFLAGS'] = True + + self.makelevel = makelevel + self.variables.set('MAKELEVEL', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_MAKEFILE, str(makelevel)) + + self.variables.set('MAKECMDGOALS', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, ' '.join(targets)) + + for vname, val in implicit.variables.iteritems(): + self.variables.set(vname, + Variables.FLAVOR_SIMPLE, + Variables.SOURCE_IMPLICIT, val) + + def foundtarget(self, t): + """ + Inform the makefile of a target which is a candidate for being the default target, + if there isn't already a default target. + """ + flavor, source, value = self.variables.get('.DEFAULT_GOAL') + if self.defaulttarget is None and t != '.PHONY' and value is None: + self.defaulttarget = t + self.variables.set('.DEFAULT_GOAL', Variables.FLAVOR_SIMPLE, + Variables.SOURCE_AUTOMATIC, t) + + def getpatternvariables(self, pattern): + assert isinstance(pattern, Pattern) + + for p, v in self._patternvariables: + if p == pattern: + return v + + v = Variables() + self._patternvariables.append( (pattern, v) ) + return v + + def getpatternvariablesfor(self, target): + for p, v in self._patternvariables: + if p.match(target): + yield v + + def hastarget(self, target): + return target in self._targets + + _globcheck = re.compile('[[*?]') + def gettarget(self, target): + assert isinstance(target, str_type) + + target = target.rstrip('/') + + assert target != '', "empty target?" + + assert not self._globcheck.match(target) + + t = self._targets.get(target, None) + if t is None: + t = Target(target, self) + self._targets[target] = t + return t + + def appendimplicitrule(self, rule): + assert isinstance(rule, PatternRule) + self.implicitrules.append(rule) + + def finishparsing(self): + """ + Various activities, such as "eval", are not allowed after parsing is + finished. In addition, various warnings and errors can only be issued + after the parsing data model is complete. All dependency resolution + and rule execution requires that parsing be finished. + """ + self.parsingfinished = True + + flavor, source, value = self.variables.get('GPATH') + if value is not None and value.resolvestr(self, self.variables, ['GPATH']).strip() != '': + raise DataError('GPATH was set: pymake does not support GPATH semantics') + + flavor, source, value = self.variables.get('VPATH') + if value is None: + self._vpath = [] + else: + self._vpath = filter(lambda e: e != '', + re.split('[%s\s]+' % os.pathsep, + value.resolvestr(self, self.variables, ['VPATH']))) + + targets = list(self._targets.itervalues()) + for t in targets: + t.explicit = True + for r in t.rules: + for p in r.prerequisites: + self.gettarget(p).explicit = True + + np = self.gettarget('.NOTPARALLEL') + if len(np.rules): + self.context = process.getcontext(1) + + flavor, source, value = self.variables.get('.DEFAULT_GOAL') + if value is not None: + self.defaulttarget = value.resolvestr(self, self.variables, ['.DEFAULT_GOAL']).strip() + + self.error = False + + def include(self, path, required=True, weak=False, loc=None): + """ + Include the makefile at `path`. + """ + self.included.append((path, required)) + fspath = util.normaljoin(self.workdir, path) + if os.path.exists(fspath): + if weak: + stmts = parser.parsedepfile(fspath) + else: + stmts = parser.parsefile(fspath) + self.variables.append('MAKEFILE_LIST', Variables.SOURCE_AUTOMATIC, path, None, self) + stmts.execute(self, weak=weak) + self.gettarget(path).explicit = True + + def addvpath(self, pattern, dirs): + """ + Add a directory to the vpath search for the given pattern. + """ + self._patternvpaths.append((pattern, dirs)) + + def clearvpath(self, pattern): + """ + Clear vpaths for the given pattern. + """ + self._patternvpaths = [(p, dirs) + for p, dirs in self._patternvpaths + if not p.match(pattern)] + + def clearallvpaths(self): + self._patternvpaths = [] + + def getvpath(self, target): + vp = list(self._vpath) + for p, dirs in self._patternvpaths: + if p.match(target): + vp.extend(dirs) + + return withoutdups(vp) + + def remakemakefiles(self, cb): + mlist = [] + for f, required in self.included: + t = self.gettarget(f) + t.explicit = True + t.resolvevpath(self) + oldmtime = t.mtime + + mlist.append((t, oldmtime)) + + _RemakeContext(self, cb) + + def getsubenvironment(self, variables): + env = dict(self.env) + for vname, v in self.exportedvars.iteritems(): + if v: + flavor, source, val = variables.get(vname) + if val is None: + strval = '' + else: + strval = val.resolvestr(self, variables, [vname]) + env[vname] = strval + else: + env.pop(vname, None) + + makeflags = '' + + env['MAKELEVEL'] = str(self.makelevel + 1) + return env diff --git a/build/pymake/pymake/functions.py b/build/pymake/pymake/functions.py new file mode 100644 index 000000000..e53fb5472 --- /dev/null +++ b/build/pymake/pymake/functions.py @@ -0,0 +1,873 @@ +""" +Makefile functions. +""" + +import parser, util +import subprocess, os, logging, sys +from globrelative import glob +from cStringIO import StringIO + +log = logging.getLogger('pymake.data') + +def emit_expansions(descend, *expansions): + """Helper function to emit all expansions within an input set.""" + for expansion in expansions: + yield expansion + + if not descend or not isinstance(expansion, list): + continue + + for e, is_func in expansion: + if is_func: + for exp in e.expansions(True): + yield exp + else: + yield e + +class Function(object): + """ + An object that represents a function call. This class is always subclassed + with the following methods and attributes: + + minargs = minimum # of arguments + maxargs = maximum # of arguments (0 means unlimited) + + def resolve(self, makefile, variables, fd, setting) + Calls the function + calls fd.write() with strings + """ + + __slots__ = ('_arguments', 'loc') + + def __init__(self, loc): + self._arguments = [] + self.loc = loc + assert self.minargs > 0 + + def __getitem__(self, key): + return self._arguments[key] + + def setup(self): + argc = len(self._arguments) + + if argc < self.minargs: + raise data.DataError("Not enough arguments to function %s, requires %s" % (self.name, self.minargs), self.loc) + + assert self.maxargs == 0 or argc <= self.maxargs, "Parser screwed up, gave us too many args" + + def append(self, arg): + assert isinstance(arg, (data.Expansion, data.StringExpansion)) + self._arguments.append(arg) + + def to_source(self): + """Convert the function back to make file "source" code.""" + if not hasattr(self, 'name'): + raise Exception("%s must implement to_source()." % self.__class__) + + # The default implementation simply prints the function name and all + # the arguments joined by a comma. + # According to the GNU make manual Section 8.1, whitespace around + # arguments is *not* part of the argument's value. So, we trim excess + # white space so we have consistent behavior. + args = [] + curly = False + for i, arg in enumerate(self._arguments): + arg = arg.to_source() + + if i == 0: + arg = arg.lstrip() + + # Are balanced parens even OK? + if arg.count('(') != arg.count(')'): + curly = True + + args.append(arg) + + if curly: + return '${%s %s}' % (self.name, ','.join(args)) + + return '$(%s %s)' % (self.name, ','.join(args)) + + def expansions(self, descend=False): + """Obtain all expansions contained within this function. + + By default, only expansions directly part of this function are + returned. If descend is True, we will descend into child expansions and + return all of the composite parts. + + This is a generator for pymake.data.BaseExpansion instances. + """ + # Our default implementation simply returns arguments. More advanced + # functions like variable references may need their own implementation. + return emit_expansions(descend, *self._arguments) + + @property + def is_filesystem_dependent(self): + """Exposes whether this function depends on the filesystem for results. + + If True, the function touches the filesystem as part of evaluation. + + This only tests whether the function itself uses the filesystem. If + this function has arguments that are functions that touch the + filesystem, this will return False. + """ + return False + + def __len__(self): + return len(self._arguments) + + def __repr__(self): + return "%s<%s>(%r)" % ( + self.__class__.__name__, self.loc, + ','.join([repr(a) for a in self._arguments]), + ) + + def __eq__(self, other): + if not hasattr(self, 'name'): + raise Exception("%s must implement __eq__." % self.__class__) + + if type(self) != type(other): + return False + + if self.name != other.name: + return False + + if len(self._arguments) != len(other._arguments): + return False + + for i in xrange(len(self._arguments)): + # According to the GNU make manual Section 8.1, whitespace around + # arguments is *not* part of the argument's value. So, we do a + # whitespace-agnostic comparison. + if i == 0: + a = self._arguments[i] + a.lstrip() + + b = other._arguments[i] + b.lstrip() + + if a != b: + return False + + continue + + if self._arguments[i] != other._arguments[i]: + return False + + return True + + def __ne__(self, other): + return not self.__eq__(other) + +class VariableRef(Function): + AUTOMATIC_VARIABLES = set(['@', '%', '<', '?', '^', '+', '|', '*']) + + __slots__ = ('vname', 'loc') + + def __init__(self, loc, vname): + self.loc = loc + assert isinstance(vname, (data.Expansion, data.StringExpansion)) + self.vname = vname + + def setup(self): + assert False, "Shouldn't get here" + + def resolve(self, makefile, variables, fd, setting): + vname = self.vname.resolvestr(makefile, variables, setting) + if vname in setting: + raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc) + + flavor, source, value = variables.get(vname) + if value is None: + log.debug("%s: variable '%s' was not set" % (self.loc, vname)) + return + + value.resolve(makefile, variables, fd, setting + [vname]) + + def to_source(self): + if isinstance(self.vname, data.StringExpansion): + if self.vname.s in self.AUTOMATIC_VARIABLES: + return '$%s' % self.vname.s + + return '$(%s)' % self.vname.s + + return '$(%s)' % self.vname.to_source() + + def expansions(self, descend=False): + return emit_expansions(descend, self.vname) + + def __repr__(self): + return "VariableRef<%s>(%r)" % (self.loc, self.vname) + + def __eq__(self, other): + if not isinstance(other, VariableRef): + return False + + return self.vname == other.vname + +class SubstitutionRef(Function): + """$(VARNAME:.c=.o) and $(VARNAME:%.c=%.o)""" + + __slots__ = ('loc', 'vname', 'substfrom', 'substto') + + def __init__(self, loc, varname, substfrom, substto): + self.loc = loc + self.vname = varname + self.substfrom = substfrom + self.substto = substto + + def setup(self): + assert False, "Shouldn't get here" + + def resolve(self, makefile, variables, fd, setting): + vname = self.vname.resolvestr(makefile, variables, setting) + if vname in setting: + raise data.DataError("Setting variable '%s' recursively references itself." % (vname,), self.loc) + + substfrom = self.substfrom.resolvestr(makefile, variables, setting) + substto = self.substto.resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(vname) + if value is None: + log.debug("%s: variable '%s' was not set" % (self.loc, vname)) + return + + f = data.Pattern(substfrom) + if not f.ispattern(): + f = data.Pattern('%' + substfrom) + substto = '%' + substto + + fd.write(' '.join([f.subst(substto, word, False) + for word in value.resolvesplit(makefile, variables, setting + [vname])])) + + def to_source(self): + return '$(%s:%s=%s)' % ( + self.vname.to_source(), + self.substfrom.to_source(), + self.substto.to_source()) + + def expansions(self, descend=False): + return emit_expansions(descend, self.vname, self.substfrom, + self.substto) + + def __repr__(self): + return "SubstitutionRef<%s>(%r:%r=%r)" % ( + self.loc, self.vname, self.substfrom, self.substto,) + + def __eq__(self, other): + if not isinstance(other, SubstitutionRef): + return False + + return self.vname == other.vname and self.substfrom == other.substfrom \ + and self.substto == other.substto + +class SubstFunction(Function): + name = 'subst' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + s = self._arguments[0].resolvestr(makefile, variables, setting) + r = self._arguments[1].resolvestr(makefile, variables, setting) + d = self._arguments[2].resolvestr(makefile, variables, setting) + fd.write(d.replace(s, r)) + +class PatSubstFunction(Function): + name = 'patsubst' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + s = self._arguments[0].resolvestr(makefile, variables, setting) + r = self._arguments[1].resolvestr(makefile, variables, setting) + + p = data.Pattern(s) + fd.write(' '.join([p.subst(r, word, False) + for word in self._arguments[2].resolvesplit(makefile, variables, setting)])) + +class StripFunction(Function): + name = 'strip' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + util.joiniter(fd, self._arguments[0].resolvesplit(makefile, variables, setting)) + +class FindstringFunction(Function): + name = 'findstring' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + s = self._arguments[0].resolvestr(makefile, variables, setting) + r = self._arguments[1].resolvestr(makefile, variables, setting) + if r.find(s) == -1: + return + fd.write(s) + +class FilterFunction(Function): + name = 'filter' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + plist = [data.Pattern(p) + for p in self._arguments[0].resolvesplit(makefile, variables, setting)] + + fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting) + if util.any((p.match(w) for p in plist))])) + +class FilteroutFunction(Function): + name = 'filter-out' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + plist = [data.Pattern(p) + for p in self._arguments[0].resolvesplit(makefile, variables, setting)] + + fd.write(' '.join([w for w in self._arguments[1].resolvesplit(makefile, variables, setting) + if not util.any((p.match(w) for p in plist))])) + +class SortFunction(Function): + name = 'sort' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + d = set(self._arguments[0].resolvesplit(makefile, variables, setting)) + util.joiniter(fd, sorted(d)) + +class WordFunction(Function): + name = 'word' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + n = self._arguments[0].resolvestr(makefile, variables, setting) + # TODO: provide better error if this doesn't convert + n = int(n) + words = list(self._arguments[1].resolvesplit(makefile, variables, setting)) + if n < 1 or n > len(words): + return + fd.write(words[n - 1]) + +class WordlistFunction(Function): + name = 'wordlist' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + nfrom = self._arguments[0].resolvestr(makefile, variables, setting) + nto = self._arguments[1].resolvestr(makefile, variables, setting) + # TODO: provide better errors if this doesn't convert + nfrom = int(nfrom) + nto = int(nto) + + words = list(self._arguments[2].resolvesplit(makefile, variables, setting)) + + if nfrom < 1: + nfrom = 1 + if nto < 1: + nto = 1 + + util.joiniter(fd, words[nfrom - 1:nto]) + +class WordsFunction(Function): + name = 'words' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + fd.write(str(len(self._arguments[0].resolvesplit(makefile, variables, setting)))) + +class FirstWordFunction(Function): + name = 'firstword' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + l = self._arguments[0].resolvesplit(makefile, variables, setting) + if len(l): + fd.write(l[0]) + +class LastWordFunction(Function): + name = 'lastword' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + l = self._arguments[0].resolvesplit(makefile, variables, setting) + if len(l): + fd.write(l[-1]) + +def pathsplit(path, default='./'): + """ + Splits a path into dirpart, filepart on the last slash. If there is no slash, dirpart + is ./ + """ + dir, slash, file = util.strrpartition(path, '/') + if dir == '': + return default, file + + return dir + slash, file + +class DirFunction(Function): + name = 'dir' + minargs = 1 + maxargs = 1 + + def resolve(self, makefile, variables, fd, setting): + fd.write(' '.join([pathsplit(path)[0] + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + +class NotDirFunction(Function): + name = 'notdir' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + fd.write(' '.join([pathsplit(path)[1] + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + +class SuffixFunction(Function): + name = 'suffix' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + @staticmethod + def suffixes(words): + for w in words: + dir, file = pathsplit(w) + base, dot, suffix = util.strrpartition(file, '.') + if base != '': + yield dot + suffix + + def resolve(self, makefile, variables, fd, setting): + util.joiniter(fd, self.suffixes(self._arguments[0].resolvesplit(makefile, variables, setting))) + +class BasenameFunction(Function): + name = 'basename' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + @staticmethod + def basenames(words): + for w in words: + dir, file = pathsplit(w, '') + base, dot, suffix = util.strrpartition(file, '.') + if dot == '': + base = suffix + + yield dir + base + + def resolve(self, makefile, variables, fd, setting): + util.joiniter(fd, self.basenames(self._arguments[0].resolvesplit(makefile, variables, setting))) + +class AddSuffixFunction(Function): + name = 'addsuffix' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + suffix = self._arguments[0].resolvestr(makefile, variables, setting) + + fd.write(' '.join([w + suffix for w in self._arguments[1].resolvesplit(makefile, variables, setting)])) + +class AddPrefixFunction(Function): + name = 'addprefix' + minargs = 2 + maxargs = 2 + + def resolve(self, makefile, variables, fd, setting): + prefix = self._arguments[0].resolvestr(makefile, variables, setting) + + fd.write(' '.join([prefix + w for w in self._arguments[1].resolvesplit(makefile, variables, setting)])) + +class JoinFunction(Function): + name = 'join' + minargs = 2 + maxargs = 2 + + __slots__ = Function.__slots__ + + @staticmethod + def iterjoin(l1, l2): + for i in xrange(0, max(len(l1), len(l2))): + i1 = i < len(l1) and l1[i] or '' + i2 = i < len(l2) and l2[i] or '' + yield i1 + i2 + + def resolve(self, makefile, variables, fd, setting): + list1 = list(self._arguments[0].resolvesplit(makefile, variables, setting)) + list2 = list(self._arguments[1].resolvesplit(makefile, variables, setting)) + + util.joiniter(fd, self.iterjoin(list1, list2)) + +class WildcardFunction(Function): + name = 'wildcard' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + patterns = self._arguments[0].resolvesplit(makefile, variables, setting) + + fd.write(' '.join([x.replace('\\','/') + for p in patterns + for x in glob(makefile.workdir, p)])) + + @property + def is_filesystem_dependent(self): + return True + +class RealpathFunction(Function): + name = 'realpath' + minargs = 1 + maxargs = 1 + + def resolve(self, makefile, variables, fd, setting): + fd.write(' '.join([os.path.realpath(os.path.join(makefile.workdir, path)).replace('\\', '/') + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + + def is_filesystem_dependent(self): + return True + +class AbspathFunction(Function): + name = 'abspath' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + assert os.path.isabs(makefile.workdir) + fd.write(' '.join([util.normaljoin(makefile.workdir, path).replace('\\', '/') + for path in self._arguments[0].resolvesplit(makefile, variables, setting)])) + +class IfFunction(Function): + name = 'if' + minargs = 1 + maxargs = 3 + + __slots__ = Function.__slots__ + + def setup(self): + Function.setup(self) + self._arguments[0].lstrip() + self._arguments[0].rstrip() + + def resolve(self, makefile, variables, fd, setting): + condition = self._arguments[0].resolvestr(makefile, variables, setting) + + if len(condition): + self._arguments[1].resolve(makefile, variables, fd, setting) + elif len(self._arguments) > 2: + return self._arguments[2].resolve(makefile, variables, fd, setting) + +class OrFunction(Function): + name = 'or' + minargs = 1 + maxargs = 0 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + for arg in self._arguments: + r = arg.resolvestr(makefile, variables, setting) + if r != '': + fd.write(r) + return + +class AndFunction(Function): + name = 'and' + minargs = 1 + maxargs = 0 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + r = '' + + for arg in self._arguments: + r = arg.resolvestr(makefile, variables, setting) + if r == '': + return + + fd.write(r) + +class ForEachFunction(Function): + name = 'foreach' + minargs = 3 + maxargs = 3 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + vname = self._arguments[0].resolvestr(makefile, variables, setting) + e = self._arguments[2] + + v = data.Variables(parent=variables) + firstword = True + + for w in self._arguments[1].resolvesplit(makefile, variables, setting): + if firstword: + firstword = False + else: + fd.write(' ') + + # The $(origin) of the local variable must be "automatic" to + # conform with GNU make. However, automatic variables have low + # priority. So, we must force its assignment to occur. + v.set(vname, data.Variables.FLAVOR_SIMPLE, + data.Variables.SOURCE_AUTOMATIC, w, force=True) + e.resolve(makefile, v, fd, setting) + +class CallFunction(Function): + name = 'call' + minargs = 1 + maxargs = 0 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + vname = self._arguments[0].resolvestr(makefile, variables, setting) + if vname in setting: + raise data.DataError("Recursively setting variable '%s'" % (vname,)) + + v = data.Variables(parent=variables) + v.set('0', data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, vname) + for i in xrange(1, len(self._arguments)): + param = self._arguments[i].resolvestr(makefile, variables, setting) + v.set(str(i), data.Variables.FLAVOR_SIMPLE, data.Variables.SOURCE_AUTOMATIC, param) + + flavor, source, e = variables.get(vname) + + if e is None: + return + + if flavor == data.Variables.FLAVOR_SIMPLE: + log.warning("%s: calling variable '%s' which is simply-expanded" % (self.loc, vname)) + + # but we'll do it anyway + e.resolve(makefile, v, fd, setting + [vname]) + +class ValueFunction(Function): + name = 'value' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + varname = self._arguments[0].resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(varname, expand=False) + if value is not None: + fd.write(value) + +class EvalFunction(Function): + name = 'eval' + minargs = 1 + maxargs = 1 + + def resolve(self, makefile, variables, fd, setting): + if makefile.parsingfinished: + # GNU make allows variables to be set by recursive expansion during + # command execution. This seems really dumb to me, so I don't! + raise data.DataError("$(eval) not allowed via recursive expansion after parsing is finished", self.loc) + + stmts = parser.parsestring(self._arguments[0].resolvestr(makefile, variables, setting), + 'evaluation from %s' % self.loc) + stmts.execute(makefile) + +class OriginFunction(Function): + name = 'origin' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + vname = self._arguments[0].resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(vname) + if source is None: + r = 'undefined' + elif source == data.Variables.SOURCE_OVERRIDE: + r = 'override' + + elif source == data.Variables.SOURCE_MAKEFILE: + r = 'file' + elif source == data.Variables.SOURCE_ENVIRONMENT: + r = 'environment' + elif source == data.Variables.SOURCE_COMMANDLINE: + r = 'command line' + elif source == data.Variables.SOURCE_AUTOMATIC: + r = 'automatic' + elif source == data.Variables.SOURCE_IMPLICIT: + r = 'default' + + fd.write(r) + +class FlavorFunction(Function): + name = 'flavor' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + varname = self._arguments[0].resolvestr(makefile, variables, setting) + + flavor, source, value = variables.get(varname) + if flavor is None: + r = 'undefined' + elif flavor == data.Variables.FLAVOR_RECURSIVE: + r = 'recursive' + elif flavor == data.Variables.FLAVOR_SIMPLE: + r = 'simple' + fd.write(r) + +class ShellFunction(Function): + name = 'shell' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + from process import prepare_command + cline = self._arguments[0].resolvestr(makefile, variables, setting) + executable, cline = prepare_command(cline, makefile.workdir, self.loc) + + # subprocess.Popen doesn't use the PATH set in the env argument for + # finding the executable on some platforms (but strangely it does on + # others!), so set os.environ['PATH'] explicitly. + oldpath = os.environ['PATH'] + if makefile.env is not None and 'PATH' in makefile.env: + os.environ['PATH'] = makefile.env['PATH'] + + log.debug("%s: running command '%s'" % (self.loc, ' '.join(cline))) + try: + p = subprocess.Popen(cline, executable=executable, env=makefile.env, shell=False, + stdout=subprocess.PIPE, cwd=makefile.workdir) + except OSError, e: + print >>sys.stderr, "Error executing command %s" % cline[0], e + return + finally: + os.environ['PATH'] = oldpath + + stdout, stderr = p.communicate() + stdout = stdout.replace('\r\n', '\n') + if stdout.endswith('\n'): + stdout = stdout[:-1] + stdout = stdout.replace('\n', ' ') + + fd.write(stdout) + +class ErrorFunction(Function): + name = 'error' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + v = self._arguments[0].resolvestr(makefile, variables, setting) + raise data.DataError(v, self.loc) + +class WarningFunction(Function): + name = 'warning' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + v = self._arguments[0].resolvestr(makefile, variables, setting) + log.warning(v) + +class InfoFunction(Function): + name = 'info' + minargs = 1 + maxargs = 1 + + __slots__ = Function.__slots__ + + def resolve(self, makefile, variables, fd, setting): + v = self._arguments[0].resolvestr(makefile, variables, setting) + print v + +functionmap = { + 'subst': SubstFunction, + 'patsubst': PatSubstFunction, + 'strip': StripFunction, + 'findstring': FindstringFunction, + 'filter': FilterFunction, + 'filter-out': FilteroutFunction, + 'sort': SortFunction, + 'word': WordFunction, + 'wordlist': WordlistFunction, + 'words': WordsFunction, + 'firstword': FirstWordFunction, + 'lastword': LastWordFunction, + 'dir': DirFunction, + 'notdir': NotDirFunction, + 'suffix': SuffixFunction, + 'basename': BasenameFunction, + 'addsuffix': AddSuffixFunction, + 'addprefix': AddPrefixFunction, + 'join': JoinFunction, + 'wildcard': WildcardFunction, + 'realpath': RealpathFunction, + 'abspath': AbspathFunction, + 'if': IfFunction, + 'or': OrFunction, + 'and': AndFunction, + 'foreach': ForEachFunction, + 'call': CallFunction, + 'value': ValueFunction, + 'eval': EvalFunction, + 'origin': OriginFunction, + 'flavor': FlavorFunction, + 'shell': ShellFunction, + 'error': ErrorFunction, + 'warning': WarningFunction, + 'info': InfoFunction, +} + +import data diff --git a/build/pymake/pymake/globrelative.py b/build/pymake/pymake/globrelative.py new file mode 100644 index 000000000..37ca28e06 --- /dev/null +++ b/build/pymake/pymake/globrelative.py @@ -0,0 +1,68 @@ +""" +Filename globbing like the python glob module with minor differences: + +* glob relative to an arbitrary directory +* include . and .. +* check that link targets exist, not just links +""" + +import os, re, fnmatch +import util + +_globcheck = re.compile('[[*?]') + +def hasglob(p): + return _globcheck.search(p) is not None + +def glob(fsdir, path): + """ + Yield paths matching the path glob. Sorts as a bonus. Excludes '.' and '..' + """ + + dir, leaf = os.path.split(path) + if dir == '': + return globpattern(fsdir, leaf) + + if hasglob(dir): + dirsfound = glob(fsdir, dir) + else: + dirsfound = [dir] + + r = [] + + for dir in dirsfound: + fspath = util.normaljoin(fsdir, dir) + if not os.path.isdir(fspath): + continue + + r.extend((util.normaljoin(dir, found) for found in globpattern(fspath, leaf))) + + return r + +def globpattern(dir, pattern): + """ + Return leaf names in the specified directory which match the pattern. + """ + + if not hasglob(pattern): + if pattern == '': + if os.path.isdir(dir): + return [''] + return [] + + if os.path.exists(util.normaljoin(dir, pattern)): + return [pattern] + return [] + + leaves = os.listdir(dir) + ['.', '..'] + + # "hidden" filenames are a bit special + if not pattern.startswith('.'): + leaves = [leaf for leaf in leaves + if not leaf.startswith('.')] + + leaves = fnmatch.filter(leaves, pattern) + leaves = filter(lambda l: os.path.exists(util.normaljoin(dir, l)), leaves) + + leaves.sort() + return leaves diff --git a/build/pymake/pymake/implicit.py b/build/pymake/pymake/implicit.py new file mode 100644 index 000000000..d73895cab --- /dev/null +++ b/build/pymake/pymake/implicit.py @@ -0,0 +1,14 @@ +"""
+Implicit variables; perhaps in the future this will also include some implicit
+rules, at least match-anything cancellation rules.
+"""
+
+variables = {
+ 'MKDIR': '%pymake.builtins mkdir',
+ 'RM': '%pymake.builtins rm -f',
+ 'SLEEP': '%pymake.builtins sleep',
+ 'TOUCH': '%pymake.builtins touch',
+ '.LIBPATTERNS': 'lib%.so lib%.a',
+ '.PYMAKE': '1',
+ }
+
diff --git a/build/pymake/pymake/parser.py b/build/pymake/pymake/parser.py new file mode 100644 index 000000000..4bff53368 --- /dev/null +++ b/build/pymake/pymake/parser.py @@ -0,0 +1,822 @@ +""" +Module for parsing Makefile syntax. + +Makefiles use a line-based parsing system. Continuations and substitutions are handled differently based on the +type of line being parsed: + +Lines with makefile syntax condense continuations to a single space, no matter the actual trailing whitespace +of the first line or the leading whitespace of the continuation. In other situations, trailing whitespace is +relevant. + +Lines with command syntax do not condense continuations: the backslash and newline are part of the command. +(GNU Make is buggy in this regard, at least on mac). + +Lines with an initial tab are commands if they can be (there is a rule or a command immediately preceding). +Otherwise, they are parsed as makefile syntax. + +This file parses into the data structures defined in the parserdata module. Those classes are what actually +do the dirty work of "executing" the parsed data into a data.Makefile. + +Four iterator functions are available: +* iterdata +* itermakefilechars +* itercommandchars + +The iterators handle line continuations and comments in different ways, but share a common calling +convention: + +Called with (data, startoffset, tokenlist, finditer) + +yield 4-tuples (flatstr, token, tokenoffset, afteroffset) +flatstr is data, guaranteed to have no tokens (may be '') +token, tokenoffset, afteroffset *may be None*. That means there is more text +coming. +""" + +import logging, re, os, sys +import data, functions, util, parserdata + +_log = logging.getLogger('pymake.parser') + +class SyntaxError(util.MakeError): + pass + +_skipws = re.compile('\S') +class Data(object): + """ + A single virtual "line", which can be multiple source lines joined with + continuations. + """ + + __slots__ = ('s', 'lstart', 'lend', 'loc') + + def __init__(self, s, lstart, lend, loc): + self.s = s + self.lstart = lstart + self.lend = lend + self.loc = loc + + @staticmethod + def fromstring(s, path): + return Data(s, 0, len(s), parserdata.Location(path, 1, 0)) + + def getloc(self, offset): + assert offset >= self.lstart and offset <= self.lend + return self.loc.offset(self.s, self.lstart, offset) + + def skipwhitespace(self, offset): + """ + Return the offset of the first non-whitespace character in data starting at offset, or None if there are + only whitespace characters remaining. + """ + m = _skipws.search(self.s, offset, self.lend) + if m is None: + return self.lend + + return m.start(0) + +_linere = re.compile(r'\\*\n') +def enumeratelines(s, filename): + """ + Enumerate lines in a string as Data objects, joining line + continuations. + """ + + off = 0 + lineno = 1 + curlines = 0 + for m in _linere.finditer(s): + curlines += 1 + start, end = m.span(0) + + if (start - end) % 2 == 0: + # odd number of backslashes is a continuation + continue + + yield Data(s, off, end - 1, parserdata.Location(filename, lineno, 0)) + + lineno += curlines + curlines = 0 + off = end + + yield Data(s, off, len(s), parserdata.Location(filename, lineno, 0)) + +_alltokens = re.compile(r'''\\*\# | # hash mark preceeded by any number of backslashes + := | + \+= | + \?= | + :: | + (?:\$(?:$|[\(\{](?:%s)\s+|.)) | # dollar sign followed by EOF, a function keyword with whitespace, or any character + :(?![\\/]) | # colon followed by anything except a slash (Windows path detection) + [=#{}();,|'"]''' % '|'.join(functions.functionmap.iterkeys()), re.VERBOSE) + +def iterdata(d, offset, tokenlist, it): + """ + Iterate over flat data without line continuations, comments, or any special escaped characters. + + Typically used to parse recursively-expanded variables. + """ + + assert len(tokenlist), "Empty tokenlist passed to iterdata is meaningless!" + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + + if offset == d.lend: + return + + s = d.s + for m in it: + mstart, mend = m.span(0) + token = s[mstart:mend] + if token in tokenlist or (token[0] == '$' and '$' in tokenlist): + yield s[offset:mstart], token, mstart, mend + else: + yield s[offset:mend], None, None, mend + offset = mend + + yield s[offset:d.lend], None, None, None + +# multiple backslashes before a newline are unescaped, halving their total number +_makecontinuations = re.compile(r'(?:\s*|((?:\\\\)+))\\\n\s*') +def _replacemakecontinuations(m): + start, end = m.span(1) + if start == -1: + return ' ' + return ' '.rjust((end - start) / 2 + 1, '\\') + +def itermakefilechars(d, offset, tokenlist, it, ignorecomments=False): + """ + Iterate over data in makefile syntax. Comments are found at unescaped # characters, and escaped newlines + are converted to single-space continuations. + """ + + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + + if offset == d.lend: + return + + s = d.s + for m in it: + mstart, mend = m.span(0) + token = s[mstart:mend] + + starttext = _makecontinuations.sub(_replacemakecontinuations, s[offset:mstart]) + + if token[-1] == '#' and not ignorecomments: + l = mend - mstart + # multiple backslashes before a hash are unescaped, halving their total number + if l % 2: + # found a comment + yield starttext + token[:(l - 1) / 2], None, None, None + return + else: + yield starttext + token[-l / 2:], None, None, mend + elif token in tokenlist or (token[0] == '$' and '$' in tokenlist): + yield starttext, token, mstart, mend + else: + yield starttext + token, None, None, mend + offset = mend + + yield _makecontinuations.sub(_replacemakecontinuations, s[offset:d.lend]), None, None, None + +_findcomment = re.compile(r'\\*\#') +def flattenmakesyntax(d, offset): + """ + A shortcut method for flattening line continuations and comments in makefile syntax without + looking for other tokens. + """ + + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + if offset == d.lend: + return '' + + s = _makecontinuations.sub(_replacemakecontinuations, d.s[offset:d.lend]) + + elements = [] + offset = 0 + for m in _findcomment.finditer(s): + mstart, mend = m.span(0) + elements.append(s[offset:mstart]) + if (mend - mstart) % 2: + # even number of backslashes... it's a comment + elements.append(''.ljust((mend - mstart - 1) / 2, '\\')) + return ''.join(elements) + + # odd number of backslashes + elements.append(''.ljust((mend - mstart - 2) / 2, '\\') + '#') + offset = mend + + elements.append(s[offset:]) + return ''.join(elements) + +def itercommandchars(d, offset, tokenlist, it): + """ + Iterate over command syntax. # comment markers are not special, and escaped newlines are included + in the output text. + """ + + assert offset >= d.lstart and offset <= d.lend, "offset %i should be between %i and %i" % (offset, d.lstart, d.lend) + + if offset == d.lend: + return + + s = d.s + for m in it: + mstart, mend = m.span(0) + token = s[mstart:mend] + starttext = s[offset:mstart].replace('\n\t', '\n') + + if token in tokenlist or (token[0] == '$' and '$' in tokenlist): + yield starttext, token, mstart, mend + else: + yield starttext + token, None, None, mend + offset = mend + + yield s[offset:d.lend].replace('\n\t', '\n'), None, None, None + +_redefines = re.compile('\s*define|\s*endef') +def iterdefinelines(it, startloc): + """ + Process the insides of a define. Most characters are included literally. Escaped newlines are treated + as they would be in makefile syntax. Internal define/endef pairs are ignored. + """ + + results = [] + + definecount = 1 + for d in it: + m = _redefines.match(d.s, d.lstart, d.lend) + if m is not None: + directive = m.group(0).strip() + if directive == 'endef': + definecount -= 1 + if definecount == 0: + return _makecontinuations.sub(_replacemakecontinuations, '\n'.join(results)) + else: + definecount += 1 + + results.append(d.s[d.lstart:d.lend]) + + # Falling off the end is an unterminated define! + raise SyntaxError("define without matching endef", startloc) + +def _ensureend(d, offset, msg): + """ + Ensure that only whitespace remains in this data. + """ + + s = flattenmakesyntax(d, offset) + if s != '' and not s.isspace(): + raise SyntaxError(msg, d.getloc(offset)) + +_eqargstokenlist = ('(', "'", '"') + +def ifeq(d, offset): + if offset > d.lend - 1: + raise SyntaxError("No arguments after conditional", d.getloc(offset)) + + # the variety of formats for this directive is rather maddening + token = d.s[offset] + if token not in _eqargstokenlist: + raise SyntaxError("No arguments after conditional", d.getloc(offset)) + + offset += 1 + + if token == '(': + arg1, t, offset = parsemakesyntax(d, offset, (',',), itermakefilechars) + if t is None: + raise SyntaxError("Expected two arguments in conditional", d.getloc(d.lend)) + + arg1.rstrip() + + offset = d.skipwhitespace(offset) + arg2, t, offset = parsemakesyntax(d, offset, (')',), itermakefilechars) + if t is None: + raise SyntaxError("Unexpected text in conditional", d.getloc(offset)) + + _ensureend(d, offset, "Unexpected text after conditional") + else: + arg1, t, offset = parsemakesyntax(d, offset, (token,), itermakefilechars) + if t is None: + raise SyntaxError("Unexpected text in conditional", d.getloc(d.lend)) + + offset = d.skipwhitespace(offset) + if offset == d.lend: + raise SyntaxError("Expected two arguments in conditional", d.getloc(offset)) + + token = d.s[offset] + if token not in '\'"': + raise SyntaxError("Unexpected text in conditional", d.getloc(offset)) + + arg2, t, offset = parsemakesyntax(d, offset + 1, (token,), itermakefilechars) + + _ensureend(d, offset, "Unexpected text after conditional") + + return parserdata.EqCondition(arg1, arg2) + +def ifneq(d, offset): + c = ifeq(d, offset) + c.expected = False + return c + +def ifdef(d, offset): + e, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) + e.rstrip() + + return parserdata.IfdefCondition(e) + +def ifndef(d, offset): + c = ifdef(d, offset) + c.expected = False + return c + +_conditionkeywords = { + 'ifeq': ifeq, + 'ifneq': ifneq, + 'ifdef': ifdef, + 'ifndef': ifndef + } + +_conditiontokens = tuple(_conditionkeywords.iterkeys()) +_conditionre = re.compile(r'(%s)(?:$|\s+)' % '|'.join(_conditiontokens)) + +_directivestokenlist = _conditiontokens + \ + ('else', 'endif', 'define', 'endef', 'override', 'include', '-include', 'includedeps', '-includedeps', 'vpath', 'export', 'unexport') + +_directivesre = re.compile(r'(%s)(?:$|\s+)' % '|'.join(_directivestokenlist)) + +_varsettokens = (':=', '+=', '?=', '=') + +def _parsefile(pathname): + fd = open(pathname, "rU") + stmts = parsestring(fd.read(), pathname) + stmts.mtime = os.fstat(fd.fileno()).st_mtime + fd.close() + return stmts + +def _checktime(path, stmts): + mtime = os.path.getmtime(path) + if mtime != stmts.mtime: + _log.debug("Re-parsing makefile '%s': mtimes differ", path) + return False + + return True + +_parsecache = util.MostUsedCache(50, _parsefile, _checktime) + +def parsefile(pathname): + """ + Parse a filename into a parserdata.StatementList. A cache is used to avoid re-parsing + makefiles that have already been parsed and have not changed. + """ + + pathname = os.path.realpath(pathname) + return _parsecache.get(pathname) + +# colon followed by anything except a slash (Windows path detection) +_depfilesplitter = re.compile(r':(?![\\/])') +# simple variable references +_vars = re.compile('\$\((\w+)\)') + +def parsedepfile(pathname): + """ + Parse a filename listing only depencencies into a parserdata.StatementList. + Simple variable references are allowed in such files. + """ + def continuation_iter(lines): + current_line = [] + for line in lines: + line = line.rstrip() + if line.endswith("\\"): + current_line.append(line.rstrip("\\")) + continue + if not len(line): + continue + current_line.append(line) + yield ''.join(current_line) + current_line = [] + if current_line: + yield ''.join(current_line) + + def get_expansion(s): + if '$' in s: + expansion = data.Expansion() + # for an input like e.g. "foo $(bar) baz", + # _vars.split returns ["foo", "bar", "baz"] + # every other element is a variable name. + for i, element in enumerate(_vars.split(s)): + if i % 2: + expansion.appendfunc(functions.VariableRef(None, + data.StringExpansion(element, None))) + elif element: + expansion.appendstr(element) + + return expansion + + return data.StringExpansion(s, None) + + pathname = os.path.realpath(pathname) + stmts = parserdata.StatementList() + for line in continuation_iter(open(pathname).readlines()): + target, deps = _depfilesplitter.split(line, 1) + stmts.append(parserdata.Rule(get_expansion(target), + get_expansion(deps), False)) + return stmts + +def parsestring(s, filename): + """ + Parse a string containing makefile data into a parserdata.StatementList. + """ + + currule = False + condstack = [parserdata.StatementList()] + + fdlines = enumeratelines(s, filename) + for d in fdlines: + assert len(condstack) > 0 + + offset = d.lstart + + if currule and offset < d.lend and d.s[offset] == '\t': + e, token, offset = parsemakesyntax(d, offset + 1, (), itercommandchars) + assert token is None + assert offset is None + condstack[-1].append(parserdata.Command(e)) + continue + + # To parse Makefile syntax, we first strip leading whitespace and + # look for initial keywords. If there are no keywords, it's either + # setting a variable or writing a rule. + + offset = d.skipwhitespace(offset) + if offset is None: + continue + + m = _directivesre.match(d.s, offset, d.lend) + if m is not None: + kword = m.group(1) + offset = m.end(0) + + if kword == 'endif': + _ensureend(d, offset, "Unexpected data after 'endif' directive") + if len(condstack) == 1: + raise SyntaxError("unmatched 'endif' directive", + d.getloc(offset)) + + condstack.pop().endloc = d.getloc(offset) + continue + + if kword == 'else': + if len(condstack) == 1: + raise SyntaxError("unmatched 'else' directive", + d.getloc(offset)) + + m = _conditionre.match(d.s, offset, d.lend) + if m is None: + _ensureend(d, offset, "Unexpected data after 'else' directive.") + condstack[-1].addcondition(d.getloc(offset), parserdata.ElseCondition()) + else: + kword = m.group(1) + if kword not in _conditionkeywords: + raise SyntaxError("Unexpected condition after 'else' directive.", + d.getloc(offset)) + + startoffset = offset + offset = d.skipwhitespace(m.end(1)) + c = _conditionkeywords[kword](d, offset) + condstack[-1].addcondition(d.getloc(startoffset), c) + continue + + if kword in _conditionkeywords: + c = _conditionkeywords[kword](d, offset) + cb = parserdata.ConditionBlock(d.getloc(d.lstart), c) + condstack[-1].append(cb) + condstack.append(cb) + continue + + if kword == 'endef': + raise SyntaxError("endef without matching define", d.getloc(offset)) + + if kword == 'define': + currule = False + vname, t, i = parsemakesyntax(d, offset, (), itermakefilechars) + vname.rstrip() + + startloc = d.getloc(d.lstart) + value = iterdefinelines(fdlines, startloc) + condstack[-1].append(parserdata.SetVariable(vname, value=value, valueloc=startloc, token='=', targetexp=None)) + continue + + if kword in ('include', '-include', 'includedeps', '-includedeps'): + if kword.startswith('-'): + required = False + kword = kword[1:] + else: + required = True + + deps = kword == 'includedeps' + + currule = False + incfile, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) + condstack[-1].append(parserdata.Include(incfile, required, deps)) + + continue + + if kword == 'vpath': + currule = False + e, t, offset = parsemakesyntax(d, offset, (), itermakefilechars) + condstack[-1].append(parserdata.VPathDirective(e)) + continue + + if kword == 'override': + currule = False + vname, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) + vname.lstrip() + vname.rstrip() + + if token is None: + raise SyntaxError("Malformed override directive, need =", d.getloc(d.lstart)) + + value = flattenmakesyntax(d, offset).lstrip() + + condstack[-1].append(parserdata.SetVariable(vname, value=value, valueloc=d.getloc(offset), token=token, targetexp=None, source=data.Variables.SOURCE_OVERRIDE)) + continue + + if kword == 'export': + currule = False + e, token, offset = parsemakesyntax(d, offset, _varsettokens, itermakefilechars) + e.lstrip() + e.rstrip() + + if token is None: + condstack[-1].append(parserdata.ExportDirective(e, concurrent_set=False)) + else: + condstack[-1].append(parserdata.ExportDirective(e, concurrent_set=True)) + + value = flattenmakesyntax(d, offset).lstrip() + condstack[-1].append(parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) + + continue + + if kword == 'unexport': + e, token, offset = parsemakesyntax(d, offset, (), itermakefilechars) + condstack[-1].append(parserdata.UnexportDirective(e)) + continue + + e, token, offset = parsemakesyntax(d, offset, _varsettokens + ('::', ':'), itermakefilechars) + if token is None: + e.rstrip() + e.lstrip() + if not e.isempty(): + condstack[-1].append(parserdata.EmptyDirective(e)) + continue + + # if we encountered real makefile syntax, the current rule is over + currule = False + + if token in _varsettokens: + e.lstrip() + e.rstrip() + + value = flattenmakesyntax(d, offset).lstrip() + + condstack[-1].append(parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=None)) + else: + doublecolon = token == '::' + + # `e` is targets or target patterns, which can end up as + # * a rule + # * an implicit rule + # * a static pattern rule + # * a target-specific variable definition + # * a pattern-specific variable definition + # any of the rules may have order-only prerequisites + # delimited by |, and a command delimited by ; + targets = e + + e, token, offset = parsemakesyntax(d, offset, + _varsettokens + (':', '|', ';'), + itermakefilechars) + if token in (None, ';'): + condstack[-1].append(parserdata.Rule(targets, e, doublecolon)) + currule = True + + if token == ';': + offset = d.skipwhitespace(offset) + e, t, offset = parsemakesyntax(d, offset, (), itercommandchars) + condstack[-1].append(parserdata.Command(e)) + + elif token in _varsettokens: + e.lstrip() + e.rstrip() + + value = flattenmakesyntax(d, offset).lstrip() + condstack[-1].append(parserdata.SetVariable(e, value=value, valueloc=d.getloc(offset), token=token, targetexp=targets)) + elif token == '|': + raise SyntaxError('order-only prerequisites not implemented', d.getloc(offset)) + else: + assert token == ':' + # static pattern rule + + pattern = e + + deps, token, offset = parsemakesyntax(d, offset, (';',), itermakefilechars) + + condstack[-1].append(parserdata.StaticPatternRule(targets, pattern, deps, doublecolon)) + currule = True + + if token == ';': + offset = d.skipwhitespace(offset) + e, token, offset = parsemakesyntax(d, offset, (), itercommandchars) + condstack[-1].append(parserdata.Command(e)) + + if len(condstack) != 1: + raise SyntaxError("Condition never terminated with endif", condstack[-1].loc) + + return condstack[0] + +_PARSESTATE_TOPLEVEL = 0 # at the top level +_PARSESTATE_FUNCTION = 1 # expanding a function call +_PARSESTATE_VARNAME = 2 # expanding a variable expansion. +_PARSESTATE_SUBSTFROM = 3 # expanding a variable expansion substitution "from" value +_PARSESTATE_SUBSTTO = 4 # expanding a variable expansion substitution "to" value +_PARSESTATE_PARENMATCH = 5 # inside nested parentheses/braces that must be matched + +class ParseStackFrame(object): + __slots__ = ('parsestate', 'parent', 'expansion', 'tokenlist', 'openbrace', 'closebrace', 'function', 'loc', 'varname', 'substfrom') + + def __init__(self, parsestate, parent, expansion, tokenlist, openbrace, closebrace, function=None, loc=None): + self.parsestate = parsestate + self.parent = parent + self.expansion = expansion + self.tokenlist = tokenlist + self.openbrace = openbrace + self.closebrace = closebrace + self.function = function + self.loc = loc + + def __str__(self): + return "<state=%i expansion=%s tokenlist=%s openbrace=%s closebrace=%s>" % (self.parsestate, self.expansion, self.tokenlist, self.openbrace, self.closebrace) + +_matchingbrace = { + '(': ')', + '{': '}', + } + +def parsemakesyntax(d, offset, stopon, iterfunc): + """ + Given Data, parse it into a data.Expansion. + + @param stopon (sequence) + Indicate characters where toplevel parsing should stop. + + @param iterfunc (generator function) + A function which is used to iterate over d, yielding (char, offset, loc) + @see iterdata + @see itermakefilechars + @see itercommandchars + + @return a tuple (expansion, token, offset). If all the data is consumed, + token and offset will be None + """ + + assert callable(iterfunc) + + stacktop = ParseStackFrame(_PARSESTATE_TOPLEVEL, None, data.Expansion(loc=d.getloc(d.lstart)), + tokenlist=stopon + ('$',), + openbrace=None, closebrace=None) + + tokeniterator = _alltokens.finditer(d.s, offset, d.lend) + + di = iterfunc(d, offset, stacktop.tokenlist, tokeniterator) + while True: # this is not a for loop because `di` changes during the function + assert stacktop is not None + try: + s, token, tokenoffset, offset = di.next() + except StopIteration: + break + + stacktop.expansion.appendstr(s) + if token is None: + continue + + parsestate = stacktop.parsestate + + if token[0] == '$': + if tokenoffset + 1 == d.lend: + # an unterminated $ expands to nothing + break + + loc = d.getloc(tokenoffset) + c = token[1] + if c == '$': + assert len(token) == 2 + stacktop.expansion.appendstr('$') + elif c in ('(', '{'): + closebrace = _matchingbrace[c] + + if len(token) > 2: + fname = token[2:].rstrip() + fn = functions.functionmap[fname](loc) + e = data.Expansion() + if len(fn) + 1 == fn.maxargs: + tokenlist = (c, closebrace, '$') + else: + tokenlist = (',', c, closebrace, '$') + + stacktop = ParseStackFrame(_PARSESTATE_FUNCTION, stacktop, + e, tokenlist, function=fn, + openbrace=c, closebrace=closebrace) + else: + e = data.Expansion() + tokenlist = (':', c, closebrace, '$') + stacktop = ParseStackFrame(_PARSESTATE_VARNAME, stacktop, + e, tokenlist, + openbrace=c, closebrace=closebrace, loc=loc) + else: + assert len(token) == 2 + e = data.Expansion.fromstring(c, loc) + stacktop.expansion.appendfunc(functions.VariableRef(loc, e)) + elif token in ('(', '{'): + assert token == stacktop.openbrace + + stacktop.expansion.appendstr(token) + stacktop = ParseStackFrame(_PARSESTATE_PARENMATCH, stacktop, + stacktop.expansion, + (token, stacktop.closebrace, '$'), + openbrace=token, closebrace=stacktop.closebrace, loc=d.getloc(tokenoffset)) + elif parsestate == _PARSESTATE_PARENMATCH: + assert token == stacktop.closebrace + stacktop.expansion.appendstr(token) + stacktop = stacktop.parent + elif parsestate == _PARSESTATE_TOPLEVEL: + assert stacktop.parent is None + return stacktop.expansion.finish(), token, offset + elif parsestate == _PARSESTATE_FUNCTION: + if token == ',': + stacktop.function.append(stacktop.expansion.finish()) + + stacktop.expansion = data.Expansion() + if len(stacktop.function) + 1 == stacktop.function.maxargs: + tokenlist = (stacktop.openbrace, stacktop.closebrace, '$') + stacktop.tokenlist = tokenlist + elif token in (')', '}'): + fn = stacktop.function + fn.append(stacktop.expansion.finish()) + fn.setup() + + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Not reached, _PARSESTATE_FUNCTION" + elif parsestate == _PARSESTATE_VARNAME: + if token == ':': + stacktop.varname = stacktop.expansion + stacktop.parsestate = _PARSESTATE_SUBSTFROM + stacktop.expansion = data.Expansion() + stacktop.tokenlist = ('=', stacktop.openbrace, stacktop.closebrace, '$') + elif token in (')', '}'): + fn = functions.VariableRef(stacktop.loc, stacktop.expansion.finish()) + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Not reached, _PARSESTATE_VARNAME" + elif parsestate == _PARSESTATE_SUBSTFROM: + if token == '=': + stacktop.substfrom = stacktop.expansion + stacktop.parsestate = _PARSESTATE_SUBSTTO + stacktop.expansion = data.Expansion() + stacktop.tokenlist = (stacktop.openbrace, stacktop.closebrace, '$') + elif token in (')', '}'): + # A substitution of the form $(VARNAME:.ee) is probably a mistake, but make + # parses it. Issue a warning. Combine the varname and substfrom expansions to + # make the compatible varname. See tests/var-substitutions.mk SIMPLE3SUBSTNAME + _log.warning("%s: Variable reference looks like substitution without =", stacktop.loc) + stacktop.varname.appendstr(':') + stacktop.varname.concat(stacktop.expansion) + fn = functions.VariableRef(stacktop.loc, stacktop.varname.finish()) + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Not reached, _PARSESTATE_SUBSTFROM" + elif parsestate == _PARSESTATE_SUBSTTO: + assert token in (')','}'), "Not reached, _PARSESTATE_SUBSTTO" + + fn = functions.SubstitutionRef(stacktop.loc, stacktop.varname.finish(), + stacktop.substfrom.finish(), stacktop.expansion.finish()) + stacktop = stacktop.parent + stacktop.expansion.appendfunc(fn) + else: + assert False, "Unexpected parse state %s" % stacktop.parsestate + + if stacktop.parent is not None and iterfunc == itercommandchars: + di = itermakefilechars(d, offset, stacktop.tokenlist, tokeniterator, + ignorecomments=True) + else: + di = iterfunc(d, offset, stacktop.tokenlist, tokeniterator) + + if stacktop.parent is not None: + raise SyntaxError("Unterminated function call", d.getloc(offset)) + + assert stacktop.parsestate == _PARSESTATE_TOPLEVEL + + return stacktop.expansion.finish(), None, None diff --git a/build/pymake/pymake/parserdata.py b/build/pymake/pymake/parserdata.py new file mode 100644 index 000000000..7b2e5443d --- /dev/null +++ b/build/pymake/pymake/parserdata.py @@ -0,0 +1,1006 @@ +import logging, re, os +import data, parser, functions, util +from cStringIO import StringIO +from pymake.globrelative import hasglob, glob + +_log = logging.getLogger('pymake.data') +_tabwidth = 4 + +class Location(object): + """ + A location within a makefile. + + For the moment, locations are just path/line/column, but in the future + they may reference parent locations for more accurate "included from" + or "evaled at" error reporting. + """ + __slots__ = ('path', 'line', 'column') + + def __init__(self, path, line, column): + self.path = path + self.line = line + self.column = column + + def offset(self, s, start, end): + """ + Returns a new location offset by + the specified string. + """ + + if start == end: + return self + + skiplines = s.count('\n', start, end) + line = self.line + skiplines + if skiplines: + lastnl = s.rfind('\n', start, end) + assert lastnl != -1 + start = lastnl + 1 + column = 0 + else: + column = self.column + + while True: + j = s.find('\t', start, end) + if j == -1: + column += end - start + break + + column += j - start + column += _tabwidth + column -= column % _tabwidth + start = j + 1 + + return Location(self.path, line, column) + + def __str__(self): + return "%s:%s:%s" % (self.path, self.line, self.column) + +def _expandwildcards(makefile, tlist): + for t in tlist: + if not hasglob(t): + yield t + else: + l = glob(makefile.workdir, t) + for r in l: + yield r + +_flagescape = re.compile(r'([\s\\])') + +def parsecommandlineargs(args): + """ + Given a set of arguments from a command-line invocation of make, + parse out the variable definitions and return (stmts, arglist, overridestr) + """ + + overrides = [] + stmts = StatementList() + r = [] + for i in xrange(0, len(args)): + a = args[i] + + vname, t, val = util.strpartition(a, ':=') + if t == '': + vname, t, val = util.strpartition(a, '=') + if t != '': + overrides.append(_flagescape.sub(r'\\\1', a)) + + vname = vname.strip() + vnameexp = data.Expansion.fromstring(vname, "Command-line argument") + + stmts.append(ExportDirective(vnameexp, concurrent_set=True)) + stmts.append(SetVariable(vnameexp, token=t, + value=val, valueloc=Location('<command-line>', i, len(vname) + len(t)), + targetexp=None, source=data.Variables.SOURCE_COMMANDLINE)) + else: + r.append(data.stripdotslash(a)) + + return stmts, r, ' '.join(overrides) + +class Statement(object): + """ + Represents parsed make file syntax. + + This is an abstract base class. Child classes are expected to implement + basic methods defined below. + """ + + def execute(self, makefile, context): + """Executes this Statement within a make file execution context.""" + raise Exception("%s must implement execute()." % self.__class__) + + def to_source(self): + """Obtain the make file "source" representation of the Statement. + + This converts an individual Statement back to a string that can again + be parsed into this Statement. + """ + raise Exception("%s must implement to_source()." % self.__class__) + + def __eq__(self, other): + raise Exception("%s must implement __eq__." % self.__class__) + + def __ne__(self, other): + return self.__eq__(other) + +class DummyRule(object): + __slots__ = () + + def addcommand(self, r): + pass + +class Rule(Statement): + """ + Rules represent how to make specific targets. + + See https://www.gnu.org/software/make/manual/make.html#Rules. + + An individual rule is composed of a target, dependencies, and a recipe. + This class only contains references to the first 2. The recipe will be + contained in Command classes which follow this one in a stream of Statement + instances. + + Instances also contain a boolean property `doublecolon` which says whether + this is a doublecolon rule. Doublecolon rules are rules that are always + executed, if they are evaluated. Normally, rules are only executed if their + target is out of date. + """ + __slots__ = ('targetexp', 'depexp', 'doublecolon') + + def __init__(self, targetexp, depexp, doublecolon): + assert isinstance(targetexp, (data.Expansion, data.StringExpansion)) + assert isinstance(depexp, (data.Expansion, data.StringExpansion)) + + self.targetexp = targetexp + self.depexp = depexp + self.doublecolon = doublecolon + + def execute(self, makefile, context): + if context.weak: + self._executeweak(makefile, context) + else: + self._execute(makefile, context) + + def _executeweak(self, makefile, context): + """ + If the context is weak (we're just handling dependencies) we can make a number of assumptions here. + This lets us go really fast and is generally good. + """ + assert context.weak + deps = self.depexp.resolvesplit(makefile, makefile.variables) + # Skip targets with no rules and no dependencies + if not deps: + return + targets = data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables)) + rule = data.Rule(list(data.stripdotslashes(deps)), self.doublecolon, loc=self.targetexp.loc, weakdeps=True) + for target in targets: + makefile.gettarget(target).addrule(rule) + makefile.foundtarget(target) + context.currule = rule + + def _execute(self, makefile, context): + assert not context.weak + + atargets = data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables)) + targets = [data.Pattern(p) for p in _expandwildcards(makefile, atargets)] + + if not len(targets): + context.currule = DummyRule() + return + + ispatterns = set((t.ispattern() for t in targets)) + if len(ispatterns) == 2: + raise data.DataError("Mixed implicit and normal rule", self.targetexp.loc) + ispattern, = ispatterns + + deps = list(_expandwildcards(makefile, data.stripdotslashes(self.depexp.resolvesplit(makefile, makefile.variables)))) + if ispattern: + rule = data.PatternRule(targets, map(data.Pattern, deps), self.doublecolon, loc=self.targetexp.loc) + makefile.appendimplicitrule(rule) + else: + rule = data.Rule(deps, self.doublecolon, loc=self.targetexp.loc, weakdeps=False) + for t in targets: + makefile.gettarget(t.gettarget()).addrule(rule) + + makefile.foundtarget(targets[0].gettarget()) + + context.currule = rule + + def dump(self, fd, indent): + print >>fd, "%sRule %s: %s" % (indent, self.targetexp, self.depexp) + + def to_source(self): + sep = ':' + + if self.doublecolon: + sep = '::' + + deps = self.depexp.to_source() + if len(deps) > 0 and not deps[0].isspace(): + sep += ' ' + + return '\n%s%s%s' % ( + self.targetexp.to_source(escape_variables=True), + sep, + deps) + + def __eq__(self, other): + if not isinstance(other, Rule): + return False + + return self.targetexp == other.targetexp \ + and self.depexp == other.depexp \ + and self.doublecolon == other.doublecolon + +class StaticPatternRule(Statement): + """ + Static pattern rules are rules which specify multiple targets based on a + string pattern. + + See https://www.gnu.org/software/make/manual/make.html#Static-Pattern + + They are like `Rule` instances except an added property, `patternexp` is + present. It contains the Expansion which represents the rule pattern. + """ + __slots__ = ('targetexp', 'patternexp', 'depexp', 'doublecolon') + + def __init__(self, targetexp, patternexp, depexp, doublecolon): + assert isinstance(targetexp, (data.Expansion, data.StringExpansion)) + assert isinstance(patternexp, (data.Expansion, data.StringExpansion)) + assert isinstance(depexp, (data.Expansion, data.StringExpansion)) + + self.targetexp = targetexp + self.patternexp = patternexp + self.depexp = depexp + self.doublecolon = doublecolon + + def execute(self, makefile, context): + if context.weak: + raise data.DataError("Static pattern rules not allowed in includedeps", self.targetexp.loc) + + targets = list(_expandwildcards(makefile, data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables)))) + + if not len(targets): + context.currule = DummyRule() + return + + patterns = list(data.stripdotslashes(self.patternexp.resolvesplit(makefile, makefile.variables))) + if len(patterns) != 1: + raise data.DataError("Static pattern rules must have a single pattern", self.patternexp.loc) + pattern = data.Pattern(patterns[0]) + + deps = [data.Pattern(p) for p in _expandwildcards(makefile, data.stripdotslashes(self.depexp.resolvesplit(makefile, makefile.variables)))] + + rule = data.PatternRule([pattern], deps, self.doublecolon, loc=self.targetexp.loc) + + for t in targets: + if data.Pattern(t).ispattern(): + raise data.DataError("Target '%s' of a static pattern rule must not be a pattern" % (t,), self.targetexp.loc) + stem = pattern.match(t) + if stem is None: + raise data.DataError("Target '%s' does not match the static pattern '%s'" % (t, pattern), self.targetexp.loc) + makefile.gettarget(t).addrule(data.PatternRuleInstance(rule, '', stem, pattern.ismatchany())) + + makefile.foundtarget(targets[0]) + context.currule = rule + + def dump(self, fd, indent): + print >>fd, "%sStaticPatternRule %s: %s: %s" % (indent, self.targetexp, self.patternexp, self.depexp) + + def to_source(self): + sep = ':' + + if self.doublecolon: + sep = '::' + + pattern = self.patternexp.to_source() + deps = self.depexp.to_source() + + if len(pattern) > 0 and pattern[0] not in (' ', '\t'): + sep += ' ' + + return '\n%s%s%s:%s' % ( + self.targetexp.to_source(escape_variables=True), + sep, + pattern, + deps) + + def __eq__(self, other): + if not isinstance(other, StaticPatternRule): + return False + + return self.targetexp == other.targetexp \ + and self.patternexp == other.patternexp \ + and self.depexp == other.depexp \ + and self.doublecolon == other.doublecolon + +class Command(Statement): + """ + Commands are things that get executed by a rule. + + A rule's recipe is composed of 0 or more Commands. + + A command is simply an expansion. Commands typically represent strings to + be executed in a shell (e.g. via system()). Although, since make files + allow arbitrary shells to be used for command execution, this isn't a + guarantee. + """ + __slots__ = ('exp',) + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + + def execute(self, makefile, context): + assert context.currule is not None + if context.weak: + raise data.DataError("rules not allowed in includedeps", self.exp.loc) + + context.currule.addcommand(self.exp) + + def dump(self, fd, indent): + print >>fd, "%sCommand %s" % (indent, self.exp,) + + def to_source(self): + # Commands have some interesting quirks when it comes to source + # formatting. First, they can be multi-line. Second, a tab needs to be + # inserted at the beginning of every line. Finally, there might be + # variable references inside the command. This means we need to escape + # variable references inside command strings. Luckily, this is handled + # by the Expansion. + s = self.exp.to_source(escape_variables=True) + + return '\n'.join(['\t%s' % line for line in s.split('\n')]) + + def __eq__(self, other): + if not isinstance(other, Command): + return False + + return self.exp == other.exp + +class SetVariable(Statement): + """ + Represents a variable assignment. + + Variable assignment comes in two different flavors. + + Simple assignment has the form: + + <Expansion> <Assignment Token> <string> + + e.g. FOO := bar + + These correspond to the fields `vnameexp`, `token`, and `value`. In + addition, `valueloc` will be a Location and `source` will be a + pymake.data.Variables.SOURCE_* constant. + + There are also target-specific variables. These are variables that only + apply in the context of a specific target. They are like the aforementioned + assignment except the `targetexp` field is set to an Expansion representing + the target they apply to. + """ + __slots__ = ('vnameexp', 'token', 'value', 'valueloc', 'targetexp', 'source') + + def __init__(self, vnameexp, token, value, valueloc, targetexp, source=None): + assert isinstance(vnameexp, (data.Expansion, data.StringExpansion)) + assert isinstance(value, str) + assert targetexp is None or isinstance(targetexp, (data.Expansion, data.StringExpansion)) + + if source is None: + source = data.Variables.SOURCE_MAKEFILE + + self.vnameexp = vnameexp + self.token = token + self.value = value + self.valueloc = valueloc + self.targetexp = targetexp + self.source = source + + def execute(self, makefile, context): + vname = self.vnameexp.resolvestr(makefile, makefile.variables) + if len(vname) == 0: + raise data.DataError("Empty variable name", self.vnameexp.loc) + + if self.targetexp is None: + setvariables = [makefile.variables] + else: + setvariables = [] + + targets = [data.Pattern(t) for t in data.stripdotslashes(self.targetexp.resolvesplit(makefile, makefile.variables))] + for t in targets: + if t.ispattern(): + setvariables.append(makefile.getpatternvariables(t)) + else: + setvariables.append(makefile.gettarget(t.gettarget()).variables) + + for v in setvariables: + if self.token == '+=': + v.append(vname, self.source, self.value, makefile.variables, makefile) + continue + + if self.token == '?=': + flavor = data.Variables.FLAVOR_RECURSIVE + oldflavor, oldsource, oldval = v.get(vname, expand=False) + if oldval is not None: + continue + value = self.value + elif self.token == '=': + flavor = data.Variables.FLAVOR_RECURSIVE + value = self.value + else: + assert self.token == ':=' + + flavor = data.Variables.FLAVOR_SIMPLE + d = parser.Data.fromstring(self.value, self.valueloc) + e, t, o = parser.parsemakesyntax(d, 0, (), parser.iterdata) + value = e.resolvestr(makefile, makefile.variables) + + v.set(vname, flavor, self.source, value) + + def dump(self, fd, indent): + print >>fd, "%sSetVariable<%s> %s %s\n%s %r" % (indent, self.valueloc, self.vnameexp, self.token, indent, self.value) + + def __eq__(self, other): + if not isinstance(other, SetVariable): + return False + + return self.vnameexp == other.vnameexp \ + and self.token == other.token \ + and self.value == other.value \ + and self.targetexp == other.targetexp \ + and self.source == other.source + + def to_source(self): + chars = [] + for i in xrange(0, len(self.value)): + c = self.value[i] + + # Literal # is escaped in variable assignment otherwise it would be + # a comment. + if c == '#': + # If a backslash precedes this, we need to escape it as well. + if i > 0 and self.value[i-1] == '\\': + chars.append('\\') + + chars.append('\\#') + continue + + chars.append(c) + + value = ''.join(chars) + + prefix = '' + if self.source == data.Variables.SOURCE_OVERRIDE: + prefix = 'override ' + + # SetVariable come in two flavors: simple and target-specific. + + # We handle the target-specific syntax first. + if self.targetexp is not None: + return '%s: %s %s %s' % ( + self.targetexp.to_source(), + self.vnameexp.to_source(), + self.token, + value) + + # The variable could be multi-line or have leading whitespace. For + # regular variable assignment, whitespace after the token but before + # the value is ignored. If we see leading whitespace in the value here, + # the variable must have come from a define. + if value.count('\n') > 0 or (len(value) and value[0].isspace()): + # The parser holds the token in vnameexp for whatever reason. + return '%sdefine %s\n%s\nendef' % ( + prefix, + self.vnameexp.to_source(), + value) + + return '%s%s %s %s' % ( + prefix, + self.vnameexp.to_source(), + self.token, + value) + +class Condition(object): + """ + An abstract "condition", either ifeq or ifdef, perhaps negated. + + See https://www.gnu.org/software/make/manual/make.html#Conditional-Syntax + + Subclasses must implement: + + def evaluate(self, makefile) + """ + + def __eq__(self, other): + raise Exception("%s must implement __eq__." % __class__) + + def __ne__(self, other): + return not self.__eq__(other) + +class EqCondition(Condition): + """ + Represents an ifeq or ifneq conditional directive. + + This directive consists of two Expansions which are compared for equality. + + The `expected` field is a bool indicating what the condition must evaluate + to in order for its body to be executed. If True, this is an "ifeq" + conditional directive. If False, an "ifneq." + """ + __slots__ = ('exp1', 'exp2', 'expected') + + def __init__(self, exp1, exp2): + assert isinstance(exp1, (data.Expansion, data.StringExpansion)) + assert isinstance(exp2, (data.Expansion, data.StringExpansion)) + + self.expected = True + self.exp1 = exp1 + self.exp2 = exp2 + + def evaluate(self, makefile): + r1 = self.exp1.resolvestr(makefile, makefile.variables) + r2 = self.exp2.resolvestr(makefile, makefile.variables) + return (r1 == r2) == self.expected + + def __str__(self): + return "ifeq (expected=%s) %s %s" % (self.expected, self.exp1, self.exp2) + + def __eq__(self, other): + if not isinstance(other, EqCondition): + return False + + return self.exp1 == other.exp1 \ + and self.exp2 == other.exp2 \ + and self.expected == other.expected + +class IfdefCondition(Condition): + """ + Represents an ifdef or ifndef conditional directive. + + This directive consists of a single expansion which represents the name of + a variable (without the leading '$') which will be checked for definition. + + The `expected` field is a bool and has the same behavior as EqCondition. + If it is True, this represents a "ifdef" conditional. If False, "ifndef." + """ + __slots__ = ('exp', 'expected') + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + self.expected = True + + def evaluate(self, makefile): + vname = self.exp.resolvestr(makefile, makefile.variables) + flavor, source, value = makefile.variables.get(vname, expand=False) + + if value is None: + return not self.expected + + return (len(value) > 0) == self.expected + + def __str__(self): + return "ifdef (expected=%s) %s" % (self.expected, self.exp) + + def __eq__(self, other): + if not isinstance(other, IfdefCondition): + return False + + return self.exp == other.exp and self.expected == other.expected + +class ElseCondition(Condition): + """ + Represents the transition between branches in a ConditionBlock. + """ + __slots__ = () + + def evaluate(self, makefile): + return True + + def __str__(self): + return "else" + + def __eq__(self, other): + return isinstance(other, ElseCondition) + +class ConditionBlock(Statement): + """ + A set of related Conditions. + + This is essentially a list of 2-tuples of (Condition, list(Statement)). + + The parser creates a ConditionBlock for all statements related to the same + conditional group. If iterating over the parser's output, where you think + you would see an ifeq, you will see a ConditionBlock containing an IfEq. In + other words, the parser collapses separate statements into this container + class. + + ConditionBlock instances may exist within other ConditionBlock if the + conditional logic is multiple levels deep. + """ + __slots__ = ('loc', '_groups') + + def __init__(self, loc, condition): + self.loc = loc + self._groups = [] + self.addcondition(loc, condition) + + def getloc(self): + return self.loc + + def addcondition(self, loc, condition): + assert isinstance(condition, Condition) + condition.loc = loc + + if len(self._groups) and isinstance(self._groups[-1][0], ElseCondition): + raise parser.SyntaxError("Multiple else conditions for block starting at %s" % self.loc, loc) + + self._groups.append((condition, StatementList())) + + def append(self, statement): + self._groups[-1][1].append(statement) + + def execute(self, makefile, context): + i = 0 + for c, statements in self._groups: + if c.evaluate(makefile): + _log.debug("Condition at %s met by clause #%i", self.loc, i) + statements.execute(makefile, context) + return + + i += 1 + + def dump(self, fd, indent): + print >>fd, "%sConditionBlock" % (indent,) + + indent2 = indent + ' ' + for c, statements in self._groups: + print >>fd, "%s Condition %s" % (indent, c) + statements.dump(fd, indent2) + print >>fd, "%s ~Condition" % (indent,) + print >>fd, "%s~ConditionBlock" % (indent,) + + def to_source(self): + lines = [] + index = 0 + for condition, statements in self: + lines.append(ConditionBlock.condition_source(condition, index)) + index += 1 + + for statement in statements: + lines.append(statement.to_source()) + + lines.append('endif') + + return '\n'.join(lines) + + def __eq__(self, other): + if not isinstance(other, ConditionBlock): + return False + + if len(self) != len(other): + return False + + for i in xrange(0, len(self)): + our_condition, our_statements = self[i] + other_condition, other_statements = other[i] + + if our_condition != other_condition: + return False + + if our_statements != other_statements: + return False + + return True + + @staticmethod + def condition_source(statement, index): + """Convert a condition to its source representation. + + The index argument defines the index of this condition inside a + ConditionBlock. If it is greater than 0, an "else" will be prepended + to the result, if necessary. + """ + prefix = '' + if isinstance(statement, (EqCondition, IfdefCondition)) and index > 0: + prefix = 'else ' + + if isinstance(statement, IfdefCondition): + s = statement.exp.s + + if statement.expected: + return '%sifdef %s' % (prefix, s) + + return '%sifndef %s' % (prefix, s) + + if isinstance(statement, EqCondition): + args = [ + statement.exp1.to_source(escape_comments=True), + statement.exp2.to_source(escape_comments=True)] + + use_quotes = False + single_quote_present = False + double_quote_present = False + for i, arg in enumerate(args): + if len(arg) > 0 and (arg[0].isspace() or arg[-1].isspace()): + use_quotes = True + + if "'" in arg: + single_quote_present = True + + if '"' in arg: + double_quote_present = True + + # Quote everything if needed. + if single_quote_present and double_quote_present: + raise Exception('Cannot format condition with multiple quotes.') + + if use_quotes: + for i, arg in enumerate(args): + # Double to single quotes. + if single_quote_present: + args[i] = '"' + arg + '"' + else: + args[i] = "'" + arg + "'" + + body = None + if use_quotes: + body = ' '.join(args) + else: + body = '(%s)' % ','.join(args) + + if statement.expected: + return '%sifeq %s' % (prefix, body) + + return '%sifneq %s' % (prefix, body) + + if isinstance(statement, ElseCondition): + return 'else' + + raise Exception('Unhandled Condition statement: %s' % + statement.__class__) + + def __iter__(self): + return iter(self._groups) + + def __len__(self): + return len(self._groups) + + def __getitem__(self, i): + return self._groups[i] + +class Include(Statement): + """ + Represents the include directive. + + See https://www.gnu.org/software/make/manual/make.html#Include + + The file to be included is represented by the Expansion defined in the + field `exp`. `required` is a bool indicating whether execution should fail + if the specified file could not be processed. + """ + __slots__ = ('exp', 'required', 'deps') + + def __init__(self, exp, required, weak): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + self.required = required + self.weak = weak + + def execute(self, makefile, context): + files = self.exp.resolvesplit(makefile, makefile.variables) + for f in files: + makefile.include(f, self.required, loc=self.exp.loc, weak=self.weak) + + def dump(self, fd, indent): + print >>fd, "%sInclude %s" % (indent, self.exp) + + def to_source(self): + prefix = '' + + if not self.required: + prefix = '-' + + return '%sinclude %s' % (prefix, self.exp.to_source()) + + def __eq__(self, other): + if not isinstance(other, Include): + return False + + return self.exp == other.exp and self.required == other.required + +class VPathDirective(Statement): + """ + Represents the vpath directive. + + See https://www.gnu.org/software/make/manual/make.html#Selective-Search + """ + __slots__ = ('exp',) + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + + def execute(self, makefile, context): + words = list(data.stripdotslashes(self.exp.resolvesplit(makefile, makefile.variables))) + if len(words) == 0: + makefile.clearallvpaths() + else: + pattern = data.Pattern(words[0]) + mpaths = words[1:] + + if len(mpaths) == 0: + makefile.clearvpath(pattern) + else: + dirs = [] + for mpath in mpaths: + dirs.extend((dir for dir in mpath.split(os.pathsep) + if dir != '')) + if len(dirs): + makefile.addvpath(pattern, dirs) + + def dump(self, fd, indent): + print >>fd, "%sVPath %s" % (indent, self.exp) + + def to_source(self): + return 'vpath %s' % self.exp.to_source() + + def __eq__(self, other): + if not isinstance(other, VPathDirective): + return False + + return self.exp == other.exp + +class ExportDirective(Statement): + """ + Represents the "export" directive. + + This is used to control exporting variables to sub makes. + + See https://www.gnu.org/software/make/manual/make.html#Variables_002fRecursion + + The `concurrent_set` field defines whether this statement occurred with or + without a variable assignment. If False, no variable assignment was + present. If True, the SetVariable immediately following this statement + originally came from this export directive (the parser splits it into + multiple statements). + """ + + __slots__ = ('exp', 'concurrent_set') + + def __init__(self, exp, concurrent_set): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + self.concurrent_set = concurrent_set + + def execute(self, makefile, context): + if self.concurrent_set: + vlist = [self.exp.resolvestr(makefile, makefile.variables)] + else: + vlist = list(self.exp.resolvesplit(makefile, makefile.variables)) + if not len(vlist): + raise data.DataError("Exporting all variables is not supported", self.exp.loc) + + for v in vlist: + makefile.exportedvars[v] = True + + def dump(self, fd, indent): + print >>fd, "%sExport (single=%s) %s" % (indent, self.single, self.exp) + + def to_source(self): + return ('export %s' % self.exp.to_source()).rstrip() + + def __eq__(self, other): + if not isinstance(other, ExportDirective): + return False + + # single is irrelevant because it just says whether the next Statement + # contains a variable definition. + return self.exp == other.exp + +class UnexportDirective(Statement): + """ + Represents the "unexport" directive. + + This is the opposite of ExportDirective. + """ + __slots__ = ('exp',) + + def __init__(self, exp): + self.exp = exp + + def execute(self, makefile, context): + vlist = list(self.exp.resolvesplit(makefile, makefile.variables)) + for v in vlist: + makefile.exportedvars[v] = False + + def dump(self, fd, indent): + print >>fd, "%sUnexport %s" % (indent, self.exp) + + def to_source(self): + return 'unexport %s' % self.exp.to_source() + + def __eq__(self, other): + if not isinstance(other, UnexportDirective): + return False + + return self.exp == other.exp + +class EmptyDirective(Statement): + """ + Represents a standalone statement, usually an Expansion. + + You will encounter EmptyDirective instances if there is a function + or similar at the top-level of a make file (e.g. outside of a rule or + variable assignment). You can also find them as the bodies of + ConditionBlock branches. + """ + __slots__ = ('exp',) + + def __init__(self, exp): + assert isinstance(exp, (data.Expansion, data.StringExpansion)) + self.exp = exp + + def execute(self, makefile, context): + v = self.exp.resolvestr(makefile, makefile.variables) + if v.strip() != '': + raise data.DataError("Line expands to non-empty value", self.exp.loc) + + def dump(self, fd, indent): + print >>fd, "%sEmptyDirective: %s" % (indent, self.exp) + + def to_source(self): + return self.exp.to_source() + + def __eq__(self, other): + if not isinstance(other, EmptyDirective): + return False + + return self.exp == other.exp + +class _EvalContext(object): + __slots__ = ('currule', 'weak') + + def __init__(self, weak): + self.weak = weak + +class StatementList(list): + """ + A list of Statement instances. + + This is what is generated by the parser when a make file is parsed. + + Consumers can iterate over all Statement instances in this collection to + statically inspect (and even modify) make files before they are executed. + """ + __slots__ = ('mtime',) + + def append(self, statement): + assert isinstance(statement, Statement) + list.append(self, statement) + + def execute(self, makefile, context=None, weak=False): + if context is None: + context = _EvalContext(weak=weak) + + for s in self: + s.execute(makefile, context) + + def dump(self, fd, indent): + for s in self: + s.dump(fd, indent) + + def __str__(self): + fd = StringIO() + self.dump(fd, '') + return fd.getvalue() + + def to_source(self): + return '\n'.join([s.to_source() for s in self]) + +def iterstatements(stmts): + for s in stmts: + yield s + if isinstance(s, ConditionBlock): + for c, sl in s: + for s2 in iterstatments(sl): yield s2 diff --git a/build/pymake/pymake/process.py b/build/pymake/pymake/process.py new file mode 100644 index 000000000..01cadf5a9 --- /dev/null +++ b/build/pymake/pymake/process.py @@ -0,0 +1,556 @@ +""" +Skipping shell invocations is good, when possible. This wrapper around subprocess does dirty work of +parsing command lines into argv and making sure that no shell magic is being used. +""" + +#TODO: ship pyprocessing? +import multiprocessing +import subprocess, shlex, re, logging, sys, traceback, os, imp, glob +import site +from collections import deque +# XXXkhuey Work around http://bugs.python.org/issue1731717 +subprocess._cleanup = lambda: None +import command, util +if sys.platform=='win32': + import win32process + +_log = logging.getLogger('pymake.process') + +_escapednewlines = re.compile(r'\\\n') + +def tokens2re(tokens): + # Create a pattern for non-escaped tokens, in the form: + # (?<!\\)(?:a|b|c...) + # This is meant to match patterns a, b, or c, or ... if they are not + # preceded by a backslash. + # where a, b, c... are in the form + # (?P<name>pattern) + # which matches the pattern and captures it in a named match group. + # The group names and patterns come are given as a dict in the function + # argument. + nonescaped = r'(?<!\\)(?:%s)' % '|'.join('(?P<%s>%s)' % (name, value) for name, value in tokens.iteritems()) + # The final pattern matches either the above pattern, or an escaped + # backslash, captured in the "escape" match group. + return re.compile('(?:%s|%s)' % (nonescaped, r'(?P<escape>\\\\)')) + +_unquoted_tokens = tokens2re({ + 'whitespace': r'[\t\r\n ]+', + 'quote': r'[\'"]', + 'comment': '#', + 'special': r'[<>&|`~(){}$;]', + 'backslashed': r'\\[^\\]', + 'glob': r'[\*\?]', +}) + +_doubly_quoted_tokens = tokens2re({ + 'quote': '"', + 'backslashedquote': r'\\"', + 'special': '\$', + 'backslashed': r'\\[^\\"]', +}) + +class MetaCharacterException(Exception): + def __init__(self, char): + self.char = char + +class ClineSplitter(list): + """ + Parses a given command line string and creates a list of command + and arguments, with wildcard expansion. + """ + def __init__(self, cline, cwd): + self.cwd = cwd + self.arg = None + self.cline = cline + self.glob = False + self._parse_unquoted() + + def _push(self, str): + """ + Push the given string as part of the current argument + """ + if self.arg is None: + self.arg = '' + self.arg += str + + def _next(self): + """ + Finalize current argument, effectively adding it to the list. + Perform globbing if needed. + """ + if self.arg is None: + return + if self.glob: + if os.path.isabs(self.arg): + path = self.arg + else: + path = os.path.join(self.cwd, self.arg) + globbed = glob.glob(path) + if not globbed: + # If globbing doesn't find anything, the literal string is + # used. + self.append(self.arg) + else: + self.extend(f[len(path)-len(self.arg):] for f in globbed) + self.glob = False + else: + self.append(self.arg) + self.arg = None + + def _parse_unquoted(self): + """ + Parse command line remainder in the context of an unquoted string. + """ + while self.cline: + # Find the next token + m = _unquoted_tokens.search(self.cline) + # If we find none, the remainder of the string can be pushed to + # the current argument and the argument finalized + if not m: + self._push(self.cline) + break + # The beginning of the string, up to the found token, is part of + # the current argument + if m.start(): + self._push(self.cline[:m.start()]) + self.cline = self.cline[m.end():] + + match = dict([(name, value) for name, value in m.groupdict().items() if value]) + if 'quote' in match: + # " or ' start a quoted string + if match['quote'] == '"': + self._parse_doubly_quoted() + else: + self._parse_quoted() + elif 'comment' in match: + # Comments are ignored. The current argument can be finalized, + # and parsing stopped. + break + elif 'special' in match: + # Unquoted, non-escaped special characters need to be sent to a + # shell. + raise MetaCharacterException, match['special'] + elif 'whitespace' in match: + # Whitespaces terminate current argument. + self._next() + elif 'escape' in match: + # Escaped backslashes turn into a single backslash + self._push('\\') + elif 'backslashed' in match: + # Backslashed characters are unbackslashed + # e.g. echo \a -> a + self._push(match['backslashed'][1]) + elif 'glob' in match: + # ? or * will need globbing + self.glob = True + self._push(m.group(0)) + else: + raise Exception, "Shouldn't reach here" + if self.arg: + self._next() + + def _parse_quoted(self): + # Single quoted strings are preserved, except for the final quote + index = self.cline.find("'") + if index == -1: + raise Exception, 'Unterminated quoted string in command' + self._push(self.cline[:index]) + self.cline = self.cline[index+1:] + + def _parse_doubly_quoted(self): + if not self.cline: + raise Exception, 'Unterminated quoted string in command' + while self.cline: + m = _doubly_quoted_tokens.search(self.cline) + if not m: + raise Exception, 'Unterminated quoted string in command' + self._push(self.cline[:m.start()]) + self.cline = self.cline[m.end():] + match = dict([(name, value) for name, value in m.groupdict().items() if value]) + if 'quote' in match: + # a double quote ends the quoted string, so go back to + # unquoted parsing + return + elif 'special' in match: + # Unquoted, non-escaped special characters in a doubly quoted + # string still have a special meaning and need to be sent to a + # shell. + raise MetaCharacterException, match['special'] + elif 'escape' in match: + # Escaped backslashes turn into a single backslash + self._push('\\') + elif 'backslashedquote' in match: + # Backslashed double quotes are un-backslashed + self._push('"') + elif 'backslashed' in match: + # Backslashed characters are kept backslashed + self._push(match['backslashed']) + +def clinetoargv(cline, cwd): + """ + If this command line can safely skip the shell, return an argv array. + @returns argv, badchar + """ + str = _escapednewlines.sub('', cline) + try: + args = ClineSplitter(str, cwd) + except MetaCharacterException, e: + return None, e.char + + if len(args) and args[0].find('=') != -1: + return None, '=' + + return args, None + +# shellwords contains a set of shell builtin commands that need to be +# executed within a shell. It also contains a set of commands that are known +# to be giving problems when run directly instead of through the msys shell. +shellwords = (':', '.', 'break', 'cd', 'continue', 'exec', 'exit', 'export', + 'getopts', 'hash', 'pwd', 'readonly', 'return', 'shift', + 'test', 'times', 'trap', 'umask', 'unset', 'alias', + 'set', 'bind', 'builtin', 'caller', 'command', 'declare', + 'echo', 'enable', 'help', 'let', 'local', 'logout', + 'printf', 'read', 'shopt', 'source', 'type', 'typeset', + 'ulimit', 'unalias', 'set', 'find') + +def prepare_command(cline, cwd, loc): + """ + Returns a list of command and arguments for the given command line string. + If the command needs to be run through a shell for some reason, the + returned list contains the shell invocation. + """ + + #TODO: call this once up-front somewhere and save the result? + shell, msys = util.checkmsyscompat() + + shellreason = None + executable = None + if msys and cline.startswith('/'): + shellreason = "command starts with /" + else: + argv, badchar = clinetoargv(cline, cwd) + if argv is None: + shellreason = "command contains shell-special character '%s'" % (badchar,) + elif len(argv) and argv[0] in shellwords: + shellreason = "command starts with shell primitive '%s'" % (argv[0],) + elif argv and (os.sep in argv[0] or os.altsep and os.altsep in argv[0]): + executable = util.normaljoin(cwd, argv[0]) + # Avoid "%1 is not a valid Win32 application" errors, assuming + # that if the executable path is to be resolved with PATH, it will + # be a Win32 executable. + if sys.platform == 'win32' and os.path.isfile(executable) and open(executable, 'rb').read(2) == "#!": + shellreason = "command executable starts with a hashbang" + + if shellreason is not None: + _log.debug("%s: using shell: %s: '%s'", loc, shellreason, cline) + if msys: + if len(cline) > 3 and cline[1] == ':' and cline[2] == '/': + cline = '/' + cline[0] + cline[2:] + argv = [shell, "-c", cline] + executable = None + + return executable, argv + +def call(cline, env, cwd, loc, cb, context, echo, justprint=False): + executable, argv = prepare_command(cline, cwd, loc) + + if not len(argv): + cb(res=0) + return + + if argv[0] == command.makepypath: + command.main(argv[1:], env, cwd, cb) + return + + if argv[0:2] == [sys.executable.replace('\\', '/'), + command.makepypath.replace('\\', '/')]: + command.main(argv[2:], env, cwd, cb) + return + + context.call(argv, executable=executable, shell=False, env=env, cwd=cwd, cb=cb, + echo=echo, justprint=justprint) + +def call_native(module, method, argv, env, cwd, loc, cb, context, echo, justprint=False, + pycommandpath=None): + context.call_native(module, method, argv, env=env, cwd=cwd, cb=cb, + echo=echo, justprint=justprint, pycommandpath=pycommandpath) + +def statustoresult(status): + """ + Convert the status returned from waitpid into a prettier numeric result. + """ + sig = status & 0xFF + if sig: + return -sig + + return status >>8 + +class Job(object): + """ + A single job to be executed on the process pool. + """ + done = False # set to true when the job completes + + def __init__(self): + self.exitcode = -127 + + def notify(self, condition, result): + condition.acquire() + self.done = True + self.exitcode = result + condition.notify() + condition.release() + + def get_callback(self, condition): + return lambda result: self.notify(condition, result) + +class PopenJob(Job): + """ + A job that executes a command using subprocess.Popen. + """ + def __init__(self, argv, executable, shell, env, cwd): + Job.__init__(self) + self.argv = argv + self.executable = executable + self.shell = shell + self.env = env + self.cwd = cwd + self.parentpid = os.getpid() + + def run(self): + assert os.getpid() != self.parentpid + # subprocess.Popen doesn't use the PATH set in the env argument for + # finding the executable on some platforms (but strangely it does on + # others!), so set os.environ['PATH'] explicitly. This is parallel- + # safe because pymake uses separate processes for parallelism, and + # each process is serial. See http://bugs.python.org/issue8557 for a + # general overview of "subprocess PATH semantics and portability". + oldpath = os.environ['PATH'] + try: + if self.env is not None and self.env.has_key('PATH'): + os.environ['PATH'] = self.env['PATH'] + p = subprocess.Popen(self.argv, executable=self.executable, shell=self.shell, env=self.env, cwd=self.cwd) + return p.wait() + except OSError, e: + print >>sys.stderr, e + return -127 + finally: + os.environ['PATH'] = oldpath + +class PythonException(Exception): + def __init__(self, message, exitcode): + Exception.__init__(self) + self.message = message + self.exitcode = exitcode + + def __str__(self): + return self.message + + +class PythonJob(Job): + """ + A job that calls a Python method. + """ + def __init__(self, module, method, argv, env, cwd, pycommandpath=None): + self.module = module + self.method = method + self.argv = argv + self.env = env + self.cwd = cwd + self.pycommandpath = pycommandpath or [] + self.parentpid = os.getpid() + + def run(self): + assert os.getpid() != self.parentpid + # os.environ is a magic dictionary. Setting it to something else + # doesn't affect the environment of subprocesses, so use clear/update + oldenv = dict(os.environ) + + # sys.path is adjusted for the entire lifetime of the command + # execution. This ensures any delayed imports will still work. + oldsyspath = list(sys.path) + try: + os.chdir(self.cwd) + os.environ.clear() + os.environ.update(self.env) + + sys.path = [] + for p in sys.path + self.pycommandpath: + site.addsitedir(p) + sys.path.extend(oldsyspath) + + if self.module not in sys.modules: + try: + __import__(self.module) + except Exception as e: + print >>sys.stderr, 'Error importing %s: %s' % ( + self.module, e) + return -127 + + m = sys.modules[self.module] + if self.method not in m.__dict__: + print >>sys.stderr, "No method named '%s' in module %s" % (self.method, self.module) + return -127 + rv = m.__dict__[self.method](self.argv) + if rv != 0 and rv is not None: + print >>sys.stderr, ( + "Native command '%s %s' returned value '%s'" % + (self.module, self.method, rv)) + return (rv if isinstance(rv, int) else 1) + + except PythonException, e: + print >>sys.stderr, e + return e.exitcode + except: + e = sys.exc_info()[1] + if isinstance(e, SystemExit) and (e.code == 0 or e.code is None): + pass # sys.exit(0) is not a failure + else: + print >>sys.stderr, e + traceback.print_exc() + return -127 + finally: + os.environ.clear() + os.environ.update(oldenv) + sys.path = oldsyspath + # multiprocessing exits via os._exit, make sure that all output + # from command gets written out before that happens. + sys.stdout.flush() + sys.stderr.flush() + + return 0 + +def job_runner(job): + """ + Run a job. Called in a Process pool. + """ + return job.run() + +class ParallelContext(object): + """ + Manages the parallel execution of processes. + """ + + _allcontexts = set() + _condition = multiprocessing.Condition() + + def __init__(self, jcount): + self.jcount = jcount + self.exit = False + + self.processpool = multiprocessing.Pool(processes=jcount) + self.pending = deque() # deque of (cb, args, kwargs) + self.running = [] # list of (subprocess, cb) + + self._allcontexts.add(self) + + def finish(self): + assert len(self.pending) == 0 and len(self.running) == 0, "pending: %i running: %i" % (len(self.pending), len(self.running)) + self.processpool.close() + self.processpool.join() + self._allcontexts.remove(self) + + def run(self): + while len(self.pending) and len(self.running) < self.jcount: + cb, args, kwargs = self.pending.popleft() + cb(*args, **kwargs) + + def defer(self, cb, *args, **kwargs): + assert self.jcount > 1 or not len(self.pending), "Serial execution error defering %r %r %r: currently pending %r" % (cb, args, kwargs, self.pending) + self.pending.append((cb, args, kwargs)) + + def _docall_generic(self, pool, job, cb, echo, justprint): + if echo is not None: + print echo + processcb = job.get_callback(ParallelContext._condition) + if justprint: + processcb(0) + else: + pool.apply_async(job_runner, args=(job,), callback=processcb) + self.running.append((job, cb)) + + def call(self, argv, shell, env, cwd, cb, echo, justprint=False, executable=None): + """ + Asynchronously call the process + """ + + job = PopenJob(argv, executable=executable, shell=shell, env=env, cwd=cwd) + self.defer(self._docall_generic, self.processpool, job, cb, echo, justprint) + + def call_native(self, module, method, argv, env, cwd, cb, + echo, justprint=False, pycommandpath=None): + """ + Asynchronously call the native function + """ + + job = PythonJob(module, method, argv, env, cwd, pycommandpath) + self.defer(self._docall_generic, self.processpool, job, cb, echo, justprint) + + @staticmethod + def _waitany(condition): + def _checkdone(): + jobs = [] + for c in ParallelContext._allcontexts: + for i in xrange(0, len(c.running)): + if c.running[i][0].done: + jobs.append(c.running[i]) + for j in jobs: + if j in c.running: + c.running.remove(j) + return jobs + + # We must acquire the lock, and then check to see if any jobs have + # finished. If we don't check after acquiring the lock it's possible + # that all outstanding jobs will have completed before we wait and we'll + # wait for notifications that have already occurred. + condition.acquire() + jobs = _checkdone() + + if jobs == []: + condition.wait() + jobs = _checkdone() + + condition.release() + + return jobs + + @staticmethod + def spin(): + """ + Spin the 'event loop', and never return. + """ + + while True: + clist = list(ParallelContext._allcontexts) + for c in clist: + c.run() + + dowait = util.any((len(c.running) for c in ParallelContext._allcontexts)) + if dowait: + # Wait on local jobs first for perf + for job, cb in ParallelContext._waitany(ParallelContext._condition): + cb(job.exitcode) + else: + assert any(len(c.pending) for c in ParallelContext._allcontexts) + +def makedeferrable(usercb, **userkwargs): + def cb(*args, **kwargs): + kwargs.update(userkwargs) + return usercb(*args, **kwargs) + + return cb + +_serialContext = None +_parallelContext = None + +def getcontext(jcount): + global _serialContext, _parallelContext + if jcount == 1: + if _serialContext is None: + _serialContext = ParallelContext(1) + return _serialContext + else: + if _parallelContext is None: + _parallelContext = ParallelContext(jcount) + return _parallelContext + diff --git a/build/pymake/pymake/util.py b/build/pymake/pymake/util.py new file mode 100644 index 000000000..c63f930cc --- /dev/null +++ b/build/pymake/pymake/util.py @@ -0,0 +1,150 @@ +import os + +class MakeError(Exception): + def __init__(self, message, loc=None): + self.msg = message + self.loc = loc + + def __str__(self): + locstr = '' + if self.loc is not None: + locstr = "%s:" % (self.loc,) + + return "%s%s" % (locstr, self.msg) + +def normaljoin(path, suffix): + """ + Combine the given path with the suffix, and normalize if necessary to shrink the path to avoid hitting path length limits + """ + result = os.path.join(path, suffix) + if len(result) > 255: + result = os.path.normpath(result) + return result + +def joiniter(fd, it): + """ + Given an iterator that returns strings, write the words with a space in between each. + """ + + it = iter(it) + for i in it: + fd.write(i) + break + + for i in it: + fd.write(' ') + fd.write(i) + +def checkmsyscompat(): + """For msys compatibility on windows, honor the SHELL environment variable, + and if $MSYSTEM == MINGW32, run commands through $SHELL -c instead of + letting Python use the system shell.""" + if 'SHELL' in os.environ: + shell = os.environ['SHELL'] + elif 'MOZILLABUILD' in os.environ: + shell = os.environ['MOZILLABUILD'] + '/msys/bin/sh.exe' + elif 'COMSPEC' in os.environ: + shell = os.environ['COMSPEC'] + else: + raise DataError("Can't find a suitable shell!") + + msys = False + if 'MSYSTEM' in os.environ and os.environ['MSYSTEM'] == 'MINGW32': + msys = True + if not shell.lower().endswith(".exe"): + shell += ".exe" + return (shell, msys) + +if hasattr(str, 'partition'): + def strpartition(str, token): + return str.partition(token) + + def strrpartition(str, token): + return str.rpartition(token) + +else: + def strpartition(str, token): + """Python 2.4 compatible str.partition""" + + offset = str.find(token) + if offset == -1: + return str, '', '' + + return str[:offset], token, str[offset + len(token):] + + def strrpartition(str, token): + """Python 2.4 compatible str.rpartition""" + + offset = str.rfind(token) + if offset == -1: + return '', '', str + + return str[:offset], token, str[offset + len(token):] + +try: + from __builtin__ import any +except ImportError: + def any(it): + for i in it: + if i: + return True + return False + +class _MostUsedItem(object): + __slots__ = ('key', 'o', 'count') + + def __init__(self, key): + self.key = key + self.o = None + self.count = 1 + + def __repr__(self): + return "MostUsedItem(key=%r, count=%i, o=%r)" % (self.key, self.count, self.o) + +class MostUsedCache(object): + def __init__(self, capacity, creationfunc, verifyfunc): + self.capacity = capacity + self.cfunc = creationfunc + self.vfunc = verifyfunc + + self.d = {} + self.active = [] # lazily sorted! + + def setactive(self, item): + if item in self.active: + return + + if len(self.active) == self.capacity: + self.active.sort(key=lambda i: i.count) + old = self.active.pop(0) + old.o = None + # print "Evicting %s" % old.key + + self.active.append(item) + + def get(self, key): + item = self.d.get(key, None) + if item is None: + item = _MostUsedItem(key) + self.d[key] = item + else: + item.count += 1 + + if item.o is not None and self.vfunc(key, item.o): + return item.o + + item.o = self.cfunc(key) + self.setactive(item) + return item.o + + def verify(self): + for k, v in self.d.iteritems(): + if v.o: + assert v in self.active + else: + assert v not in self.active + + def debugitems(self): + l = [i.key for i in self.active] + l.sort() + return l diff --git a/build/pymake/pymake/win32process.py b/build/pymake/pymake/win32process.py new file mode 100644 index 000000000..880a26a5b --- /dev/null +++ b/build/pymake/pymake/win32process.py @@ -0,0 +1,28 @@ +from ctypes import windll, POINTER, byref, WinError +from ctypes.wintypes import WINFUNCTYPE, HANDLE, DWORD, BOOL + +INFINITE = -1 +WAIT_FAILED = 0xFFFFFFFF + +LPDWORD = POINTER(DWORD) +_GetExitCodeProcessProto = WINFUNCTYPE(BOOL, HANDLE, LPDWORD) +_GetExitCodeProcess = _GetExitCodeProcessProto(("GetExitCodeProcess", windll.kernel32)) +def GetExitCodeProcess(h): + exitcode = DWORD() + r = _GetExitCodeProcess(h, byref(exitcode)) + if r is 0: + raise WinError() + return exitcode.value + +_WaitForMultipleObjectsProto = WINFUNCTYPE(DWORD, DWORD, POINTER(HANDLE), BOOL, DWORD) +_WaitForMultipleObjects = _WaitForMultipleObjectsProto(("WaitForMultipleObjects", windll.kernel32)) + +def WaitForAnyProcess(processes): + arrtype = HANDLE * len(processes) + harray = arrtype(*(int(p._handle) for p in processes)) + + r = _WaitForMultipleObjects(len(processes), harray, False, INFINITE) + if r == WAIT_FAILED: + raise WinError() + + return processes[r], GetExitCodeProcess(int(processes[r]._handle)) <<8 diff --git a/build/pymake/tests/automatic-variables.mk b/build/pymake/tests/automatic-variables.mk new file mode 100644 index 000000000..5302c08ea --- /dev/null +++ b/build/pymake/tests/automatic-variables.mk @@ -0,0 +1,79 @@ +$(shell \ +mkdir -p src/subd; \ +mkdir subd; \ +touch dummy; \ +sleep 2; \ +touch subd/test.out src/subd/test.in2; \ +sleep 2; \ +touch subd/test.out2 src/subd/test.in; \ +sleep 2; \ +touch subd/host_test.out subd/host_test.out2; \ +sleep 2; \ +touch host_prog; \ +) + +VPATH = src + +all: prog host_prog prog dir/ + test "$@" = "all" + test "$<" = "prog" + test "$^" = "prog host_prog dir" + test "$?" = "prog host_prog dir" + test "$+" = "prog host_prog prog dir" + test "$(@D)" = "." + test "$(@F)" = "all" + test "$(<D)" = "." + test "$(<F)" = "prog" + test "$(^D)" = ". . ." + test "$(^F)" = "prog host_prog dir" + test "$(?D)" = ". . ." + test "$(?F)" = "prog host_prog dir" + test "$(+D)" = ". . . ." + test "$(+F)" = "prog host_prog prog dir" + @echo TEST-PASS + +dir/: + test "$@" = "dir" + test "$<" = "" + test "$^" = "" + test "$(@D)" = "." + test "$(@F)" = "dir" + mkdir $@ + +prog: subd/test.out subd/test.out2 + test "$@" = "prog" + test "$<" = "subd/test.out" + test "$^" = "subd/test.out subd/test.out2" # ^ + test "$?" = "subd/test.out subd/test.out2" # ? + cat $< + test "$$(cat $<)" = "remade" + test "$$(cat $(word 2,$^))" = "" + +host_prog: subd/host_test.out subd/host_test.out2 + @echo TEST-FAIL No need to remake + +%.out: %.in dummy + test "$@" = "subd/test.out" + test "$*" = "subd/test" # * + test "$<" = "src/subd/test.in" # < + test "$^" = "src/subd/test.in dummy" # ^ + test "$?" = "src/subd/test.in" # ? + test "$+" = "src/subd/test.in dummy" # + + test "$(@D)" = "subd" + test "$(@F)" = "test.out" + test "$(*D)" = "subd" + test "$(*F)" = "test" + test "$(<D)" = "src/subd" + test "$(<F)" = "test.in" + test "$(^D)" = "src/subd ." # ^D + test "$(^F)" = "test.in dummy" + test "$(?D)" = "src/subd" + test "$(?F)" = "test.in" + test "$(+D)" = "src/subd ." # +D + test "$(+F)" = "test.in dummy" + printf "remade" >$@ + +%.out2: %.in2 dummy + @echo TEST_FAIL No need to remake + +.PHONY: all diff --git a/build/pymake/tests/bad-command-continuation.mk b/build/pymake/tests/bad-command-continuation.mk new file mode 100644 index 000000000..d9ceccfc2 --- /dev/null +++ b/build/pymake/tests/bad-command-continuation.mk @@ -0,0 +1,3 @@ +all: + echo 'hello'\ +TEST-PASS diff --git a/build/pymake/tests/call.mk b/build/pymake/tests/call.mk new file mode 100644 index 000000000..9eeb7e00c --- /dev/null +++ b/build/pymake/tests/call.mk @@ -0,0 +1,12 @@ +test = $0 +reverse = $2 $1 +twice = $1$1 +sideeffect = $(shell echo "called$1:" >>dummyfile) + +all: + test "$(call test)" = "test" + test "$(call reverse,1,2)" = "2 1" +# expansion happens *before* substitution, thank sanity + test "$(call twice,$(sideeffect))" = "" + test `cat dummyfile` = "called:" + @echo TEST-PASS diff --git a/build/pymake/tests/cmd-stripdotslash.mk b/build/pymake/tests/cmd-stripdotslash.mk new file mode 100644 index 000000000..ce5ed4244 --- /dev/null +++ b/build/pymake/tests/cmd-stripdotslash.mk @@ -0,0 +1,5 @@ +all: + $(MAKE) -f $(TESTPATH)/cmd-stripdotslash.mk ./foo + +./foo: + @echo TEST-PASS diff --git a/build/pymake/tests/cmdgoals.mk b/build/pymake/tests/cmdgoals.mk new file mode 100644 index 000000000..a3b25e751 --- /dev/null +++ b/build/pymake/tests/cmdgoals.mk @@ -0,0 +1,9 @@ +default: + test "$(MAKECMDGOALS)" = "" + $(MAKE) -f $(TESTPATH)/cmdgoals.mk t1 t2 + @echo TEST-PASS + +t1: + test "$(MAKECMDGOALS)" = "t1 t2" + +t2: diff --git a/build/pymake/tests/commandmodifiers.mk b/build/pymake/tests/commandmodifiers.mk new file mode 100644 index 000000000..8440462f3 --- /dev/null +++ b/build/pymake/tests/commandmodifiers.mk @@ -0,0 +1,21 @@ +define COMMAND +$(1) + $(1) + +endef + +all: + $(call COMMAND,@true #TEST-FAIL) + $(call COMMAND,-exit 4) + $(call COMMAND,@-exit 1 # TEST-FAIL) + $(call COMMAND,-@exit 1 # TEST-FAIL) + $(call COMMAND,+exit 0) + $(call COMMAND,+-exit 1) + $(call COMMAND,@+exit 0 # TEST-FAIL) + $(call COMMAND,+@exit 0 # TEST-FAIL) + $(call COMMAND,-+@exit 1 # TEST-FAIL) + $(call COMMAND,+-@exit 1 # TEST-FAIL) + $(call COMMAND,@+-exit 1 # TEST-FAIL) + $(call COMMAND,@+-@+-exit 1 # TEST-FAIL) + $(call COMMAND,@@++exit 0 # TEST-FAIL) + @echo TEST-PASS diff --git a/build/pymake/tests/comment-parsing.mk b/build/pymake/tests/comment-parsing.mk new file mode 100644 index 000000000..d469e1aea --- /dev/null +++ b/build/pymake/tests/comment-parsing.mk @@ -0,0 +1,29 @@ +# where do comments take effect? + +VAR = val1 # comment +VAR2 = lit2\#hash +VAR2_1 = lit2.1\\\#hash +VAR3 = val3 +VAR4 = lit4\\#backslash +VAR4_1 = lit4\\\\#backslash +VAR5 = lit5\char +VAR6 = lit6\\char +VAR7 = lit7\\ +VAR8 = lit8\\\\ +VAR9 = lit9\\\\extra +# This comment extends to the next line \ +VAR3 = ignored + +all: + test "$(VAR)" = "val1 " + test "$(VAR2)" = "lit2#hash" + test '$(VAR2_1)' = 'lit2.1\#hash' + test "$(VAR3)" = "val3" + test '$(VAR4)' = 'lit4\' + test '$(VAR4_1)' = 'lit4\\' + test '$(VAR5)' = 'lit5\char' + test '$(VAR6)' = 'lit6\\char' + test '$(VAR7)' = 'lit7\\' + test '$(VAR8)' = 'lit8\\\\' + test '$(VAR9)' = 'lit9\\\\extra' + @echo "TEST-PASS" diff --git a/build/pymake/tests/continuations-in-functions.mk b/build/pymake/tests/continuations-in-functions.mk new file mode 100644 index 000000000..533df6176 --- /dev/null +++ b/build/pymake/tests/continuations-in-functions.mk @@ -0,0 +1,6 @@ +all: + test 'Hello world.' = '$(if 1,Hello \ + world.)' + test '(Hello world.)' != '(Hello \ + world.)' + @echo TEST-PASS diff --git a/build/pymake/tests/datatests.py b/build/pymake/tests/datatests.py new file mode 100644 index 000000000..513028b0b --- /dev/null +++ b/build/pymake/tests/datatests.py @@ -0,0 +1,237 @@ +import pymake.data, pymake.functions, pymake.util +import unittest +import re +from cStringIO import StringIO + +def multitest(cls): + for name in cls.testdata.iterkeys(): + def m(self, name=name): + return self.runSingle(*self.testdata[name]) + + setattr(cls, 'test_%s' % name, m) + return cls + +class SplitWordsTest(unittest.TestCase): + testdata = ( + (' test test.c test.o ', ['test', 'test.c', 'test.o']), + ('\ttest\t test.c \ntest.o', ['test', 'test.c', 'test.o']), + ) + + def runTest(self): + for s, e in self.testdata: + w = s.split() + self.assertEqual(w, e, 'splitwords(%r)' % (s,)) + +class GetPatSubstTest(unittest.TestCase): + testdata = ( + ('%.c', '%.o', ' test test.c test.o ', 'test test.o test.o'), + ('%', '%.o', ' test.c test.o ', 'test.c.o test.o.o'), + ('foo', 'bar', 'test foo bar', 'test bar bar'), + ('foo', '%bar', 'test foo bar', 'test %bar bar'), + ('%', 'perc_%', 'path', 'perc_path'), + ('\\%', 'sub%', 'p %', 'p sub%'), + ('%.c', '\\%%.o', 'foo.c bar.o baz.cpp', '%foo.o bar.o baz.cpp'), + ) + + def runTest(self): + for s, r, d, e in self.testdata: + words = d.split() + p = pymake.data.Pattern(s) + a = ' '.join((p.subst(r, word, False) + for word in words)) + self.assertEqual(a, e, 'Pattern(%r).subst(%r, %r)' % (s, r, d)) + +class LRUTest(unittest.TestCase): + # getkey, expected, funccount, debugitems + expected = ( + (0, '', 1, (0,)), + (0, '', 2, (0,)), + (1, ' ', 3, (1, 0)), + (1, ' ', 3, (1, 0)), + (0, '', 4, (0, 1)), + (2, ' ', 5, (2, 0, 1)), + (1, ' ', 5, (1, 2, 0)), + (3, ' ', 6, (3, 1, 2)), + ) + + def spaceFunc(self, l): + self.funccount += 1 + return ''.ljust(l) + + def runTest(self): + self.funccount = 0 + c = pymake.util.LRUCache(3, self.spaceFunc, lambda k, v: k % 2) + self.assertEqual(tuple(c.debugitems()), ()) + + for i in xrange(0, len(self.expected)): + k, e, fc, di = self.expected[i] + + v = c.get(k) + self.assertEqual(v, e) + self.assertEqual(self.funccount, fc, + "funccount, iteration %i, got %i expected %i" % (i, self.funccount, fc)) + goti = tuple(c.debugitems()) + self.assertEqual(goti, di, + "debugitems, iteration %i, got %r expected %r" % (i, goti, di)) + +class EqualityTest(unittest.TestCase): + def test_string_expansion(self): + s1 = pymake.data.StringExpansion('foo bar', None) + s2 = pymake.data.StringExpansion('foo bar', None) + + self.assertEqual(s1, s2) + + def test_expansion_simple(self): + s1 = pymake.data.Expansion(None) + s2 = pymake.data.Expansion(None) + + self.assertEqual(s1, s2) + + s1.appendstr('foo') + s2.appendstr('foo') + self.assertEqual(s1, s2) + + def test_expansion_string_finish(self): + """Adjacent strings should normalize to same value.""" + s1 = pymake.data.Expansion(None) + s2 = pymake.data.Expansion(None) + + s1.appendstr('foo') + s2.appendstr('foo') + + s1.appendstr(' bar') + s1.appendstr(' baz') + s2.appendstr(' bar baz') + + self.assertEqual(s1, s2) + + def test_function(self): + s1 = pymake.data.Expansion(None) + s2 = pymake.data.Expansion(None) + + n1 = pymake.data.StringExpansion('FOO', None) + n2 = pymake.data.StringExpansion('FOO', None) + + v1 = pymake.functions.VariableRef(None, n1) + v2 = pymake.functions.VariableRef(None, n2) + + s1.appendfunc(v1) + s2.appendfunc(v2) + + self.assertEqual(s1, s2) + + +class StringExpansionTest(unittest.TestCase): + def test_base_expansion_interface(self): + s1 = pymake.data.StringExpansion('FOO', None) + + self.assertTrue(s1.is_static_string) + + funcs = list(s1.functions()) + self.assertEqual(len(funcs), 0) + + funcs = list(s1.functions(True)) + self.assertEqual(len(funcs), 0) + + refs = list(s1.variable_references()) + self.assertEqual(len(refs), 0) + + +class ExpansionTest(unittest.TestCase): + def test_is_static_string(self): + e1 = pymake.data.Expansion() + e1.appendstr('foo') + + self.assertTrue(e1.is_static_string) + + e1.appendstr('bar') + self.assertTrue(e1.is_static_string) + + vname = pymake.data.StringExpansion('FOO', None) + func = pymake.functions.VariableRef(None, vname) + + e1.appendfunc(func) + + self.assertFalse(e1.is_static_string) + + def test_get_functions(self): + e1 = pymake.data.Expansion() + e1.appendstr('foo') + + vname1 = pymake.data.StringExpansion('FOO', None) + vname2 = pymake.data.StringExpansion('BAR', None) + + func1 = pymake.functions.VariableRef(None, vname1) + func2 = pymake.functions.VariableRef(None, vname2) + + e1.appendfunc(func1) + e1.appendfunc(func2) + + funcs = list(e1.functions()) + self.assertEqual(len(funcs), 2) + + func3 = pymake.functions.SortFunction(None) + func3.append(vname1) + + e1.appendfunc(func3) + + funcs = list(e1.functions()) + self.assertEqual(len(funcs), 3) + + refs = list(e1.variable_references()) + self.assertEqual(len(refs), 2) + + def test_get_functions_descend(self): + e1 = pymake.data.Expansion() + vname1 = pymake.data.StringExpansion('FOO', None) + func1 = pymake.functions.VariableRef(None, vname1) + e2 = pymake.data.Expansion() + e2.appendfunc(func1) + + func2 = pymake.functions.SortFunction(None) + func2.append(e2) + + e1.appendfunc(func2) + + funcs = list(e1.functions()) + self.assertEqual(len(funcs), 1) + + funcs = list(e1.functions(True)) + self.assertEqual(len(funcs), 2) + + self.assertTrue(isinstance(funcs[0], pymake.functions.SortFunction)) + + def test_is_filesystem_dependent(self): + e = pymake.data.Expansion() + vname1 = pymake.data.StringExpansion('FOO', None) + func1 = pymake.functions.VariableRef(None, vname1) + e.appendfunc(func1) + + self.assertFalse(e.is_filesystem_dependent) + + func2 = pymake.functions.WildcardFunction(None) + func2.append(vname1) + e.appendfunc(func2) + + self.assertTrue(e.is_filesystem_dependent) + + def test_is_filesystem_dependent_descend(self): + sort = pymake.functions.SortFunction(None) + wildcard = pymake.functions.WildcardFunction(None) + + e = pymake.data.StringExpansion('foo/*', None) + wildcard.append(e) + + e = pymake.data.Expansion(None) + e.appendfunc(wildcard) + + sort.append(e) + + e = pymake.data.Expansion(None) + e.appendfunc(sort) + + self.assertTrue(e.is_filesystem_dependent) + + +if __name__ == '__main__': + unittest.main() diff --git a/build/pymake/tests/default-goal-set-first.mk b/build/pymake/tests/default-goal-set-first.mk new file mode 100644 index 000000000..00a5b53a2 --- /dev/null +++ b/build/pymake/tests/default-goal-set-first.mk @@ -0,0 +1,7 @@ +.DEFAULT_GOAL := default + +not-default: + @echo TEST-FAIL did not run default rule + +default: + @echo TEST-PASS diff --git a/build/pymake/tests/default-goal.mk b/build/pymake/tests/default-goal.mk new file mode 100644 index 000000000..699d6c0cd --- /dev/null +++ b/build/pymake/tests/default-goal.mk @@ -0,0 +1,8 @@ +not-default: + @echo TEST-FAIL did not run default rule + +default: + @echo $(if $(filter not-default,$(INTERMEDIATE_DEFAULT_GOAL)),TEST-PASS,TEST-FAIL .DEFAULT_GOAL not set by $(MAKE)) + +INTERMEDIATE_DEFAULT_GOAL := $(.DEFAULT_GOAL) +.DEFAULT_GOAL := default diff --git a/build/pymake/tests/default-target.mk b/build/pymake/tests/default-target.mk new file mode 100644 index 000000000..701ac6916 --- /dev/null +++ b/build/pymake/tests/default-target.mk @@ -0,0 +1,14 @@ +test: VAR = value + +%.do: + @echo TEST-FAIL: ran target "$@", should have run "all" + +.PHONY: test + +all: + @echo TEST-PASS: the default target is all + +test: + @echo TEST-FAIL: ran target "$@", should have run "all" + +test.do: diff --git a/build/pymake/tests/default-target2.mk b/build/pymake/tests/default-target2.mk new file mode 100644 index 000000000..b5a4b1bbf --- /dev/null +++ b/build/pymake/tests/default-target2.mk @@ -0,0 +1,6 @@ +test.foo: %.foo: + test "$@" = "test.foo" + @echo TEST-PASS made test.foo by default + +all: + @echo TEST-FAIL made $@, should have made test.foo diff --git a/build/pymake/tests/define-directive.mk b/build/pymake/tests/define-directive.mk new file mode 100644 index 000000000..789988666 --- /dev/null +++ b/build/pymake/tests/define-directive.mk @@ -0,0 +1,69 @@ +define COMMANDS +shellvar=hello +test "$$shellvar" != "hello" +endef + +define COMMANDS2 +shellvar=hello; \ + test "$$shellvar" = "hello" +endef + +define VARWITHCOMMENT # comment +value +endef + +define TEST3 + whitespace +endef + +define TEST4 +define TEST5 +random +endef + endef + +ifdef TEST5 +$(error TEST5 should not be set) +endif + +define TEST6 + define TEST7 +random +endef +endef + +ifdef TEST7 +$(error TEST7 should not be set) +endif + +define TEST8 +is this # a comment? +endef + +ifneq ($(TEST8),is this \# a comment?) +$(error TEST8 value not expected: $(TEST8)) +endif + +# A backslash continuation "hides" the endef +define TEST9 +value \ +endef +endef + +# Test ridiculous spacing + define TEST10 + define TEST11 + baz +endef +define TEST12 + foo + endef + endef + +all: + $(COMMANDS) + $(COMMANDS2) + test '$(VARWITHCOMMENT)' = 'value' + test '$(COMMANDS2)' = 'shellvar=hello; test "$$shellvar" = "hello"' + test "$(TEST3)" = " whitespace" + @echo TEST-PASS diff --git a/build/pymake/tests/depfailed.mk b/build/pymake/tests/depfailed.mk new file mode 100644 index 000000000..ce4137c38 --- /dev/null +++ b/build/pymake/tests/depfailed.mk @@ -0,0 +1,4 @@ +#T returncode: 2 + +all: foo.out foo.in + @echo TEST-PASS diff --git a/build/pymake/tests/depfailedj.mk b/build/pymake/tests/depfailedj.mk new file mode 100644 index 000000000..a94c74f6f --- /dev/null +++ b/build/pymake/tests/depfailedj.mk @@ -0,0 +1,10 @@ +#T returncode: 2 +#T commandline: ['-j4'] + +$(shell touch foo.in) + +all: foo.in foo.out missing + @echo TEST-PASS + +%.out: %.in + cp $< $@ diff --git a/build/pymake/tests/diamond-deps.mk b/build/pymake/tests/diamond-deps.mk new file mode 100644 index 000000000..40a4176d9 --- /dev/null +++ b/build/pymake/tests/diamond-deps.mk @@ -0,0 +1,13 @@ +# If the dependency graph includes a diamond dependency, we should only remake +# once! + +all: depA depB + cat testfile + test `cat testfile` = "data"; + @echo TEST-PASS + +depA: testfile +depB: testfile + +testfile: + printf "data" >>$@ diff --git a/build/pymake/tests/dotslash-dir.mk b/build/pymake/tests/dotslash-dir.mk new file mode 100644 index 000000000..8b30d1e3c --- /dev/null +++ b/build/pymake/tests/dotslash-dir.mk @@ -0,0 +1,8 @@ +#T grep-for: "dotslash-built" +.PHONY: $(dir foo) + +all: $(dir foo) + @echo TEST-PASS + +$(dir foo): + @echo dotslash-built diff --git a/build/pymake/tests/dotslash-parse.mk b/build/pymake/tests/dotslash-parse.mk new file mode 100644 index 000000000..91461bedb --- /dev/null +++ b/build/pymake/tests/dotslash-parse.mk @@ -0,0 +1,4 @@ +./: + +# This is merely a test to see that pymake doesn't choke on parsing ./ +$(info TEST-PASS) diff --git a/build/pymake/tests/dotslash-phony.mk b/build/pymake/tests/dotslash-phony.mk new file mode 100644 index 000000000..06b6ae78d --- /dev/null +++ b/build/pymake/tests/dotslash-phony.mk @@ -0,0 +1,3 @@ +.PHONY: ./ +./: + @echo TEST-PASS diff --git a/build/pymake/tests/dotslash.mk b/build/pymake/tests/dotslash.mk new file mode 100644 index 000000000..585db96b7 --- /dev/null +++ b/build/pymake/tests/dotslash.mk @@ -0,0 +1,9 @@ +$(shell touch foo.in) + +all: foo.out + test "$(wildcard ./*.in)" = "./foo.in" + @echo TEST-PASS + +./%.out: %.in + test "$@" = "foo.out" + cp $< $@ diff --git a/build/pymake/tests/doublecolon-exists.mk b/build/pymake/tests/doublecolon-exists.mk new file mode 100644 index 000000000..5d99a1f6b --- /dev/null +++ b/build/pymake/tests/doublecolon-exists.mk @@ -0,0 +1,16 @@ +$(shell touch foo.testfile1 foo.testfile2) + +# when a rule has commands and no prerequisites, should it be executed? +# double-colon: yes +# single-colon: no + +all: foo.testfile1 foo.testfile2 + test "$$(cat foo.testfile1)" = "" + test "$$(cat foo.testfile2)" = "remade:foo.testfile2" + @echo TEST-PASS + +foo.testfile1: + @echo TEST-FAIL + +foo.testfile2:: + printf "remade:$@"> $@ diff --git a/build/pymake/tests/doublecolon-priordeps.mk b/build/pymake/tests/doublecolon-priordeps.mk new file mode 100644 index 000000000..6cdf3a8e7 --- /dev/null +++ b/build/pymake/tests/doublecolon-priordeps.mk @@ -0,0 +1,19 @@ +#T commandline: ['-j3'] + +# All *prior* dependencies of a doublecolon rule must be satisfied before +# subsequent commands are run. + +all:: target1 + +all:: target2 + test -f target1 + @echo TEST-PASS + +target1: + touch starting-$@ + sleep 1 + touch $@ + +target2: + sleep 0.1 + test -f starting-target1 diff --git a/build/pymake/tests/doublecolon-remake.mk b/build/pymake/tests/doublecolon-remake.mk new file mode 100644 index 000000000..52aa9265c --- /dev/null +++ b/build/pymake/tests/doublecolon-remake.mk @@ -0,0 +1,4 @@ +$(shell touch somefile) + +all:: somefile + @echo TEST-PASS diff --git a/build/pymake/tests/dynamic-var.mk b/build/pymake/tests/dynamic-var.mk new file mode 100644 index 000000000..0993b9ccf --- /dev/null +++ b/build/pymake/tests/dynamic-var.mk @@ -0,0 +1,18 @@ +# The *name* of variables can be constructed dynamically. + +VARNAME = FOOBAR + +$(VARNAME) = foovalue +$(VARNAME)2 = foo2value + +$(VARNAME:%BAR=%BAM) = foobam + +all: + test "$(FOOBAR)" = "foovalue" + test "$(flavor FOOBAZ)" = "undefined" + test "$(FOOBAR2)" = "bazvalue" + test "$(FOOBAM)" = "foobam" + @echo TEST-PASS + +VARNAME = FOOBAZ +FOOBAR2 = bazvalue diff --git a/build/pymake/tests/empty-arg.mk b/build/pymake/tests/empty-arg.mk new file mode 100644 index 000000000..616e5b694 --- /dev/null +++ b/build/pymake/tests/empty-arg.mk @@ -0,0 +1,2 @@ +all: + @ sh -c 'if [ $$# = 3 ] ; then echo TEST-PASS; else echo TEST-FAIL; fi' -- a "" b diff --git a/build/pymake/tests/empty-command-semicolon.mk b/build/pymake/tests/empty-command-semicolon.mk new file mode 100644 index 000000000..07789f3f1 --- /dev/null +++ b/build/pymake/tests/empty-command-semicolon.mk @@ -0,0 +1,5 @@ +all: + @echo TEST-PASS + +foo: ; + diff --git a/build/pymake/tests/empty-with-deps.mk b/build/pymake/tests/empty-with-deps.mk new file mode 100644 index 000000000..284e5a113 --- /dev/null +++ b/build/pymake/tests/empty-with-deps.mk @@ -0,0 +1,4 @@ +default.test: default.c + +default.c: + @echo TEST-PASS diff --git a/build/pymake/tests/env-var-append.mk b/build/pymake/tests/env-var-append.mk new file mode 100644 index 000000000..4db39c45f --- /dev/null +++ b/build/pymake/tests/env-var-append.mk @@ -0,0 +1,7 @@ +#T environment: {'FOO': 'TEST'} + +FOO += $(BAR) +BAR := PASS + +all: + @echo $(subst $(NULL) ,-,$(FOO)) diff --git a/build/pymake/tests/env-var-append2.mk b/build/pymake/tests/env-var-append2.mk new file mode 100644 index 000000000..fc0735d88 --- /dev/null +++ b/build/pymake/tests/env-var-append2.mk @@ -0,0 +1,8 @@ +#T environment: {'FOO': '$(BAZ)'} + +FOO += $(BAR) +BAR := PASS +BAZ := TEST + +all: + @echo $(subst $(NULL) ,-,$(FOO)) diff --git a/build/pymake/tests/eof-continuation.mk b/build/pymake/tests/eof-continuation.mk new file mode 100644 index 000000000..daeaabc3e --- /dev/null +++ b/build/pymake/tests/eof-continuation.mk @@ -0,0 +1,5 @@ +all: + test '$(TESTVAR)' = 'testval\' + @echo TEST-PASS + +TESTVAR = testval\
\ No newline at end of file diff --git a/build/pymake/tests/escape-chars.mk b/build/pymake/tests/escape-chars.mk new file mode 100644 index 000000000..ebea33074 --- /dev/null +++ b/build/pymake/tests/escape-chars.mk @@ -0,0 +1,26 @@ +space = $(NULL) $(NULL) +hello$(space)world$(space) = hellovalue + +A = aval + +VAR = value1\\ +VARAWFUL = value1\\#comment +VAR2 = value2 +VAR3 = test\$A +VAR4 = value4\\value5 + +VAR5 = value1\\ \ \ + value2 + +EPERCENT = \% + +all: + test "$(hello world )" = "hellovalue" + test "$(VAR)" = "value1\\" + test '$(VARAWFUL)' = 'value1\' + test "$(VAR2)" = "value2" + test "$(VAR3)" = "test\aval" + test "$(VAR4)" = "value4\\value5" + test "$(VAR5)" = "value1\\ \ value2" + test "$(EPERCENT)" = "\%" + @echo TEST-PASS diff --git a/build/pymake/tests/escaped-continuation.mk b/build/pymake/tests/escaped-continuation.mk new file mode 100644 index 000000000..537f7547f --- /dev/null +++ b/build/pymake/tests/escaped-continuation.mk @@ -0,0 +1,6 @@ +#T returncode: 2 + +all: + echo "Hello" \\ + test "world" = "not!" + @echo TEST-PASS diff --git a/build/pymake/tests/eval-duringexecute.mk b/build/pymake/tests/eval-duringexecute.mk new file mode 100644 index 000000000..dff848032 --- /dev/null +++ b/build/pymake/tests/eval-duringexecute.mk @@ -0,0 +1,12 @@ +#T returncode: 2 + +# Once parsing is finished, recursive expansion in commands are not allowed to create any new rules (it may only set variables) + +define MORERULE +all: + @echo TEST-FAIL +endef + +all: + $(eval $(MORERULE)) + @echo done diff --git a/build/pymake/tests/eval.mk b/build/pymake/tests/eval.mk new file mode 100644 index 000000000..de9759f02 --- /dev/null +++ b/build/pymake/tests/eval.mk @@ -0,0 +1,7 @@ +TESTVAR = val1 + +$(eval TESTVAR = val2) + +all: + test "$(TESTVAR)" = "val2" + @echo TEST-PASS diff --git a/build/pymake/tests/exit-code.mk b/build/pymake/tests/exit-code.mk new file mode 100644 index 000000000..84dcffcf9 --- /dev/null +++ b/build/pymake/tests/exit-code.mk @@ -0,0 +1,5 @@ +#T returncode: 2 + +all: + exit 1 + @echo TEST-PASS diff --git a/build/pymake/tests/file-functions-symlinks.mk b/build/pymake/tests/file-functions-symlinks.mk new file mode 100644 index 000000000..dcc0f6eef --- /dev/null +++ b/build/pymake/tests/file-functions-symlinks.mk @@ -0,0 +1,22 @@ +#T returncode-on: {'win32': 2} +$(shell \ +touch test.file; \ +ln -s test.file test.symlink; \ +ln -s test.missing missing.symlink; \ +touch .testhidden; \ +mkdir foo; \ +touch foo/testfile; \ +ln -s foo symdir; \ +) + +all: + test "$(abspath test.file test.symlink)" = "$(CURDIR)/test.file $(CURDIR)/test.symlink" + test "$(realpath test.file test.symlink)" = "$(CURDIR)/test.file $(CURDIR)/test.file" + test "$(sort $(wildcard *))" = "foo symdir test.file test.symlink" + test "$(sort $(wildcard .*))" = ". .. .testhidden" + test "$(sort $(wildcard test*))" = "test.file test.symlink" + test "$(sort $(wildcard foo/*))" = "foo/testfile" + test "$(sort $(wildcard ./*))" = "./foo ./symdir ./test.file ./test.symlink" + test "$(sort $(wildcard f?o/*))" = "foo/testfile" + test "$(sort $(wildcard */*))" = "foo/testfile symdir/testfile" + @echo TEST-PASS diff --git a/build/pymake/tests/file-functions.mk b/build/pymake/tests/file-functions.mk new file mode 100644 index 000000000..7e4c68e85 --- /dev/null +++ b/build/pymake/tests/file-functions.mk @@ -0,0 +1,19 @@ +$(shell \ +touch test.file; \ +touch .testhidden; \ +mkdir foo; \ +touch foo/testfile; \ +) + +all: + test "$(abspath test.file)" = "$(CURDIR)/test.file" + test "$(realpath test.file)" = "$(CURDIR)/test.file" + test "$(sort $(wildcard *))" = "foo test.file" +# commented out because GNU make matches . and .. while python doesn't, and I don't +# care enough +# test "$(sort $(wildcard .*))" = ". .. .testhidden" + test "$(sort $(wildcard test*))" = "test.file" + test "$(sort $(wildcard foo/*))" = "foo/testfile" + test "$(sort $(wildcard ./*))" = "./foo ./test.file" + test "$(sort $(wildcard f?o/*))" = "foo/testfile" + @echo TEST-PASS diff --git a/build/pymake/tests/foreach-local-variable.mk b/build/pymake/tests/foreach-local-variable.mk new file mode 100644 index 000000000..2551621eb --- /dev/null +++ b/build/pymake/tests/foreach-local-variable.mk @@ -0,0 +1,8 @@ +# This test ensures that a local variable in a $(foreach) is bound to +# the local value, not a global value. +i := dummy + +all: + test "$(foreach i,foo bar,found:$(i))" = "found:foo found:bar" + test "$(i)" = "dummy" + @echo TEST-PASS diff --git a/build/pymake/tests/formattingtests.py b/build/pymake/tests/formattingtests.py new file mode 100644 index 000000000..7aad6d4cc --- /dev/null +++ b/build/pymake/tests/formattingtests.py @@ -0,0 +1,289 @@ +# This file contains test code for the formatting of parsed statements back to +# make file "source." It essentially verifies to to_source() functions +# scattered across the tree. + +import glob +import logging +import os.path +import unittest + +from pymake.data import Expansion +from pymake.data import StringExpansion +from pymake.functions import BasenameFunction +from pymake.functions import SubstitutionRef +from pymake.functions import VariableRef +from pymake.functions import WordlistFunction +from pymake.parserdata import Include +from pymake.parserdata import SetVariable +from pymake.parser import parsestring +from pymake.parser import SyntaxError + +class TestBase(unittest.TestCase): + pass + +class VariableRefTest(TestBase): + def test_string_name(self): + e = StringExpansion('foo', None) + v = VariableRef(None, e) + + self.assertEqual(v.to_source(), '$(foo)') + + def test_special_variable(self): + e = StringExpansion('<', None) + v = VariableRef(None, e) + + self.assertEqual(v.to_source(), '$<') + + def test_expansion_simple(self): + e = Expansion() + e.appendstr('foo') + e.appendstr('bar') + + v = VariableRef(None, e) + + self.assertEqual(v.to_source(), '$(foobar)') + +class StandardFunctionTest(TestBase): + def test_basename(self): + e1 = StringExpansion('foo', None) + v = VariableRef(None, e1) + e2 = Expansion(None) + e2.appendfunc(v) + + b = BasenameFunction(None) + b.append(e2) + + self.assertEqual(b.to_source(), '$(basename $(foo))') + + def test_wordlist(self): + e1 = StringExpansion('foo', None) + e2 = StringExpansion('bar ', None) + e3 = StringExpansion(' baz', None) + + w = WordlistFunction(None) + w.append(e1) + w.append(e2) + w.append(e3) + + self.assertEqual(w.to_source(), '$(wordlist foo,bar , baz)') + + def test_curly_brackets(self): + e1 = Expansion(None) + e1.appendstr('foo') + + e2 = Expansion(None) + e2.appendstr('foo ( bar') + + f = WordlistFunction(None) + f.append(e1) + f.append(e2) + + self.assertEqual(f.to_source(), '${wordlist foo,foo ( bar}') + +class StringExpansionTest(TestBase): + def test_simple(self): + e = StringExpansion('foobar', None) + self.assertEqual(e.to_source(), 'foobar') + + e = StringExpansion('$var', None) + self.assertEqual(e.to_source(), '$var') + + def test_escaping(self): + e = StringExpansion('$var', None) + self.assertEqual(e.to_source(escape_variables=True), '$$var') + + e = StringExpansion('this is # not a comment', None) + self.assertEqual(e.to_source(escape_comments=True), + 'this is \# not a comment') + + def test_empty(self): + e = StringExpansion('', None) + self.assertEqual(e.to_source(), '') + + e = StringExpansion(' ', None) + self.assertEqual(e.to_source(), ' ') + +class ExpansionTest(TestBase): + def test_single_string(self): + e = Expansion() + e.appendstr('foo') + + self.assertEqual(e.to_source(), 'foo') + + def test_multiple_strings(self): + e = Expansion() + e.appendstr('hello') + e.appendstr('world') + + self.assertEqual(e.to_source(), 'helloworld') + + def test_string_escape(self): + e = Expansion() + e.appendstr('$var') + self.assertEqual(e.to_source(), '$var') + self.assertEqual(e.to_source(escape_variables=True), '$$var') + + e = Expansion() + e.appendstr('foo') + e.appendstr(' $bar') + self.assertEqual(e.to_source(escape_variables=True), 'foo $$bar') + +class SubstitutionRefTest(TestBase): + def test_simple(self): + name = StringExpansion('foo', None) + c = StringExpansion('%.c', None) + o = StringExpansion('%.o', None) + s = SubstitutionRef(None, name, c, o) + + self.assertEqual(s.to_source(), '$(foo:%.c=%.o)') + +class SetVariableTest(TestBase): + def test_simple(self): + v = SetVariable(StringExpansion('foo', None), '=', 'bar', None, None) + self.assertEqual(v.to_source(), 'foo = bar') + + def test_multiline(self): + s = 'hello\nworld' + foo = StringExpansion('FOO', None) + + v = SetVariable(foo, '=', s, None, None) + + self.assertEqual(v.to_source(), 'define FOO\nhello\nworld\nendef') + + def test_multiline_immediate(self): + source = 'define FOO :=\nhello\nworld\nendef' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + def test_target_specific(self): + foo = StringExpansion('FOO', None) + bar = StringExpansion('BAR', None) + + v = SetVariable(foo, '+=', 'value', None, bar) + + self.assertEqual(v.to_source(), 'BAR: FOO += value') + +class IncludeTest(TestBase): + def test_include(self): + e = StringExpansion('rules.mk', None) + i = Include(e, True, False) + self.assertEqual(i.to_source(), 'include rules.mk') + + i = Include(e, False, False) + self.assertEqual(i.to_source(), '-include rules.mk') + +class IfdefTest(TestBase): + def test_simple(self): + source = 'ifdef FOO\nbar := $(value)\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + + def test_nested(self): + source = 'ifdef FOO\nifdef BAR\nhello = world\nendif\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + + def test_negation(self): + source = 'ifndef FOO\nbar += value\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + +class IfeqTest(TestBase): + def test_simple(self): + source = 'ifeq ($(foo),bar)\nhello = $(world)\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements[0].to_source(), source) + + def test_negation(self): + source = 'ifneq (foo,bar)\nhello = world\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + +class ConditionBlocksTest(TestBase): + def test_mixed_conditions(self): + source = 'ifdef FOO\nifeq ($(FOO),bar)\nvar += $(value)\nendif\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + def test_extra_statements(self): + source = 'ifdef FOO\nF := 1\nifdef BAR\nB += 1\nendif\nC = 1\nendif' + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + def test_whitespace_preservation(self): + source = "ifeq ' x' 'x '\n$(error stripping)\nendif" + + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), source) + + source = 'ifneq (x , x)\n$(error stripping)\nendif' + statements = parsestring(source, 'foo.mk') + self.assertEqual(statements.to_source(), + 'ifneq (x,x)\n$(error stripping)\nendif') + +class MakefileCorupusTest(TestBase): + """Runs the make files from the pymake corpus through the formatter. + + All the above tests are child's play compared to this. + """ + + # Our reformatting isn't perfect. We ignore files with known failures until + # we make them work. + # TODO Address these formatting corner cases. + _IGNORE_FILES = [ + # We are thrown off by backslashes at end of lines. + 'comment-parsing.mk', + 'escape-chars.mk', + 'include-notfound.mk', + ] + + def _get_test_files(self): + ourdir = os.path.dirname(os.path.abspath(__file__)) + + for makefile in glob.glob(os.path.join(ourdir, '*.mk')): + if os.path.basename(makefile) in self._IGNORE_FILES: + continue + + source = None + with open(makefile, 'rU') as fh: + source = fh.read() + + try: + yield (makefile, source, parsestring(source, makefile)) + except SyntaxError: + continue + + def test_reparse_consistency(self): + for filename, source, statements in self._get_test_files(): + reformatted = statements.to_source() + + # We should be able to parse the reformatted source fine. + new_statements = parsestring(reformatted, filename) + + # If we do the formatting again, the representation shouldn't + # change. i.e. the only lossy change should be the original + # (whitespace and some semantics aren't preserved). + reformatted_again = new_statements.to_source() + self.assertEqual(reformatted, reformatted_again, + '%s has lossless reformat.' % filename) + + self.assertEqual(len(statements), len(new_statements)) + + for i in xrange(0, len(statements)): + original = statements[i] + formatted = new_statements[i] + + self.assertEqual(original, formatted, '%s %d: %s != %s' % (filename, + i, original, formatted)) + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + unittest.main() diff --git a/build/pymake/tests/func-refs.mk b/build/pymake/tests/func-refs.mk new file mode 100644 index 000000000..82ab17ba8 --- /dev/null +++ b/build/pymake/tests/func-refs.mk @@ -0,0 +1,11 @@ +unknown var = uval + +all: + test "$(subst a,b,value)" = "vblue" + test "${subst a,b,va)lue}" = "vb)lue" + test "$(subst /,\,ab/c)" = "ab\c" + test '$(subst a,b,\\#)' = '\\#' + test "$( subst a,b,value)" = "" + test "$(Subst a,b,value)" = "" + test "$(unknown var)" = "uval" + @echo TEST-PASS diff --git a/build/pymake/tests/functions.mk b/build/pymake/tests/functions.mk new file mode 100644 index 000000000..817be07aa --- /dev/null +++ b/build/pymake/tests/functions.mk @@ -0,0 +1,36 @@ +all: + test "$(subst e,EE,hello)" = "hEEllo" + test "$(strip $(NULL) test data )" = "test data" + test "$(findstring hell,hello)" = "hell" + test "$(findstring heaven,hello)" = "" + test "$(filter foo/%.c b%,foo/a.c b.c foo/a.o)" = "foo/a.c b.c" + test "$(filter foo,foo bar)" = "foo" + test "$(filter-out foo/%.c b%,foo/a.c b.c foo/a.o)" = "foo/a.o" + test "$(filter-out %.c,foo,bar.c foo,bar.o)" = "foo,bar.o" + test "$(sort .go a b aa A c cc)" = ".go A a aa b c cc" + test "$(word 1, hello )" = "hello" + test "$(word 2, hello )" = "" + test "$(wordlist 1, 2, foo bar baz )" = "foo bar" + test "$(words 1 2 3)" = "3" + test "$(words )" = "0" + test "$(firstword $(NULL) foo bar baz)" = "foo" + test "$(firstword )" = "" + test "$(dir foo.c path/foo.o dir/dir2/)" = "./ path/ dir/dir2/" + test "$(notdir foo.c path/foo.o dir/dir2/)" = "foo.c foo.o " + test "$(suffix src/foo.c dir/my.dir/foo foo.o)" = ".c .o" + test "$(basename src/foo.c dir/my.dir/foo foo.c .c)" = "src/foo dir/my.dir/foo foo " + test "$(addprefix src/,foo bar.c dir/foo)" = "src/foo src/bar.c src/dir/foo" + test "$(addsuffix .c,foo dir/bar)" = "foo.c dir/bar.c" + test "$(join a b c, 1 2 3)" = "a1 b2 c3" + test "$(join a b, 1 2 3)" = "a1 b2 3" + test "$(join a b c, 1 2)" = "a1 b2 c" + test "$(if $(NULL) ,yes)" = "" + test "$(if 1,yes,no)" = "yes" + test "$(if ,yes,no )" = "no " + test "$(if ,$(error Short-circuit problem))" = "" + test "$(or $(NULL),1)" = "1" + test "$(or $(NULL),2,$(warning TEST-FAIL bad or short-circuit))" = "2" + test "$(and ,$(warning TEST-FAIL bad and short-circuit))" = "" + test "$(and 1,2)" = "2" + test "$(foreach i,foo bar,found:$(i))" = "found:foo found:bar" + @echo TEST-PASS diff --git a/build/pymake/tests/functiontests.py b/build/pymake/tests/functiontests.py new file mode 100644 index 000000000..43a344a05 --- /dev/null +++ b/build/pymake/tests/functiontests.py @@ -0,0 +1,54 @@ +import unittest + +import pymake.data +import pymake.functions + +class VariableRefTest(unittest.TestCase): + def test_get_expansions(self): + e = pymake.data.StringExpansion('FOO', None) + f = pymake.functions.VariableRef(None, e) + + exps = list(f.expansions()) + self.assertEqual(len(exps), 1) + +class GetExpansionsTest(unittest.TestCase): + def test_get_arguments(self): + f = pymake.functions.SubstFunction(None) + + e1 = pymake.data.StringExpansion('FOO', None) + e2 = pymake.data.StringExpansion('BAR', None) + e3 = pymake.data.StringExpansion('BAZ', None) + + f.append(e1) + f.append(e2) + f.append(e3) + + exps = list(f.expansions()) + self.assertEqual(len(exps), 3) + + def test_descend(self): + f = pymake.functions.StripFunction(None) + + e = pymake.data.Expansion(None) + + e1 = pymake.data.StringExpansion('FOO', None) + f1 = pymake.functions.VariableRef(None, e1) + e.appendfunc(f1) + + f2 = pymake.functions.WildcardFunction(None) + e2 = pymake.data.StringExpansion('foo/*', None) + f2.append(e2) + e.appendfunc(f2) + + f.append(e) + + exps = list(f.expansions()) + self.assertEqual(len(exps), 1) + + exps = list(f.expansions(True)) + self.assertEqual(len(exps), 3) + + self.assertFalse(f.is_filesystem_dependent) + +if __name__ == '__main__': + unittest.main() diff --git a/build/pymake/tests/if-syntaxerr.mk b/build/pymake/tests/if-syntaxerr.mk new file mode 100644 index 000000000..c172492ef --- /dev/null +++ b/build/pymake/tests/if-syntaxerr.mk @@ -0,0 +1,6 @@ +#T returncode: 2 + +ifeq ($(FOO,VAR)) +all: + @echo TEST_FAIL +endif diff --git a/build/pymake/tests/ifdefs-nesting.mk b/build/pymake/tests/ifdefs-nesting.mk new file mode 100644 index 000000000..340530ffa --- /dev/null +++ b/build/pymake/tests/ifdefs-nesting.mk @@ -0,0 +1,13 @@ +ifdef RANDOM +ifeq (,$(error Not evaluated!)) +endif +endif + +ifdef RANDOM +ifeq (,) +else ifeq (,$(error Not evaluated!)) +endif +endif + +all: + @echo TEST-PASS diff --git a/build/pymake/tests/ifdefs.mk b/build/pymake/tests/ifdefs.mk new file mode 100644 index 000000000..a779d197b --- /dev/null +++ b/build/pymake/tests/ifdefs.mk @@ -0,0 +1,127 @@ +ifdef FOO +$(error FOO is not defined!) +endif + +FOO = foo +FOOFOUND = false +BARFOUND = false +BAZFOUND = false + +ifdef FOO +FOOFOUND = true +else ifdef BAR +BARFOUND = true +else +BAZFOUND = true +endif + +BAR2 = bar2 +FOO2FOUND = false +BAR2FOUND = false +BAZ2FOUND = false + +ifdef FOO2 +FOO2FOUND = true +else ifdef BAR2 +BAR2FOUND = true +else +BAZ2FOUND = true +endif + +FOO3FOUND = false +BAR3FOUND = false +BAZ3FOUND = false + +ifdef FOO3 +FOO3FOUND = true +else ifdef BAR3 +BAR3FOUND = true +else +BAZ3FOUND = true +endif + +ifdef RANDOM +CONTINUATION = \ +else \ +endif +endif + +ifndef ASDFJK +else +$(error ASFDJK was not set) +endif + +TESTSET = + +ifdef TESTSET +$(error TESTSET was not set) +endif + +TESTEMPTY = $(NULL) +ifndef TESTEMPTY +$(error TEST-FAIL TESTEMPTY was probably expanded!) +endif + +# ifneq ( a,a) +# $(error Arguments to ifeq should be stripped before evaluation) +# endif + +XSPACE = x # trick + +ifneq ($(NULL),$(NULL)) +$(error TEST-FAIL ifneq) +endif + +ifneq (x , x) +$(error argument-stripping1) +endif + +ifeq ( x,x ) +$(error argument-stripping2) +endif + +ifneq ($(XSPACE), x ) +$(error argument-stripping3) +endif + +ifeq 'x ' ' x' +$(error TEST-FAIL argument-stripping4) +endif + +all: + test $(FOOFOUND) = true # FOOFOUND + test $(BARFOUND) = false # BARFOUND + test $(BAZFOUND) = false # BAZFOUND + test $(FOO2FOUND) = false # FOO2FOUND + test $(BAR2FOUND) = true # BAR2FOUND + test $(BAZ2FOUND) = false # BAZ2FOUND + test $(FOO3FOUND) = false # FOO3FOUND + test $(BAR3FOUND) = false # BAR3FOUND + test $(BAZ3FOUND) = true # BAZ3FOUND +ifneq ($(FOO),foo) + echo TEST-FAIL 'FOO neq foo: "$(FOO)"' +endif +ifneq ($(FOO), foo) # Whitespace after the comma is stripped + echo TEST-FAIL 'FOO plus whitespace' +endif +ifeq ($(FOO), foo ) # But not trailing whitespace + echo TEST-FAIL 'FOO plus trailing whitespace' +endif +ifeq ( $(FOO),foo) # Not whitespace after the paren + echo TEST-FAIL 'FOO with leading whitespace' +endif +ifeq ($(FOO),$(NULL) foo) # Nor whitespace after expansion + echo TEST-FAIL 'FOO with embedded ws' +endif +ifeq ($(BAR2),bar) + echo TEST-FAIL 'BAR2 eq bar' +endif +ifeq '$(BAR3FOUND)' 'false' + echo BAR3FOUND is ok +else + echo TEST-FAIL BAR3FOUND is not ok +endif +ifndef FOO + echo TEST-FAIL "foo not defined?" +endif + @echo TEST-PASS diff --git a/build/pymake/tests/ignore-error.mk b/build/pymake/tests/ignore-error.mk new file mode 100644 index 000000000..dc8d3a72c --- /dev/null +++ b/build/pymake/tests/ignore-error.mk @@ -0,0 +1,13 @@ +all: + -rm foo + +-rm bar + -+rm baz + @-rm bah + -@rm humbug + +-@rm sincere + +@-rm flattery + @+-rm will + @-+rm not + -+@rm save + -@+rm you + @echo TEST-PASS diff --git a/build/pymake/tests/implicit-chain.mk b/build/pymake/tests/implicit-chain.mk new file mode 100644 index 000000000..16288b3f5 --- /dev/null +++ b/build/pymake/tests/implicit-chain.mk @@ -0,0 +1,12 @@ +all: test.prog + test "$$(cat $<)" = "Program: Object: Source: test.source" + @echo TEST-PASS + +%.prog: %.object + printf "Program: %s" "$$(cat $<)" > $@ + +%.object: %.source + printf "Object: %s" "$$(cat $<)" > $@ + +%.source: + printf "Source: %s" $@ > $@ diff --git a/build/pymake/tests/implicit-dir.mk b/build/pymake/tests/implicit-dir.mk new file mode 100644 index 000000000..c7f75e8d4 --- /dev/null +++ b/build/pymake/tests/implicit-dir.mk @@ -0,0 +1,16 @@ +# Implicit rules have special instructions to deal with directories, so that a pattern rule which doesn't directly apply +# may still be used. + +all: dir/host_test.otest + +host_%.otest: %.osource extra.file + @echo making $@ from $< + +test.osource: + @echo TEST-FAIL should have made dir/test.osource + +dir/test.osource: + @echo TEST-PASS made the correct dependency + +extra.file: + @echo building $@ diff --git a/build/pymake/tests/implicit-terminal.mk b/build/pymake/tests/implicit-terminal.mk new file mode 100644 index 000000000..db2e244ed --- /dev/null +++ b/build/pymake/tests/implicit-terminal.mk @@ -0,0 +1,16 @@ +#T returncode: 2 + +# the %.object rule is "terminal". This means that additional implicit rules cannot be chained to it. + +all: test.prog + test "$$(cat $<)" = "Program: Object: Source: test.source" + @echo TEST-FAIL + +%.prog: %.object + printf "Program: %s" "$$(cat $<)" > $@ + +%.object:: %.source + printf "Object: %s" "$$(cat $<)" > $@ + +%.source: + printf "Source: %s" $@ > $@ diff --git a/build/pymake/tests/implicitsubdir.mk b/build/pymake/tests/implicitsubdir.mk new file mode 100644 index 000000000..b9d854a2a --- /dev/null +++ b/build/pymake/tests/implicitsubdir.mk @@ -0,0 +1,12 @@ +$(shell \ +mkdir foo; \ +touch test.in \ +) + +all: foo/test.out + @echo TEST-PASS + +foo/%.out: %.in + cp $< $@ + + diff --git a/build/pymake/tests/include-dynamic.mk b/build/pymake/tests/include-dynamic.mk new file mode 100644 index 000000000..571895dc3 --- /dev/null +++ b/build/pymake/tests/include-dynamic.mk @@ -0,0 +1,21 @@ +$(shell \ +if ! test -f include-dynamic.inc; then \ + echo "TESTVAR = oldval" > include-dynamic.inc; \ + sleep 2; \ + echo "TESTVAR = newval" > include-dynamic.inc.in; \ +fi \ +) + +# before running the 'all' rule, we should be rebuilding include-dynamic.inc, +# because there is a rule to do so + +all: + test $(TESTVAR) = newval + test "$(MAKE_RESTARTS)" = 1 + @echo TEST-PASS + +include-dynamic.inc: include-dynamic.inc.in + test "$(MAKE_RESTARTS)" = "" + cp $< $@ + +include include-dynamic.inc diff --git a/build/pymake/tests/include-file.inc b/build/pymake/tests/include-file.inc new file mode 100644 index 000000000..d5d495dec --- /dev/null +++ b/build/pymake/tests/include-file.inc @@ -0,0 +1 @@ +INCLUDED = yes diff --git a/build/pymake/tests/include-missing.mk b/build/pymake/tests/include-missing.mk new file mode 100644 index 000000000..583d0a065 --- /dev/null +++ b/build/pymake/tests/include-missing.mk @@ -0,0 +1,9 @@ +#T returncode: 2 + +# If an include file isn't present and doesn't have a rule to remake it, make +# should fail. + +include notfound.mk + +all: + @echo TEST-FAIL diff --git a/build/pymake/tests/include-notfound.mk b/build/pymake/tests/include-notfound.mk new file mode 100644 index 000000000..1ee7e05b2 --- /dev/null +++ b/build/pymake/tests/include-notfound.mk @@ -0,0 +1,19 @@ +ifdef __WIN32__ +PS:=\\# +else +PS:=/ +endif + +ifneq ($(strip $(MAKEFILE_LIST)),$(NATIVE_TESTPATH)$(PS)include-notfound.mk) +$(error MAKEFILE_LIST incorrect: '$(MAKEFILE_LIST)' (expected '$(NATIVE_TESTPATH)$(PS)include-notfound.mk')) +endif + +-include notfound.inc-dummy + +ifneq ($(strip $(MAKEFILE_LIST)),$(NATIVE_TESTPATH)$(PS)include-notfound.mk) +$(error MAKEFILE_LIST incorrect: '$(MAKEFILE_LIST)' (expected '$(NATIVE_TESTPATH)$(PS)include-notfound.mk')) +endif + +all: + @echo TEST-PASS + diff --git a/build/pymake/tests/include-optional-warning.mk b/build/pymake/tests/include-optional-warning.mk new file mode 100644 index 000000000..901938dff --- /dev/null +++ b/build/pymake/tests/include-optional-warning.mk @@ -0,0 +1,4 @@ +-include TEST-FAIL.mk + +all: + @echo TEST-PASS diff --git a/build/pymake/tests/include-regen.mk b/build/pymake/tests/include-regen.mk new file mode 100644 index 000000000..c86e0c78d --- /dev/null +++ b/build/pymake/tests/include-regen.mk @@ -0,0 +1,10 @@ +# avoid infinite loops by not remaking makefiles with +# double-colon no-dependency rules +# http://www.gnu.org/software/make/manual/make.html#Remaking-Makefiles +-include notfound.mk + +all: + @echo TEST-PASS + +notfound.mk:: + @echo TEST-FAIL diff --git a/build/pymake/tests/include-regen2.mk b/build/pymake/tests/include-regen2.mk new file mode 100644 index 000000000..fc7fef073 --- /dev/null +++ b/build/pymake/tests/include-regen2.mk @@ -0,0 +1,10 @@ +# make should make makefiles that it has rules for if they are
+# included
+include test.mk
+
+all:
+ test "$(X)" = "1"
+ @echo "TEST-PASS"
+
+test.mk:
+ @echo "X = 1" > $@
diff --git a/build/pymake/tests/include-regen3.mk b/build/pymake/tests/include-regen3.mk new file mode 100644 index 000000000..878ce0adc --- /dev/null +++ b/build/pymake/tests/include-regen3.mk @@ -0,0 +1,10 @@ +# make should make makefiles that it has rules for if they are
+# included
+-include test.mk
+
+all:
+ test "$(X)" = "1"
+ @echo "TEST-PASS"
+
+test.mk:
+ @echo "X = 1" > $@
diff --git a/build/pymake/tests/include-test.mk b/build/pymake/tests/include-test.mk new file mode 100644 index 000000000..3608fc269 --- /dev/null +++ b/build/pymake/tests/include-test.mk @@ -0,0 +1,8 @@ +$(shell echo "INCLUDED2 = yes" >local-include.inc) + +include $(TESTPATH)/include-file.inc local-include.inc + +all: + test "$(INCLUDED)" = "yes" + test "$(INCLUDED2)" = "yes" + @echo TEST-PASS diff --git a/build/pymake/tests/includedeps-norebuild.mk b/build/pymake/tests/includedeps-norebuild.mk new file mode 100644 index 000000000..e30abd439 --- /dev/null +++ b/build/pymake/tests/includedeps-norebuild.mk @@ -0,0 +1,15 @@ +#T gmake skip + +$(shell \ +touch filemissing; \ +sleep 2; \ +touch file1; \ +) + +all: file1 + @echo TEST-PASS + +includedeps $(TESTPATH)/includedeps.deps + +file1: + @echo TEST-FAIL diff --git a/build/pymake/tests/includedeps-sideeffects.mk b/build/pymake/tests/includedeps-sideeffects.mk new file mode 100644 index 000000000..7e4ea30a2 --- /dev/null +++ b/build/pymake/tests/includedeps-sideeffects.mk @@ -0,0 +1,10 @@ +#T gmake skip +#T returncode: 2 + +all: file1 filemissing + @echo TEST-PASS + +includedeps $(TESTPATH)/includedeps.deps + +file: + touch $@ diff --git a/build/pymake/tests/includedeps-stripdotslash.deps b/build/pymake/tests/includedeps-stripdotslash.deps new file mode 100644 index 000000000..352fca1bb --- /dev/null +++ b/build/pymake/tests/includedeps-stripdotslash.deps @@ -0,0 +1 @@ +./test: TEST-PASS diff --git a/build/pymake/tests/includedeps-stripdotslash.mk b/build/pymake/tests/includedeps-stripdotslash.mk new file mode 100644 index 000000000..ee942e6db --- /dev/null +++ b/build/pymake/tests/includedeps-stripdotslash.mk @@ -0,0 +1,8 @@ +#T gmake skip + +test: + @echo $< + +includedeps $(TESTPATH)/includedeps-stripdotslash.deps + +TEST-PASS: diff --git a/build/pymake/tests/includedeps-variables.deps b/build/pymake/tests/includedeps-variables.deps new file mode 100644 index 000000000..ba69e9b6c --- /dev/null +++ b/build/pymake/tests/includedeps-variables.deps @@ -0,0 +1 @@ +$(FILE)1: filemissing diff --git a/build/pymake/tests/includedeps-variables.mk b/build/pymake/tests/includedeps-variables.mk new file mode 100644 index 000000000..314618da4 --- /dev/null +++ b/build/pymake/tests/includedeps-variables.mk @@ -0,0 +1,10 @@ +#T gmake skip + +FILE = includedeps-variables + +all: $(FILE)1 + +includedeps $(TESTPATH)/includedeps-variables.deps + +filemissing: + @echo TEST-PASS diff --git a/build/pymake/tests/includedeps.deps b/build/pymake/tests/includedeps.deps new file mode 100644 index 000000000..d3017c078 --- /dev/null +++ b/build/pymake/tests/includedeps.deps @@ -0,0 +1 @@ +file1: filemissing diff --git a/build/pymake/tests/includedeps.mk b/build/pymake/tests/includedeps.mk new file mode 100644 index 000000000..deaa71fe8 --- /dev/null +++ b/build/pymake/tests/includedeps.mk @@ -0,0 +1,9 @@ +#T gmake skip + +all: file1 + @echo TEST-PASS + +includedeps $(TESTPATH)/includedeps.deps + +file1: + touch $@ diff --git a/build/pymake/tests/info.mk b/build/pymake/tests/info.mk new file mode 100644 index 000000000..8dddfd815 --- /dev/null +++ b/build/pymake/tests/info.mk @@ -0,0 +1,8 @@ +#T grep-for: "info-printed\ninfo-nth" +all: + +INFO = info-printed + +$(info $(INFO)) +$(info $(subst second,nth,info-second)) +$(info TEST-PASS) diff --git a/build/pymake/tests/justprint-native.mk b/build/pymake/tests/justprint-native.mk new file mode 100644 index 000000000..580e402e9 --- /dev/null +++ b/build/pymake/tests/justprint-native.mk @@ -0,0 +1,28 @@ +## $(TOUCH) and $(RM) are native commands in pymake.
+## Test that pymake --just-print just prints them.
+
+ifndef TOUCH
+TOUCH = touch
+endif
+
+all:
+ $(RM) justprint-native-file1.txt
+ $(TOUCH) justprint-native-file2.txt
+ $(MAKE) --just-print -f $(TESTPATH)/justprint-native.mk justprint_target > justprint.log
+# make --just-print shouldn't have actually done anything.
+ test ! -f justprint-native-file1.txt
+ test -f justprint-native-file2.txt
+# but it should have printed each command
+ grep -q 'touch justprint-native-file1.txt' justprint.log
+ grep -q 'rm -f justprint-native-file2.txt' justprint.log
+ grep -q 'this string is "unlikely to appear in the log by chance"' justprint.log
+# tidy up
+ $(RM) justprint-native-file2.txt
+ @echo TEST-PASS
+
+justprint_target:
+ $(TOUCH) justprint-native-file1.txt
+ $(RM) justprint-native-file2.txt
+ this string is "unlikely to appear in the log by chance"
+
+.PHONY: justprint_target
diff --git a/build/pymake/tests/justprint.mk b/build/pymake/tests/justprint.mk new file mode 100644 index 000000000..be11ba8de --- /dev/null +++ b/build/pymake/tests/justprint.mk @@ -0,0 +1,5 @@ +#T commandline: ['-n']
+
+all:
+ false # without -n, we wouldn't get past this
+ TEST-PASS # heh
diff --git a/build/pymake/tests/keep-going-doublecolon.mk b/build/pymake/tests/keep-going-doublecolon.mk new file mode 100644 index 000000000..fa5b31df8 --- /dev/null +++ b/build/pymake/tests/keep-going-doublecolon.mk @@ -0,0 +1,16 @@ +#T commandline: ['-k'] +#T returncode: 2 +#T grep-for: "TEST-PASS" + +all:: t1 + @echo TEST-FAIL "(t1)" + +all:: t2 + @echo TEST-PASS + +t1: + @false + +t2: + touch $@ + diff --git a/build/pymake/tests/keep-going-parallel.mk b/build/pymake/tests/keep-going-parallel.mk new file mode 100644 index 000000000..a91d1a6ed --- /dev/null +++ b/build/pymake/tests/keep-going-parallel.mk @@ -0,0 +1,11 @@ +#T commandline: ['-k', '-j2'] +#T returncode: 2 +#T grep-for: "TEST-PASS" + +all: t1 slow1 slow2 slow3 t2 + +t2: + @echo TEST-PASS + +slow%: + sleep 1 diff --git a/build/pymake/tests/keep-going.mk b/build/pymake/tests/keep-going.mk new file mode 100644 index 000000000..4c709288c --- /dev/null +++ b/build/pymake/tests/keep-going.mk @@ -0,0 +1,14 @@ +#T commandline: ['-k'] +#T returncode: 2 +#T grep-for: "TEST-PASS" + +all: t2 t3 + +t1: + @false + +t2: t1 + @echo TEST-FAIL + +t3: + @echo TEST-PASS diff --git a/build/pymake/tests/line-continuations.mk b/build/pymake/tests/line-continuations.mk new file mode 100644 index 000000000..8b44480ea --- /dev/null +++ b/build/pymake/tests/line-continuations.mk @@ -0,0 +1,24 @@ +VAR = val1 \ + val2 + +VAR2 = val1space\ +val2 + +VAR3 = val3 \\\ + cont3 + +all: otarget test.target + test "$(VAR)" = "val1 val2 " + test "$(VAR2)" = "val1space val2" + test '$(VAR3)' = 'val3 \ cont3' + test "hello \ + world" = "hello world" + test "hello" = \ +"hello" + @echo TEST-PASS + +otarget: ; test "hello\ + world" = "helloworld" + +test.target: %.target: ; test "hello\ + world" = "helloworld" diff --git a/build/pymake/tests/link-search.mk b/build/pymake/tests/link-search.mk new file mode 100644 index 000000000..ea827f391 --- /dev/null +++ b/build/pymake/tests/link-search.mk @@ -0,0 +1,7 @@ +$(shell \ +touch libfoo.so \ +) + +all: -lfoo + test "$<" = "libfoo.so" + @echo TEST-PASS diff --git a/build/pymake/tests/makeflags.mk b/build/pymake/tests/makeflags.mk new file mode 100644 index 000000000..288ff7866 --- /dev/null +++ b/build/pymake/tests/makeflags.mk @@ -0,0 +1,7 @@ +#T environment: {'MAKEFLAGS': 'OVAR=oval'} + +all: + test "$(OVAR)" = "oval" + test "$$OVAR" = "oval" + @echo TEST-PASS + diff --git a/build/pymake/tests/matchany.mk b/build/pymake/tests/matchany.mk new file mode 100644 index 000000000..7876c90a3 --- /dev/null +++ b/build/pymake/tests/matchany.mk @@ -0,0 +1,14 @@ +#T returncode: 2 + +# we should fail to make foo.ooo from foo.ooo.test +all: foo.ooo + @echo TEST-FAIL + +%.ooo: + +# this match-anything pattern should not apply to %.ooo +%: %.test + cp $< $@ + +foo.ooo.test: + touch $@ diff --git a/build/pymake/tests/matchany2.mk b/build/pymake/tests/matchany2.mk new file mode 100644 index 000000000..d21d9702c --- /dev/null +++ b/build/pymake/tests/matchany2.mk @@ -0,0 +1,13 @@ +# we should succeed in making foo.ooo from foo.ooo.test +all: foo.ooo + @echo TEST-PASS + +%.ooo: %.ccc + exit 1 + +# this match-anything rule is terminal, and therefore applies +%:: %.test + cp $< $@ + +foo.ooo.test: + touch $@ diff --git a/build/pymake/tests/matchany3.mk b/build/pymake/tests/matchany3.mk new file mode 100644 index 000000000..83de8af2b --- /dev/null +++ b/build/pymake/tests/matchany3.mk @@ -0,0 +1,10 @@ +$(shell \ +echo "target" > target.in; \ +) + +all: target + test "$$(cat $^)" = "target" + @echo TEST-PASS + +%: %.in + cp $< $@ diff --git a/build/pymake/tests/mkdir-fail.mk b/build/pymake/tests/mkdir-fail.mk new file mode 100644 index 000000000..b05734aa9 --- /dev/null +++ b/build/pymake/tests/mkdir-fail.mk @@ -0,0 +1,7 @@ +#T returncode: 2 +all: + mkdir newdir/subdir + test ! -d newdir/subdir + test ! -d newdir + rm -r newdir + @echo TEST-PASS diff --git a/build/pymake/tests/mkdir.mk b/build/pymake/tests/mkdir.mk new file mode 100644 index 000000000..413348f77 --- /dev/null +++ b/build/pymake/tests/mkdir.mk @@ -0,0 +1,27 @@ +MKDIR ?= mkdir + +all: + $(MKDIR) newdir + test -d newdir + # subdir, parent exists + $(MKDIR) newdir/subdir + test -d newdir/subdir + # -p, existing dir + $(MKDIR) -p newdir + # -p, existing subdir + $(MKDIR) -p newdir/subdir + # multiple subdirs, existing parent + $(MKDIR) newdir/subdir1 newdir/subdir2 + test -d newdir/subdir1 -a -d newdir/subdir2 + rm -r newdir + # -p, subdir, no existing parent + $(MKDIR) -p newdir/subdir + test -d newdir/subdir + rm -r newdir + # -p, multiple subdirs, no existing parent + $(MKDIR) -p newdir/subdir1 newdir/subdir2 + test -d newdir/subdir1 -a -d newdir/subdir2 + # -p, multiple existing subdirs + $(MKDIR) -p newdir/subdir1 newdir/subdir2 + rm -r newdir + @echo TEST-PASS diff --git a/build/pymake/tests/multiple-rules-prerequisite-merge.mk b/build/pymake/tests/multiple-rules-prerequisite-merge.mk new file mode 100644 index 000000000..480d3b58c --- /dev/null +++ b/build/pymake/tests/multiple-rules-prerequisite-merge.mk @@ -0,0 +1,25 @@ +# When a target is defined multiple times, the prerequisites should get +# merged. + +default: foo bar baz + +foo: + test "$<" = "foo.in1" + @echo TEST-PASS + +foo: foo.in1 + +bar: bar.in1 + test "$<" = "bar.in1" + test "$^" = "bar.in1 bar.in2" + @echo TEST-PASS + +bar: bar.in2 + +baz: baz.in2 +baz: baz.in1 + test "$<" = "baz.in1" + test "$^" = "baz.in1 baz.in2" + @echo TEST-PASS + +foo.in1 bar.in1 bar.in2 baz.in1 baz.in2: diff --git a/build/pymake/tests/native-command-delay-load.mk b/build/pymake/tests/native-command-delay-load.mk new file mode 100644 index 000000000..a9f3774eb --- /dev/null +++ b/build/pymake/tests/native-command-delay-load.mk @@ -0,0 +1,12 @@ +#T gmake skip + +# This test exists to verify that sys.path is adjusted during command +# execution and that delay importing a module will work. + +CMD = %pycmd delayloadfn +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) + @echo TEST-PASS + diff --git a/build/pymake/tests/native-command-raise.mk b/build/pymake/tests/native-command-raise.mk new file mode 100644 index 000000000..d1b28b331 --- /dev/null +++ b/build/pymake/tests/native-command-raise.mk @@ -0,0 +1,9 @@ +#T gmake skip +#T returncode: 2 +#T grep-for: "Exception: info-exception" + +CMD = %pycmd asplode_raise +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + @$(CMD) info-exception diff --git a/build/pymake/tests/native-command-return-fail1.mk b/build/pymake/tests/native-command-return-fail1.mk new file mode 100644 index 000000000..0cf085ae2 --- /dev/null +++ b/build/pymake/tests/native-command-return-fail1.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode_return +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 1 diff --git a/build/pymake/tests/native-command-return-fail2.mk b/build/pymake/tests/native-command-return-fail2.mk new file mode 100644 index 000000000..c071fc879 --- /dev/null +++ b/build/pymake/tests/native-command-return-fail2.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode_return +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) not-an-integer diff --git a/build/pymake/tests/native-command-return.mk b/build/pymake/tests/native-command-return.mk new file mode 100644 index 000000000..3e4d2e0c4 --- /dev/null +++ b/build/pymake/tests/native-command-return.mk @@ -0,0 +1,11 @@ +#T gmake skip + +CMD = %pycmd asplode_return +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 0 + -$(CMD) 1 + $(CMD) None + -$(CMD) not-an-integer + @echo TEST-PASS diff --git a/build/pymake/tests/native-command-shell-glob.mk b/build/pymake/tests/native-command-shell-glob.mk new file mode 100644 index 000000000..4bcdad8b9 --- /dev/null +++ b/build/pymake/tests/native-command-shell-glob.mk @@ -0,0 +1,11 @@ +#T gmake skip +all: + mkdir shell-glob-test + touch shell-glob-test/foo.txt + touch shell-glob-test/bar.txt + touch shell-glob-test/a.foo + touch shell-glob-test/b.foo + $(RM) shell-glob-test/*.txt + $(RM) shell-glob-test/?.foo + rmdir shell-glob-test + @echo TEST-PASS diff --git a/build/pymake/tests/native-command-sys-exit-fail1.mk b/build/pymake/tests/native-command-sys-exit-fail1.mk new file mode 100644 index 000000000..8e74800ed --- /dev/null +++ b/build/pymake/tests/native-command-sys-exit-fail1.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 1 diff --git a/build/pymake/tests/native-command-sys-exit-fail2.mk b/build/pymake/tests/native-command-sys-exit-fail2.mk new file mode 100644 index 000000000..0a04395ad --- /dev/null +++ b/build/pymake/tests/native-command-sys-exit-fail2.mk @@ -0,0 +1,8 @@ +#T gmake skip +#T returncode: 2 + +CMD = %pycmd asplode +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) not-an-integer diff --git a/build/pymake/tests/native-command-sys-exit.mk b/build/pymake/tests/native-command-sys-exit.mk new file mode 100644 index 000000000..c04913aca --- /dev/null +++ b/build/pymake/tests/native-command-sys-exit.mk @@ -0,0 +1,11 @@ +#T gmake skip + +CMD = %pycmd asplode +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) 0 + -$(CMD) 1 + $(CMD) None + -$(CMD) not-an-integer + @echo TEST-PASS diff --git a/build/pymake/tests/native-environment.mk b/build/pymake/tests/native-environment.mk new file mode 100644 index 000000000..36bd5894a --- /dev/null +++ b/build/pymake/tests/native-environment.mk @@ -0,0 +1,11 @@ +#T gmake skip +export EXPECTED := some data + +PYCOMMANDPATH = $(TESTPATH) + +all: + %pycmd writeenvtofile results EXPECTED + test "$$(cat results)" = "$(EXPECTED)" + %pycmd writesubprocessenvtofile results EXPECTED + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS diff --git a/build/pymake/tests/native-pycommandpath-sep.mk b/build/pymake/tests/native-pycommandpath-sep.mk new file mode 100644 index 000000000..b1c2c2b97 --- /dev/null +++ b/build/pymake/tests/native-pycommandpath-sep.mk @@ -0,0 +1,21 @@ +#T gmake skip +EXPECTED := some data + +# verify that we can load native command modules from +# multiple directories in PYCOMMANDPATH separated by the native +# path separator +ifdef __WIN32__ +PS:=; +else +PS:=: +endif +CMD = %pycmd writetofile +CMD2 = %pymod writetofile +PYCOMMANDPATH = $(TESTPATH)$(PS)$(TESTPATH)/subdir + +all: + $(CMD) results $(EXPECTED) + test "$$(cat results)" = "$(EXPECTED)" + $(CMD2) results2 $(EXPECTED) + test "$$(cat results2)" = "$(EXPECTED)" + @echo TEST-PASS diff --git a/build/pymake/tests/native-pycommandpath.mk b/build/pymake/tests/native-pycommandpath.mk new file mode 100644 index 000000000..dd0fbc9f9 --- /dev/null +++ b/build/pymake/tests/native-pycommandpath.mk @@ -0,0 +1,15 @@ +#T gmake skip +EXPECTED := some data + +# verify that we can load native command modules from +# multiple space-separated directories in PYCOMMANDPATH +CMD = %pycmd writetofile +CMD2 = %pymod writetofile +PYCOMMANDPATH = $(TESTPATH) $(TESTPATH)/subdir + +all: + $(CMD) results $(EXPECTED) + test "$$(cat results)" = "$(EXPECTED)" + $(CMD2) results2 $(EXPECTED) + test "$$(cat results2)" = "$(EXPECTED)" + @echo TEST-PASS diff --git a/build/pymake/tests/native-simple.mk b/build/pymake/tests/native-simple.mk new file mode 100644 index 000000000..626a58670 --- /dev/null +++ b/build/pymake/tests/native-simple.mk @@ -0,0 +1,12 @@ +ifndef TOUCH
+TOUCH = touch
+endif
+
+all: testfile {testfile2} (testfile3)
+ test -f testfile
+ test -f {testfile2}
+ test -f "(testfile3)"
+ @echo TEST-PASS
+
+testfile {testfile2} (testfile3):
+ $(TOUCH) "$@"
diff --git a/build/pymake/tests/native-touch.mk b/build/pymake/tests/native-touch.mk new file mode 100644 index 000000000..811161ece --- /dev/null +++ b/build/pymake/tests/native-touch.mk @@ -0,0 +1,15 @@ +TOUCH ?= touch + +foo: + $(TOUCH) bar + $(TOUCH) baz + $(MAKE) -f $(TESTPATH)/native-touch.mk baz + $(TOUCH) -t 198007040802 baz + $(MAKE) -f $(TESTPATH)/native-touch.mk baz + +bar: + $(TOUCH) $@ + +baz: bar + echo TEST-PASS + $(TOUCH) $@ diff --git a/build/pymake/tests/newlines.mk b/build/pymake/tests/newlines.mk new file mode 100644 index 000000000..5d8195c94 --- /dev/null +++ b/build/pymake/tests/newlines.mk @@ -0,0 +1,30 @@ +#T gmake skip + +# Test that we handle \\\n properly + +all: dep1 dep2 dep3 + cat testfile + test `cat testfile` = "data"; + test "$$(cat results)" = "$(EXPECTED)"; + @echo TEST-PASS + +# Test that something that still needs to go to the shell works +testfile: + printf "data" \ + >>$@ + +dep1: testfile + +# Test that something that does not need to go to the shell works +dep2: + $(echo foo) \ + $(echo bar) + +export EXPECTED := some data + +CMD = %pycmd writeenvtofile +PYCOMMANDPATH = $(TESTPATH) + +dep3: + $(CMD) \ + results EXPECTED diff --git a/build/pymake/tests/no-remake.mk b/build/pymake/tests/no-remake.mk new file mode 100644 index 000000000..c8df81bc3 --- /dev/null +++ b/build/pymake/tests/no-remake.mk @@ -0,0 +1,7 @@ +$(shell date >testfile) + +all: testfile + @echo TEST-PASS + +testfile: + @echo TEST-FAIL "We shouldn't have remade this!" diff --git a/build/pymake/tests/nosuchfile.mk b/build/pymake/tests/nosuchfile.mk new file mode 100644 index 000000000..cca9ce1e9 --- /dev/null +++ b/build/pymake/tests/nosuchfile.mk @@ -0,0 +1,4 @@ +#T returncode: 2 + +all: + reallythereisnosuchcommand diff --git a/build/pymake/tests/notargets.mk b/build/pymake/tests/notargets.mk new file mode 100644 index 000000000..8e55d944f --- /dev/null +++ b/build/pymake/tests/notargets.mk @@ -0,0 +1,5 @@ +$(NULL): foo.c + @echo TEST-FAIL + +all: + @echo TEST-PASS diff --git a/build/pymake/tests/notparallel.mk b/build/pymake/tests/notparallel.mk new file mode 100644 index 000000000..4fd8b1a8d --- /dev/null +++ b/build/pymake/tests/notparallel.mk @@ -0,0 +1,8 @@ +#T commandline: ['-j3'] + +include $(TESTPATH)/serial-rule-execution.mk + +all:: + $(MAKE) -f $(TESTPATH)/parallel-simple.mk + +.NOTPARALLEL: diff --git a/build/pymake/tests/oneline-command-continuations.mk b/build/pymake/tests/oneline-command-continuations.mk new file mode 100644 index 000000000..c11f3df52 --- /dev/null +++ b/build/pymake/tests/oneline-command-continuations.mk @@ -0,0 +1,5 @@ +all: test + @echo TEST-PASS + +test: ; test "Hello \ + world" = "Hello world" diff --git a/build/pymake/tests/override-propagate.mk b/build/pymake/tests/override-propagate.mk new file mode 100644 index 000000000..a1663ff41 --- /dev/null +++ b/build/pymake/tests/override-propagate.mk @@ -0,0 +1,37 @@ +#T commandline: ['-w', 'OVAR=oval'] + +OVAR=mval + +all: vartest run-override + $(MAKE) -f $(TESTPATH)/override-propagate.mk vartest + @echo TEST-PASS + +CLINE := OVAR=oval TESTPATH=$(TESTPATH) NATIVE_TESTPATH=$(NATIVE_TESTPATH) +ifdef __WIN32__ +CLINE += __WIN32__=1 +endif + +SORTED_CLINE := $(subst \,\\,$(sort $(CLINE))) + +vartest: + @echo MAKELEVEL: '$(MAKELEVEL)' + test '$(value MAKEFLAGS)' = 'w -- $$(MAKEOVERRIDES)' + test '$(origin MAKEFLAGS)' = 'file' + test '$(value MAKEOVERRIDES)' = '$${-*-command-variables-*-}' + test "$(sort $(MAKEOVERRIDES))" = "$(SORTED_CLINE)" + test '$(origin MAKEOVERRIDES)' = 'environment' + test '$(origin -*-command-variables-*-)' = 'automatic' + test "$(origin OVAR)" = "command line" + test "$(OVAR)" = "oval" + +run-override: MAKEOVERRIDES= +run-override: + test "$(OVAR)" = "oval" + $(MAKE) -f $(TESTPATH)/override-propagate.mk otest + +otest: + test '$(value MAKEFLAGS)' = 'w' + test '$(value MAKEOVERRIDES)' = '$${-*-command-variables-*-}' + test '$(MAKEOVERRIDES)' = '' + test '$(origin -*-command-variables-*-)' = 'undefined' + test "$(OVAR)" = "mval" diff --git a/build/pymake/tests/parallel-dep-resolution.mk b/build/pymake/tests/parallel-dep-resolution.mk new file mode 100644 index 000000000..7967eba2d --- /dev/null +++ b/build/pymake/tests/parallel-dep-resolution.mk @@ -0,0 +1,8 @@ +#T commandline: ['-j3'] +#T returncode: 2 + +all: t1 t2 + +t1: + sleep 1 + touch t1 t2 diff --git a/build/pymake/tests/parallel-dep-resolution2.mk b/build/pymake/tests/parallel-dep-resolution2.mk new file mode 100644 index 000000000..7d61e6b3e --- /dev/null +++ b/build/pymake/tests/parallel-dep-resolution2.mk @@ -0,0 +1,9 @@ +#T commandline: ['-j3'] +#T returncode: 2 + +all:: + sleep 1 + touch somefile + +all:: somefile + @echo TEST-PASS diff --git a/build/pymake/tests/parallel-native.mk b/build/pymake/tests/parallel-native.mk new file mode 100644 index 000000000..d50cfbdbb --- /dev/null +++ b/build/pymake/tests/parallel-native.mk @@ -0,0 +1,21 @@ +#T commandline: ['-j2'] + +# ensure that calling python commands doesn't block other targets +ifndef SLEEP +SLEEP := sleep +endif + +PRINTF = printf "$@:0:" >>results +EXPECTED = target2:0:target1:0: + +all:: target1 target2 + cat results + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS + +target1: + $(SLEEP) 0.1 + $(PRINTF) + +target2: + $(PRINTF) diff --git a/build/pymake/tests/parallel-simple.mk b/build/pymake/tests/parallel-simple.mk new file mode 100644 index 000000000..f1aafc5f1 --- /dev/null +++ b/build/pymake/tests/parallel-simple.mk @@ -0,0 +1,27 @@ +#T commandline: ['-j2'] + +# CAUTION: this makefile is also used by serial-toparallel.mk + +define SLOWMAKE +printf "$@:0:" >>results +sleep 0.5 +printf "$@:1:" >>results +sleep 0.5 +printf "$@:2:" >>results +endef + +EXPECTED = target1:0:target2:0:target1:1:target2:1:target1:2:target2:2: + +all:: target1 target2 + cat results + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS + +target1: + $(SLOWMAKE) + +target2: + sleep 0.1 + $(SLOWMAKE) + +.PHONY: all diff --git a/build/pymake/tests/parallel-submake.mk b/build/pymake/tests/parallel-submake.mk new file mode 100644 index 000000000..65cb2cf7c --- /dev/null +++ b/build/pymake/tests/parallel-submake.mk @@ -0,0 +1,17 @@ +#T commandline: ['-j2'] + +# A submake shouldn't return control to the parent until it has actually finished doing everything. + +all: + -$(MAKE) -f $(TESTPATH)/parallel-submake.mk subtarget + cat results + test "$$(cat results)" = "0123" + @echo TEST-PASS + +subtarget: succeed-slowly fail-quickly + +succeed-slowly: + printf 0 >>results; sleep 1; printf 1 >>results; sleep 1; printf 2 >>results; sleep 1; printf 3 >>results + +fail-quickly: + exit 1 diff --git a/build/pymake/tests/parallel-toserial.mk b/build/pymake/tests/parallel-toserial.mk new file mode 100644 index 000000000..9a355eb33 --- /dev/null +++ b/build/pymake/tests/parallel-toserial.mk @@ -0,0 +1,31 @@ +#T commandline: ['-j4'] + +# Test that -j1 in a submake has the proper effect. + +define SLOWCMD +printf "$@:0:" >>$(RFILE) +sleep 0.5 +printf "$@:1:" >>$(RFILE) +endef + +all: p1 p2 +subtarget: s1 s2 + +p1 p2: RFILE = presult +s1 s2: RFILE = sresult + +p1 s1: + $(SLOWCMD) + +p2 s2: + sleep 0.1 + $(SLOWCMD) + +all: + $(MAKE) -j1 -f $(TESTPATH)/parallel-toserial.mk subtarget + printf "presult: %s\n" "$$(cat presult)" + test "$$(cat presult)" = "p1:0:p2:0:p1:1:p2:1:" + printf "sresult: %s\n" "$$(cat sresult)" + test "$$(cat sresult)" = "s1:0:s1:1:s2:0:s2:1:" + @echo TEST-PASS + diff --git a/build/pymake/tests/parallel-waiting.mk b/build/pymake/tests/parallel-waiting.mk new file mode 100644 index 000000000..40a6e0d50 --- /dev/null +++ b/build/pymake/tests/parallel-waiting.mk @@ -0,0 +1,21 @@ +#T commandline: ['-j2'] + +EXPECTED = target1:before:target2:1:target2:2:target2:3:target1:after + +all:: target1 target2 + cat results + test "$$(cat results)" = "$(EXPECTED)" + @echo TEST-PASS + +target1: + printf "$@:before:" >>results + sleep 4 + printf "$@:after" >>results + +target2: + sleep 0.2 + printf "$@:1:" >>results + sleep 0.1 + printf "$@:2:" >>results + sleep 0.1 + printf "$@:3:" >>results diff --git a/build/pymake/tests/parentheses.mk b/build/pymake/tests/parentheses.mk new file mode 100644 index 000000000..f207234ff --- /dev/null +++ b/build/pymake/tests/parentheses.mk @@ -0,0 +1,2 @@ +all: + @(echo TEST-PASS) diff --git a/build/pymake/tests/parsertests.py b/build/pymake/tests/parsertests.py new file mode 100644 index 000000000..ab6406be0 --- /dev/null +++ b/build/pymake/tests/parsertests.py @@ -0,0 +1,314 @@ +import pymake.data, pymake.parser, pymake.parserdata, pymake.functions +import unittest +import logging + +from cStringIO import StringIO + +def multitest(cls): + for name in cls.testdata.iterkeys(): + def m(self, name=name): + return self.runSingle(*self.testdata[name]) + + setattr(cls, 'test_%s' % name, m) + return cls + +class TestBase(unittest.TestCase): + def assertEqual(self, a, b, msg=""): + """Actually print the values which weren't equal, if things don't work out!""" + unittest.TestCase.assertEqual(self, a, b, "%s got %r expected %r" % (msg, a, b)) + +class DataTest(TestBase): + testdata = { + 'oneline': + ("He\tllo", "f", 1, 0, + ((0, "f", 1, 0), (2, "f", 1, 2), (3, "f", 1, 4))), + 'twoline': + ("line1 \n\tl\tine2", "f", 1, 4, + ((0, "f", 1, 4), (5, "f", 1, 9), (6, "f", 1, 10), (7, "f", 2, 0), (8, "f", 2, 4), (10, "f", 2, 8), (13, "f", 2, 11))), + } + + def runSingle(self, data, filename, line, col, results): + d = pymake.parser.Data(data, 0, len(data), pymake.parserdata.Location(filename, line, col)) + for pos, file, lineno, col in results: + loc = d.getloc(pos) + self.assertEqual(loc.path, file, "data file offset %i" % pos) + self.assertEqual(loc.line, lineno, "data line offset %i" % pos) + self.assertEqual(loc.column, col, "data col offset %i" % pos) +multitest(DataTest) + +class LineEnumeratorTest(TestBase): + testdata = { + 'simple': ( + 'Hello, world', [ + ('Hello, world', 1), + ] + ), + 'multi': ( + 'Hello\nhappy \n\nworld\n', [ + ('Hello', 1), + ('happy ', 2), + ('', 3), + ('world', 4), + ('', 5), + ] + ), + 'continuation': ( + 'Hello, \\\n world\nJellybeans!', [ + ('Hello, \\\n world', 1), + ('Jellybeans!', 3), + ] + ), + 'multislash': ( + 'Hello, \\\\\n world', [ + ('Hello, \\\\', 1), + (' world', 2), + ] + ) + } + + def runSingle(self, s, lines): + gotlines = [(d.s[d.lstart:d.lend], d.loc.line) for d in pymake.parser.enumeratelines(s, 'path')] + self.assertEqual(gotlines, lines) + +multitest(LineEnumeratorTest) + +class IterTest(TestBase): + testdata = { + 'plaindata': ( + pymake.parser.iterdata, + "plaindata # test\n", + "plaindata # test\n" + ), + 'makecomment': ( + pymake.parser.itermakefilechars, + "VAR = val # comment", + "VAR = val " + ), + 'makeescapedcomment': ( + pymake.parser.itermakefilechars, + "VAR = val \# escaped hash", + "VAR = val # escaped hash" + ), + 'makeescapedslash': ( + pymake.parser.itermakefilechars, + "VAR = val\\\\", + "VAR = val\\\\", + ), + 'makecontinuation': ( + pymake.parser.itermakefilechars, + "VAR = VAL \\\n continuation # comment \\\n continuation", + "VAR = VAL continuation " + ), + 'makecontinuation2': ( + pymake.parser.itermakefilechars, + "VAR = VAL \\ \\\n continuation", + "VAR = VAL \\ continuation" + ), + 'makeawful': ( + pymake.parser.itermakefilechars, + "VAR = VAL \\\\# comment\n", + "VAR = VAL \\" + ), + 'command': ( + pymake.parser.itercommandchars, + "echo boo # comment", + "echo boo # comment", + ), + 'commandcomment': ( + pymake.parser.itercommandchars, + "echo boo \# comment", + "echo boo \# comment", + ), + 'commandcontinue': ( + pymake.parser.itercommandchars, + "echo boo # \\\n\t command 2", + "echo boo # \\\n command 2" + ), + } + + def runSingle(self, ifunc, idata, expected): + d = pymake.parser.Data.fromstring(idata, 'IterTest data') + + it = pymake.parser._alltokens.finditer(d.s, 0, d.lend) + actual = ''.join( [c for c, t, o, oo in ifunc(d, 0, ('dummy-token',), it)] ) + self.assertEqual(actual, expected) + + if ifunc == pymake.parser.itermakefilechars: + print "testing %r" % expected + self.assertEqual(pymake.parser.flattenmakesyntax(d, 0), expected) + +multitest(IterTest) + + +# 'define': ( +# pymake.parser.iterdefinechars, +# "endef", +# "" +# ), +# 'definenesting': ( +# pymake.parser.iterdefinechars, +# """define BAR # comment +#random text +#endef not what you think! +#endef # comment is ok\n""", +# """define BAR # comment +#random text +#endef not what you think!""" +# ), +# 'defineescaped': ( +# pymake.parser.iterdefinechars, +# """value \\ +#endef +#endef\n""", +# "value endef" +# ), + +class MakeSyntaxTest(TestBase): + # (string, startat, stopat, stopoffset, expansion + testdata = { + 'text': ('hello world', 0, (), None, ['hello world']), + 'singlechar': ('hello $W', 0, (), None, + ['hello ', + {'type': 'VariableRef', + '.vname': ['W']} + ]), + 'stopat': ('hello: world', 0, (':', '='), 6, ['hello']), + 'funccall': ('h $(flavor FOO)', 0, (), None, + ['h ', + {'type': 'FlavorFunction', + '[0]': ['FOO']} + ]), + 'escapedollar': ('hello$$world', 0, (), None, ['hello$world']), + 'varref': ('echo $(VAR)', 0, (), None, + ['echo ', + {'type': 'VariableRef', + '.vname': ['VAR']} + ]), + 'dynamicvarname': ('echo $($(VARNAME):.c=.o)', 0, (':',), None, + ['echo ', + {'type': 'SubstitutionRef', + '.vname': [{'type': 'VariableRef', + '.vname': ['VARNAME']} + ], + '.substfrom': ['.c'], + '.substto': ['.o']} + ]), + 'substref': (' $(VAR:VAL) := $(VAL)', 0, (':=', '+=', '=', ':'), 15, + [' ', + {'type': 'VariableRef', + '.vname': ['VAR:VAL']}, + ' ']), + 'vadsubstref': (' $(VAR:VAL) = $(VAL)', 15, (), None, + [{'type': 'VariableRef', + '.vname': ['VAL']}, + ]), + } + + def compareRecursive(self, actual, expected, path): + self.assertEqual(len(actual), len(expected), + "compareRecursive: %s %r" % (path, actual)) + for i in xrange(0, len(actual)): + ipath = path + [i] + + a, isfunc = actual[i] + e = expected[i] + if isinstance(e, str): + self.assertEqual(a, e, "compareRecursive: %s" % (ipath,)) + else: + self.assertEqual(type(a), getattr(pymake.functions, e['type']), + "compareRecursive: %s" % (ipath,)) + for k, v in e.iteritems(): + if k == 'type': + pass + elif k[0] == '[': + item = int(k[1:-1]) + proppath = ipath + [item] + self.compareRecursive(a[item], v, proppath) + elif k[0] == '.': + item = k[1:] + proppath = ipath + [item] + self.compareRecursive(getattr(a, item), v, proppath) + else: + raise Exception("Unexpected property at %s: %s" % (ipath, k)) + + def runSingle(self, s, startat, stopat, stopoffset, expansion): + d = pymake.parser.Data.fromstring(s, pymake.parserdata.Location('testdata', 1, 0)) + + a, t, offset = pymake.parser.parsemakesyntax(d, startat, stopat, pymake.parser.itermakefilechars) + self.compareRecursive(a, expansion, []) + self.assertEqual(offset, stopoffset) + +multitest(MakeSyntaxTest) + +class VariableTest(TestBase): + testdata = """ + VAR = value + VARNAME = TESTVAR + $(VARNAME) = testvalue + $(VARNAME:VAR=VAL) = moretesting + IMM := $(VARNAME) # this is a comment + MULTIVAR = val1 \\ + val2 + VARNAME = newname + """ + expected = {'VAR': 'value', + 'VARNAME': 'newname', + 'TESTVAR': 'testvalue', + 'TESTVAL': 'moretesting', + 'IMM': 'TESTVAR ', + 'MULTIVAR': 'val1 val2', + 'UNDEF': None} + + def runTest(self): + stmts = pymake.parser.parsestring(self.testdata, 'VariableTest') + + m = pymake.data.Makefile() + stmts.execute(m) + for k, v in self.expected.iteritems(): + flavor, source, val = m.variables.get(k) + if val is None: + self.assertEqual(val, v, 'variable named %s' % k) + else: + self.assertEqual(val.resolvestr(m, m.variables), v, 'variable named %s' % k) + +class SimpleRuleTest(TestBase): + testdata = """ + VAR = value +TSPEC = dummy +all: TSPEC = myrule +all:: test test2 $(VAR) + echo "Hello, $(TSPEC)" + +%.o: %.c + $(CC) -o $@ $< +""" + + def runTest(self): + stmts = pymake.parser.parsestring(self.testdata, 'SimpleRuleTest') + + m = pymake.data.Makefile() + stmts.execute(m) + self.assertEqual(m.defaulttarget, 'all', "Default target") + + self.assertTrue(m.hastarget('all'), "Has 'all' target") + target = m.gettarget('all') + rules = target.rules + self.assertEqual(len(rules), 1, "Number of rules") + prereqs = rules[0].prerequisites + self.assertEqual(prereqs, ['test', 'test2', 'value'], "Prerequisites") + commands = rules[0].commands + self.assertEqual(len(commands), 1, "Number of commands") + expanded = commands[0].resolvestr(m, target.variables) + self.assertEqual(expanded, 'echo "Hello, myrule"') + + irules = m.implicitrules + self.assertEqual(len(irules), 1, "Number of implicit rules") + + irule = irules[0] + self.assertEqual(len(irule.targetpatterns), 1, "%.o target pattern count") + self.assertEqual(len(irule.prerequisites), 1, "%.o prerequisite count") + self.assertEqual(irule.targetpatterns[0].match('foo.o'), 'foo', "%.o stem") + +if __name__ == '__main__': + logging.basicConfig(level=logging.DEBUG) + unittest.main() diff --git a/build/pymake/tests/path-length.mk b/build/pymake/tests/path-length.mk new file mode 100644 index 000000000..10c33b5ed --- /dev/null +++ b/build/pymake/tests/path-length.mk @@ -0,0 +1,9 @@ +#T gmake skip + +$(shell \ +mkdir foo; \ +touch tfile; \ +) + +all: foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../foo/../tfile + @echo TEST-PASS diff --git a/build/pymake/tests/pathdir/pathtest b/build/pymake/tests/pathdir/pathtest new file mode 100755 index 000000000..17037159f --- /dev/null +++ b/build/pymake/tests/pathdir/pathtest @@ -0,0 +1,2 @@ +#!/bin/sh +echo Called shell script: 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24 diff --git a/build/pymake/tests/pathdir/pathtest.exe b/build/pymake/tests/pathdir/pathtest.exe Binary files differnew file mode 100644 index 000000000..3178db9a9 --- /dev/null +++ b/build/pymake/tests/pathdir/pathtest.exe diff --git a/build/pymake/tests/pathdir/src/Makefile b/build/pymake/tests/pathdir/src/Makefile new file mode 100644 index 000000000..6c24bd8f9 --- /dev/null +++ b/build/pymake/tests/pathdir/src/Makefile @@ -0,0 +1,2 @@ +pathtest.exe: pathtest.cpp + cl -EHsc -MT $^ diff --git a/build/pymake/tests/pathdir/src/pathtest.cpp b/build/pymake/tests/pathdir/src/pathtest.cpp new file mode 100644 index 000000000..bef8d8a11 --- /dev/null +++ b/build/pymake/tests/pathdir/src/pathtest.cpp @@ -0,0 +1,6 @@ +#include <cstdio> + +int main() { + std::printf("Called Windows executable: 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24\n"); + return 0; +} diff --git a/build/pymake/tests/patsubst.mk b/build/pymake/tests/patsubst.mk new file mode 100644 index 000000000..0c3efdc4b --- /dev/null +++ b/build/pymake/tests/patsubst.mk @@ -0,0 +1,7 @@ +all: + test "$(patsubst foo,%.bar,foo)" = "%.bar" + test "$(patsubst \%word,replace,word %word other)" = "word replace other" + test "$(patsubst %.c,\%%.o,foo.c bar.o baz.cpp)" = "%foo.o bar.o baz.cpp" + test "$(patsubst host_%.c,host_%.o,dir/host_foo.c host_bar.c)" = "dir/host_foo.c host_bar.o" + test "$(patsubst foo,bar,dir/foo foo baz)" = "dir/foo bar baz" + @echo TEST-PASS diff --git a/build/pymake/tests/phony.mk b/build/pymake/tests/phony.mk new file mode 100644 index 000000000..36db4d121 --- /dev/null +++ b/build/pymake/tests/phony.mk @@ -0,0 +1,10 @@ +$(shell \ +touch dep; \ +sleep 2; \ +touch all; \ +) + +all:: dep + @echo TEST-PASS + +.PHONY: all diff --git a/build/pymake/tests/pycmd.py b/build/pymake/tests/pycmd.py new file mode 100644 index 000000000..83b9b966b --- /dev/null +++ b/build/pymake/tests/pycmd.py @@ -0,0 +1,38 @@ +import os, sys, subprocess + +def writetofile(args): + with open(args[0], 'w') as f: + f.write(' '.join(args[1:])) + +def writeenvtofile(args): + with open(args[0], 'w') as f: + f.write(os.environ[args[1]]) + +def writesubprocessenvtofile(args): + with open(args[0], 'w') as f: + p = subprocess.Popen([sys.executable, "-c", + "import os; print os.environ['%s']" % args[1]], + stdout=subprocess.PIPE, stderr=subprocess.PIPE) + stdout, stderr = p.communicate() + assert p.returncode == 0 + f.write(stdout) + +def convertasplode(arg): + try: + return int(arg) + except: + return (None if arg == "None" else arg) + +def asplode(args): + arg0 = convertasplode(args[0]) + sys.exit(arg0) + +def asplode_return(args): + arg0 = convertasplode(args[0]) + return arg0 + +def asplode_raise(args): + raise Exception(args[0]) + +def delayloadfn(args): + import delayload diff --git a/build/pymake/tests/recursive-set.mk b/build/pymake/tests/recursive-set.mk new file mode 100644 index 000000000..853f90463 --- /dev/null +++ b/build/pymake/tests/recursive-set.mk @@ -0,0 +1,7 @@ +#T returncode: 2 + +FOO = $(FOO) + +all: + echo $(FOO) + @echo TEST-FAIL diff --git a/build/pymake/tests/recursive-set2.mk b/build/pymake/tests/recursive-set2.mk new file mode 100644 index 000000000..b68e34f0d --- /dev/null +++ b/build/pymake/tests/recursive-set2.mk @@ -0,0 +1,8 @@ +#T returncode: 2 + +FOO = $(BAR) +BAR = $(FOO) + +all: + echo $(FOO) + @echo TEST-FAIL diff --git a/build/pymake/tests/remake-mtime.mk b/build/pymake/tests/remake-mtime.mk new file mode 100644 index 000000000..47c775b93 --- /dev/null +++ b/build/pymake/tests/remake-mtime.mk @@ -0,0 +1,14 @@ +# mtime(dep1) < mtime(target) so the target should not be made +$(shell touch dep1; sleep 1; touch target) + +all: target + echo TEST-PASS + +target: dep1 + echo TEST-FAIL target should not have been made + +dep1: dep2 + @echo "Remaking dep1 (actually not)" + +dep2: + @echo "Making dep2 (actually not)" diff --git a/build/pymake/tests/rm-fail.mk b/build/pymake/tests/rm-fail.mk new file mode 100644 index 000000000..1a9aefb57 --- /dev/null +++ b/build/pymake/tests/rm-fail.mk @@ -0,0 +1,7 @@ +#T returncode: 2 +all: + mkdir newdir + test -d newdir + touch newdir/newfile + $(RM) newdir + @echo TEST-PASS diff --git a/build/pymake/tests/rm.mk b/build/pymake/tests/rm.mk new file mode 100644 index 000000000..6c7140e39 --- /dev/null +++ b/build/pymake/tests/rm.mk @@ -0,0 +1,21 @@ +all: +# $(RM) defaults to -f + $(RM) nosuchfile + touch newfile + test -f newfile + $(RM) newfile + test ! -f newfile + mkdir newdir + test -d newdir + touch newdir/newfile + mkdir newdir/subdir + $(RM) -r newdir/subdir + test ! -d newdir/subdir + test -d newdir + mkdir newdir/subdir1 newdir/subdir2 + $(RM) -r newdir/subdir1 newdir/subdir2 + test ! -d newdir/subdir1 -a ! -d newdir/subdir2 + test -d newdir + $(RM) -r newdir + test ! -d newdir + @echo TEST-PASS diff --git a/build/pymake/tests/runtests.py b/build/pymake/tests/runtests.py new file mode 100644 index 000000000..ab149ecfb --- /dev/null +++ b/build/pymake/tests/runtests.py @@ -0,0 +1,215 @@ +#!/usr/bin/env python +""" +Run the test(s) listed on the command line. If a directory is listed, the script will recursively +walk the directory for files named .mk and run each. + +For each test, we run gmake -f test.mk. By default, make must exit with an exit code of 0, and must print 'TEST-PASS'. + +Each test is run in an empty directory. + +The test file may contain lines at the beginning to alter the default behavior. These are all evaluated as python: + +#T commandline: ['extra', 'params', 'here'] +#T returncode: 2 +#T returncode-on: {'win32': 2} +#T environment: {'VAR': 'VALUE} +#T grep-for: "text" +""" + +from subprocess import Popen, PIPE, STDOUT +from optparse import OptionParser +import os, re, sys, shutil, glob + +class ParentDict(dict): + def __init__(self, parent, **kwargs): + self.d = dict(kwargs) + self.parent = parent + + def __setitem__(self, k, v): + self.d[k] = v + + def __getitem__(self, k): + if k in self.d: + return self.d[k] + + return self.parent[k] + +thisdir = os.path.dirname(os.path.abspath(__file__)) + +pymake = [sys.executable, os.path.join(os.path.dirname(thisdir), 'make.py')] +manifest = os.path.join(thisdir, 'tests.manifest') + +o = OptionParser() +o.add_option('-g', '--gmake', + dest="gmake", default="gmake") +o.add_option('-d', '--tempdir', + dest="tempdir", default="_mktests") +opts, args = o.parse_args() + +if len(args) == 0: + args = [thisdir] + +makefiles = [] +for a in args: + if os.path.isfile(a): + makefiles.append(a) + elif os.path.isdir(a): + makefiles.extend(sorted(glob.glob(os.path.join(a, '*.mk')))) + +def runTest(makefile, make, logfile, options): + """ + Given a makefile path, test it with a given `make` and return + (pass, message). + """ + + if os.path.exists(opts.tempdir): shutil.rmtree(opts.tempdir) + os.mkdir(opts.tempdir, 0755) + + logfd = open(logfile, 'w') + p = Popen(make + options['commandline'], stdout=logfd, stderr=STDOUT, env=options['env']) + logfd.close() + retcode = p.wait() + + if retcode != options['returncode']: + return False, "FAIL (returncode=%i)" % retcode + + logfd = open(logfile) + stdout = logfd.read() + logfd.close() + + if stdout.find('TEST-FAIL') != -1: + print stdout + return False, "FAIL (TEST-FAIL printed)" + + if options['grepfor'] and stdout.find(options['grepfor']) == -1: + print stdout + return False, "FAIL (%s not in output)" % options['grepfor'] + + if options['returncode'] == 0 and stdout.find('TEST-PASS') == -1: + print stdout + return False, 'FAIL (No TEST-PASS printed)' + + if options['returncode'] != 0: + return True, 'PASS (retcode=%s)' % retcode + + return True, 'PASS' + +print "%-30s%-28s%-28s" % ("Test:", "gmake:", "pymake:") + +gmakefails = 0 +pymakefails = 0 + +tre = re.compile('^#T (gmake |pymake )?([a-z-]+)(?:: (.*))?$') + +for makefile in makefiles: + # For some reason, MAKEFILE_LIST uses native paths in GNU make on Windows + # (even in MSYS!) so we pass both TESTPATH and NATIVE_TESTPATH + cline = ['-C', opts.tempdir, '-f', os.path.abspath(makefile), 'TESTPATH=%s' % thisdir.replace('\\','/'), 'NATIVE_TESTPATH=%s' % thisdir] + if sys.platform == 'win32': + #XXX: hack so we can specialize the separator character on windows. + # we really shouldn't need this, but y'know + cline += ['__WIN32__=1'] + + options = { + 'returncode': 0, + 'grepfor': None, + 'env': dict(os.environ), + 'commandline': cline, + 'pass': True, + 'skip': False, + } + + gmakeoptions = ParentDict(options) + pymakeoptions = ParentDict(options) + + dmap = {None: options, 'gmake ': gmakeoptions, 'pymake ': pymakeoptions} + + mdata = open(makefile) + for line in mdata: + line = line.strip() + m = tre.search(line) + if m is None: + break + + make, key, data = m.group(1, 2, 3) + d = dmap[make] + if data is not None: + data = eval(data) + if key == 'commandline': + assert make is None + d['commandline'].extend(data) + elif key == 'returncode': + d['returncode'] = data + elif key == 'returncode-on': + if sys.platform in data: + d['returncode'] = data[sys.platform] + elif key == 'environment': + for k, v in data.iteritems(): + d['env'][k] = v + elif key == 'grep-for': + d['grepfor'] = data + elif key == 'fail': + d['pass'] = False + elif key == 'skip': + d['skip'] = True + else: + print >>sys.stderr, "%s: Unexpected #T key: %s" % (makefile, key) + sys.exit(1) + + mdata.close() + + if gmakeoptions['skip']: + gmakepass, gmakemsg = True, '' + else: + gmakepass, gmakemsg = runTest(makefile, [opts.gmake], + makefile + '.gmakelog', gmakeoptions) + + if gmakeoptions['pass']: + if not gmakepass: + gmakefails += 1 + else: + if gmakepass: + gmakefails += 1 + gmakemsg = "UNEXPECTED PASS" + else: + gmakemsg = "KNOWN FAIL" + + if pymakeoptions['skip']: + pymakepass, pymakemsg = True, '' + else: + pymakepass, pymakemsg = runTest(makefile, pymake, + makefile + '.pymakelog', pymakeoptions) + + if pymakeoptions['pass']: + if not pymakepass: + pymakefails += 1 + else: + if pymakepass: + pymakefails += 1 + pymakemsg = "UNEXPECTED PASS" + else: + pymakemsg = "OK (known fail)" + + print "%-30.30s%-28.28s%-28.28s" % (os.path.basename(makefile), + gmakemsg, pymakemsg) + +print +print "Summary:" +print "%-30s%-28s%-28s" % ("", "gmake:", "pymake:") + +if gmakefails == 0: + gmakemsg = 'PASS' +else: + gmakemsg = 'FAIL (%i failures)' % gmakefails + +if pymakefails == 0: + pymakemsg = 'PASS' +else: + pymakemsg = 'FAIL (%i failures)' % pymakefails + +print "%-30.30s%-28.28s%-28.28s" % ('', gmakemsg, pymakemsg) + +shutil.rmtree(opts.tempdir) + +if gmakefails or pymakefails: + sys.exit(1) diff --git a/build/pymake/tests/serial-dep-resolution.mk b/build/pymake/tests/serial-dep-resolution.mk new file mode 100644 index 000000000..e65f1ed03 --- /dev/null +++ b/build/pymake/tests/serial-dep-resolution.mk @@ -0,0 +1,5 @@ +all: t1 t2 + @echo TEST-PASS + +t1: + touch t1 t2 diff --git a/build/pymake/tests/serial-doublecolon-execution.mk b/build/pymake/tests/serial-doublecolon-execution.mk new file mode 100644 index 000000000..1871cb13a --- /dev/null +++ b/build/pymake/tests/serial-doublecolon-execution.mk @@ -0,0 +1,18 @@ +#T commandline: ['-j3'] + +# Commands of double-colon rules are always executed in order. + +all: dc + cat status + test "$$(cat status)" = "all1:all2:" + @echo TEST-PASS + +dc:: slowt + printf "all1:" >> status + +dc:: + sleep 0.2 + printf "all2:" >> status + +slowt: + sleep 1 diff --git a/build/pymake/tests/serial-rule-execution.mk b/build/pymake/tests/serial-rule-execution.mk new file mode 100644 index 000000000..da5b177de --- /dev/null +++ b/build/pymake/tests/serial-rule-execution.mk @@ -0,0 +1,5 @@ +all:: + touch somefile + +all:: somefile + @echo TEST-PASS diff --git a/build/pymake/tests/serial-rule-execution2.mk b/build/pymake/tests/serial-rule-execution2.mk new file mode 100644 index 000000000..252a7df83 --- /dev/null +++ b/build/pymake/tests/serial-rule-execution2.mk @@ -0,0 +1,13 @@ +#T returncode: 2 + +# The dependencies of the command rule of a single-colon target are resolved before the rules without commands. + +all: export + +export: + sleep 1 + touch somefile + +all: somefile + test -f somefile + @echo TEST-PASS diff --git a/build/pymake/tests/serial-toparallel.mk b/build/pymake/tests/serial-toparallel.mk new file mode 100644 index 000000000..a980badc7 --- /dev/null +++ b/build/pymake/tests/serial-toparallel.mk @@ -0,0 +1,5 @@ +all:: + $(MAKE) -j2 -f $(TESTPATH)/parallel-simple.mk + +all:: results + @echo TEST-PASS diff --git a/build/pymake/tests/shellfunc.mk b/build/pymake/tests/shellfunc.mk new file mode 100644 index 000000000..1e408dbac --- /dev/null +++ b/build/pymake/tests/shellfunc.mk @@ -0,0 +1,7 @@ +all: testfile + test "$(shell cat $<)" = "Hello world" + test "$(shell printf "\n")" = "" + @echo TEST-PASS + +testfile: + printf "Hello\nworld\n" > $@ diff --git a/build/pymake/tests/simple-makeflags.mk b/build/pymake/tests/simple-makeflags.mk new file mode 100644 index 000000000..c7c92ec9d --- /dev/null +++ b/build/pymake/tests/simple-makeflags.mk @@ -0,0 +1,10 @@ +# There once was a time when MAKEFLAGS=w without any following spaces would +# cause us to treat w as a target, not a flag. Silly! + +MAKEFLAGS=w + +all: + $(MAKE) -f $(TESTPATH)/simple-makeflags.mk subt + @echo TEST-PASS + +subt: diff --git a/build/pymake/tests/sort.mk b/build/pymake/tests/sort.mk new file mode 100644 index 000000000..e1313ad5c --- /dev/null +++ b/build/pymake/tests/sort.mk @@ -0,0 +1,4 @@ +# sort should remove duplicates +all: + @test "$(sort x a y b z c a z b x c y)" = "a b c x y z" + @echo "TEST-PASS" diff --git a/build/pymake/tests/specified-target.mk b/build/pymake/tests/specified-target.mk new file mode 100644 index 000000000..3b23fbf69 --- /dev/null +++ b/build/pymake/tests/specified-target.mk @@ -0,0 +1,7 @@ +#T commandline: ['VAR=all', '$(VAR)'] + +all: + @echo TEST-FAIL: unexpected target 'all' + +$$(VAR): + @echo TEST-PASS: expected target '$$(VAR)' diff --git a/build/pymake/tests/static-pattern.mk b/build/pymake/tests/static-pattern.mk new file mode 100644 index 000000000..f613b8c9a --- /dev/null +++ b/build/pymake/tests/static-pattern.mk @@ -0,0 +1,5 @@ +#T returncode: 2 + +out/host_foo.o: host_%.o: host_%.c out + cp $< $@ + @echo TEST-FAIL diff --git a/build/pymake/tests/static-pattern2.mk b/build/pymake/tests/static-pattern2.mk new file mode 100644 index 000000000..08ed834fd --- /dev/null +++ b/build/pymake/tests/static-pattern2.mk @@ -0,0 +1,10 @@ +all: foo.out + test -f $^ + @echo TEST-PASS + +foo.out: %.out: %.in + test "$*" = "foo" + cp $^ $@ + +foo.in: + touch $@ diff --git a/build/pymake/tests/subdir/delayload.py b/build/pymake/tests/subdir/delayload.py new file mode 100644 index 000000000..bdd6669db --- /dev/null +++ b/build/pymake/tests/subdir/delayload.py @@ -0,0 +1 @@ +# This module exists to test delay importing of modules at run-time. diff --git a/build/pymake/tests/subdir/pymod.py b/build/pymake/tests/subdir/pymod.py new file mode 100644 index 000000000..1a47d8af2 --- /dev/null +++ b/build/pymake/tests/subdir/pymod.py @@ -0,0 +1,5 @@ +import testmodule + +def writetofile(args): + with open(args[0], 'w') as f: + f.write(' '.join(args[1:])) diff --git a/build/pymake/tests/subdir/testmodule.py b/build/pymake/tests/subdir/testmodule.py new file mode 100644 index 000000000..05b2f821a --- /dev/null +++ b/build/pymake/tests/subdir/testmodule.py @@ -0,0 +1,3 @@ +# This is an empty module. It is imported by pymod.py to test that if a module +# is loaded from the PYCOMMANDPATH, it can import other modules from the same +# directory correctly. diff --git a/build/pymake/tests/submake-path.makefile2 b/build/pymake/tests/submake-path.makefile2 new file mode 100644 index 000000000..1266db7d1 --- /dev/null +++ b/build/pymake/tests/submake-path.makefile2 @@ -0,0 +1,11 @@ +# -*- Mode: Makefile -*- + +shellresult := $(shell pathtest) +ifneq (2f7cdd0b-7277-48c1-beaf-56cb0dbacb24,$(filter $(shellresult),2f7cdd0b-7277-48c1-beaf-56cb0dbacb24)) +$(error pathtest not found in submake shell function) +endif + +all: + @pathtest + @pathtest | grep -q 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24 + @echo TEST-PASS diff --git a/build/pymake/tests/submake-path.mk b/build/pymake/tests/submake-path.mk new file mode 100644 index 000000000..b6432276d --- /dev/null +++ b/build/pymake/tests/submake-path.mk @@ -0,0 +1,16 @@ +#T gmake skip +#T grep-for: "2f7cdd0b-7277-48c1-beaf-56cb0dbacb24" + +ifdef __WIN32__ +PS:=; +else +PS:=: +endif + +export PATH := $(TESTPATH)/pathdir$(PS)$(PATH) + +# This is similar to subprocess-path.mk, except we also check $(shell) +# invocations since they're affected by exported environment variables too, +# but only in submakes! +all: + $(MAKE) -f $(TESTPATH)/submake-path.makefile2 diff --git a/build/pymake/tests/submake.makefile2 b/build/pymake/tests/submake.makefile2 new file mode 100644 index 000000000..12ce94834 --- /dev/null +++ b/build/pymake/tests/submake.makefile2 @@ -0,0 +1,24 @@ +# -*- Mode: Makefile -*- + +$(info MAKEFLAGS = '$(MAKEFLAGS)') +$(info MAKE = '$(MAKE)') +$(info value MAKE = "$(value MAKE)") + +shellresult := $(shell echo -n $$EVAR) +ifneq ($(shellresult),eval) +$(error EVAR should be eval, is instead $(shellresult)) +endif + +all: + env + test "$(MAKELEVEL)" = "1" + echo "value(MAKE)" '$(value MAKE)' + echo "value(MAKE_COMMAND)" = '$(value MAKE_COMMAND)' + test "$(origin CVAR)" = "command line" + test "$(CVAR)" = "c val=spac\ed" + test "$(origin EVAR)" = "environment" + test "$(EVAR)" = "eval" + test "$(OVAL)" = "cline" + test "$(OVAL2)" = "cline2" + test "$(ALLVAR)" = "allspecific" + @echo TEST-PASS diff --git a/build/pymake/tests/submake.mk b/build/pymake/tests/submake.mk new file mode 100644 index 000000000..41e47134b --- /dev/null +++ b/build/pymake/tests/submake.mk @@ -0,0 +1,16 @@ +#T commandline: ['CVAR=c val=spac\\ed', 'OVAL=cline', 'OVAL2=cline2'] + +export EVAR = eval +override OVAL = makefile + +# exporting an override variable doesn't mean it's an override variable +override OVAL2 = makefile2 +export OVAL2 + +export ALLVAR +ALLVAR = general +all: ALLVAR = allspecific + +all: + test "$(MAKELEVEL)" = "0" + $(MAKE) -f $(TESTPATH)/submake.makefile2 diff --git a/build/pymake/tests/subprocess-path.mk b/build/pymake/tests/subprocess-path.mk new file mode 100644 index 000000000..f63921414 --- /dev/null +++ b/build/pymake/tests/subprocess-path.mk @@ -0,0 +1,32 @@ +#T gmake skip +#T grep-for: "2f7cdd0b-7277-48c1-beaf-56cb0dbacb24" + +ifdef __WIN32__ +PS:=; +else +PS:=: +endif + +export PATH := $(TESTPATH)/pathdir$(PS)$(PATH) + +# Test two commands. The first one shouldn't go through the shell and the +# second one should. The pathdir subdirectory has a Windows executable called +# pathtest.exe and a shell script called pathtest. We don't care which one is +# run, just that one of the two is (we use a uuid + grep-for to make sure +# that happens). +# +# FAQ: +# Q. Why skip GNU Make? +# A. Because $(TESTPATH) is a Windows-style path, and MSYS make doesn't take +# too kindly to Windows paths in the PATH environment variable. +# +# Q. Why use an exe and not a batch file? +# A. The use cases here were all exe files without the extension. Batch file +# lookup has broken semantics if the .bat extension isn't passed. +# +# Q. Why are the commands silent? +# A. So that we don't pass the grep-for test by mistake. +all: + @pathtest + @pathtest | grep -q 2f7cdd0b-7277-48c1-beaf-56cb0dbacb24 + @echo TEST-PASS diff --git a/build/pymake/tests/tab-intro.mk b/build/pymake/tests/tab-intro.mk new file mode 100644 index 000000000..1c25ce747 --- /dev/null +++ b/build/pymake/tests/tab-intro.mk @@ -0,0 +1,16 @@ +# Initial tab characters should be treated well. + + THIS = a value + + ifdef THIS + VAR = conditional value + endif + +all: + test "$(THIS)" = "another value" + test "$(VAR)" = "conditional value" + @echo TEST-PASS + +THAT = makefile syntax + + THIS = another value diff --git a/build/pymake/tests/target-specific.mk b/build/pymake/tests/target-specific.mk new file mode 100644 index 000000000..217ed155e --- /dev/null +++ b/build/pymake/tests/target-specific.mk @@ -0,0 +1,30 @@ +TESTVAR = anonval + +all: target.suffix target.suffix2 dummy host_test.py my.test1 my.test2 + @echo TEST-PASS + +target.suffix: TESTVAR = testval + +%.suffix: + test "$(TESTVAR)" = "testval" + +%.suffix2: TESTVAR = testval2 + +%.suffix2: + test "$(TESTVAR)" = "testval2" + +%my: TESTVAR = dummyval + +dummy: + test "$(TESTVAR)" = "dummyval" + +%.py: TESTVAR = pyval +host_%.py: TESTVAR = hostval + +host_test.py: + test "$(TESTVAR)" = "hostval" + +%.test1 %.test2: TESTVAR = %val + +my.test1 my.test2: + test "$(TESTVAR)" = "%val" diff --git a/build/pymake/tests/unexport.mk b/build/pymake/tests/unexport.mk new file mode 100644 index 000000000..424411603 --- /dev/null +++ b/build/pymake/tests/unexport.mk @@ -0,0 +1,15 @@ +#T environment: {'ENVVAR': 'envval'} + +VAR1 = val1 +VAR2 = val2 +VAR3 = val3 + +unexport VAR3 +export VAR1 VAR2 VAR3 +unexport VAR2 ENVVAR +unexport + +all: + test "$(ENVVAR)" = "envval" # unexport.mk + $(MAKE) -f $(TESTPATH)/unexport.submk + @echo TEST-PASS diff --git a/build/pymake/tests/unexport.submk b/build/pymake/tests/unexport.submk new file mode 100644 index 000000000..8db6163de --- /dev/null +++ b/build/pymake/tests/unexport.submk @@ -0,0 +1,15 @@ +# -@- Mode: Makefile -@- + +unexport VAR1 + +all: + env + test "$(VAR1)" = "val1" + test "$(origin VAR1)" = "environment" + test "$(VAR2)" = "" # VAR2 + test "$(VAR3)" = "val3" + test "$(ENVVAR)" = "" + $(MAKE) -f $(TESTPATH)/unexport.submk subt + +subt: + test "$(VAR1)" = "" diff --git a/build/pymake/tests/unterminated-dollar.mk b/build/pymake/tests/unterminated-dollar.mk new file mode 100644 index 000000000..dee9a207b --- /dev/null +++ b/build/pymake/tests/unterminated-dollar.mk @@ -0,0 +1,6 @@ +VAR = value$ +VAR2 = other + +all: + test "$(VAR)" = "value" + @echo TEST-PASS diff --git a/build/pymake/tests/var-change-flavor.mk b/build/pymake/tests/var-change-flavor.mk new file mode 100644 index 000000000..0cccf0bd6 --- /dev/null +++ b/build/pymake/tests/var-change-flavor.mk @@ -0,0 +1,12 @@ +VAR = value1 +VAR := value2 + +VAR2 := val1 +VAR2 = val2 + +default: + test "$(flavor VAR)" = "simple" + test "$(VAR)" = "value2" + test "$(flavor VAR2)" = "recursive" + test "$(VAR2)" = "val2" + @echo "TEST-PASS" diff --git a/build/pymake/tests/var-commandline.mk b/build/pymake/tests/var-commandline.mk new file mode 100644 index 000000000..e2cdad457 --- /dev/null +++ b/build/pymake/tests/var-commandline.mk @@ -0,0 +1,8 @@ +#T commandline: ['TESTVAR=$(MAKEVAL)', 'TESTVAR2:=$(MAKEVAL)'] + +MAKEVAL=testvalue + +all: + test "$(TESTVAR)" = "testvalue" + test "$(TESTVAR2)" = "" + @echo "TEST-PASS"
\ No newline at end of file diff --git a/build/pymake/tests/var-overrides.mk b/build/pymake/tests/var-overrides.mk new file mode 100644 index 000000000..bd0765d19 --- /dev/null +++ b/build/pymake/tests/var-overrides.mk @@ -0,0 +1,21 @@ +#T commandline: ['CLINEVAR=clineval', 'CLINEVAR2=clineval2'] + +# this doesn't actually test overrides yet, because they aren't implemented in pymake, +# but testing origins in general is important + +MVAR = mval +CLINEVAR = deadbeef + +override CLINEVAR2 = mval2 + +all: + test "$(origin NOVAR)" = "undefined" + test "$(CLINEVAR)" = "clineval" + test "$(origin CLINEVAR)" = "command line" + test "$(MVAR)" = "mval" + test "$(origin MVAR)" = "file" + test "$(@)" = "all" + test "$(origin @)" = "automatic" + test "$(origin CLINEVAR2)" = "override" + test "$(CLINEVAR2)" = "mval2" + @echo TEST-PASS diff --git a/build/pymake/tests/var-ref.mk b/build/pymake/tests/var-ref.mk new file mode 100644 index 000000000..3bc1886f9 --- /dev/null +++ b/build/pymake/tests/var-ref.mk @@ -0,0 +1,19 @@ +VAR = value +VAR2 == value + +VAR5 = $(NULL) $(NULL) +VARC = value # comment + +$(VAR3) + $(VAR4) +$(VAR5) + +VAR6$(VAR5) = val6 + +all: + test "$( VAR)" = "" + test "$(VAR2)" = "= value" + test "${VAR2}" = "= value" + test "$(VAR6 )" = "val6" + test "$(VARC)" = "value " + @echo TEST-PASS diff --git a/build/pymake/tests/var-set.mk b/build/pymake/tests/var-set.mk new file mode 100644 index 000000000..1603e7a35 --- /dev/null +++ b/build/pymake/tests/var-set.mk @@ -0,0 +1,55 @@ +#T commandline: ['OBASIC=oval'] + +BASIC = val + +TEST = $(TEST) + +TEST2 = $(TES +TEST2 += T) + +TES T = val + +RECVAR = foo +RECVAR += var baz + +IMMVAR := bloo +IMMVAR += $(RECVAR) + +BASIC ?= notval + +all: BASIC = valall +all: RECVAR += $(BASIC) +all: IMMVAR += $(BASIC) +all: UNSET += more +all: OBASIC += allmore + +CHECKLIT = $(NULL) check +all: CHECKLIT += appendliteral + +RECVAR = blimey + +TESTEMPTY = \ + $(NULL) + +all: other + test "$(TEST2)" = "val" + test '$(value TEST2)' = '$$(TES T)' + test "$(RECVAR)" = "blimey valall" + test "$(IMMVAR)" = "bloo foo var baz valall" + test "$(UNSET)" = "more" + test "$(OBASIC)" = "oval" + test "$(CHECKLIT)" = " check appendliteral" + test "$(TESTEMPTY)" = "" + @echo TEST-PASS + +OVAR = oval +OVAR ?= onotval + +other: OVAR ?= ooval +other: LATERVAR ?= lateroverride + +LATERVAR = olater + +other: + test "$(OVAR)" = "oval" + test "$(LATERVAR)" = "lateroverride" diff --git a/build/pymake/tests/var-substitutions.mk b/build/pymake/tests/var-substitutions.mk new file mode 100644 index 000000000..d5627d7bd --- /dev/null +++ b/build/pymake/tests/var-substitutions.mk @@ -0,0 +1,49 @@ +SIMPLEVAR = aabb.cc +SIMPLEPERCENT = test_value%extra + +SIMPLE3SUBSTNAME = SIMPLEVAR:.dd +$(SIMPLE3SUBSTNAME) = weirdval + +PERCENT = dummy + +SIMPLESUBST = $(SIMPLEVAR:.cc=.dd) +SIMPLE2SUBST = $(SIMPLEVAR:.cc) +SIMPLE3SUBST = $(SIMPLEVAR:.dd) +SIMPLE4SUBST = $(SIMPLEVAR:.cc=.dd=.ee) +SIMPLE5SUBST = $(SIMPLEVAR:.cc=%.dd) +PERCENTSUBST = $(SIMPLEVAR:%.cc=%.ee) +PERCENT2SUBST = $(SIMPLEVAR:aa%.cc=ff%.f) +PERCENT3SUBST = $(SIMPLEVAR:aa%.dd=gg%.gg) +PERCENT4SUBST = $(SIMPLEVAR:aa%.cc=gg) +PERCENT5SUBST = $(SIMPLEVAR:aa) +PERCENT6SUBST = $(SIMPLEVAR:%.cc=%.dd=%.ee) +PERCENT7SUBST = $(SIMPLEVAR:$(PERCENT).cc=%.dd) +PERCENT8SUBST = $(SIMPLEVAR:%.cc=$(PERCENT).dd) +PERCENT9SUBST = $(SIMPLEVAR:$(PERCENT).cc=$(PERCENT).dd) +PERCENT10SUBST = $(SIMPLEVAR:%%.bb.cc=zz.bb.cc) +PERCENT11SUBST = $(SIMPLEPERCENT:test%value%extra=other%value%extra) + +SPACEDVAR = $(NULL) ex1.c ex2.c $(NULL) +SPACEDSUBST = $(SPACEDVAR:.c=.o) + +all: + test "$(SIMPLESUBST)" = "aabb.dd" + test "$(SIMPLE2SUBST)" = "" + test "$(SIMPLE3SUBST)" = "weirdval" + test "$(SIMPLE4SUBST)" = "aabb.dd=.ee" + test "$(SIMPLE5SUBST)" = "aabb%.dd" + test "$(PERCENTSUBST)" = "aabb.ee" + test "$(PERCENT2SUBST)" = "ffbb.f" + test "$(PERCENT3SUBST)" = "aabb.cc" + test "$(PERCENT4SUBST)" = "gg" + test "$(PERCENT5SUBST)" = "" + test "$(PERCENT6SUBST)" = "aabb.dd=%.ee" + test "$(PERCENT7SUBST)" = "aabb.dd" + test "$(PERCENT8SUBST)" = "aabb.dd" + test "$(PERCENT9SUBST)" = "aabb.dd" + test "$(PERCENT10SUBST)" = "aabb.cc" + test "$(PERCENT11SUBST)" = "other_value%extra" + test "$(SPACEDSUBST)" = "ex1.o ex2.o" + @echo TEST-PASS + +PERCENT = % diff --git a/build/pymake/tests/vpath-directive-dynamic.mk b/build/pymake/tests/vpath-directive-dynamic.mk new file mode 100644 index 000000000..9aa1bf956 --- /dev/null +++ b/build/pymake/tests/vpath-directive-dynamic.mk @@ -0,0 +1,12 @@ +$(shell \ +mkdir subd1; \ +touch subd1/test.in; \ +) + +VVAR = %.in subd1 + +vpath $(VVAR) + +all: test.in + test "$<" = "subd1/test.in" + @echo TEST-PASS diff --git a/build/pymake/tests/vpath-directive.mk b/build/pymake/tests/vpath-directive.mk new file mode 100644 index 000000000..4c7d4bf39 --- /dev/null +++ b/build/pymake/tests/vpath-directive.mk @@ -0,0 +1,31 @@ +# On Windows, MSYS make takes Unix paths but Pymake takes Windows paths +VPSEP := $(if $(and $(__WIN32__),$(.PYMAKE)),;,:) + +$(shell \ +mkdir subd1 subd2 subd3; \ +printf "reallybaddata" >subd1/foo.in; \ +printf "gooddata" >subd2/foo.in; \ +printf "baddata" >subd3/foo.in; \ +touch subd1/foo.in2 subd2/foo.in2 subd3/foo.in2; \ +) + +vpath %.in subd + +vpath +vpath %.in subd2$(VPSEP)subd3 + +vpath %.in2 subd0 +vpath f%.in2 subd1 +vpath %.in2 $(VPSEP)subd2 + +%.out: %.in + test "$<" = "subd2/foo.in" + cp $< $@ + +%.out2: %.in2 + test "$<" = "subd1/foo.in2" + cp $< $@ + +all: foo.out foo.out2 + test "$$(cat foo.out)" = "gooddata" + @echo TEST-PASS diff --git a/build/pymake/tests/vpath.mk b/build/pymake/tests/vpath.mk new file mode 100644 index 000000000..06f52180c --- /dev/null +++ b/build/pymake/tests/vpath.mk @@ -0,0 +1,18 @@ +VPATH = foo bar + +$(shell \ +mkdir foo; touch foo/tfile1; \ +mkdir bar; touch bar/tfile2 bar/tfile3 bar/test.objtest; \ +sleep 2; \ +touch bar/test.source; \ +) + +all: tfile1 tfile2 tfile3 test.objtest test.source + test "$^" = "foo/tfile1 bar/tfile2 tfile3 test.objtest bar/test.source" + @echo TEST-PASS + +tfile3: test.objtest + +%.objtest: %.source + test "$<" = bar/test.source + test "$@" = test.objtest diff --git a/build/pymake/tests/vpath2.mk b/build/pymake/tests/vpath2.mk new file mode 100644 index 000000000..be73ffe5c --- /dev/null +++ b/build/pymake/tests/vpath2.mk @@ -0,0 +1,18 @@ +VPATH = foo bar + +$(shell \ +mkdir bar; touch bar/test.source; \ +sleep 2; \ +mkdir foo; touch foo/tfile1; \ +touch bar/tfile2 bar/tfile3 bar/test.objtest; \ +) + +all: tfile1 tfile2 tfile3 test.objtest test.source + test "$^" = "foo/tfile1 bar/tfile2 bar/tfile3 bar/test.objtest bar/test.source" + @echo TEST-PASS + +tfile3: test.objtest + +%.objtest: %.source + test "$<" = bar/test.source + test "$@" = test.objtest diff --git a/build/pymake/tests/wildcards.mk b/build/pymake/tests/wildcards.mk new file mode 100644 index 000000000..24ff3f14c --- /dev/null +++ b/build/pymake/tests/wildcards.mk @@ -0,0 +1,22 @@ +$(shell \ +mkdir foo; \ +touch a.c b.c c.out foo/d.c; \ +sleep 2; \ +touch c.in; \ +) + +VPATH = foo + +all: c.out prog + cat $< + test "$$(cat $<)" = "remadec.out" + @echo TEST-PASS + +*.out: %.out: %.in + test "$@" = c.out + test "$<" = c.in + printf "remade$@" >$@ + +prog: *.c + test "$^" = "a.c b.c" + touch $@ diff --git a/build/pymake/tests/windows-paths.mk b/build/pymake/tests/windows-paths.mk new file mode 100644 index 000000000..5f33a9050 --- /dev/null +++ b/build/pymake/tests/windows-paths.mk @@ -0,0 +1,5 @@ +all: + touch file.in + printf "%s: %s\n\ttrue" '$(CURDIR)/file.out' '$(CURDIR)/file.in' >test.mk + $(MAKE) -f test.mk $(CURDIR)/file.out + @echo TEST-PASS diff --git a/build/qemu-wrap b/build/qemu-wrap new file mode 100755 index 000000000..e33938955 --- /dev/null +++ b/build/qemu-wrap @@ -0,0 +1,24 @@ +#!/bin/bash +# this script creates a wrapper shell script for an executable. The idea is the actual executable cannot be +# executed natively (it was cross compiled), but we want to run tests natively. Running this script +# as part of the compilation process will move the non-native executable to a new location, and replace it +# with a script that will run it under qemu. +while [[ -n $1 ]]; do + case $1 in + --qemu) QEMU="$2"; shift 2;; + --libdir) LIBDIR="$2"; shift 2;; + --ld) LD="$2"; shift 2;; + *) exe="$1"; shift;; + esac +done +if [[ -z $LIBDIR ]]; then + echo "You need to specify a directory for the cross libraries when you configure the shell" + echo "You can do this with --with-cross-lib=" + exit 1 +fi +LD=${LD:-$LIBDIR/ld-linux.so.3} +mv $exe $exe.target +# Just hardcode the path to the executable. It'll be pretty obvious if it is doing the wrong thing. + +echo $'#!/bin/bash\n' $QEMU -E LD_LIBRARY_PATH="${LIBDIR}" "$LD" "$(readlink -f "$exe.target")" '"$@"' >"$exe" +chmod +x $exe
\ No newline at end of file diff --git a/build/release/info.py b/build/release/info.py new file mode 100644 index 000000000..9f42edd5a --- /dev/null +++ b/build/release/info.py @@ -0,0 +1,218 @@ +from datetime import datetime +import os +from os import path +import re +import shutil +import sys +from urllib2 import urlopen + +from release.paths import makeCandidatesDir + +import logging +log = logging.getLogger(__name__) + +# If version has two parts with no trailing specifiers like "rc", we +# consider it a "final" release for which we only create a _RELEASE tag. +FINAL_RELEASE_REGEX = "^\d+\.\d+$" + + +class ConfigError(Exception): + pass + + +def getBuildID(platform, product, version, buildNumber, nightlyDir='nightly', + server='stage.mozilla.org'): + infoTxt = makeCandidatesDir(product, version, buildNumber, nightlyDir, + protocol='http', server=server) + \ + '%s_info.txt' % platform + try: + buildInfo = urlopen(infoTxt).read() + except: + log.error("Failed to retrieve %s" % infoTxt) + raise + + for line in buildInfo.splitlines(): + key, value = line.rstrip().split('=', 1) + if key == 'buildID': + return value + + +def findOldBuildIDs(product, version, buildNumber, platforms, + nightlyDir='nightly', server='stage.mozilla.org'): + ids = {} + if buildNumber <= 1: + return ids + for n in range(1, buildNumber): + for platform in platforms: + if platform not in ids: + ids[platform] = [] + try: + id = getBuildID(platform, product, version, n, nightlyDir, + server) + ids[platform].append(id) + except Exception, e: + log.error("Hit exception: %s" % e) + return ids + + +def getReleaseConfigName(product, branch, version=None, staging=False): + # XXX: Horrible hack for bug 842741. Because Thunderbird release + # and esr both build out of esr17 repositories we'll bump the wrong + # config for release without this. + if product == 'thunderbird' and 'esr17' in branch and version and 'esr' not in version: + cfg = 'release-thunderbird-comm-release.py' + else: + cfg = 'release-%s-%s.py' % (product, branch) + if staging: + cfg = 'staging_%s' % cfg + return cfg + + +def readReleaseConfig(configfile, required=[]): + return readConfig(configfile, keys=['releaseConfig'], required=required) + + +def readBranchConfig(dir, localconfig, branch, required=[]): + shutil.copy(localconfig, path.join(dir, "localconfig.py")) + oldcwd = os.getcwd() + os.chdir(dir) + sys.path.append(".") + try: + return readConfig("config.py", keys=['BRANCHES', branch], + required=required) + finally: + os.chdir(oldcwd) + sys.path.remove(".") + + +def readConfig(configfile, keys=[], required=[]): + c = {} + execfile(configfile, c) + for k in keys: + c = c[k] + items = c.keys() + err = False + for key in required: + if key not in items: + err = True + log.error("Required item `%s' missing from %s" % (key, c)) + if err: + raise ConfigError("Missing at least one item in config, see above") + return c + + +def isFinalRelease(version): + return bool(re.match(FINAL_RELEASE_REGEX, version)) + + +def getBaseTag(product, version): + product = product.upper() + version = version.replace('.', '_') + return '%s_%s' % (product, version) + + +def getTags(baseTag, buildNumber, buildTag=True): + t = ['%s_RELEASE' % baseTag] + if buildTag: + t.append('%s_BUILD%d' % (baseTag, int(buildNumber))) + return t + + +def getRuntimeTag(tag): + return "%s_RUNTIME" % tag + + +def getReleaseTag(tag): + return "%s_RELEASE" % tag + + +def generateRelbranchName(version, prefix='GECKO'): + return '%s%s_%s_RELBRANCH' % ( + prefix, version.replace('.', ''), + datetime.now().strftime('%Y%m%d%H')) + + +def getReleaseName(product, version, buildNumber): + return '%s-%s-build%s' % (product.title(), version, str(buildNumber)) + + +def getRepoMatchingBranch(branch, sourceRepositories): + for sr in sourceRepositories.values(): + if branch in sr['path']: + return sr + return None + + +def fileInfo(filepath, product): + """Extract information about a release file. Returns a dictionary with the + following keys set: + 'product', 'version', 'locale', 'platform', 'contents', 'format', + 'pathstyle' + + 'contents' is one of 'complete', 'installer' + 'format' is one of 'mar' or 'exe' + 'pathstyle' is either 'short' or 'long', and refers to if files are all in + one directory, with the locale as part of the filename ('short' paths, + firefox 3.0 style filenames), or if the locale names are part of the + directory structure, but not the file name itself ('long' paths, + firefox 3.5+ style filenames) + """ + try: + # Mozilla 1.9.0 style (aka 'short') paths + # e.g. firefox-3.0.12.en-US.win32.complete.mar + filename = os.path.basename(filepath) + m = re.match("^(%s)-([0-9.]+)\.([-a-zA-Z]+)\.(win32)\.(complete|installer)\.(mar|exe)$" % product, filename) + if not m: + raise ValueError("Could not parse: %s" % filename) + return {'product': m.group(1), + 'version': m.group(2), + 'locale': m.group(3), + 'platform': m.group(4), + 'contents': m.group(5), + 'format': m.group(6), + 'pathstyle': 'short', + 'leading_path': '', + } + except: + # Mozilla 1.9.1 and on style (aka 'long') paths + # e.g. update/win32/en-US/firefox-3.5.1.complete.mar + # win32/en-US/Firefox Setup 3.5.1.exe + ret = {'pathstyle': 'long'} + if filepath.endswith('.mar'): + ret['format'] = 'mar' + m = re.search("update/(win32|linux-i686|linux-x86_64|mac|mac64)/([-a-zA-Z]+)/(%s)-(\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?)\.(complete)\.mar" % product, filepath) + if not m: + raise ValueError("Could not parse: %s" % filepath) + ret['platform'] = m.group(1) + ret['locale'] = m.group(2) + ret['product'] = m.group(3) + ret['version'] = m.group(4) + ret['contents'] = m.group(5) + ret['leading_path'] = '' + elif filepath.endswith('.exe'): + ret['format'] = 'exe' + ret['contents'] = 'installer' + # EUballot builds use a different enough style of path than others + # that we can't catch them in the same regexp + if filepath.find('win32-EUballot') != -1: + ret['platform'] = 'win32' + m = re.search("(win32-EUballot/)([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+\d+)?(?:\ \w+\ \d+)?)\.exe" % product, filepath) + if not m: + raise ValueError("Could not parse: %s" % filepath) + ret['leading_path'] = m.group(1) + ret['locale'] = m.group(2) + ret['product'] = m.group(3).lower() + ret['version'] = m.group(4) + else: + m = re.search("(partner-repacks/[-a-zA-Z0-9_]+/|)(win32|mac|linux-i686)/([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?(?:\ \w+\ \d+)?)\.exe" % product, filepath) + if not m: + raise ValueError("Could not parse: %s" % filepath) + ret['leading_path'] = m.group(1) + ret['platform'] = m.group(2) + ret['locale'] = m.group(3) + ret['product'] = m.group(4).lower() + ret['version'] = m.group(5) + else: + raise ValueError("Unknown filetype for %s" % filepath) + + return ret diff --git a/build/release/sanity.py b/build/release/sanity.py new file mode 100644 index 000000000..aba855989 --- /dev/null +++ b/build/release/sanity.py @@ -0,0 +1,124 @@ +import difflib +import logging +import re +import urllib2 +from util.commands import run_cmd, get_output +from util.hg import get_repo_name, make_hg_url +from subprocess import CalledProcessError + +log = logging.getLogger(__name__) + + +def check_buildbot(): + """check if buildbot command works""" + try: + run_cmd(['buildbot', '--version']) + except CalledProcessError: + log.error("FAIL: buildbot command doesn't work", exc_info=True) + raise + + +def find_version(contents, versionNumber): + """Given an open readable file-handle look for the occurrence + of the version # in the file""" + ret = re.search(re.compile(re.escape(versionNumber), re.DOTALL), contents) + return ret + + +def locale_diff(locales1, locales2): + """ accepts two lists and diffs them both ways, returns any differences + found """ + diff_list = [locale for locale in locales1 if not locale in locales2] + diff_list.extend(locale for locale in locales2 if not locale in locales1) + return diff_list + + +def get_buildbot_username_param(): + cmd = ['buildbot', 'sendchange', '--help'] + output = get_output(cmd) + if "-W, --who=" in output: + return "--who" + else: + return "--username" + + +def sendchange(branch, revision, username, master, products): + """Send the change to buildbot to kick off the release automation""" + if isinstance(products, basestring): + products = [products] + cmd = [ + 'buildbot', + 'sendchange', + get_buildbot_username_param(), + username, + '--master', + master, + '--branch', + branch, + '-p', + 'products:%s' % ','.join(products), + '-p', + 'script_repo_revision:%s' % revision, + 'release_or_beta' + ] + logging.info("Executing: %s" % cmd) + run_cmd(cmd) + + +def verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, platform, + mozconfigWhitelist={}): + """Compares mozconfig to nightly_mozconfig and compare to an optional + whitelist of known differences. mozconfig_pair and nightly_mozconfig_pair + are pairs containing the mozconfig's identifier and the list of lines in + the mozconfig.""" + + # unpack the pairs to get the names, the names are just for + # identifying the mozconfigs when logging the error messages + mozconfig_name, mozconfig_lines = mozconfig_pair + nightly_mozconfig_name, nightly_mozconfig_lines = nightly_mozconfig_pair + + missing_args = mozconfig_lines == [] or nightly_mozconfig_lines == [] + if missing_args: + log.info("Missing mozconfigs to compare for %s" % platform) + return False + + success = True + + diffInstance = difflib.Differ() + diff_result = diffInstance.compare(mozconfig_lines, nightly_mozconfig_lines) + diffList = list(diff_result) + + for line in diffList: + clean_line = line[1:].strip() + if (line[0] == '-' or line[0] == '+') and len(clean_line) > 1: + # skip comment lines + if clean_line.startswith('#'): + continue + # compare to whitelist + message = "" + if line[0] == '-': + if platform in mozconfigWhitelist.get('release', {}): + if clean_line in \ + mozconfigWhitelist['release'][platform]: + continue + elif line[0] == '+': + if platform in mozconfigWhitelist.get('nightly', {}): + if clean_line in \ + mozconfigWhitelist['nightly'][platform]: + continue + else: + log.warning("%s not in %s %s!" % ( + clean_line, platform, + mozconfigWhitelist['nightly'][platform])) + else: + log.error("Skipping line %s!" % line) + continue + message = "found in %s but not in %s: %s" + if line[0] == '-': + log.error(message % (mozconfig_name, + nightly_mozconfig_name, clean_line)) + else: + log.error(message % (nightly_mozconfig_name, + mozconfig_name, clean_line)) + success = False + return success diff --git a/build/sanitizers/asan_blacklist_win.txt b/build/sanitizers/asan_blacklist_win.txt new file mode 100644 index 000000000..13b505272 --- /dev/null +++ b/build/sanitizers/asan_blacklist_win.txt @@ -0,0 +1,26 @@ +# This is originally copied from Chromium tools/memory/asan/blacklist_win.txt.
+# The rules in this file are only applied at compile time. If you can modify the
+# source in question, consider function attributes to disable instrumentation.
+
+# Bug 1200740 - ASan crash due to child process function interceptions
+# Sandbox executes some of its code before the ASan RTL gets initialized and
+# maps shadow memory. As a result, instrmented code tries to access unavailable
+# shadow memory and faults.
+fun:*TargetNtSetInformationThread@20
+fun:*TargetNtOpenThreadToken@20
+fun:*TargetNtOpenThreadTokenEx@24
+fun:*TargetNtMapViewOfSection@44
+fun:*AutoProtectMemory*sandbox*
+fun:*EatResolverThunk*sandbox*
+fun:*InterceptionAgent*sandbox*
+fun:*ResolverThunk*sandbox*
+fun:*Target*SandboxFactory*sandbox*
+fun:*ProcessState*sandbox*
+src:*pe_image.h
+src:*pe_image.cc
+src:*resolver_32.cc
+src:*filesystem_interception.cc
+src:*process_thread_interception.cc
+src:*registry_interception.cc
+src:*sandbox_nt_util.cc
+src:*sync_interception.cc
diff --git a/build/sanitizers/lsan_suppressions.txt b/build/sanitizers/lsan_suppressions.txt new file mode 100644 index 000000000..77bd87116 --- /dev/null +++ b/build/sanitizers/lsan_suppressions.txt @@ -0,0 +1,74 @@ +### !!! Please do not add suppressions for new leaks in Gecko code, unless they are intentional !!! + +### +### Some of these leak in every test run. +### + +# LSan runs with a shallow stack depth and no debug symbols, so some small intentional +# leaks in system libraries show up with this. You do not want this enabled +# when running locally with a deep stack, as it can catch too much. +leak:libc.so + +# nsComponentManagerImpl intentionally leaks factory entries, and probably some other stuff. +leak:nsComponentManagerImpl +# These two variants are needed when fast unwind is disabled and stack depth is limited. +leak:mozJSComponentLoader::LoadModule +leak:nsNativeModuleLoader::LoadModule + +# Bug 981220 - Pixman fails to free TLS memory. +leak:pixman_implementation_lookup_composite + +# Bug 987918 - Font shutdown leaks when CLEANUP_MEMORY is not enabled. +leak:libfontconfig.so +leak:GI___strdup +# The symbol is really __GI___strdup, but if you have the leading _, it doesn't suppress it. + +# Bug 1078015 - If the process terminates during a PR_Sleep, LSAN detects a leak +leak:PR_Sleep + +### +### Many leaks only affect some test suites. The suite annotations are not checked. +### + +# Bug 979928 - WebRTC leaks in different mochitest suites. +leak:NR_reg_init +# nr_reg_local_init should be redundant with NR_reg_init, but on Aurora +# we get fewer stack frames for some reason. +leak:nr_reg_local_init +leak:r_log_register +leak:nr_reg_set + +# This is a one-time leak in mochitest-bc, so it is probably okay to ignore. +leak:GlobalPrinters::InitializeGlobalPrinters +leak:nsPSPrinterList::GetPrinterList + +# Bug 1028456 - Various NSPR fd-related leaks in different mochitest suites. +leak:_PR_Getfd + +# Bug 1028483 - The XML parser sometimes leaks an object. Mostly happens in toolkit/components/thumbnails. +leak:processInternalEntity + +# Bug 1187421 - NSS does not always free the error stack in different mochitest suites. +leak:nss_ClearErrorStack + +### +### Leaks with system libraries in their stacks. These show up across a number of tests. +### Better symbols and disabling fast stackwalking may help diagnose these. +### + +leak:libcairo.so +leak:libdl.so +leak:libdricore.so +leak:libdricore9.2.1.so +leak:libGL.so +leak:libglib-2.0.so +leak:libglsl.so +leak:libp11-kit.so +leak:libpixman-1.so +leak:libpulse.so +leak:libpulsecommon-1.1.so +leak:libresolv.so +leak:libstdc++.so +leak:libXrandr.so +leak:pthread_setspecific_internal +leak:swrast_dri.so diff --git a/build/sanitizers/tsan_suppressions.txt b/build/sanitizers/tsan_suppressions.txt new file mode 100644 index 000000000..3ea562e67 --- /dev/null +++ b/build/sanitizers/tsan_suppressions.txt @@ -0,0 +1,21 @@ +# Bug 931149 +race:DoImageDataComplete + +# Bug 939786 +race:_pt_root + +# Bug 939788 +# Tracked by http://code.google.com/p/thread-sanitizer/issues/detail?id=40 +race:ELMCreationDetector + +# Bug 939790 +race:xpcom/components/nsComponentManager.h + +# Bug 939807 +race:ComputeUTCTime + +# Bug 939805 +race:g_variant_type_info_get + +# Bug 844759 +race:js::gc::ArenaLists::allocateFromArenaInline diff --git a/build/sccache.mk b/build/sccache.mk new file mode 100644 index 000000000..c57befe3d --- /dev/null +++ b/build/sccache.mk @@ -0,0 +1,18 @@ +# 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/. + +ifdef OBJDIR +BASE_DIR = $(OBJDIR) +else +# OSX Universal builds only do upload in the first MOZ_BUILD_PROJECTS +BASE_DIR = $(MOZ_OBJDIR)/$(firstword $(MOZ_BUILD_PROJECTS)) +endif + +preflight_all: + # Terminate any sccache server that might still be around + -python2.7 $(TOPSRCDIR)/sccache/sccache.py > /dev/null 2>&1 + +postflight_all: + # Terminate sccache server. This prints sccache stats. + -python2.7 $(TOPSRCDIR)/sccache/sccache.py 2>&1 | gzip > $(BASE_DIR)/dist/sccache.log.gz diff --git a/build/subconfigure.py b/build/subconfigure.py new file mode 100644 index 000000000..6c152f406 --- /dev/null +++ b/build/subconfigure.py @@ -0,0 +1,441 @@ +# 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 script is used to capture the content of config.status-generated +# files and subsequently restore their timestamp if they haven't changed. + +import argparse +import errno +import itertools +import os +import re +import subprocess +import sys +import pickle + +import mozpack.path as mozpath + + +class Pool(object): + def __new__(cls, size): + try: + import multiprocessing + size = min(size, multiprocessing.cpu_count()) + return multiprocessing.Pool(size) + except: + return super(Pool, cls).__new__(cls) + + def imap_unordered(self, fn, iterable): + return itertools.imap(fn, iterable) + + def close(self): + pass + + def join(self): + pass + + +class File(object): + def __init__(self, path): + self._path = path + self._content = open(path, 'rb').read() + stat = os.stat(path) + self._times = (stat.st_atime, stat.st_mtime) + + @property + def path(self): + return self._path + + @property + def mtime(self): + return self._times[1] + + @property + def modified(self): + '''Returns whether the file was modified since the instance was + created. Result is memoized.''' + if hasattr(self, '_modified'): + return self._modified + + modified = True + if os.path.exists(self._path): + if open(self._path, 'rb').read() == self._content: + modified = False + self._modified = modified + return modified + + def update_time(self): + '''If the file hasn't changed since the instance was created, + restore its old modification time.''' + if not self.modified: + os.utime(self._path, self._times) + + +# As defined in the various sub-configures in the tree +PRECIOUS_VARS = set([ + 'build_alias', + 'host_alias', + 'target_alias', + 'CC', + 'CFLAGS', + 'LDFLAGS', + 'LIBS', + 'CPPFLAGS', + 'CPP', + 'CCC', + 'CXXFLAGS', + 'CXX', + 'CCASFLAGS', + 'CCAS', +]) + + +CONFIGURE_DATA = 'configure.pkl' + + +# Autoconf, in some of the sub-configures used in the tree, likes to error +# out when "precious" variables change in value. The solution it gives to +# straighten things is to either run make distclean or remove config.cache. +# There's no reason not to do the latter automatically instead of failing, +# doing the cleanup (which, on buildbots means a full clobber), and +# restarting from scratch. +def maybe_clear_cache(data): + env = dict(data['env']) + for kind in ('target', 'host', 'build'): + arg = data[kind] + if arg is not None: + env['%s_alias' % kind] = arg + # configure can take variables assignments in its arguments, and that + # overrides whatever is in the environment. + for arg in data['args']: + if arg[:1] != '-' and '=' in arg: + key, value = arg.split('=', 1) + env[key] = value + + comment = re.compile(r'^\s+#') + cache = {} + with open(data['cache-file']) as f: + for line in f: + if not comment.match(line) and '=' in line: + key, value = line.rstrip(os.linesep).split('=', 1) + # If the value is quoted, unquote it + if value[:1] == "'": + value = value[1:-1].replace("'\\''", "'") + cache[key] = value + for precious in PRECIOUS_VARS: + # If there is no entry at all for that precious variable, then + # its value is not precious for that particular configure. + if 'ac_cv_env_%s_set' % precious not in cache: + continue + is_set = cache.get('ac_cv_env_%s_set' % precious) == 'set' + value = cache.get('ac_cv_env_%s_value' % precious) if is_set else None + if value != env.get(precious): + print 'Removing %s because of %s value change from:' \ + % (data['cache-file'], precious) + print ' %s' % (value if value is not None else 'undefined') + print 'to:' + print ' %s' % env.get(precious, 'undefined') + os.remove(data['cache-file']) + return True + return False + + +def split_template(s): + """Given a "file:template" string, returns "file", "template". If the string + is of the form "file" (without a template), returns "file", "file.in".""" + if ':' in s: + return s.split(':', 1) + return s, '%s.in' % s + + +def get_config_files(data): + config_status = mozpath.join(data['objdir'], 'config.status') + if not os.path.exists(config_status): + return [], [] + + configure = mozpath.join(data['srcdir'], 'configure') + config_files = [] + command_files = [] + + # Scan the config.status output for information about configuration files + # it generates. + config_status_output = subprocess.check_output( + [data['shell'], '-c', '%s --help' % config_status], + stderr=subprocess.STDOUT).splitlines() + state = None + for line in config_status_output: + if line.startswith('Configuration') and line.endswith(':'): + if line.endswith('commands:'): + state = 'commands' + else: + state = 'config' + elif not line.strip(): + state = None + elif state: + for f, t in (split_template(couple) for couple in line.split()): + f = mozpath.join(data['objdir'], f) + t = mozpath.join(data['srcdir'], t) + if state == 'commands': + command_files.append(f) + else: + config_files.append((f, t)) + + return config_files, command_files + + +def prepare(srcdir, objdir, shell, args): + parser = argparse.ArgumentParser() + parser.add_argument('--target', type=str) + parser.add_argument('--host', type=str) + parser.add_argument('--build', type=str) + parser.add_argument('--cache-file', type=str) + # The --srcdir argument is simply ignored. It's a useless autoconf feature + # that we don't support well anyways. This makes it stripped from `others` + # and allows to skip setting it when calling the subconfigure (configure + # will take it from the configure path anyways). + parser.add_argument('--srcdir', type=str) + + data_file = os.path.join(objdir, CONFIGURE_DATA) + previous_args = None + if os.path.exists(data_file): + with open(data_file, 'rb') as f: + data = pickle.load(f) + previous_args = data['args'] + + # Msys likes to break environment variables and command line arguments, + # so read those from stdin, as they are passed from the configure script + # when necessary (on windows). + input = sys.stdin.read() + if input: + data = {a: b for [a, b] in eval(input)} + environ = {a: b for a, b in data['env']} + # These environment variables as passed from old-configure may contain + # posix-style paths, which will not be meaningful to the js + # subconfigure, which runs as a native python process, so use their + # values from the environment. In the case of autoconf implemented + # subconfigures, Msys will re-convert them properly. + for var in ('HOME', 'TERM', 'PATH', 'TMPDIR', 'TMP', + 'TEMP', 'INCLUDE'): + if var in environ and var in os.environ: + environ[var] = os.environ[var] + args = data['args'] + else: + environ = os.environ + + args, others = parser.parse_known_args(args) + + data = { + 'target': args.target, + 'host': args.host, + 'build': args.build, + 'args': others, + 'shell': shell, + 'srcdir': srcdir, + 'env': environ, + } + + if args.cache_file: + data['cache-file'] = mozpath.normpath(mozpath.join(os.getcwd(), + args.cache_file)) + else: + data['cache-file'] = mozpath.join(objdir, 'config.cache') + + if previous_args is not None: + data['previous-args'] = previous_args + + try: + os.makedirs(objdir) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + with open(data_file, 'wb') as f: + pickle.dump(data, f) + + +def prefix_lines(text, prefix): + return ''.join('%s> %s' % (prefix, line) for line in text.splitlines(True)) + + +def run(objdir): + ret = 0 + output = '' + + with open(os.path.join(objdir, CONFIGURE_DATA), 'rb') as f: + data = pickle.load(f) + + data['objdir'] = objdir + + cache_file = data['cache-file'] + cleared_cache = True + if os.path.exists(cache_file): + cleared_cache = maybe_clear_cache(data) + + config_files, command_files = get_config_files(data) + contents = [] + for f, t in config_files: + contents.append(File(f)) + + # AC_CONFIG_COMMANDS actually only registers tags, not file names + # but most commands are tagged with the file name they create. + # However, a few don't, or are tagged with a directory name (and their + # command is just to create that directory) + for f in command_files: + if os.path.isfile(f): + contents.append(File(f)) + + # Only run configure if one of the following is true: + # - config.status doesn't exist + # - config.status is older than configure + # - the configure arguments changed + # - the environment changed in a way that requires a cache clear. + configure = mozpath.join(data['srcdir'], 'configure') + config_status_path = mozpath.join(objdir, 'config.status') + skip_configure = True + if not os.path.exists(config_status_path): + skip_configure = False + config_status = None + else: + config_status = File(config_status_path) + if config_status.mtime < os.path.getmtime(configure) or \ + data.get('previous-args', data['args']) != data['args'] or \ + cleared_cache: + skip_configure = False + + relobjdir = os.path.relpath(objdir, os.getcwd()) + + if not skip_configure: + if mozpath.normsep(relobjdir) == 'js/src': + # Because configure is a shell script calling a python script + # calling a shell script, on Windows, with msys screwing the + # environment, we lose the benefits from our own efforts in this + # script to get past the msys problems. So manually call the python + # script instead, so that we don't do a native->msys transition + # here. Then the python configure will still have the right + # environment when calling the shell configure. + command = [ + sys.executable, + os.path.join(os.path.dirname(__file__), '..', 'configure.py'), + '--enable-project=js', + ] + data['env']['OLD_CONFIGURE'] = os.path.join( + os.path.dirname(configure), 'old-configure') + else: + command = [data['shell'], configure] + for kind in ('target', 'build', 'host'): + if data.get(kind) is not None: + command += ['--%s=%s' % (kind, data[kind])] + command += data['args'] + command += ['--cache-file=%s' % cache_file] + + # Pass --no-create to configure so that it doesn't run config.status. + # We're going to run it ourselves. + command += ['--no-create'] + + print prefix_lines('configuring', relobjdir) + print prefix_lines('running %s' % ' '.join(command[:-1]), relobjdir) + sys.stdout.flush() + try: + output += subprocess.check_output(command, + stderr=subprocess.STDOUT, cwd=objdir, env=data['env']) + except subprocess.CalledProcessError as e: + return relobjdir, e.returncode, e.output + + # Leave config.status with a new timestamp if configure is newer than + # its original mtime. + if config_status and os.path.getmtime(configure) <= config_status.mtime: + config_status.update_time() + + # Only run config.status if one of the following is true: + # - config.status changed or did not exist + # - one of the templates for config files is newer than the corresponding + # config file. + skip_config_status = True + if not config_status or config_status.modified: + # If config.status doesn't exist after configure (because it's not + # an autoconf configure), skip it. + if os.path.exists(config_status_path): + skip_config_status = False + else: + # config.status changed or was created, so we need to update the + # list of config and command files. + config_files, command_files = get_config_files(data) + for f, t in config_files: + if not os.path.exists(t) or \ + os.path.getmtime(f) < os.path.getmtime(t): + skip_config_status = False + + if not skip_config_status: + if skip_configure: + print prefix_lines('running config.status', relobjdir) + sys.stdout.flush() + try: + output += subprocess.check_output([data['shell'], '-c', + './config.status'], stderr=subprocess.STDOUT, cwd=objdir, + env=data['env']) + except subprocess.CalledProcessError as e: + ret = e.returncode + output += e.output + + for f in contents: + f.update_time() + + return relobjdir, ret, output + + +def subconfigure(args): + parser = argparse.ArgumentParser() + parser.add_argument('--list', type=str, + help='File containing a list of subconfigures to run') + parser.add_argument('--skip', type=str, + help='File containing a list of Subconfigures to skip') + parser.add_argument('subconfigures', type=str, nargs='*', + help='Subconfigures to run if no list file is given') + args, others = parser.parse_known_args(args) + subconfigures = args.subconfigures + if args.list: + subconfigures.extend(open(args.list, 'rb').read().splitlines()) + if args.skip: + skips = set(open(args.skip, 'rb').read().splitlines()) + subconfigures = [s for s in subconfigures if s not in skips] + + if not subconfigures: + return 0 + + ret = 0 + # One would think using a ThreadPool would be faster, considering + # everything happens in subprocesses anyways, but no, it's actually + # slower on Windows. (20s difference overall!) + pool = Pool(len(subconfigures)) + for relobjdir, returncode, output in \ + pool.imap_unordered(run, subconfigures): + print prefix_lines(output, relobjdir) + sys.stdout.flush() + ret = max(returncode, ret) + if ret: + break + pool.close() + pool.join() + return ret + + +def main(args): + if args[0] != '--prepare': + return subconfigure(args) + + topsrcdir = os.path.abspath(args[1]) + subdir = args[2] + # subdir can be of the form srcdir:objdir + if ':' in subdir: + srcdir, subdir = subdir.split(':', 1) + else: + srcdir = subdir + srcdir = os.path.join(topsrcdir, srcdir) + objdir = os.path.abspath(subdir) + + return prepare(srcdir, objdir, args[3], args[4:]) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/build/submit_telemetry_data.py b/build/submit_telemetry_data.py new file mode 100644 index 000000000..918d840b7 --- /dev/null +++ b/build/submit_telemetry_data.py @@ -0,0 +1,77 @@ +# 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 errno +import logging +import os +import sys +import time + +HERE = os.path.abspath(os.path.dirname(__file__)) +sys.path.append(os.path.join(HERE, '..', 'python', 'requests')) +import requests + + +# Server to which to submit telemetry data +BUILD_TELEMETRY_SERVER = 'http://52.88.27.118/build-metrics-dev' + + +def submit_telemetry_data(statedir): + + # No data to work with anyway + outgoing = os.path.join(statedir, 'telemetry', 'outgoing') + if not os.path.isdir(outgoing): + return 0 + + submitted = os.path.join(statedir, 'telemetry', 'submitted') + try: + os.mkdir(submitted) + except OSError as e: + if e.errno != errno.EEXIST: + raise + + session = requests.Session() + for filename in os.listdir(outgoing): + path = os.path.join(outgoing, filename) + if os.path.isdir(path) or not path.endswith('.json'): + continue + with open(path, 'r') as f: + data = f.read() + try: + r = session.post(BUILD_TELEMETRY_SERVER, data=data, + headers={'Content-Type': 'application/json'}) + except Exception as e: + logging.error('Exception posting to telemetry ' + 'server: %s' % str(e)) + break + # TODO: some of these errors are likely not recoverable, as + # written, we'll retry indefinitely + if r.status_code != 200: + logging.error('Error posting to telemetry: %s %s' % + (r.status_code, r.text)) + continue + + os.rename(os.path.join(outgoing, filename), + os.path.join(submitted, filename)) + + session.close() + + # Discard submitted data that is >= 30 days old + now = time.time() + for filename in os.listdir(submitted): + ctime = os.stat(os.path.join(submitted, filename)).st_ctime + if now - ctime >= 60*60*24*30: + os.remove(os.path.join(submitted, filename)) + + return 0 + + +if __name__ == '__main__': + if len(sys.argv) != 2: + print('usage: python submit_telemetry_data.py <statedir>') + sys.exit(1) + statedir = sys.argv[1] + logging.basicConfig(filename=os.path.join(statedir, 'telemetry', 'telemetry.log'), + format='%(asctime)s %(message)s') + sys.exit(submit_telemetry_data(statedir)) diff --git a/build/telemetry-schema.json b/build/telemetry-schema.json new file mode 100644 index 000000000..6157bf9d5 --- /dev/null +++ b/build/telemetry-schema.json @@ -0,0 +1,24 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + "type": "object", + "properties": { + "argv": {"type": "array"}, + "system": { + "type": "object", + "properties": { + "architecture": {"type": "array"}, + "linux_distribution": {"type": "array"}, + "mac_ver": {"type": "array"}, + "machine": {"type": "string"}, + "python_version": {"type": "string"}, + "release": {"type": "string"}, + "system": {"type": "string"}, + "version": {"type": "string"}, + "win_ver": {"type": "array"} + }, + "required": ["architecture", "machine", "python_version", + "release", "system", "version"] + } + }, + "required": ["argv", "system"] +} diff --git a/build/templates.mozbuild b/build/templates.mozbuild new file mode 100644 index 000000000..85b700c2a --- /dev/null +++ b/build/templates.mozbuild @@ -0,0 +1,131 @@ +# -*- Mode: python; 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/. + +@template +def Binary(): + '''Generic template for target binaries. Meant to be used by other + templates.''' + + if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']: + USE_LIBS += ['stdc++compat'] + + # Ideally, we'd support not adding this to the LIB_IS_C_ONLY case, + # but that variable is actually only set in db/sqlite/src, which + # doesn't build a shared library on the relevant platforms anyways. + # Eventually, though, we should detect LIB_IS_C_ONLY based on the + # associated SOURCES (and there might actually be places where we + # haven't set it but should have). + if CONFIG['STLPORT_LIBS']: + OS_LIBS += [CONFIG['STLPORT_LIBS']] + + +@template +def Program(name): + '''Template for program executables.''' + PROGRAM = name + + Binary() + + +@template +def SimplePrograms(names, ext='.cpp'): + '''Template for simple program executables. + + Those have a single source with the same base name as the executable. + ''' + SIMPLE_PROGRAMS += names + SOURCES += ['%s%s' % (name, ext) for name in names] + + Binary() + + +@template +def CppUnitTests(names, ext='.cpp'): + '''Template for C++ unit tests. + + Those have a single source with the same base name as the executable. + ''' + CPP_UNIT_TESTS += names + SOURCES += ['%s%s' % (name, ext) for name in names] + + Binary() + + +@template +def Library(name): + '''Template for libraries.''' + LIBRARY_NAME = name + + +@template +def RustLibrary(name): + '''Template for Rust libraries.''' + Library(name) + + IS_RUST_LIBRARY = True + + +@template +def SharedLibrary(name): + '''Template for shared libraries.''' + Library(name) + + FORCE_SHARED_LIB = True + + Binary() + + +@template +def Framework(name): + '''Template for OSX Frameworks.''' + SharedLibrary(name) + + IS_FRAMEWORK = True + + +@template +def HostStdCppCompat(): + '''Template for libstdc++ compatibility for host binaries.''' + + if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']: + HOST_USE_LIBS += ['host_stdc++compat'] + + +@template +def HostProgram(name, c_only=False): + '''Template for build tools executables.''' + HOST_PROGRAM = name + + # With context-based templates, this won't be needed anymore, and will + # work better than relying on the caller setting it, but at the moment, + # this is the best we have. And it doesn't matter /that/ much, so there's + # really only one place using this, where it does matter to avoid the + # extra dependency (because it creates a circular one). + if not c_only: + HostStdCppCompat() + + +@template +def HostSimplePrograms(names, ext='.cpp'): + '''Template for simple build tools executables. + + Those have a single source with the same base name as the executable. + ''' + HOST_SIMPLE_PROGRAMS += names + HOST_SOURCES += ['%s%s' % (name.replace('host_', ''), ext) + for name in names] + + HostStdCppCompat() + + +@template +def HostLibrary(name): + '''Template for build tools libraries.''' + HOST_LIBRARY_NAME = name + + +include('gecko_templates.mozbuild') +include('test_templates.mozbuild') diff --git a/build/test_templates.mozbuild b/build/test_templates.mozbuild new file mode 100644 index 000000000..31b3b37c1 --- /dev/null +++ b/build/test_templates.mozbuild @@ -0,0 +1,39 @@ +# -*- Mode: python; 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/. + +@template +def GeneratedTestCertificate(name): + if not CONFIG['COMPILE_ENVIRONMENT']: + return + + GENERATED_FILES += [name] + props = GENERATED_FILES[name] + props.script = '/security/manager/ssl/tests/unit/pycert.py' + props.inputs = ['%s.certspec' % name] + # Turn RELATIVEDIR into list entry: like + # 'security/manager/ssl/tests/unit/bad_certs' -> + # TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.bad_certs. + files = TEST_HARNESS_FILES.xpcshell + for part in RELATIVEDIR.split('/'): + files = files[part] + files += ['!%s' % name] + +@template +def GeneratedTestKey(name): + if not CONFIG['COMPILE_ENVIRONMENT']: + return + + GENERATED_FILES += [name] + props = GENERATED_FILES[name] + props.script = '/security/manager/ssl/tests/unit/pykey.py' + props.inputs = ['%s.keyspec' % name] + # Turn RELATIVEDIR into list entry: like + # 'security/manager/ssl/tests/unit/bad_certs' -> + # TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.bad_certs. + files = TEST_HARNESS_FILES.xpcshell + for part in RELATIVEDIR.split('/'): + files = files[part] + files += ['!%s' % name] diff --git a/build/unix/aix.exp b/build/unix/aix.exp new file mode 100644 index 000000000..20f7cb431 --- /dev/null +++ b/build/unix/aix.exp @@ -0,0 +1,5 @@ +# 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/. + +NSGetModule diff --git a/build/unix/build-binutils/build-binutils.sh b/build/unix/build-binutils/build-binutils.sh new file mode 100755 index 000000000..ab675f55a --- /dev/null +++ b/build/unix/build-binutils/build-binutils.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +binutils_version=2.25.1 +make_flags='-j12' + +root_dir="$1" +if [ -z "$root_dir" -o ! -d "$root_dir" ]; then + root_dir=$(mktemp -d) +fi +cd $root_dir + +if test -z $TMPDIR; then + TMPDIR=/tmp/ +fi + +# Download the source of the specified version of binutils +wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/binutils/binutils-${binutils_version}.tar.bz2 || exit 1 +tar xjf $TMPDIR/binutils-${binutils_version}.tar.bz2 + +# Build binutils +mkdir binutils-objdir +cd binutils-objdir + +../binutils-$binutils_version/configure --prefix /tools/binutils/ --enable-gold --enable-plugins --disable-nls || exit 1 +make $make_flags || exit 1 +make install $make_flags DESTDIR=$root_dir || exit 1 + +cd .. + +# Make a package of the built binutils +cd $root_dir/tools +tar caf $root_dir/binutils.tar.xz binutils/ diff --git a/build/unix/build-gcc/PR64905.patch b/build/unix/build-gcc/PR64905.patch new file mode 100644 index 000000000..b86c99b9f --- /dev/null +++ b/build/unix/build-gcc/PR64905.patch @@ -0,0 +1,11 @@ +--- trunk/gcc/lra-eliminations.c 2015/02/04 20:00:48 220415 ++++ trunk/gcc/lra-eliminations.c 2015/02/04 20:02:21 220416 +@@ -182,6 +182,8 @@ + if (! value + && ep->from == FRAME_POINTER_REGNUM && ep->to == STACK_POINTER_REGNUM) + frame_pointer_needed = 1; ++ if (!frame_pointer_needed) ++ REGNO_POINTER_ALIGN (HARD_FRAME_POINTER_REGNUM) = 0; + } + + /* Map: eliminable "from" register -> its current elimination, diff --git a/build/unix/build-gcc/build-gcc.sh b/build/unix/build-gcc/build-gcc.sh new file mode 100755 index 000000000..df3bc5dfd --- /dev/null +++ b/build/unix/build-gcc/build-gcc.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +gcc_version=4.8.5 +binutils_version=2.25.1 +this_path=$(readlink -f $(dirname $0)) +make_flags='-j12' + +root_dir="$1" +if [ -z "$root_dir" -o ! -d "$root_dir" ]; then + root_dir=$(mktemp -d) +fi +cd $root_dir + +if test -z $TMPDIR; then + TMPDIR=/tmp/ +fi + +wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/binutils/binutils-$binutils_version.tar.bz2 || exit 1 +tar xjf $TMPDIR/binutils-$binutils_version.tar.bz2 +mkdir binutils-objdir +cd binutils-objdir +# gold is disabled because we don't use it on automation, and also we ran into +# some issues with it using this script in build-clang.py. +../binutils-$binutils_version/configure --prefix /tools/gcc/ --disable-gold --enable-plugins --disable-nls || exit 1 +make $make_flags || exit 1 +make install $make_flags DESTDIR=$root_dir || exit 1 +cd .. + +case "$gcc_version" in +*-*) + wget -c -P $TMPDIR ftp://gcc.gnu.org/pub/gcc/snapshots/$gcc_version/gcc-$gcc_version.tar.bz2 || exit 1 + ;; +*) + wget -c -P $TMPDIR ftp://ftp.gnu.org/gnu/gcc/gcc-$gcc_version/gcc-$gcc_version.tar.bz2 || exit 1 + ;; +esac +tar xjf $TMPDIR/gcc-$gcc_version.tar.bz2 +cd gcc-$gcc_version + +./contrib/download_prerequisites + +patch -p1 < "${this_path}/PR64905.patch" || exit 1 + +cd .. +mkdir gcc-objdir +cd gcc-objdir +../gcc-$gcc_version/configure --prefix=/tools/gcc --enable-languages=c,c++ --disable-nls --disable-gnu-unique-object --enable-__cxa_atexit --with-arch-32=pentiumpro || exit 1 +make $make_flags || exit 1 +make $make_flags install DESTDIR=$root_dir || exit 1 + +cd $root_dir/tools +tar caf $root_dir/gcc.tar.xz gcc/ diff --git a/build/unix/build-gtk3/build-gtk3.sh b/build/unix/build-gtk3/build-gtk3.sh new file mode 100644 index 000000000..6e03d4a52 --- /dev/null +++ b/build/unix/build-gtk3/build-gtk3.sh @@ -0,0 +1,150 @@ +#!/bin/bash + +# Use "build-gtk.sh" or "build-gtk.sh 64" to build a 64-bits tarball for tooltool. +# Use "build-gtk.sh 32" to build a 32-bits tarball for tooltool. + +# Mock environments used: +# - 64-bits: +# https://s3.amazonaws.com/mozilla-releng-mock-archive/67b65e51eb091fba7941a04d249343924a3ee653 +# + libxml2-devel.x86_64 gettext.x86_64 libjpeg-devel.x86_64 +# - 32-bits: +# https://s3.amazonaws.com/mozilla-releng-mock-archive/58d76c6acca148a1aedcbec7fc1b8212e12807b4 +# + libxml2-devel.i686 gettext.i686 libjpeg-devel.i686 + +set -e + +pkg_config_version=0.28 +fontconfig_version=2.8.0 +libffi_version=3.0.13 +glib_version=2.34.3 +gdk_pixbuf_version=2.26.5 +pixman_version=0.20.2 +cairo_version=1.10.2 +pango_version=1.30.1 +atk_version=2.2.0 +gtk__version=3.4.4 + +pkg_config_url=http://pkgconfig.freedesktop.org/releases/pkg-config-${pkg_config_version}.tar.gz +fontconfig_url=http://www.freedesktop.org/software/fontconfig/release/fontconfig-${fontconfig_version}.tar.gz +libffi_url=ftp://sourceware.org/pub/libffi/libffi-${libffi_version}.tar.gz +glib_url=http://ftp.gnome.org/pub/gnome/sources/glib/${glib_version%.*}/glib-${glib_version}.tar.xz +gdk_pixbuf_url=http://ftp.gnome.org/pub/gnome/sources/gdk-pixbuf/${gdk_pixbuf_version%.*}/gdk-pixbuf-${gdk_pixbuf_version}.tar.xz +pixman_url=http://cairographics.org/releases/pixman-${pixman_version}.tar.gz +cairo_url=http://cairographics.org/releases/cairo-${cairo_version}.tar.gz +pango_url=http://ftp.gnome.org/pub/GNOME/sources/pango/${pango_version%.*}/pango-${pango_version}.tar.xz +atk_url=http://ftp.gnome.org/pub/GNOME/sources/atk/${atk_version%.*}/atk-${atk_version}.tar.xz +gtk__url=http://ftp.gnome.org/pub/gnome/sources/gtk+/${gtk__version%.*}/gtk+-${gtk__version}.tar.xz + +cwd=$(pwd) +root_dir=$(mktemp -d) +cd $root_dir + +if test -z $TMPDIR; then + TMPDIR=/tmp/ +fi + +make_flags=-j12 + +build() { + name=$1 + shift + pkg=$(echo $name | tr '+-' '__') + version=$(eval echo \$${pkg}_version) + url=$(eval echo \$${pkg}_url) + wget -c -P $TMPDIR $url + tar -axf $TMPDIR/$name-$version.tar.* + mkdir -p build/$name + cd build/$name + eval ../../$name-$version/configure --disable-static $* $configure_args + make $make_flags + make install DESTDIR=$root_dir/gtk3 + find $root_dir/gtk3 -name \*.la -delete + cd ../.. +} + +case "$1" in +32) + configure_args='--host=i686-pc-linux --build=i686-pc-linux CC="gcc -m32" CXX="g++ -m32"' + lib=lib + ;; +*) + configure_args= + lib=lib64 + ;; +esac + +export PKG_CONFIG_LIBDIR=/usr/$lib/pkgconfig:/usr/share/pkgconfig + +# The pkg-config version in the mock images is buggy is how it handles +# PKG_CONFIG_SYSROOT_DIR. So we need our own. +build pkg-config + +ln -sf /usr/include $root_dir/gtk3/usr/ +ln -sf /usr/$lib $root_dir/gtk3/usr/ +if [ "$lib" = lib64 ]; then + ln -s lib $root_dir/gtk3/usr/local/lib64 +fi +export PKG_CONFIG_PATH=$root_dir/gtk3/usr/local/lib/pkgconfig +export PKG_CONFIG_SYSROOT_DIR=$root_dir/gtk3 +export LD_LIBRARY_PATH=$root_dir/gtk3/usr/local/lib +export PATH=$root_dir/gtk3/usr/local/bin:${PATH} + +build fontconfig +build libffi +build glib +build gdk-pixbuf --without-libtiff +build pixman --disable-gtk +build cairo --enable-tee +build pango +build atk +make_flags="$make_flags GLIB_COMPILE_SCHEMAS=glib-compile-schemas" +build gtk+ + +rm -rf $root_dir/gtk3/usr/local/share/gtk-doc +rm -rf $root_dir/gtk3/usr/local/share/locale + +# mock build environment doesn't have fonts in /usr/share/fonts, but +# has some in /usr/share/X11/fonts. Add this directory to the +# fontconfig configuration without changing the gtk3 tooltool package. +cat << EOF > $root_dir/gtk3/usr/local/etc/fonts/local.conf +<?xml version="1.0"?> +<!DOCTYPE fontconfig SYSTEM "fonts.dtd"> +<fontconfig> + <dir>/usr/share/X11/fonts</dir> +</fontconfig> +EOF + +cat <<EOF > $root_dir/gtk3/setup.sh +#!/bin/sh + +cd \$(dirname \$0) +HERE=\$(pwd) + +# pango expects absolute paths in pango.modules, and TOOLTOOL_DIR may vary... +LD_LIBRARY_PATH=\$HERE/usr/local/lib \ +PANGO_SYSCONFDIR=\$HERE/usr/local/etc \ +PANGO_LIBDIR=\$HERE/usr/local/lib \ +\$HERE/usr/local/bin/pango-querymodules > \$HERE/usr/local/etc/pango/pango.modules + +# same with gdb-pixbuf and loaders.cache +LD_LIBRARY_PATH=\$HERE/usr/local/lib \ +GDK_PIXBUF_MODULE_FILE=\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache \ +GDK_PIXBUF_MODULEDIR=\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders \ +\$HERE/usr/local/bin/gdk-pixbuf-query-loaders > \ +\$HERE/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache + +# The fontconfig version in the tooltool package has known uses of +# uninitialized memory when creating its cache, and while most users +# will already have an existing cache, running Firefox on automation +# will create it. Combined with valgrind, this generates irrelevant +# errors. +# So create the fontconfig cache beforehand. +FONTCONFIG_PATH=\$HERE/usr/local/etc/fonts \ +LD_LIBRARY_PATH=\$HERE/usr/local/lib \ +\$HERE/usr/local/bin/fc-cache +EOF + +chmod +x $root_dir/gtk3/setup.sh + +cd $cwd +tar -C $root_dir -Jcf gtk3.tar.xz gtk3 diff --git a/build/unix/elfhack/Makefile.in b/build/unix/elfhack/Makefile.in new file mode 100644 index 000000000..f1c8c91ee --- /dev/null +++ b/build/unix/elfhack/Makefile.in @@ -0,0 +1,48 @@ +# +# 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/. + +OS_CXXFLAGS := $(filter-out -fno-exceptions,$(OS_CXXFLAGS)) -fexceptions + +include $(topsrcdir)/config/rules.mk + +test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): %$(DLL_SUFFIX): %.$(OBJ_SUFFIX) elfhack + $(MKSHLIB) $(LDFLAGS) $< -nostartfiles + @echo === + @echo === If you get failures below, please file a bug describing the error + @echo === and your environment \(compiler and linker versions\), and use + @echo === --disable-elf-hack until this is fixed. + @echo === + # Fail if the library doesn't have $(DT_TYPE) .dynamic info + $(TOOLCHAIN_PREFIX)readelf -d $@ | grep '($(DT_TYPE))' + @rm -f $@.bak + $(CURDIR)/elfhack -b -f $@ + # Fail if the backup file doesn't exist + [ -f '$@.bak' ] + # Fail if the new library doesn't contain less relocations + [ $$($(TOOLCHAIN_PREFIX)objdump -R $@.bak | wc -l) -gt $$(objdump -R $@ | wc -l) ] + +test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX): DSO_SONAME=$@ +test-array$(DLL_SUFFIX): DT_TYPE=INIT_ARRAY +test-ctors$(DLL_SUFFIX): DT_TYPE=INIT + +.PRECIOUS: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) + +GARBAGE += test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) test-array$(DLL_SUFFIX).bak test-ctors$(DLL_SUFFIX).bak + +ifndef CROSS_COMPILE +ifdef COMPILE_ENVIRONMENT +libs:: test-array$(DLL_SUFFIX) test-ctors$(DLL_SUFFIX) + +dummy: dummy.$(OBJ_SUFFIX) + $(CC) -o $@ $^ $(LDFLAGS) + +libs:: dummy + # Will either crash or return exit code 1 if elfhack is broken + LD_PRELOAD=$(CURDIR)/test-array$(DLL_SUFFIX) $(CURDIR)/dummy + LD_PRELOAD=$(CURDIR)/test-ctors$(DLL_SUFFIX) $(CURDIR)/dummy + +GARBAGE += dummy +endif +endif diff --git a/build/unix/elfhack/README b/build/unix/elfhack/README new file mode 100644 index 000000000..8c68031e3 --- /dev/null +++ b/build/unix/elfhack/README @@ -0,0 +1,28 @@ +Elfhack is a program to optimize ELF binaries for size and cold startup +speed. + +Presently, it is quite experimental, though it works well for the target +it was created for: Firefox's libxul.so. + +Elfhack currently only does one thing: packing dynamic relocations ; +which ends up being a quite complex task, that can be summarized this +way: +- Remove RELATIVE relocations from the .rel.dyn/.rela.dyn section. +- Inject a small code able to apply relative relocations "by hand" + after the .rel.dyn/.rela.dyn section. +- Inject a section containing relocative relocations in a different + and more packed format, after the small code. +- Register the small code as DT_INIT function. Make the small code call + what was initially the DT_INIT function, if there was one. +- Remove the hole between the new section containing relative + relocations and the following sections, adjusting offsets and base + addresses accordingly. +- Adjust PT_LOAD entries to fit new offsets, and add an additional + PT_LOAD entry when that is necessary to handle the discrepancy between + offsets and base addresses, meaning the section offsets may yet again + need adjustments. +- Adjust various DT_* dynamic tags to fit the new ELF layout. +- Adjust section headers. +- Adjust ELF headers. + +See http://glandium.org/blog/?p=1177#relocations for some figures. diff --git a/build/unix/elfhack/dummy.c b/build/unix/elfhack/dummy.c new file mode 100644 index 000000000..f30fcb5b1 --- /dev/null +++ b/build/unix/elfhack/dummy.c @@ -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/. */ + +extern __attribute__((visibility("default"), weak)) int print_status(); + +int main() { + return print_status(); +} diff --git a/build/unix/elfhack/elf.cpp b/build/unix/elfhack/elf.cpp new file mode 100644 index 000000000..743afdead --- /dev/null +++ b/build/unix/elfhack/elf.cpp @@ -0,0 +1,921 @@ +/* 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/. */ + +#undef NDEBUG +#include <cstring> +#include <assert.h> +#include "elfxx.h" + +template <class endian, typename R, typename T> +void Elf_Ehdr_Traits::swap(T &t, R &r) +{ + memcpy(r.e_ident, t.e_ident, sizeof(r.e_ident)); + r.e_type = endian::swap(t.e_type); + r.e_machine = endian::swap(t.e_machine); + r.e_version = endian::swap(t.e_version); + r.e_entry = endian::swap(t.e_entry); + r.e_phoff = endian::swap(t.e_phoff); + r.e_shoff = endian::swap(t.e_shoff); + r.e_flags = endian::swap(t.e_flags); + r.e_ehsize = endian::swap(t.e_ehsize); + r.e_phentsize = endian::swap(t.e_phentsize); + r.e_phnum = endian::swap(t.e_phnum); + r.e_shentsize = endian::swap(t.e_shentsize); + r.e_shnum = endian::swap(t.e_shnum); + r.e_shstrndx = endian::swap(t.e_shstrndx); +} + +template <class endian, typename R, typename T> +void Elf_Phdr_Traits::swap(T &t, R &r) +{ + r.p_type = endian::swap(t.p_type); + r.p_offset = endian::swap(t.p_offset); + r.p_vaddr = endian::swap(t.p_vaddr); + r.p_paddr = endian::swap(t.p_paddr); + r.p_filesz = endian::swap(t.p_filesz); + r.p_memsz = endian::swap(t.p_memsz); + r.p_flags = endian::swap(t.p_flags); + r.p_align = endian::swap(t.p_align); +} + +template <class endian, typename R, typename T> +void Elf_Shdr_Traits::swap(T &t, R &r) +{ + r.sh_name = endian::swap(t.sh_name); + r.sh_type = endian::swap(t.sh_type); + r.sh_flags = endian::swap(t.sh_flags); + r.sh_addr = endian::swap(t.sh_addr); + r.sh_offset = endian::swap(t.sh_offset); + r.sh_size = endian::swap(t.sh_size); + r.sh_link = endian::swap(t.sh_link); + r.sh_info = endian::swap(t.sh_info); + r.sh_addralign = endian::swap(t.sh_addralign); + r.sh_entsize = endian::swap(t.sh_entsize); +} + +template <class endian, typename R, typename T> +void Elf_Dyn_Traits::swap(T &t, R &r) +{ + r.d_tag = endian::swap(t.d_tag); + r.d_un.d_val = endian::swap(t.d_un.d_val); +} + +template <class endian, typename R, typename T> +void Elf_Sym_Traits::swap(T &t, R &r) +{ + r.st_name = endian::swap(t.st_name); + r.st_value = endian::swap(t.st_value); + r.st_size = endian::swap(t.st_size); + r.st_info = t.st_info; + r.st_other = t.st_other; + r.st_shndx = endian::swap(t.st_shndx); +} + +template <class endian> +struct _Rel_info { + static inline void swap(Elf32_Word &t, Elf32_Word &r) { r = endian::swap(t); } + static inline void swap(Elf64_Xword &t, Elf64_Xword &r) { r = endian::swap(t); } + static inline void swap(Elf64_Xword &t, Elf32_Word &r) { + r = endian::swap(ELF32_R_INFO(ELF64_R_SYM(t), ELF64_R_TYPE(t))); + } + static inline void swap(Elf32_Word &t, Elf64_Xword &r) { + r = endian::swap(ELF64_R_INFO(ELF32_R_SYM(t), ELF32_R_TYPE(t))); + } +}; + +template <class endian, typename R, typename T> +void Elf_Rel_Traits::swap(T &t, R &r) +{ + r.r_offset = endian::swap(t.r_offset); + _Rel_info<endian>::swap(t.r_info, r.r_info); +} + +template <class endian, typename R, typename T> +void Elf_Rela_Traits::swap(T &t, R &r) +{ + r.r_offset = endian::swap(t.r_offset); + _Rel_info<endian>::swap(t.r_info, r.r_info); + r.r_addend = endian::swap(t.r_addend); +} + +static const Elf32_Shdr null32_section = + { 0, SHT_NULL, 0, 0, 0, 0, SHN_UNDEF, 0, 0, 0 }; + +Elf_Shdr null_section(null32_section); + +Elf_Ehdr::Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data) +: serializable<Elf_Ehdr_Traits>(file, ei_class, ei_data), + ElfSection(null_section, nullptr, nullptr) +{ + shdr.sh_size = Elf_Ehdr::size(ei_class); +} + +Elf::Elf(std::ifstream &file) +{ + if (!file.is_open()) + throw std::runtime_error("Error opening file"); + + file.exceptions(std::ifstream::eofbit | std::ifstream::failbit | std::ifstream::badbit); + // Read ELF magic number and identification information + char e_ident[EI_VERSION]; + file.seekg(0); + file.read(e_ident, sizeof(e_ident)); + file.seekg(0); + ehdr = new Elf_Ehdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]); + + // ELFOSABI_LINUX is kept unsupported because I haven't looked whether + // STB_GNU_UNIQUE or STT_GNU_IFUNC would need special casing. + if ((ehdr->e_ident[EI_OSABI] != ELFOSABI_NONE) && (ehdr->e_ident[EI_ABIVERSION] != 0)) + throw std::runtime_error("unsupported ELF ABI"); + + if (ehdr->e_version != 1) + throw std::runtime_error("unsupported ELF version"); + + // Sanity checks + if (ehdr->e_shnum == 0) + throw std::runtime_error("sstripped ELF files aren't supported"); + + if (ehdr->e_ehsize != Elf_Ehdr::size(e_ident[EI_CLASS])) + throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_ehsize != sizeof(ehdr)"); + + if (ehdr->e_shentsize != Elf_Shdr::size(e_ident[EI_CLASS])) + throw std::runtime_error("unsupported ELF inconsistency: ehdr.e_shentsize != sizeof(shdr)"); + + if (ehdr->e_phnum == 0) { + if (ehdr->e_phoff != 0) + throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phoff != 0"); + if (ehdr->e_phentsize != 0) + throw std::runtime_error("unsupported ELF inconsistency: e_phnum == 0 && e_phentsize != 0"); + } else if (ehdr->e_phoff != ehdr->e_ehsize) + throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phoff != ehdr->e_ehsize"); + else if (ehdr->e_phentsize != Elf_Phdr::size(e_ident[EI_CLASS])) + throw std::runtime_error("unsupported ELF inconsistency: ehdr->e_phentsize != sizeof(phdr)"); + + // Read section headers + Elf_Shdr **shdr = new Elf_Shdr *[ehdr->e_shnum]; + file.seekg(ehdr->e_shoff); + for (int i = 0; i < ehdr->e_shnum; i++) + shdr[i] = new Elf_Shdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]); + + // Sanity check in section header for index 0 + if ((shdr[0]->sh_name != 0) || (shdr[0]->sh_type != SHT_NULL) || + (shdr[0]->sh_flags != 0) || (shdr[0]->sh_addr != 0) || + (shdr[0]->sh_offset != 0) || (shdr[0]->sh_size != 0) || + (shdr[0]->sh_link != SHN_UNDEF) || (shdr[0]->sh_info != 0) || + (shdr[0]->sh_addralign != 0) || (shdr[0]->sh_entsize != 0)) + throw std::runtime_error("Section header for index 0 contains unsupported values"); + + if ((shdr[ehdr->e_shstrndx]->sh_link != 0) || (shdr[ehdr->e_shstrndx]->sh_info != 0)) + throw std::runtime_error("unsupported ELF content: string table with sh_link != 0 || sh_info != 0"); + + // Store these temporarily + tmp_shdr = shdr; + tmp_file = &file; + + // Fill sections list + sections = new ElfSection *[ehdr->e_shnum]; + for (int i = 0; i < ehdr->e_shnum; i++) + sections[i] = nullptr; + for (int i = 1; i < ehdr->e_shnum; i++) { + if (sections[i] != nullptr) + continue; + getSection(i); + } + Elf_Shdr s; + s.sh_name = 0; + s.sh_type = SHT_NULL; + s.sh_flags = 0; + s.sh_addr = 0; + s.sh_offset = ehdr->e_shoff; + s.sh_entsize = Elf_Shdr::size(e_ident[EI_CLASS]); + s.sh_size = s.sh_entsize * ehdr->e_shnum; + s.sh_link = 0; + s.sh_info = 0; + s.sh_addralign = (e_ident[EI_CLASS] == ELFCLASS32) ? 4 : 8; + shdr_section = new ElfSection(s, nullptr, nullptr); + + // Fake section for program headers + s.sh_offset = ehdr->e_phoff; + s.sh_addr = ehdr->e_phoff; + s.sh_entsize = Elf_Phdr::size(e_ident[EI_CLASS]); + s.sh_size = s.sh_entsize * ehdr->e_phnum; + phdr_section = new ElfSection(s, nullptr, nullptr); + + phdr_section->insertAfter(ehdr, false); + + sections[1]->insertAfter(phdr_section, false); + for (int i = 2; i < ehdr->e_shnum; i++) { + // TODO: this should be done in a better way + if ((shdr_section->getPrevious() == nullptr) && (shdr[i]->sh_offset > ehdr->e_shoff)) { + shdr_section->insertAfter(sections[i - 1], false); + sections[i]->insertAfter(shdr_section, false); + } else + sections[i]->insertAfter(sections[i - 1], false); + } + if (shdr_section->getPrevious() == nullptr) + shdr_section->insertAfter(sections[ehdr->e_shnum - 1], false); + + tmp_file = nullptr; + tmp_shdr = nullptr; + for (int i = 0; i < ehdr->e_shnum; i++) + delete shdr[i]; + delete[] shdr; + + eh_shstrndx = (ElfStrtab_Section *)sections[ehdr->e_shstrndx]; + + // Skip reading program headers if there aren't any + if (ehdr->e_phnum == 0) + return; + + // Read program headers + file.seekg(ehdr->e_phoff); + for (int i = 0; i < ehdr->e_phnum; i++) { + Elf_Phdr phdr(file, e_ident[EI_CLASS], e_ident[EI_DATA]); + if (phdr.p_type == PT_LOAD) { + // Default alignment for PT_LOAD on x86-64 prevents elfhack from + // doing anything useful. However, the system doesn't actually + // require such a big alignment, so in order for elfhack to work + // efficiently, reduce alignment when it's originally the default + // one. + if ((ehdr->e_machine == EM_X86_64) && (phdr.p_align == 0x200000)) + phdr.p_align = 0x1000; + } + ElfSegment *segment = new ElfSegment(&phdr); + // Some segments aren't entirely filled (if at all) by sections + // For those, we use fake sections + if ((phdr.p_type == PT_LOAD) && (phdr.p_offset == 0)) { + // Use a fake section for ehdr and phdr + ehdr->getShdr().sh_addr = phdr.p_vaddr; + phdr_section->getShdr().sh_addr += phdr.p_vaddr; + segment->addSection(ehdr); + segment->addSection(phdr_section); + } + if (phdr.p_type == PT_PHDR) + segment->addSection(phdr_section); + for (int j = 1; j < ehdr->e_shnum; j++) + if (phdr.contains(sections[j])) + segment->addSection(sections[j]); + // Make sure that our view of segments corresponds to the original + // ELF file. + // GNU gold likes to start some segments before the first section + // they contain. https://sourceware.org/bugzilla/show_bug.cgi?id=19392 + unsigned int gold_adjustment = segment->getAddr() - phdr.p_vaddr; + assert(segment->getFileSize() == phdr.p_filesz - gold_adjustment); + // gold makes TLS segments end on an aligned virtual address, even + // when the underlying section ends before that, while bfd ld + // doesn't. It's fine if we don't keep that alignment. + unsigned int memsize = segment->getMemSize(); + if (phdr.p_type == PT_TLS && memsize != phdr.p_memsz) { + unsigned int align = segment->getAlign(); + memsize = (memsize + align - 1) & ~(align - 1); + } + assert(memsize == phdr.p_memsz - gold_adjustment); + segments.push_back(segment); + } + + new (&eh_entry) ElfLocation(ehdr->e_entry, this); +} + +Elf::~Elf() +{ + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + delete *seg; + delete[] sections; + ElfSection *section = ehdr; + while (section != nullptr) { + ElfSection *next = section->getNext(); + delete section; + section = next; + } +} + +// TODO: This shouldn't fail after inserting sections +ElfSection *Elf::getSection(int index) +{ + if ((index < -1) || (index >= ehdr->e_shnum)) + throw std::runtime_error("Section index out of bounds"); + if (index == -1) + index = ehdr->e_shstrndx; // TODO: should be fixed to use the actual current number + // Special case: the section at index 0 is void + if (index == 0) + return nullptr; + // Infinite recursion guard + if (sections[index] == (ElfSection *)this) + return nullptr; + if (sections[index] == nullptr) { + sections[index] = (ElfSection *)this; + switch (tmp_shdr[index]->sh_type) { + case SHT_DYNAMIC: + sections[index] = new ElfDynamic_Section(*tmp_shdr[index], tmp_file, this); + break; + case SHT_REL: + sections[index] = new ElfRel_Section<Elf_Rel>(*tmp_shdr[index], tmp_file, this); + break; + case SHT_RELA: + sections[index] = new ElfRel_Section<Elf_Rela>(*tmp_shdr[index], tmp_file, this); + break; + case SHT_DYNSYM: + case SHT_SYMTAB: + sections[index] = new ElfSymtab_Section(*tmp_shdr[index], tmp_file, this); + break; + case SHT_STRTAB: + sections[index] = new ElfStrtab_Section(*tmp_shdr[index], tmp_file, this); + break; + default: + sections[index] = new ElfSection(*tmp_shdr[index], tmp_file, this); + } + } + return sections[index]; +} + +ElfSection *Elf::getSectionAt(unsigned int offset) +{ + for (int i = 1; i < ehdr->e_shnum; i++) { + ElfSection *section = getSection(i); + if ((section != nullptr) && (section->getFlags() & SHF_ALLOC) && !(section->getFlags() & SHF_TLS) && + (offset >= section->getAddr()) && (offset < section->getAddr() + section->getSize())) + return section; + } + return nullptr; +} + +ElfSegment *Elf::getSegmentByType(unsigned int type, ElfSegment *last) +{ + std::vector<ElfSegment *>::iterator seg; + if (last) { + seg = std::find(segments.begin(), segments.end(), last); + ++seg; + } else + seg = segments.begin(); + for (; seg != segments.end(); seg++) + if ((*seg)->getType() == type) + return *seg; + return nullptr; +} + +void Elf::removeSegment(ElfSegment *segment) +{ + if (!segment) + return; + std::vector<ElfSegment *>::iterator seg; + seg = std::find(segments.begin(), segments.end(), segment); + if (seg == segments.end()) + return; + segment->clear(); + segments.erase(seg); +} + +ElfDynamic_Section *Elf::getDynSection() +{ + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + if (((*seg)->getType() == PT_DYNAMIC) && ((*seg)->getFirstSection() != nullptr) && + (*seg)->getFirstSection()->getType() == SHT_DYNAMIC) + return (ElfDynamic_Section *)(*seg)->getFirstSection(); + + return nullptr; +} + +void Elf::normalize() +{ + // fixup section headers sh_name; TODO: that should be done by sections + // themselves + for (ElfSection *section = ehdr; section != nullptr; section = section->getNext()) { + if (section->getIndex() == 0) + continue; + else + ehdr->e_shnum = section->getIndex() + 1; + section->getShdr().sh_name = eh_shstrndx->getStrIndex(section->getName()); + } + ehdr->markDirty(); + // Check segments consistency + int i = 0; + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++, i++) { + std::list<ElfSection *>::iterator it = (*seg)->begin(); + for (ElfSection *last = *(it++); it != (*seg)->end(); last = *(it++)) { + if (((*it)->getType() != SHT_NOBITS) && + ((*it)->getAddr() - last->getAddr()) != ((*it)->getOffset() - last->getOffset())) { + throw std::runtime_error("Segments inconsistency"); + } + } + } + // fixup ehdr before writing + if (ehdr->e_phnum != segments.size()) { + ehdr->e_phnum = segments.size(); + phdr_section->getShdr().sh_size = segments.size() * Elf_Phdr::size(ehdr->e_ident[EI_CLASS]); + phdr_section->getNext()->markDirty(); + } + // fixup shdr before writing + if (ehdr->e_shnum != shdr_section->getSize() / shdr_section->getEntSize()) + shdr_section->getShdr().sh_size = ehdr->e_shnum * Elf_Shdr::size(ehdr->e_ident[EI_CLASS]); + ehdr->e_shoff = shdr_section->getOffset(); + ehdr->e_entry = eh_entry.getValue(); + ehdr->e_shstrndx = eh_shstrndx->getIndex(); +} + +void Elf::write(std::ofstream &file) +{ + normalize(); + for (ElfSection *section = ehdr; + section != nullptr; section = section->getNext()) { + file.seekp(section->getOffset()); + if (section == phdr_section) { + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) { + Elf_Phdr phdr; + phdr.p_type = (*seg)->getType(); + phdr.p_flags = (*seg)->getFlags(); + phdr.p_offset = (*seg)->getOffset(); + phdr.p_vaddr = (*seg)->getAddr(); + phdr.p_paddr = phdr.p_vaddr + (*seg)->getVPDiff(); + phdr.p_filesz = (*seg)->getFileSize(); + phdr.p_memsz = (*seg)->getMemSize(); + phdr.p_align = (*seg)->getAlign(); + phdr.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + } + } else if (section == shdr_section) { + null_section.serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + for (ElfSection *sec = ehdr; sec!= nullptr; sec = sec->getNext()) { + if (sec->getType() != SHT_NULL) + sec->getShdr().serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + } + } else + section->serialize(file, ehdr->e_ident[EI_CLASS], ehdr->e_ident[EI_DATA]); + } +} + +ElfSection::ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent) +: shdr(s), + link(shdr.sh_link == SHN_UNDEF ? nullptr : parent->getSection(shdr.sh_link)), + next(nullptr), previous(nullptr), index(-1) +{ + if ((file == nullptr) || (shdr.sh_type == SHT_NULL) || (shdr.sh_type == SHT_NOBITS)) + data = nullptr; + else { + data = new char[shdr.sh_size]; + int pos = file->tellg(); + file->seekg(shdr.sh_offset); + file->read(data, shdr.sh_size); + file->seekg(pos); + } + if (shdr.sh_name == 0) + name = nullptr; + else { + ElfStrtab_Section *strtab = (ElfStrtab_Section *) parent->getSection(-1); + // Special case (see elfgeneric.cpp): if strtab is nullptr, the + // section being created is the strtab. + if (strtab == nullptr) + name = &data[shdr.sh_name]; + else + name = strtab->getStr(shdr.sh_name); + } + // Only SHT_REL/SHT_RELA sections use sh_info to store a section + // number. + if ((shdr.sh_type == SHT_REL) || (shdr.sh_type == SHT_RELA)) + info.section = shdr.sh_info ? parent->getSection(shdr.sh_info) : nullptr; + else + info.index = shdr.sh_info; +} + +unsigned int ElfSection::getAddr() +{ + if (shdr.sh_addr != (Elf32_Word)-1) + return shdr.sh_addr; + + // It should be safe to adjust sh_addr for all allocated sections that + // are neither SHT_NOBITS nor SHT_PROGBITS + if ((previous != nullptr) && isRelocatable()) { + unsigned int addr = previous->getAddr(); + if (previous->getType() != SHT_NOBITS) + addr += previous->getSize(); + + if (addr & (getAddrAlign() - 1)) + addr = (addr | (getAddrAlign() - 1)) + 1; + + return (shdr.sh_addr = addr); + } + return shdr.sh_addr; +} + +unsigned int ElfSection::getOffset() +{ + if (shdr.sh_offset != (Elf32_Word)-1) + return shdr.sh_offset; + + if (previous == nullptr) + return (shdr.sh_offset = 0); + + unsigned int offset = previous->getOffset(); + + ElfSegment *ptload = getSegmentByType(PT_LOAD); + ElfSegment *prev_ptload = previous->getSegmentByType(PT_LOAD); + + if (ptload && (ptload == prev_ptload)) { + offset += getAddr() - previous->getAddr(); + return (shdr.sh_offset = offset); + } + + if (previous->getType() != SHT_NOBITS) + offset += previous->getSize(); + + Elf32_Word align = 0x1000; + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + align = std::max(align, (*seg)->getAlign()); + + Elf32_Word mask = align - 1; + // SHF_TLS is used for .tbss which is some kind of special case. + if (((getType() != SHT_NOBITS) || (getFlags() & SHF_TLS)) && (getFlags() & SHF_ALLOC)) { + if ((getAddr() & mask) < (offset & mask)) + offset = (offset | mask) + (getAddr() & mask) + 1; + else + offset = (offset & ~mask) + (getAddr() & mask); + } + if ((getType() != SHT_NOBITS) && (offset & (getAddrAlign() - 1))) + offset = (offset | (getAddrAlign() - 1)) + 1; + + // Two subsequent sections can't be mapped in the same page in memory + // if they aren't in the same 4K block on disk. + if ((getType() != SHT_NOBITS) && getAddr()) { + if (((offset >> 12) != (previous->getOffset() >> 12)) && + ((getAddr() >> 12) == (previous->getAddr() >> 12))) + throw std::runtime_error("Moving section would require overlapping segments"); + } + + return (shdr.sh_offset = offset); +} + +int ElfSection::getIndex() +{ + if (index != -1) + return index; + if (getType() == SHT_NULL) + return (index = 0); + ElfSection *reference; + for (reference = previous; (reference != nullptr) && (reference->getType() == SHT_NULL); reference = reference->getPrevious()); + if (reference == nullptr) + return (index = 1); + return (index = reference->getIndex() + 1); +} + +Elf_Shdr &ElfSection::getShdr() +{ + getOffset(); + if (shdr.sh_link == (Elf32_Word)-1) + shdr.sh_link = getLink() ? getLink()->getIndex() : 0; + if (shdr.sh_info == (Elf32_Word)-1) + shdr.sh_info = ((getType() == SHT_REL) || (getType() == SHT_RELA)) ? + (getInfo().section ? getInfo().section->getIndex() : 0) : + getInfo().index; + + return shdr; +} + +ElfSegment::ElfSegment(Elf_Phdr *phdr) +: type(phdr->p_type), v_p_diff(phdr->p_paddr - phdr->p_vaddr), + flags(phdr->p_flags), align(phdr->p_align), vaddr(phdr->p_vaddr), + filesz(phdr->p_filesz), memsz(phdr->p_memsz) {} + +void ElfSegment::addSection(ElfSection *section) +{ + // Make sure all sections in PT_GNU_RELRO won't be moved by elfhack + assert(!((type == PT_GNU_RELRO) && (section->isRelocatable()))); + + //TODO: Check overlapping sections + std::list<ElfSection *>::iterator i; + for (i = sections.begin(); i != sections.end(); ++i) + if ((*i)->getAddr() > section->getAddr()) + break; + sections.insert(i, section); + section->addToSegment(this); +} + +void ElfSegment::removeSection(ElfSection *section) +{ + sections.remove(section); + section->removeFromSegment(this); +} + +unsigned int ElfSegment::getFileSize() +{ + if (type == PT_GNU_RELRO || isElfHackFillerSegment()) + return filesz; + + if (sections.empty()) + return 0; + // Search the last section that is not SHT_NOBITS + std::list<ElfSection *>::reverse_iterator i; + for (i = sections.rbegin(); (i != sections.rend()) && ((*i)->getType() == SHT_NOBITS); ++i); + // All sections are SHT_NOBITS + if (i == sections.rend()) + return 0; + + unsigned int end = (*i)->getAddr() + (*i)->getSize(); + + return end - sections.front()->getAddr(); +} + +unsigned int ElfSegment::getMemSize() +{ + if (type == PT_GNU_RELRO || isElfHackFillerSegment()) + return memsz; + + if (sections.empty()) + return 0; + + unsigned int end = sections.back()->getAddr() + sections.back()->getSize(); + + return end - sections.front()->getAddr(); +} + +unsigned int ElfSegment::getOffset() +{ + if ((type == PT_GNU_RELRO) && !sections.empty() && + (sections.front()->getAddr() != vaddr)) + throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start"); + + // Neither bionic nor glibc linkers seem to like when the offset of that segment is 0 + if (isElfHackFillerSegment()) + return vaddr; + + return sections.empty() ? 0 : sections.front()->getOffset(); +} + +unsigned int ElfSegment::getAddr() +{ + if ((type == PT_GNU_RELRO) && !sections.empty() && + (sections.front()->getAddr() != vaddr)) + throw std::runtime_error("PT_GNU_RELRO segment doesn't start on a section start"); + + if (isElfHackFillerSegment()) + return vaddr; + + return sections.empty() ? 0 : sections.front()->getAddr(); +} + +void ElfSegment::clear() +{ + for (std::list<ElfSection *>::iterator i = sections.begin(); i != sections.end(); ++i) + (*i)->removeFromSegment(this); + sections.clear(); +} + +ElfValue *ElfDynamic_Section::getValueForType(unsigned int tag) +{ + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) + if (dyns[i].tag == tag) + return dyns[i].value; + + return nullptr; +} + +ElfSection *ElfDynamic_Section::getSectionForType(unsigned int tag) +{ + ElfValue *value = getValueForType(tag); + return value ? value->getSection() : nullptr; +} + +bool ElfDynamic_Section::setValueForType(unsigned int tag, ElfValue *val) +{ + unsigned int i; + unsigned int shnum = shdr.sh_size / shdr.sh_entsize; + for (i = 0; (i < shnum) && (dyns[i].tag != DT_NULL); i++) + if (dyns[i].tag == tag) { + delete dyns[i].value; + dyns[i].value = val; + return true; + } + // If we get here, this means we didn't match for the given tag + // Most of the time, there are a few DT_NULL entries, that we can + // use to add our value, but if we are on the last entry, we can't. + if (i >= shnum - 1) + return false; + + dyns[i].tag = tag; + dyns[i].value = val; + return true; +} + +ElfDynamic_Section::ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) +: ElfSection(s, file, parent) +{ + int pos = file->tellg(); + dyns.resize(s.sh_size / s.sh_entsize); + file->seekg(shdr.sh_offset); + // Here we assume tags refer to only one section (e.g. DT_RELSZ accounts + // for .rel.dyn size) + for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) { + Elf_Dyn dyn(*file, parent->getClass(), parent->getData()); + dyns[i].tag = dyn.d_tag; + switch (dyn.d_tag) { + case DT_NULL: + case DT_SYMBOLIC: + case DT_TEXTREL: + case DT_BIND_NOW: + dyns[i].value = new ElfValue(); + break; + case DT_NEEDED: + case DT_SONAME: + case DT_RPATH: + case DT_PLTREL: + case DT_RUNPATH: + case DT_FLAGS: + case DT_RELACOUNT: + case DT_RELCOUNT: + case DT_VERDEFNUM: + case DT_VERNEEDNUM: + dyns[i].value = new ElfPlainValue(dyn.d_un.d_val); + break; + case DT_PLTGOT: + case DT_HASH: + case DT_STRTAB: + case DT_SYMTAB: + case DT_RELA: + case DT_INIT: + case DT_FINI: + case DT_REL: + case DT_JMPREL: + case DT_INIT_ARRAY: + case DT_FINI_ARRAY: + case DT_GNU_HASH: + case DT_VERSYM: + case DT_VERNEED: + case DT_VERDEF: + dyns[i].value = new ElfLocation(dyn.d_un.d_ptr, parent); + break; + default: + dyns[i].value = nullptr; + } + } + // Another loop to get the section sizes + for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) + switch (dyns[i].tag) { + case DT_PLTRELSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_JMPREL)); + break; + case DT_RELASZ: + dyns[i].value = new ElfSize(getSectionForType(DT_RELA)); + break; + case DT_STRSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_STRTAB)); + break; + case DT_RELSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_REL)); + break; + case DT_INIT_ARRAYSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_INIT_ARRAY)); + break; + case DT_FINI_ARRAYSZ: + dyns[i].value = new ElfSize(getSectionForType(DT_FINI_ARRAY)); + break; + case DT_RELAENT: + dyns[i].value = new ElfEntSize(getSectionForType(DT_RELA)); + break; + case DT_SYMENT: + dyns[i].value = new ElfEntSize(getSectionForType(DT_SYMTAB)); + break; + case DT_RELENT: + dyns[i].value = new ElfEntSize(getSectionForType(DT_REL)); + break; + } + + file->seekg(pos); +} + +ElfDynamic_Section::~ElfDynamic_Section() +{ + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) + delete dyns[i].value; +} + +void ElfDynamic_Section::serialize(std::ofstream &file, char ei_class, char ei_data) +{ + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + Elf_Dyn dyn; + dyn.d_tag = dyns[i].tag; + dyn.d_un.d_val = (dyns[i].value != nullptr) ? dyns[i].value->getValue() : 0; + dyn.serialize(file, ei_class, ei_data); + } +} + +ElfSymtab_Section::ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) +: ElfSection(s, file, parent) +{ + int pos = file->tellg(); + syms.resize(s.sh_size / s.sh_entsize); + ElfStrtab_Section *strtab = (ElfStrtab_Section *)getLink(); + file->seekg(shdr.sh_offset); + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + Elf_Sym sym(*file, parent->getClass(), parent->getData()); + syms[i].name = strtab->getStr(sym.st_name); + syms[i].info = sym.st_info; + syms[i].other = sym.st_other; + ElfSection *section = (sym.st_shndx == SHN_ABS) ? nullptr : parent->getSection(sym.st_shndx); + new (&syms[i].value) ElfLocation(section, sym.st_value, ElfLocation::ABSOLUTE); + syms[i].size = sym.st_size; + syms[i].defined = (sym.st_shndx != SHN_UNDEF); + } + file->seekg(pos); +} + +void +ElfSymtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data) +{ + ElfStrtab_Section *strtab = (ElfStrtab_Section *)getLink(); + for (unsigned int i = 0; i < shdr.sh_size / shdr.sh_entsize; i++) { + Elf_Sym sym; + sym.st_name = strtab->getStrIndex(syms[i].name); + sym.st_info = syms[i].info; + sym.st_other = syms[i].other; + sym.st_value = syms[i].value.getValue(); + ElfSection *section = syms[i].value.getSection(); + if (syms[i].defined) + sym.st_shndx = section ? section->getIndex() : SHN_ABS; + else + sym.st_shndx = SHN_UNDEF; + sym.st_size = syms[i].size; + sym.serialize(file, ei_class, ei_data); + } +} + +Elf_SymValue * +ElfSymtab_Section::lookup(const char *name, unsigned int type_filter) +{ + for (std::vector<Elf_SymValue>::iterator sym = syms.begin(); + sym != syms.end(); sym++) { + if ((type_filter & (1 << ELF32_ST_TYPE(sym->info))) && + (strcmp(sym->name, name) == 0)) { + return &*sym; + } + } + return nullptr; +} + +const char * +ElfStrtab_Section::getStr(unsigned int index) +{ + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) { + if (index < t->used) + return t->buf + index; + index -= t->used; + } + assert(1 == 0); + return nullptr; +} + +const char * +ElfStrtab_Section::getStr(const char *string) +{ + if (string == nullptr) + return nullptr; + + // If the given string is within the section, return it + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) + if ((string >= t->buf) && (string < t->buf + t->used)) + return string; + + // TODO: should scan in the section to find an existing string + + // If not, we need to allocate the string in the section + size_t len = strlen(string) + 1; + + if (table.back().size - table.back().used < len) + table.resize(table.size() + 1); + + char *alloc_str = table.back().buf + table.back().used; + memcpy(alloc_str, string, len); + table.back().used += len; + + shdr.sh_size += len; + markDirty(); + + return alloc_str; +} + +unsigned int +ElfStrtab_Section::getStrIndex(const char *string) +{ + if (string == nullptr) + return 0; + + unsigned int index = 0; + string = getStr(string); + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) { + if ((string >= t->buf) && (string < t->buf + t->used)) + return index + (string - t->buf); + index += t->used; + } + + assert(1 == 0); + return 0; +} + +void +ElfStrtab_Section::serialize(std::ofstream &file, char ei_class, char ei_data) +{ + file.seekp(getOffset()); + for (std::vector<table_storage>::iterator t = table.begin(); + t != table.end(); t++) + file.write(t->buf, t->used); +} diff --git a/build/unix/elfhack/elfhack.cpp b/build/unix/elfhack/elfhack.cpp new file mode 100644 index 000000000..8c1184237 --- /dev/null +++ b/build/unix/elfhack/elfhack.cpp @@ -0,0 +1,823 @@ +/* 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/. */ + +#undef NDEBUG +#include <assert.h> +#include <cstring> +#include <cstdlib> +#include <cstdio> +#include "elfxx.h" + +#define ver "0" +#define elfhack_data ".elfhack.data.v" ver +#define elfhack_text ".elfhack.text.v" ver + +#ifndef R_ARM_V4BX +#define R_ARM_V4BX 0x28 +#endif +#ifndef R_ARM_CALL +#define R_ARM_CALL 0x1c +#endif +#ifndef R_ARM_JUMP24 +#define R_ARM_JUMP24 0x1d +#endif +#ifndef R_ARM_THM_JUMP24 +#define R_ARM_THM_JUMP24 0x1e +#endif + +char *rundir = nullptr; + +template <typename T> +struct wrapped { + T value; +}; + +class Elf_Addr_Traits { +public: + typedef wrapped<Elf32_Addr> Type32; + typedef wrapped<Elf64_Addr> Type64; + + template <class endian, typename R, typename T> + static inline void swap(T &t, R &r) { + r.value = endian::swap(t.value); + } +}; + +typedef serializable<Elf_Addr_Traits> Elf_Addr; + +class Elf_RelHack_Traits { +public: + typedef Elf32_Rel Type32; + typedef Elf32_Rel Type64; + + template <class endian, typename R, typename T> + static inline void swap(T &t, R &r) { + r.r_offset = endian::swap(t.r_offset); + r.r_info = endian::swap(t.r_info); + } +}; + +typedef serializable<Elf_RelHack_Traits> Elf_RelHack; + +class ElfRelHack_Section: public ElfSection { +public: + ElfRelHack_Section(Elf_Shdr &s) + : ElfSection(s, nullptr, nullptr) + { + name = elfhack_data; + }; + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + for (std::vector<Elf_RelHack>::iterator i = rels.begin(); + i != rels.end(); ++i) + (*i).serialize(file, ei_class, ei_data); + } + + bool isRelocatable() { + return true; + } + + void push_back(Elf_RelHack &r) { + rels.push_back(r); + shdr.sh_size = rels.size() * shdr.sh_entsize; + } +private: + std::vector<Elf_RelHack> rels; +}; + +class ElfRelHackCode_Section: public ElfSection { +public: + ElfRelHackCode_Section(Elf_Shdr &s, Elf &e, unsigned int init) + : ElfSection(s, nullptr, nullptr), parent(e), init(init) { + std::string file(rundir); + file += "/inject/"; + switch (parent.getMachine()) { + case EM_386: + file += "x86"; + break; + case EM_X86_64: + file += "x86_64"; + break; + case EM_ARM: + file += "arm"; + break; + default: + throw std::runtime_error("unsupported architecture"); + } + file += ".o"; + std::ifstream inject(file.c_str(), std::ios::in|std::ios::binary); + elf = new Elf(inject); + if (elf->getType() != ET_REL) + throw std::runtime_error("object for injected code is not ET_REL"); + if (elf->getMachine() != parent.getMachine()) + throw std::runtime_error("architecture of object for injected code doesn't match"); + + ElfSymtab_Section *symtab = nullptr; + + // Find the symbol table. + for (ElfSection *section = elf->getSection(1); section != nullptr; + section = section->getNext()) { + if (section->getType() == SHT_SYMTAB) + symtab = (ElfSymtab_Section *) section; + } + if (symtab == nullptr) + throw std::runtime_error("Couldn't find a symbol table for the injected code"); + + // Find the init symbol + entry_point = -1; + Elf_SymValue *sym = symtab->lookup(init ? "init" : "init_noinit"); + if (!sym) + throw std::runtime_error("Couldn't find an 'init' symbol in the injected code"); + + entry_point = sym->value.getValue(); + + // Get all relevant sections from the injected code object. + add_code_section(sym->value.getSection()); + + // Adjust code sections offsets according to their size + std::vector<ElfSection *>::iterator c = code.begin(); + (*c)->getShdr().sh_addr = 0; + for(ElfSection *last = *(c++); c != code.end(); c++) { + unsigned int addr = last->getShdr().sh_addr + last->getSize(); + if (addr & ((*c)->getAddrAlign() - 1)) + addr = (addr | ((*c)->getAddrAlign() - 1)) + 1; + (*c)->getShdr().sh_addr = addr; + // We need to align this section depending on the greater + // alignment required by code sections. + if (shdr.sh_addralign < (*c)->getAddrAlign()) + shdr.sh_addralign = (*c)->getAddrAlign(); + } + shdr.sh_size = code.back()->getAddr() + code.back()->getSize(); + data = new char[shdr.sh_size]; + char *buf = data; + for (c = code.begin(); c != code.end(); c++) { + memcpy(buf, (*c)->getData(), (*c)->getSize()); + buf += (*c)->getSize(); + } + name = elfhack_text; + } + + ~ElfRelHackCode_Section() { + delete elf; + } + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + // Readjust code offsets + for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) + (*c)->getShdr().sh_addr += getAddr(); + + // Apply relocations + for (std::vector<ElfSection *>::iterator c = code.begin(); c != code.end(); c++) { + for (ElfSection *rel = elf->getSection(1); rel != nullptr; rel = rel->getNext()) + if (((rel->getType() == SHT_REL) || + (rel->getType() == SHT_RELA)) && + (rel->getInfo().section == *c)) { + if (rel->getType() == SHT_REL) + apply_relocations((ElfRel_Section<Elf_Rel> *)rel, *c); + else + apply_relocations((ElfRel_Section<Elf_Rela> *)rel, *c); + } + } + + ElfSection::serialize(file, ei_class, ei_data); + } + + bool isRelocatable() { + return true; + } + + unsigned int getEntryPoint() { + return entry_point; + } +private: + void add_code_section(ElfSection *section) + { + if (section) { + /* Don't add section if it's already been added in the past */ + for (auto s = code.begin(); s != code.end(); ++s) { + if (section == *s) + return; + } + code.push_back(section); + find_code(section); + } + } + + /* Look at the relocations associated to the given section to find other + * sections that it requires */ + void find_code(ElfSection *section) + { + for (ElfSection *s = elf->getSection(1); s != nullptr; + s = s->getNext()) { + if (((s->getType() == SHT_REL) || + (s->getType() == SHT_RELA)) && + (s->getInfo().section == section)) { + if (s->getType() == SHT_REL) + scan_relocs_for_code((ElfRel_Section<Elf_Rel> *)s); + else + scan_relocs_for_code((ElfRel_Section<Elf_Rela> *)s); + } + } + } + + template <typename Rel_Type> + void scan_relocs_for_code(ElfRel_Section<Rel_Type> *rel) + { + ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink(); + for (auto r = rel->rels.begin(); r != rel->rels.end(); r++) { + ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection(); + add_code_section(section); + } + } + + class pc32_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + return addr + addend - offset - base_addr; + } + }; + + class arm_plt32_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + // We don't care about sign_extend because the only case where this is + // going to be used only jumps forward. + Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr) >> 2; + tmp = (addend + tmp) & 0x00ffffff; + return (addend & 0xff000000) | tmp; + } + }; + + class arm_thm_jump24_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + /* Follows description of b.w and bl instructions as per + ARM Architecture Reference Manual ARM® v7-A and ARM® v7-R edition, A8.6.16 + We limit ourselves to Encoding T4 of b.w and Encoding T1 of bl. + We don't care about sign_extend because the only case where this is + going to be used only jumps forward. */ + Elf32_Addr tmp = (Elf32_Addr) (addr - offset - base_addr); + unsigned int word0 = addend & 0xffff, + word1 = addend >> 16; + + /* Encoding T4 of B.W is 10x1 ; Encoding T1 of BL is 11x1. */ + unsigned int type = (word1 & 0xd000) >> 12; + if (((word0 & 0xf800) != 0xf000) || ((type & 0x9) != 0x9)) + throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for B.W <label> and BL <label>"); + + /* When the target address points to ARM code, switch a BL to a + * BLX. This however can't be done with a B.W without adding a + * trampoline, which is not supported as of now. */ + if ((addr & 0x1) == 0) { + if (type == 0x9) + throw std::runtime_error("R_ARM_THM_JUMP24/R_ARM_THM_CALL relocation only supported for BL <label> when label points to ARM code"); + /* The address of the target is always relative to a 4-bytes + * aligned address, so if the address of the BL instruction is + * not 4-bytes aligned, adjust for it. */ + if ((base_addr + offset) & 0x2) + tmp += 2; + /* Encoding T2 of BLX is 11x0. */ + type = 0xc; + } + + unsigned int s = (word0 & (1 << 10)) >> 10; + unsigned int j1 = (word1 & (1 << 13)) >> 13; + unsigned int j2 = (word1 & (1 << 11)) >> 11; + unsigned int i1 = j1 ^ s ? 0 : 1; + unsigned int i2 = j2 ^ s ? 0 : 1; + + tmp += ((s << 24) | (i1 << 23) | (i2 << 22) | ((word0 & 0x3ff) << 12) | ((word1 & 0x7ff) << 1)); + + s = (tmp & (1 << 24)) >> 24; + j1 = ((tmp & (1 << 23)) >> 23) ^ !s; + j2 = ((tmp & (1 << 22)) >> 22) ^ !s; + + return 0xf000 | (s << 10) | ((tmp & (0x3ff << 12)) >> 12) | + (type << 28) | (j1 << 29) | (j2 << 27) | ((tmp & 0xffe) << 15); + } + }; + + class gotoff_relocation { + public: + Elf32_Addr operator()(unsigned int base_addr, Elf32_Off offset, + Elf32_Word addend, unsigned int addr) + { + return addr + addend; + } + }; + + template <class relocation_type> + void apply_relocation(ElfSection *the_code, char *base, Elf_Rel *r, unsigned int addr) + { + relocation_type relocation; + Elf32_Addr value; + memcpy(&value, base + r->r_offset, 4); + value = relocation(the_code->getAddr(), r->r_offset, value, addr); + memcpy(base + r->r_offset, &value, 4); + } + + template <class relocation_type> + void apply_relocation(ElfSection *the_code, char *base, Elf_Rela *r, unsigned int addr) + { + relocation_type relocation; + Elf32_Addr value = relocation(the_code->getAddr(), r->r_offset, r->r_addend, addr); + memcpy(base + r->r_offset, &value, 4); + } + + template <typename Rel_Type> + void apply_relocations(ElfRel_Section<Rel_Type> *rel, ElfSection *the_code) + { + assert(rel->getType() == Rel_Type::sh_type); + char *buf = data + (the_code->getAddr() - code.front()->getAddr()); + // TODO: various checks on the sections + ElfSymtab_Section *symtab = (ElfSymtab_Section *)rel->getLink(); + for (typename std::vector<Rel_Type>::iterator r = rel->rels.begin(); r != rel->rels.end(); r++) { + // TODO: various checks on the symbol + const char *name = symtab->syms[ELF32_R_SYM(r->r_info)].name; + unsigned int addr; + if (symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection() == nullptr) { + if (strcmp(name, "relhack") == 0) { + addr = getNext()->getAddr(); + } else if (strcmp(name, "elf_header") == 0) { + // TODO: change this ungly hack to something better + ElfSection *ehdr = parent.getSection(1)->getPrevious()->getPrevious(); + addr = ehdr->getAddr(); + } else if (strcmp(name, "original_init") == 0) { + addr = init; + } else if (strcmp(name, "_GLOBAL_OFFSET_TABLE_") == 0) { + // We actually don't need a GOT, but need it as a reference for + // GOTOFF relocations. We'll just use the start of the ELF file + addr = 0; + } else if (strcmp(name, "") == 0) { + // This is for R_ARM_V4BX, until we find something better + addr = -1; + } else { + throw std::runtime_error("Unsupported symbol in relocation"); + } + } else { + ElfSection *section = symtab->syms[ELF32_R_SYM(r->r_info)].value.getSection(); + assert((section->getType() == SHT_PROGBITS) && (section->getFlags() & SHF_EXECINSTR)); + addr = symtab->syms[ELF32_R_SYM(r->r_info)].value.getValue(); + } + // Do the relocation +#define REL(machine, type) (EM_ ## machine | (R_ ## machine ## _ ## type << 8)) + switch (elf->getMachine() | (ELF32_R_TYPE(r->r_info) << 8)) { + case REL(X86_64, PC32): + case REL(386, PC32): + case REL(386, GOTPC): + case REL(ARM, GOTPC): + case REL(ARM, REL32): + apply_relocation<pc32_relocation>(the_code, buf, &*r, addr); + break; + case REL(ARM, CALL): + case REL(ARM, JUMP24): + case REL(ARM, PLT32): + apply_relocation<arm_plt32_relocation>(the_code, buf, &*r, addr); + break; + case REL(ARM, THM_PC22 /* THM_CALL */): + case REL(ARM, THM_JUMP24): + apply_relocation<arm_thm_jump24_relocation>(the_code, buf, &*r, addr); + break; + case REL(386, GOTOFF): + case REL(ARM, GOTOFF): + apply_relocation<gotoff_relocation>(the_code, buf, &*r, addr); + break; + case REL(ARM, V4BX): + // Ignore R_ARM_V4BX relocations + break; + default: + throw std::runtime_error("Unsupported relocation type"); + } + } + } + + Elf *elf, &parent; + std::vector<ElfSection *> code; + unsigned int init; + int entry_point; +}; + +unsigned int get_addend(Elf_Rel *rel, Elf *elf) { + ElfLocation loc(rel->r_offset, elf); + Elf_Addr addr(loc.getBuffer(), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData()); + return addr.value; +} + +unsigned int get_addend(Elf_Rela *rel, Elf *elf) { + return rel->r_addend; +} + +void set_relative_reloc(Elf_Rel *rel, Elf *elf, unsigned int value) { + ElfLocation loc(rel->r_offset, elf); + Elf_Addr addr; + addr.value = value; + addr.serialize(const_cast<char *>(loc.getBuffer()), Elf_Addr::size(elf->getClass()), elf->getClass(), elf->getData()); +} + +void set_relative_reloc(Elf_Rela *rel, Elf *elf, unsigned int value) { + // ld puts the value of relocated relocations both in the addend and + // at r_offset. For consistency, keep it that way. + set_relative_reloc((Elf_Rel *)rel, elf, value); + rel->r_addend = value; +} + +void maybe_split_segment(Elf *elf, ElfSegment *segment, bool fill) +{ + std::list<ElfSection *>::iterator it = segment->begin(); + for (ElfSection *last = *(it++); it != segment->end(); last = *(it++)) { + // When two consecutive non-SHT_NOBITS sections are apart by more + // than the alignment of the section, the second can be moved closer + // to the first, but this requires the segment to be split. + if (((*it)->getType() != SHT_NOBITS) && (last->getType() != SHT_NOBITS) && + ((*it)->getOffset() - last->getOffset() - last->getSize() > segment->getAlign())) { + // Probably very wrong. + Elf_Phdr phdr; + phdr.p_type = PT_LOAD; + phdr.p_vaddr = 0; + phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff(); + phdr.p_flags = segment->getFlags(); + phdr.p_align = segment->getAlign(); + phdr.p_filesz = (unsigned int)-1; + phdr.p_memsz = (unsigned int)-1; + ElfSegment *newSegment = new ElfSegment(&phdr); + elf->insertSegmentAfter(segment, newSegment); + ElfSection *section = *it; + for (; it != segment->end(); ++it) { + newSegment->addSection(*it); + } + for (it = newSegment->begin(); it != newSegment->end(); it++) { + segment->removeSection(*it); + } + // Fill the virtual address space gap left between the two PT_LOADs + // with a new PT_LOAD with no permissions. This avoids the linker + // (especially bionic's) filling the gap with anonymous memory, + // which breakpad doesn't like. + // /!\ running strip on a elfhacked binary will break this filler + // PT_LOAD. + if (!fill) + break; + // Insert dummy segment to normalize the entire Elf with the header + // sizes adjusted, before inserting a filler segment. + { + memset(&phdr, 0, sizeof(phdr)); + ElfSegment dummySegment(&phdr); + elf->insertSegmentAfter(segment, &dummySegment); + elf->normalize(); + elf->removeSegment(&dummySegment); + } + ElfSection *previous = section->getPrevious(); + phdr.p_type = PT_LOAD; + phdr.p_vaddr = (previous->getAddr() + previous->getSize() + segment->getAlign() - 1) & ~(segment->getAlign() - 1); + phdr.p_paddr = phdr.p_vaddr + segment->getVPDiff(); + phdr.p_flags = 0; + phdr.p_align = 0; + phdr.p_filesz = (section->getAddr() & ~(newSegment->getAlign() - 1)) - phdr.p_vaddr; + phdr.p_memsz = phdr.p_filesz; + if (phdr.p_filesz) { + newSegment = new ElfSegment(&phdr); + assert(newSegment->isElfHackFillerSegment()); + elf->insertSegmentAfter(segment, newSegment); + } else { + elf->normalize(); + } + break; + } + } +} + +template <typename Rel_Type> +int do_relocation_section(Elf *elf, unsigned int rel_type, unsigned int rel_type2, bool force, bool fill) +{ + ElfDynamic_Section *dyn = elf->getDynSection(); + if (dyn == nullptr) { + fprintf(stderr, "Couldn't find SHT_DYNAMIC section\n"); + return -1; + } + + ElfSegment *relro = elf->getSegmentByType(PT_GNU_RELRO); + + ElfRel_Section<Rel_Type> *section = (ElfRel_Section<Rel_Type> *)dyn->getSectionForType(Rel_Type::d_tag); + assert(section->getType() == Rel_Type::sh_type); + + Elf32_Shdr relhack32_section = + { 0, SHT_PROGBITS, SHF_ALLOC, 0, (Elf32_Off)-1, 0, SHN_UNDEF, 0, + Elf_RelHack::size(elf->getClass()), Elf_RelHack::size(elf->getClass()) }; // TODO: sh_addralign should be an alignment, not size + Elf32_Shdr relhackcode32_section = + { 0, SHT_PROGBITS, SHF_ALLOC | SHF_EXECINSTR, 0, (Elf32_Off)-1, 0, + SHN_UNDEF, 0, 1, 0 }; + + unsigned int entry_sz = Elf_Addr::size(elf->getClass()); + + // The injected code needs to be executed before any init code in the + // binary. There are three possible cases: + // - The binary has no init code at all. In this case, we will add a + // DT_INIT entry pointing to the injected code. + // - The binary has a DT_INIT entry. In this case, we will interpose: + // we change DT_INIT to point to the injected code, and have the + // injected code call the original DT_INIT entry point. + // - The binary has no DT_INIT entry, but has a DT_INIT_ARRAY. In this + // case, we interpose as well, by replacing the first entry in the + // array to point to the injected code, and have the injected code + // call the original first entry. + // The binary may have .ctors instead of DT_INIT_ARRAY, for its init + // functions, but this falls into the second case above, since .ctors + // are actually run by DT_INIT code. + ElfValue *value = dyn->getValueForType(DT_INIT); + unsigned int original_init = value ? value->getValue() : 0; + ElfSection *init_array = nullptr; + if (!value || !value->getValue()) { + value = dyn->getValueForType(DT_INIT_ARRAYSZ); + if (value && value->getValue() >= entry_sz) + init_array = dyn->getSectionForType(DT_INIT_ARRAY); + } + + Elf_Shdr relhack_section(relhack32_section); + Elf_Shdr relhackcode_section(relhackcode32_section); + ElfRelHack_Section *relhack = new ElfRelHack_Section(relhack_section); + + ElfSymtab_Section *symtab = (ElfSymtab_Section *) section->getLink(); + Elf_SymValue *sym = symtab->lookup("__cxa_pure_virtual"); + + std::vector<Rel_Type> new_rels; + Elf_RelHack relhack_entry; + relhack_entry.r_offset = relhack_entry.r_info = 0; + size_t init_array_reloc = 0; + for (typename std::vector<Rel_Type>::iterator i = section->rels.begin(); + i != section->rels.end(); i++) { + // We don't need to keep R_*_NONE relocations + if (!ELF32_R_TYPE(i->r_info)) + continue; + ElfLocation loc(i->r_offset, elf); + // __cxa_pure_virtual is a function used in vtables to point at pure + // virtual methods. The __cxa_pure_virtual function usually abort()s. + // These functions are however normally never called. In the case + // where they would, jumping to the null address instead of calling + // __cxa_pure_virtual is going to work just as well. So we can remove + // relocations for the __cxa_pure_virtual symbol and null out the + // content at the offset pointed by the relocation. + if (sym) { + if (sym->defined) { + // If we are statically linked to libstdc++, the + // __cxa_pure_virtual symbol is defined in our lib, and we + // have relative relocations (rel_type) for it. + if (ELF32_R_TYPE(i->r_info) == rel_type) { + Elf_Addr addr(loc.getBuffer(), entry_sz, elf->getClass(), elf->getData()); + if (addr.value == sym->value.getValue()) { + memset((char *)loc.getBuffer(), 0, entry_sz); + continue; + } + } + } else { + // If we are dynamically linked to libstdc++, the + // __cxa_pure_virtual symbol is undefined in our lib, and we + // have absolute relocations (rel_type2) for it. + if ((ELF32_R_TYPE(i->r_info) == rel_type2) && + (sym == &symtab->syms[ELF32_R_SYM(i->r_info)])) { + memset((char *)loc.getBuffer(), 0, entry_sz); + continue; + } + } + } + // Keep track of the relocation associated with the first init_array entry. + if (init_array && i->r_offset == init_array->getAddr()) { + if (init_array_reloc) { + fprintf(stderr, "Found multiple relocations for the first init_array entry. Skipping\n"); + return -1; + } + new_rels.push_back(*i); + init_array_reloc = new_rels.size(); + } else if (!(loc.getSection()->getFlags() & SHF_WRITE) || (ELF32_R_TYPE(i->r_info) != rel_type) || + (relro && (i->r_offset >= relro->getAddr()) && + (i->r_offset < relro->getAddr() + relro->getMemSize()))) { + // Don't pack relocations happening in non writable sections. + // Our injected code is likely not to be allowed to write there. + new_rels.push_back(*i); + } else { + // TODO: check that i->r_addend == *i->r_offset + if (i->r_offset == relhack_entry.r_offset + relhack_entry.r_info * entry_sz) { + relhack_entry.r_info++; + } else { + if (relhack_entry.r_offset) + relhack->push_back(relhack_entry); + relhack_entry.r_offset = i->r_offset; + relhack_entry.r_info = 1; + } + } + } + if (relhack_entry.r_offset) + relhack->push_back(relhack_entry); + // Last entry must be nullptr + relhack_entry.r_offset = relhack_entry.r_info = 0; + relhack->push_back(relhack_entry); + + unsigned int old_end = section->getOffset() + section->getSize(); + + if (init_array) { + if (! init_array_reloc) { + fprintf(stderr, "Didn't find relocation for DT_INIT_ARRAY's first entry. Skipping\n"); + return -1; + } + Rel_Type *rel = &new_rels[init_array_reloc - 1]; + unsigned int addend = get_addend(rel, elf); + // Use relocated value of DT_INIT_ARRAY's first entry for the + // function to be called by the injected code. + if (ELF32_R_TYPE(rel->r_info) == rel_type) { + original_init = addend; + } else if (ELF32_R_TYPE(rel->r_info) == rel_type2) { + ElfSymtab_Section *symtab = (ElfSymtab_Section *)section->getLink(); + original_init = symtab->syms[ELF32_R_SYM(rel->r_info)].value.getValue() + addend; + } else { + fprintf(stderr, "Unsupported relocation type for DT_INIT_ARRAY's first entry. Skipping\n"); + return -1; + } + } + + section->rels.assign(new_rels.begin(), new_rels.end()); + section->shrink(new_rels.size() * section->getEntSize()); + + ElfRelHackCode_Section *relhackcode = new ElfRelHackCode_Section(relhackcode_section, *elf, original_init); + relhackcode->insertBefore(section); + relhack->insertAfter(relhackcode); + if (section->getOffset() + section->getSize() >= old_end) { + fprintf(stderr, "No gain. Skipping\n"); + return -1; + } + + // Adjust PT_LOAD segments + for (ElfSegment *segment = elf->getSegmentByType(PT_LOAD); segment; + segment = elf->getSegmentByType(PT_LOAD, segment)) { + maybe_split_segment(elf, segment, fill); + } + + // Ensure Elf sections will be at their final location. + elf->normalize(); + ElfLocation *init = new ElfLocation(relhackcode, relhackcode->getEntryPoint()); + if (init_array) { + // Adjust the first DT_INIT_ARRAY entry to point at the injected code + // by transforming its relocation into a relative one pointing to the + // address of the injected code. + Rel_Type *rel = §ion->rels[init_array_reloc - 1]; + rel->r_info = ELF32_R_INFO(0, rel_type); // Set as a relative relocation + set_relative_reloc(§ion->rels[init_array_reloc - 1], elf, init->getValue()); + } else if (!dyn->setValueForType(DT_INIT, init)) { + fprintf(stderr, "Can't grow .dynamic section to set DT_INIT. Skipping\n"); + return -1; + } + // TODO: adjust the value according to the remaining number of relative relocations + if (dyn->getValueForType(Rel_Type::d_tag_count)) + dyn->setValueForType(Rel_Type::d_tag_count, new ElfPlainValue(0)); + + return 0; +} + +static inline int backup_file(const char *name) +{ + std::string fname(name); + fname += ".bak"; + return rename(name, fname.c_str()); +} + +void do_file(const char *name, bool backup = false, bool force = false, bool fill = false) +{ + std::ifstream file(name, std::ios::in|std::ios::binary); + Elf elf(file); + unsigned int size = elf.getSize(); + fprintf(stderr, "%s: ", name); + if (elf.getType() != ET_DYN) { + fprintf(stderr, "Not a shared object. Skipping\n"); + return; + } + + for (ElfSection *section = elf.getSection(1); section != nullptr; + section = section->getNext()) { + if (section->getName() && + (strncmp(section->getName(), ".elfhack.", 9) == 0)) { + fprintf(stderr, "Already elfhacked. Skipping\n"); + return; + } + } + + int exit = -1; + switch (elf.getMachine()) { + case EM_386: + exit = do_relocation_section<Elf_Rel>(&elf, R_386_RELATIVE, R_386_32, force, fill); + break; + case EM_X86_64: + exit = do_relocation_section<Elf_Rela>(&elf, R_X86_64_RELATIVE, R_X86_64_64, force, fill); + break; + case EM_ARM: + exit = do_relocation_section<Elf_Rel>(&elf, R_ARM_RELATIVE, R_ARM_ABS32, force, fill); + break; + } + if (exit == 0) { + if (!force && (elf.getSize() >= size)) { + fprintf(stderr, "No gain. Skipping\n"); + } else if (backup && backup_file(name) != 0) { + fprintf(stderr, "Couln't create backup file\n"); + } else { + std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc); + elf.write(ofile); + fprintf(stderr, "Reduced by %d bytes\n", size - elf.getSize()); + } + } +} + +void undo_file(const char *name, bool backup = false) +{ + std::ifstream file(name, std::ios::in|std::ios::binary); + Elf elf(file); + unsigned int size = elf.getSize(); + fprintf(stderr, "%s: ", name); + if (elf.getType() != ET_DYN) { + fprintf(stderr, "Not a shared object. Skipping\n"); + return; + } + + ElfSection *data = nullptr, *text = nullptr; + for (ElfSection *section = elf.getSection(1); section != nullptr; + section = section->getNext()) { + if (section->getName() && + (strcmp(section->getName(), elfhack_data) == 0)) + data = section; + if (section->getName() && + (strcmp(section->getName(), elfhack_text) == 0)) + text = section; + } + + if (!data || !text) { + fprintf(stderr, "Not elfhacked. Skipping\n"); + return; + } + if (data != text->getNext()) { + fprintf(stderr, elfhack_data " section not following " elfhack_text ". Skipping\n"); + return; + } + + ElfSegment *first = elf.getSegmentByType(PT_LOAD); + ElfSegment *second = elf.getSegmentByType(PT_LOAD, first); + ElfSegment *filler = nullptr; + // If the second PT_LOAD is a filler from elfhack --fill, check the third. + if (second->isElfHackFillerSegment()) { + filler = second; + second = elf.getSegmentByType(PT_LOAD, filler); + } + if (second->getFlags() != first->getFlags()) { + fprintf(stderr, "Couldn't identify elfhacked PT_LOAD segments. Skipping\n"); + return; + } + // Move sections from the second PT_LOAD to the first, and remove the + // second PT_LOAD segment. + for (std::list<ElfSection *>::iterator section = second->begin(); + section != second->end(); ++section) + first->addSection(*section); + + elf.removeSegment(second); + if (filler) + elf.removeSegment(filler); + + if (backup && backup_file(name) != 0) { + fprintf(stderr, "Couln't create backup file\n"); + } else { + std::ofstream ofile(name, std::ios::out|std::ios::binary|std::ios::trunc); + elf.write(ofile); + fprintf(stderr, "Grown by %d bytes\n", elf.getSize() - size); + } +} + +int main(int argc, char *argv[]) +{ + int arg; + bool backup = false; + bool force = false; + bool revert = false; + bool fill = false; + char *lastSlash = rindex(argv[0], '/'); + if (lastSlash != nullptr) + rundir = strndup(argv[0], lastSlash - argv[0]); + for (arg = 1; arg < argc; arg++) { + if (strcmp(argv[arg], "-f") == 0) + force = true; + else if (strcmp(argv[arg], "-b") == 0) + backup = true; + else if (strcmp(argv[arg], "-r") == 0) + revert = true; + else if (strcmp(argv[arg], "--fill") == 0) + fill = true; + else if (revert) { + undo_file(argv[arg], backup); + } else + do_file(argv[arg], backup, force, fill); + } + + free(rundir); + return 0; +} diff --git a/build/unix/elfhack/elfxx.h b/build/unix/elfhack/elfxx.h new file mode 100644 index 000000000..a05b02348 --- /dev/null +++ b/build/unix/elfhack/elfxx.h @@ -0,0 +1,701 @@ +/* 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 <stdexcept> +#include <list> +#include <vector> +#include <cstring> +#include <iostream> +#include <fstream> +#include <algorithm> +#include <elf.h> +#include <asm/byteorder.h> + +// Technically, __*_to_cpu and __cpu_to* function are equivalent, +// so swap can use either of both. +#define def_swap(endian, type, bits) \ +static inline type ## bits ## _t swap(type ## bits ## _t i) { \ + return __ ## endian ## bits ## _to_cpu(i); \ +} + +class little_endian { +public: +def_swap(le, uint, 16); +def_swap(le, uint, 32); +def_swap(le, uint, 64); +def_swap(le, int, 16); +def_swap(le, int, 32); +def_swap(le, int, 64); +}; + +class big_endian { +public: +def_swap(be, uint, 16); +def_swap(be, uint, 32); +def_swap(be, uint, 64); +def_swap(be, int, 16); +def_swap(be, int, 32); +def_swap(be, int, 64); +}; + +// forward declaration +class ElfSection; +class ElfSegment; +// TODO: Rename Elf_* types +class Elf_Ehdr; +class Elf_Phdr; +class Elf; +class ElfDynamic_Section; +class ElfStrtab_Section; + +class Elf_Ehdr_Traits { +public: + typedef Elf32_Ehdr Type32; + typedef Elf64_Ehdr Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Phdr_Traits { +public: + typedef Elf32_Phdr Type32; + typedef Elf64_Phdr Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Shdr_Traits { +public: + typedef Elf32_Shdr Type32; + typedef Elf64_Shdr Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Dyn_Traits { +public: + typedef Elf32_Dyn Type32; + typedef Elf64_Dyn Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Sym_Traits { +public: + typedef Elf32_Sym Type32; + typedef Elf64_Sym Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Rel_Traits { +public: + typedef Elf32_Rel Type32; + typedef Elf64_Rel Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class Elf_Rela_Traits { +public: + typedef Elf32_Rela Type32; + typedef Elf64_Rela Type64; + + template <class endian, typename R, typename T> + static void swap(T &t, R &r); +}; + +class ElfValue { +public: + virtual unsigned int getValue() { return 0; } + virtual ElfSection *getSection() { return nullptr; } +}; + +class ElfPlainValue: public ElfValue { + unsigned int value; +public: + ElfPlainValue(unsigned int val): value(val) {}; + unsigned int getValue() { return value; } +}; + +class ElfLocation: public ElfValue { + ElfSection *section; + unsigned int offset; +public: + enum position { ABSOLUTE, RELATIVE }; + ElfLocation(): section(nullptr), offset(0) {}; + ElfLocation(ElfSection *section, unsigned int off, enum position pos = RELATIVE); + ElfLocation(unsigned int location, Elf *elf); + unsigned int getValue(); + ElfSection *getSection() { return section; } + const char *getBuffer(); +}; + +class ElfSize: public ElfValue { + ElfSection *section; +public: + ElfSize(ElfSection *s): section(s) {}; + unsigned int getValue(); + ElfSection *getSection() { return section; } +}; + +class ElfEntSize: public ElfValue { + ElfSection *section; +public: + ElfEntSize(ElfSection *s): section(s) {}; + unsigned int getValue(); + ElfSection *getSection() { return section; } +}; + +template <typename T> +class serializable: public T::Type32 { +public: + serializable() {}; + serializable(const typename T::Type32 &p): T::Type32(p) {}; + +private: + template <typename R> + void init(const char *buf, size_t len, char ei_data) + { + R e; + assert(len >= sizeof(e)); + memcpy(&e, buf, sizeof(e)); + if (ei_data == ELFDATA2LSB) { + T::template swap<little_endian>(e, *this); + return; + } else if (ei_data == ELFDATA2MSB) { + T::template swap<big_endian>(e, *this); + return; + } + throw std::runtime_error("Unsupported ELF data encoding"); + } + + template <typename R> + void serialize(const char *buf, size_t len, char ei_data) + { + assert(len >= sizeof(R)); + if (ei_data == ELFDATA2LSB) { + T::template swap<little_endian>(*this, *(R *)buf); + return; + } else if (ei_data == ELFDATA2MSB) { + T::template swap<big_endian>(*this, *(R *)buf); + return; + } + throw std::runtime_error("Unsupported ELF data encoding"); + } + +public: + serializable(const char *buf, size_t len, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + init<typename T::Type32>(buf, len, ei_data); + return; + } else if (ei_class == ELFCLASS64) { + init<typename T::Type64>(buf, len, ei_data); + return; + } + throw std::runtime_error("Unsupported ELF class"); + } + + serializable(std::ifstream &file, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + typename T::Type32 e; + file.read((char *)&e, sizeof(e)); + init<typename T::Type32>((char *)&e, sizeof(e), ei_data); + return; + } else if (ei_class == ELFCLASS64) { + typename T::Type64 e; + file.read((char *)&e, sizeof(e)); + init<typename T::Type64>((char *)&e, sizeof(e), ei_data); + return; + } + throw std::runtime_error("Unsupported ELF class or data encoding"); + } + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + typename T::Type32 e; + serialize<typename T::Type32>((char *)&e, sizeof(e), ei_data); + file.write((char *)&e, sizeof(e)); + return; + } else if (ei_class == ELFCLASS64) { + typename T::Type64 e; + serialize<typename T::Type64>((char *)&e, sizeof(e), ei_data); + file.write((char *)&e, sizeof(e)); + return; + } + throw std::runtime_error("Unsupported ELF class or data encoding"); + } + + void serialize(char *buf, size_t len, char ei_class, char ei_data) + { + if (ei_class == ELFCLASS32) { + serialize<typename T::Type32>(buf, len, ei_data); + return; + } else if (ei_class == ELFCLASS64) { + serialize<typename T::Type64>(buf, len, ei_data); + return; + } + throw std::runtime_error("Unsupported ELF class"); + } + + static inline unsigned int size(char ei_class) + { + if (ei_class == ELFCLASS32) + return sizeof(typename T::Type32); + else if (ei_class == ELFCLASS64) + return sizeof(typename T::Type64); + return 0; + } +}; + +typedef serializable<Elf_Shdr_Traits> Elf_Shdr; + +class Elf { +public: + Elf(std::ifstream &file); + ~Elf(); + + /* index == -1 is treated as index == ehdr.e_shstrndx */ + ElfSection *getSection(int index); + + ElfSection *getSectionAt(unsigned int offset); + + ElfSegment *getSegmentByType(unsigned int type, ElfSegment *last = nullptr); + + ElfDynamic_Section *getDynSection(); + + void normalize(); + void write(std::ofstream &file); + + char getClass(); + char getData(); + char getType(); + char getMachine(); + unsigned int getSize(); + + void insertSegmentAfter(ElfSegment *previous, ElfSegment *segment) { + std::vector<ElfSegment *>::iterator prev = std::find(segments.begin(), segments.end(), previous); + segments.insert(prev + 1, segment); + } + + void removeSegment(ElfSegment *segment); + +private: + Elf_Ehdr *ehdr; + ElfLocation eh_entry; + ElfStrtab_Section *eh_shstrndx; + ElfSection **sections; + std::vector<ElfSegment *> segments; + ElfSection *shdr_section, *phdr_section; + /* Values used only during initialization */ + Elf_Shdr **tmp_shdr; + std::ifstream *tmp_file; +}; + +class ElfSection { +public: + typedef union { + ElfSection *section; + int index; + } SectionInfo; + + ElfSection(Elf_Shdr &s, std::ifstream *file, Elf *parent); + + virtual ~ElfSection() { + delete[] data; + } + + const char *getName() { return name; } + unsigned int getType() { return shdr.sh_type; } + unsigned int getFlags() { return shdr.sh_flags; } + unsigned int getAddr(); + unsigned int getSize() { return shdr.sh_size; } + unsigned int getAddrAlign() { return shdr.sh_addralign; } + unsigned int getEntSize() { return shdr.sh_entsize; } + const char *getData() { return data; } + ElfSection *getLink() { return link; } + SectionInfo getInfo() { return info; } + + void shrink(unsigned int newsize) { + if (newsize < shdr.sh_size) + shdr.sh_size = newsize; + } + + unsigned int getOffset(); + int getIndex(); + Elf_Shdr &getShdr(); + + ElfSection *getNext() { return next; } + ElfSection *getPrevious() { return previous; } + + virtual bool isRelocatable() { + return ((getType() == SHT_SYMTAB) || + (getType() == SHT_STRTAB) || + (getType() == SHT_RELA) || + (getType() == SHT_HASH) || + (getType() == SHT_NOTE) || + (getType() == SHT_REL) || + (getType() == SHT_DYNSYM) || + (getType() == SHT_GNU_HASH) || + (getType() == SHT_GNU_verdef) || + (getType() == SHT_GNU_verneed) || + (getType() == SHT_GNU_versym) || + getSegmentByType(PT_INTERP)) && + (getFlags() & SHF_ALLOC); + } + + void insertAfter(ElfSection *section, bool dirty = true) { + if (previous != nullptr) + previous->next = next; + if (next != nullptr) + next->previous = previous; + previous = section; + if (section != nullptr) { + next = section->next; + section->next = this; + } else + next = nullptr; + if (next != nullptr) + next->previous = this; + if (dirty) + markDirty(); + insertInSegments(section->segments); + } + + void insertBefore(ElfSection *section, bool dirty = true) { + if (previous != nullptr) + previous->next = next; + if (next != nullptr) + next->previous = previous; + next = section; + if (section != nullptr) { + previous = section->previous; + section->previous = this; + } else + previous = nullptr; + if (previous != nullptr) + previous->next = this; + if (dirty) + markDirty(); + insertInSegments(section->segments); + } + + void markDirty() { + if (link != nullptr) + shdr.sh_link = -1; + if (info.index) + shdr.sh_info = -1; + shdr.sh_offset = -1; + if (isRelocatable()) + shdr.sh_addr = -1; + if (next) + next->markDirty(); + } + + virtual void serialize(std::ofstream &file, char ei_class, char ei_data) + { + if (getType() == SHT_NOBITS) + return; + file.seekp(getOffset()); + file.write(data, getSize()); + } + +private: + friend class ElfSegment; + + void addToSegment(ElfSegment *segment) { + segments.push_back(segment); + } + + void removeFromSegment(ElfSegment *segment) { + std::vector<ElfSegment *>::iterator i = std::find(segments.begin(), segments.end(), segment); + segments.erase(i, i + 1); + } + + ElfSegment *getSegmentByType(unsigned int type); + + void insertInSegments(std::vector<ElfSegment *> &segs); + +protected: + Elf_Shdr shdr; + char *data; + const char *name; +private: + ElfSection *link; + SectionInfo info; + ElfSection *next, *previous; + int index; + std::vector<ElfSegment *> segments; +}; + +class ElfSegment { +public: + ElfSegment(Elf_Phdr *phdr); + + unsigned int getType() { return type; } + unsigned int getFlags() { return flags; } + unsigned int getAlign() { return align; } + + ElfSection *getFirstSection() { return sections.empty() ? nullptr : sections.front(); } + int getVPDiff() { return v_p_diff; } + unsigned int getFileSize(); + unsigned int getMemSize(); + unsigned int getOffset(); + unsigned int getAddr(); + + void addSection(ElfSection *section); + void removeSection(ElfSection *section); + + std::list<ElfSection *>::iterator begin() { return sections.begin(); } + std::list<ElfSection *>::iterator end() { return sections.end(); } + + void clear(); + + bool isElfHackFillerSegment() { + return type == PT_LOAD && flags == 0; + } +private: + unsigned int type; + int v_p_diff; // Difference between physical and virtual address + unsigned int flags; + unsigned int align; + std::list<ElfSection *> sections; + // The following are only really used for PT_GNU_RELRO until something + // better is found. + unsigned int vaddr; + unsigned int filesz, memsz; +}; + +class Elf_Ehdr: public serializable<Elf_Ehdr_Traits>, public ElfSection { +public: + Elf_Ehdr(std::ifstream &file, char ei_class, char ei_data); + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + serializable<Elf_Ehdr_Traits>::serialize(file, ei_class, ei_data); + } +}; + +class Elf_Phdr: public serializable<Elf_Phdr_Traits> { +public: + Elf_Phdr() {}; + Elf_Phdr(std::ifstream &file, char ei_class, char ei_data) + : serializable<Elf_Phdr_Traits>(file, ei_class, ei_data) {}; + bool contains(ElfSection *section) + { + unsigned int size = section->getSize(); + unsigned int addr = section->getAddr(); + // This may be biased, but should work in most cases + if ((section->getFlags() & SHF_ALLOC) == 0) + return false; + // Special case for PT_DYNAMIC. Eventually, this should + // be better handled than special cases + if ((p_type == PT_DYNAMIC) && (section->getType() != SHT_DYNAMIC)) + return false; + // Special case for PT_TLS. + if ((p_type == PT_TLS) && !(section->getFlags() & SHF_TLS)) + return false; + return (addr >= p_vaddr) && + (addr + size <= p_vaddr + p_memsz); + + } +}; + +typedef serializable<Elf_Dyn_Traits> Elf_Dyn; + +struct Elf_DynValue { + unsigned int tag; + ElfValue *value; +}; + +class ElfDynamic_Section: public ElfSection { +public: + ElfDynamic_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent); + ~ElfDynamic_Section(); + + void serialize(std::ofstream &file, char ei_class, char ei_data); + + ElfValue *getValueForType(unsigned int tag); + ElfSection *getSectionForType(unsigned int tag); + bool setValueForType(unsigned int tag, ElfValue *val); +private: + std::vector<Elf_DynValue> dyns; +}; + +typedef serializable<Elf_Sym_Traits> Elf_Sym; + +struct Elf_SymValue { + const char *name; + unsigned char info; + unsigned char other; + ElfLocation value; + unsigned int size; + bool defined; +}; + +#define STT(type) (1 << STT_ ##type) + +class ElfSymtab_Section: public ElfSection { +public: + ElfSymtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent); + + void serialize(std::ofstream &file, char ei_class, char ei_data); + + Elf_SymValue *lookup(const char *name, unsigned int type_filter = STT(OBJECT) | STT(FUNC)); + +//private: // Until we have a real API + std::vector<Elf_SymValue> syms; +}; + +class Elf_Rel: public serializable<Elf_Rel_Traits> { +public: + Elf_Rel(std::ifstream &file, char ei_class, char ei_data) + : serializable<Elf_Rel_Traits>(file, ei_class, ei_data) {}; + + static const unsigned int sh_type = SHT_REL; + static const unsigned int d_tag = DT_REL; + static const unsigned int d_tag_count = DT_RELCOUNT; +}; + +class Elf_Rela: public serializable<Elf_Rela_Traits> { +public: + Elf_Rela(std::ifstream &file, char ei_class, char ei_data) + : serializable<Elf_Rela_Traits>(file, ei_class, ei_data) {}; + + static const unsigned int sh_type = SHT_RELA; + static const unsigned int d_tag = DT_RELA; + static const unsigned int d_tag_count = DT_RELACOUNT; +}; + +template <class Rel> +class ElfRel_Section: public ElfSection { +public: + ElfRel_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) + : ElfSection(s, file, parent) + { + int pos = file->tellg(); + file->seekg(shdr.sh_offset); + for (unsigned int i = 0; i < s.sh_size / s.sh_entsize; i++) { + Rel r(*file, parent->getClass(), parent->getData()); + rels.push_back(r); + } + file->seekg(pos); + } + + void serialize(std::ofstream &file, char ei_class, char ei_data) + { + for (typename std::vector<Rel>::iterator i = rels.begin(); + i != rels.end(); ++i) + (*i).serialize(file, ei_class, ei_data); + } +//private: // Until we have a real API + std::vector<Rel> rels; +}; + +class ElfStrtab_Section: public ElfSection { +public: + ElfStrtab_Section(Elf_Shdr &s, std::ifstream *file, Elf *parent) + : ElfSection(s, file, parent) + { + table.push_back(table_storage(data, shdr.sh_size)); + } + + ~ElfStrtab_Section() + { + for (std::vector<table_storage>::iterator t = table.begin() + 1; + t != table.end(); t++) + delete[] t->buf; + } + + const char *getStr(unsigned int index); + + const char *getStr(const char *string); + + unsigned int getStrIndex(const char *string); + + void serialize(std::ofstream &file, char ei_class, char ei_data); +private: + struct table_storage { + unsigned int size, used; + char *buf; + + table_storage(): size(4096), used(0), buf(new char[4096]) {} + table_storage(const char *data, unsigned int sz) + : size(sz), used(sz), buf(const_cast<char *>(data)) {} + }; + std::vector<table_storage> table; +}; + +inline char Elf::getClass() { + return ehdr->e_ident[EI_CLASS]; +} + +inline char Elf::getData() { + return ehdr->e_ident[EI_DATA]; +} + +inline char Elf::getType() { + return ehdr->e_type; +} + +inline char Elf::getMachine() { + return ehdr->e_machine; +} + +inline unsigned int Elf::getSize() { + ElfSection *section; + for (section = shdr_section /* It's usually not far from the end */; + section->getNext() != nullptr; section = section->getNext()); + return section->getOffset() + section->getSize(); +} + +inline ElfSegment *ElfSection::getSegmentByType(unsigned int type) { + for (std::vector<ElfSegment *>::iterator seg = segments.begin(); seg != segments.end(); seg++) + if ((*seg)->getType() == type) + return *seg; + return nullptr; +} + +inline void ElfSection::insertInSegments(std::vector<ElfSegment *> &segs) { + for (std::vector<ElfSegment *>::iterator it = segs.begin(); it != segs.end(); ++it) { + (*it)->addSection(this); + } +} + +inline ElfLocation::ElfLocation(ElfSection *section, unsigned int off, enum position pos) +: section(section) { + if ((pos == ABSOLUTE) && section) + offset = off - section->getAddr(); + else + offset = off; +} + +inline ElfLocation::ElfLocation(unsigned int location, Elf *elf) { + section = elf->getSectionAt(location); + offset = location - (section ? section->getAddr() : 0); +} + +inline unsigned int ElfLocation::getValue() { + return (section ? section->getAddr() : 0) + offset; +} + +inline const char *ElfLocation::getBuffer() { + return section ? section->getData() + offset : nullptr; +} + +inline unsigned int ElfSize::getValue() { + return section->getSize(); +} + +inline unsigned int ElfEntSize::getValue() { + return section->getEntSize(); +} diff --git a/build/unix/elfhack/inject.c b/build/unix/elfhack/inject.c new file mode 100644 index 000000000..942426330 --- /dev/null +++ b/build/unix/elfhack/inject.c @@ -0,0 +1,52 @@ +/* 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 <stdint.h> +#include <elf.h> + +/* The Android NDK headers define those */ +#undef Elf_Ehdr +#undef Elf_Addr + +#if defined(__LP64__) +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Addr Elf64_Addr +#else +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Addr Elf32_Addr +#endif + +extern __attribute__((visibility("hidden"))) void original_init(int argc, char **argv, char **env); + +extern __attribute__((visibility("hidden"))) Elf32_Rel relhack[]; +extern __attribute__((visibility("hidden"))) Elf_Ehdr elf_header; + +static inline __attribute__((always_inline)) +void do_relocations(void) +{ + Elf32_Rel *rel; + Elf_Addr *ptr, *start; + for (rel = relhack; rel->r_offset; rel++) { + start = (Elf_Addr *)((intptr_t)&elf_header + rel->r_offset); + for (ptr = start; ptr < &start[rel->r_info]; ptr++) + *ptr += (intptr_t)&elf_header; + } +} + +__attribute__((section(".text._init_noinit"))) +int init_noinit(int argc, char **argv, char **env) +{ + do_relocations(); + return 0; +} + +__attribute__((section(".text._init"))) +int init(int argc, char **argv, char **env) +{ + do_relocations(); + original_init(argc, argv, env); + // Ensure there is no tail-call optimization, avoiding the use of the + // B.W instruction in Thumb for the call above. + return 0; +} diff --git a/build/unix/elfhack/inject/Makefile.in b/build/unix/elfhack/inject/Makefile.in new file mode 100644 index 000000000..ef27b077b --- /dev/null +++ b/build/unix/elfhack/inject/Makefile.in @@ -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/. + +include $(topsrcdir)/config/rules.mk + +export:: $(CSRCS:.c=.$(OBJ_SUFFIX)) + +$(CSRCS): %.c: ../inject.c + cp $< $@ + +GARBAGE += $(CSRCS) + +CFLAGS := -O2 -fno-stack-protector $(filter -m% -I%,$(CFLAGS)) diff --git a/build/unix/elfhack/inject/moz.build b/build/unix/elfhack/inject/moz.build new file mode 100644 index 000000000..44d2ea76c --- /dev/null +++ b/build/unix/elfhack/inject/moz.build @@ -0,0 +1,26 @@ +# -*- Mode: python; 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/. + +# dummy library name to avoid skipping building the source here, which +# we only need the object for. +Library('elfhack_inject') + +DIST_INSTALL = False + +if CONFIG['TARGET_CPU'].endswith('86'): + cpu = 'x86' +elif CONFIG['TARGET_CPU'].startswith('arm'): + cpu = 'arm' +else: + cpu = CONFIG['TARGET_CPU'] + +SOURCES += [ + "!%s.c" % cpu, +] + +NO_PGO = True + +NO_VISIBILITY_FLAGS = True diff --git a/build/unix/elfhack/moz.build b/build/unix/elfhack/moz.build new file mode 100644 index 000000000..abc4c2e01 --- /dev/null +++ b/build/unix/elfhack/moz.build @@ -0,0 +1,28 @@ +# -*- Mode: python; 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/. + +DIST_INSTALL = False +DIRS += ['inject'] + +if not CONFIG['CROSS_COMPILE']: + SOURCES += [ + 'dummy.c', + 'test-array.c', + 'test-ctors.c', + ] + + if '-flto' in CONFIG['OS_CFLAGS']: + SOURCES['test-array.c'].flags += ['-fno-lto'] + SOURCES['test-ctors.c'].flags += ['-fno-lto'] + +HOST_SOURCES += [ + 'elf.cpp', + 'elfhack.cpp', +] + +HostProgram('elfhack') + +NO_PGO = True diff --git a/build/unix/elfhack/test-array.c b/build/unix/elfhack/test-array.c new file mode 100644 index 000000000..202c26666 --- /dev/null +++ b/build/unix/elfhack/test-array.c @@ -0,0 +1,8 @@ +/* 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 "test.c" + +__attribute__((section(".init_array"), used)) +static void (*init_array[])() = { end_test, test }; diff --git a/build/unix/elfhack/test-ctors.c b/build/unix/elfhack/test-ctors.c new file mode 100644 index 000000000..4fff05807 --- /dev/null +++ b/build/unix/elfhack/test-ctors.c @@ -0,0 +1,17 @@ +/* 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 "test.c" + +/* Recent binutils would put .ctors content into a .init_array section */ +__attribute__((section(".manual_ctors"), used)) +static void (*ctors[])() = { (void (*)())-1, end_test, test, NULL }; + +__attribute__((section(".init"))) +void _init() { + void (**func)() = &ctors[sizeof(ctors) / sizeof(void (*)()) - 1]; + while (*(--func) != (void (*)())-1) { + (*func)(); + } +} diff --git a/build/unix/elfhack/test.c b/build/unix/elfhack/test.c new file mode 100644 index 000000000..3393f03b5 --- /dev/null +++ b/build/unix/elfhack/test.c @@ -0,0 +1,162 @@ +/* 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/. */ + +#ifdef DEF +DEF(This) +DEF(is) +DEF(a) +DEF(test) +DEF(of) +DEF(string) +DEF(array) +DEF(for) +DEF(use) +DEF(with) +DEF(elfhack) +DEF(to) +DEF(see) +DEF(whether) +DEF(it) +DEF(breaks) +DEF(anything) +DEF(but) +DEF(one) +DEF(needs) +DEF(quite) +DEF(some) +DEF(strings) +DEF(before) +DEF(the) +DEF(program) +DEF(can) +DEF(do) +DEF(its) +DEF(work) +DEF(efficiently) +DEF(Without) +DEF(enough) +DEF(data) +DEF(relocation) +DEF(sections) +DEF(are) +DEF(not) +DEF(sufficiently) +DEF(large) +DEF(and) +DEF(injected) +DEF(code) +DEF(wouldnt) +DEF(fit) +DEF(Said) +DEF(otherwise) +DEF(we) +DEF(need) +DEF(more) +DEF(words) +DEF(than) +DEF(up) +DEF(here) +DEF(so) +DEF(that) +DEF(relocations) +DEF(take) +DEF(significant) +DEF(bytes) +DEF(amounts) +DEF(which) +DEF(isnt) +DEF(exactly) +DEF(easily) +DEF(achieved) +DEF(like) +DEF(this) +DEF(Actually) +DEF(I) +DEF(must) +DEF(cheat) +DEF(by) +DEF(including) +DEF(these) +DEF(phrases) +DEF(several) +DEF(times) + +#else +#pragma GCC visibility push(default) +#include <stdlib.h> +#include <stdio.h> + +#define DEF(w) static const char str_ ## w[] = #w; +#include "test.c" +#undef DEF + +const char *strings[] = { +#define DEF(w) str_ ## w, +#include "test.c" +#include "test.c" +#include "test.c" +}; + +/* Create a hole between two zones of relative relocations */ +const int hole[] = { + 42, 42, 42, 42 +}; + +const char *strings2[] = { +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#undef DEF +}; + +static int ret = 1; + +int print_status() { + fprintf(stderr, "%s\n", ret ? "FAIL" : "PASS"); + return ret; +} + +/* On ARM, this creates a .tbss section before .init_array, which + * elfhack could then pick instead of .init_array. + * Also, when .tbss is big enough, elfhack may wrongfully consider + * following sections as part of the PT_TLS segment. + * Finally, gold makes TLS segments end on an aligned virtual address, + * even when the underlying section ends before that, and elfhack + * sanity checks may yield an error. */ +__thread int foo; +__thread long long int bar[512]; + +void end_test() { + static int count = 0; + /* Only exit when both constructors have been called */ + if (++count == 2) + ret = 0; +} + +void test() { + int i = 0, j = 0; +#define DEF_(a,i,w) \ + if (a[i++] != str_ ## w) return; +#define DEF(w) DEF_(strings,i,w) +#include "test.c" +#include "test.c" +#include "test.c" +#undef DEF +#define DEF(w) DEF_(strings2,j,w) +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#include "test.c" +#undef DEF + if (i != sizeof(strings)/sizeof(strings[0]) && + j != sizeof(strings2)/sizeof(strings2[0])) + fprintf(stderr, "WARNING: Test doesn't cover the whole array\n"); + end_test(); +} + +#pragma GCC visibility pop +#endif diff --git a/build/unix/gnu-ld-scripts/components-export-list b/build/unix/gnu-ld-scripts/components-export-list new file mode 100644 index 000000000..8c2470886 --- /dev/null +++ b/build/unix/gnu-ld-scripts/components-export-list @@ -0,0 +1 @@ +_NSModule diff --git a/build/unix/gnu-ld-scripts/components-version-script b/build/unix/gnu-ld-scripts/components-version-script new file mode 100644 index 000000000..bc1d5ec33 --- /dev/null +++ b/build/unix/gnu-ld-scripts/components-version-script @@ -0,0 +1,7 @@ +EXPORTED { + global: + NSModule; + NSGetModule; + __RLD_MAP; + local: *; +}; diff --git a/build/unix/moz.build b/build/unix/moz.build new file mode 100644 index 000000000..99d0280b7 --- /dev/null +++ b/build/unix/moz.build @@ -0,0 +1,22 @@ +# -*- Mode: python; 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/. + +if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'] or CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']: + DIRS += ['stdc++compat'] + +if CONFIG['USE_ELF_HACK']: + DIRS += ['elfhack'] + +if CONFIG['LLVM_SYMBOLIZER']: + FINAL_TARGET_FILES += ['/' + CONFIG['LLVM_SYMBOLIZER']] + +SDK_FILES.bin += [ + 'run-mozilla.sh', +] + +FINAL_TARGET_FILES += [ + 'run-mozilla.sh', +] diff --git a/build/unix/mozconfig.asan b/build/unix/mozconfig.asan new file mode 100644 index 000000000..84113a417 --- /dev/null +++ b/build/unix/mozconfig.asan @@ -0,0 +1,27 @@ +MOZ_AUTOMATION_L10N_CHECK=0 + +. "$topsrcdir/build/mozconfig.common" + +# Use Clang as specified in manifest +export CC="$topsrcdir/clang/bin/clang -fgnu89-inline" +export CXX="$topsrcdir/clang/bin/clang++" +export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer" + +# Use a newer binutils, from the tooltool gcc package, if it's there +if [ -e "$topsrcdir/gcc/bin/ld" ]; then + export CC="$CC -B $topsrcdir/gcc/bin" + export CXX="$CXX -B $topsrcdir/gcc/bin" +fi + +# Enable ASan specific code and build workarounds +ac_add_options --enable-address-sanitizer + +# Mandatory options required for ASan builds (both on Linux and Mac) +export MOZ_DEBUG_SYMBOLS=1 +ac_add_options --enable-debug-symbols +ac_add_options --disable-install-strip +ac_add_options --disable-jemalloc +ac_add_options --disable-crashreporter +ac_add_options --disable-elf-hack + +. "$topsrcdir/build/unix/mozconfig.stdcxx" diff --git a/build/unix/mozconfig.gtk b/build/unix/mozconfig.gtk new file mode 100644 index 000000000..6f535f8a7 --- /dev/null +++ b/build/unix/mozconfig.gtk @@ -0,0 +1,28 @@ +# To do try builds with Gtk+2, uncomment the following line, and remove +# everything after that. +#ac_add_options --enable-default-toolkit=cairo-gtk2 + +TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} + +# $TOOLTOOL_DIR/gtk3 comes from tooltool, and must be included in the tooltool manifest. +if [ -z "$PKG_CONFIG_LIBDIR" ]; then + echo PKG_CONFIG_LIBDIR must be set >&2 + exit 1 +fi +export PKG_CONFIG_SYSROOT_DIR="$TOOLTOOL_DIR/gtk3" +export PKG_CONFIG_PATH="$TOOLTOOL_DIR/gtk3/usr/local/lib/pkgconfig" +PKG_CONFIG="$TOOLTOOL_DIR/gtk3/usr/local/bin/pkg-config" +export PATH="$TOOLTOOL_DIR/gtk3/usr/local/bin:${PATH}" +# Ensure cairo, gdk-pixbuf, etc. are not taken from the system installed packages. +LDFLAGS="-L$TOOLTOOL_DIR/gtk3/usr/local/lib ${LDFLAGS}" +ac_add_options --enable-default-toolkit=cairo-gtk3 + +# Set things up to use Gtk+3 from the tooltool package +mk_add_options "export FONTCONFIG_PATH=$TOOLTOOL_DIR/gtk3/usr/local/etc/fonts" +mk_add_options "export PANGO_SYSCONFDIR=$TOOLTOOL_DIR/gtk3/usr/local/etc" +mk_add_options "export PANGO_LIBDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib" +mk_add_options "export GDK_PIXBUF_MODULE_FILE=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders.cache" +mk_add_options "export GDK_PIXBUF_MODULEDIR=$TOOLTOOL_DIR/gtk3/usr/local/lib/gdk-pixbuf-2.0/2.10.0/loaders" + +LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOOLTOOL_DIR/gtk3/usr/local/lib +mk_add_options "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" diff --git a/build/unix/mozconfig.linux b/build/unix/mozconfig.linux new file mode 100644 index 000000000..f63f406e6 --- /dev/null +++ b/build/unix/mozconfig.linux @@ -0,0 +1,38 @@ +if [ "x$IS_NIGHTLY" = "xyes" ]; then + # Some nightlies (eg: Mulet) don't want these set. + MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1} + MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1} + MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1} +fi + +. "$topsrcdir/build/mozconfig.common" + +TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} + +# some b2g desktop builds still happen on i686 machines, and the tooltool +# toolchain is x86_64 only. +# We also deal with valgrind builds here, they don't use tooltool manifests at +# all yet. +if [ -z "$no_tooltool" ] +then + CC="$TOOLTOOL_DIR/gcc/bin/gcc" + CXX="$TOOLTOOL_DIR/gcc/bin/g++" + + # We want to make sure we use binutils and other binaries in the tooltool + # package. + mk_add_options PATH="$TOOLTOOL_DIR/gcc/bin:$PATH" +else + CC="/tools/gcc-4.7.3-0moz1/bin/gcc" + CXX="/tools/gcc-4.7.3-0moz1/bin/g++" +fi + +ac_add_options --enable-elf-hack + +. "$topsrcdir/build/unix/mozconfig.stdcxx" + +# PKG_CONFIG_LIBDIR is appropriately overridden in mozconfig.linux32 +export PKG_CONFIG_LIBDIR=/usr/lib64/pkgconfig:/usr/share/pkgconfig + +export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token + +. "$topsrcdir/build/unix/mozconfig.gtk" diff --git a/build/unix/mozconfig.linux32 b/build/unix/mozconfig.linux32 new file mode 100644 index 000000000..309767751 --- /dev/null +++ b/build/unix/mozconfig.linux32 @@ -0,0 +1,12 @@ +. "$topsrcdir/build/unix/mozconfig.linux" + +export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig + +if test `uname -m` = "x86_64"; then + # -march=pentiumpro is what our 32-bit native toolchain defaults to + CC="$CC -m32 -march=pentiumpro" + CXX="$CXX -m32 -march=pentiumpro" + ac_add_options --target=i686-pc-linux + ac_add_options --host=i686-pc-linux + ac_add_options --x-libraries=/usr/lib +fi diff --git a/build/unix/mozconfig.stdcxx b/build/unix/mozconfig.stdcxx new file mode 100644 index 000000000..787e9b443 --- /dev/null +++ b/build/unix/mozconfig.stdcxx @@ -0,0 +1,15 @@ +# Avoid dependency on libstdc++ 4.7 +ac_add_options --enable-stdcxx-compat + +TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} + +if [ -f "$TOOLTOOL_DIR/clang/lib/libstdc++.so" ]; then + LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOOLTOOL_DIR/clang/lib +elif [ -f "$TOOLTOOL_DIR/gcc/lib/libstdc++.so" ]; then + # We put both 32-bits and 64-bits library path in LD_LIBRARY_PATH: ld.so + # will prefer the files in the 32-bits path when loading 32-bits executables, + # and the files in the 64-bits path when loading 64-bits executables. + LD_LIBRARY_PATH=${LD_LIBRARY_PATH:+$LD_LIBRARY_PATH:}$TOOLTOOL_DIR/gcc/lib64:$TOOLTOOL_DIR/gcc/lib +fi + +mk_add_options "export LD_LIBRARY_PATH=$LD_LIBRARY_PATH" diff --git a/build/unix/mozconfig.tsan b/build/unix/mozconfig.tsan new file mode 100644 index 000000000..f78c1071f --- /dev/null +++ b/build/unix/mozconfig.tsan @@ -0,0 +1,34 @@ +MOZ_AUTOMATION_L10N_CHECK=0 + +. "$topsrcdir/build/mozconfig.common" + +# Use Clang as specified in manifest +export CC="$topsrcdir/clang/bin/clang" +export CXX="$topsrcdir/clang/bin/clang++" +export LLVM_SYMBOLIZER="$topsrcdir/clang/bin/llvm-symbolizer" + +# Use a newer binutils, from the tooltool gcc package, if it's there +if [ -e "$topsrcdir/gcc/bin/ld" ]; then + export CC="$CC -B $topsrcdir/gcc/bin" + export CXX="$CXX -B $topsrcdir/gcc/bin" +fi + +# Enable TSan specific code and build workarounds +ac_add_options --enable-thread-sanitizer + +# The ThreadSanitizer is not compatible with sandboxing +# (see bug 1182565) +ac_add_options --disable-sandbox + +# These are required by TSan +ac_add_options --disable-jemalloc +ac_add_options --disable-crashreporter +ac_add_options --disable-elf-hack +ac_add_options --enable-pie + +# Keep symbols to symbolize TSan traces +ac_add_options --disable-install-strip +# -gline-tables-only results in significantly smaller binaries. +ac_add_options --enable-debug-symbols="-gline-tables-only" + +. "$topsrcdir/build/unix/mozconfig.stdcxx" diff --git a/build/unix/mozilla.in b/build/unix/mozilla.in new file mode 100644 index 000000000..d2251eae0 --- /dev/null +++ b/build/unix/mozilla.in @@ -0,0 +1,108 @@ +#!/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/. + +## +## Usage: +## +## $ mozilla [args] +## +## This script is meant to run the application binary from mozilla/dist/bin. +## +## The script will setup all the environment voodoo needed to make +## the application binary to work. +## + +#uncomment for debugging +#set -x + +moz_libdir=%MOZAPPDIR% + +# Use run-mozilla.sh in the current dir if it exists +# If not, then start resolving symlinks until we find run-mozilla.sh +found=0 +progname="$0" +curdir=`dirname "$progname"` +progbase=`basename "$progname"` +run_moz="$curdir/run-mozilla.sh" +if test -x "$run_moz"; then + dist_bin="$curdir" + found=1 +else + here=`/bin/pwd` + while [ -h "$progname" ]; do + bn=`basename "$progname"` + cd `dirname "$progname"` + # Resolve symlink of dirname + cd `/bin/pwd` + progname=`/bin/ls -l "$bn" | sed -e 's/^.* -> //' ` + progbase=`basename "$progname"` + if [ ! -x "$progname" ]; then + break + fi + curdir=`dirname "$progname"` + run_moz="$curdir/run-mozilla.sh" + if [ -x "$run_moz" ]; then + cd "$curdir" + dist_bin=`/bin/pwd` + run_moz="$dist_bin/run-mozilla.sh" + found=1 + break + fi + done + cd "$here" +fi +if [ $found = 0 ]; then + # Check default compile-time libdir + if [ -x "$moz_libdir/run-mozilla.sh" ]; then + dist_bin="$moz_libdir" + run_moz="$moz_libdir/run-mozilla.sh" + else + echo "Cannot find %MOZ_APP_DISPLAYNAME% runtime directory. Exiting." + exit 1 + fi +fi + +script_args="" +debugging=0 +MOZILLA_BIN="${progbase}-bin" + +if [ "$OSTYPE" = "beos" ]; then + mimeset -F "$MOZILLA_BIN" +fi + +pass_arg_count=0 +while [ $# -gt $pass_arg_count ] +do + case "$1" in + -p | --pure | -pure) + MOZILLA_BIN="${MOZILLA_BIN}.pure" + shift + ;; + -g | --debug) + script_args="$script_args -g" + debugging=1 + shift + ;; + -d | --debugger) + script_args="$script_args -d $2" + shift 2 + ;; + *) + # Move the unrecognized argument to the end of the list. + arg="$1" + shift + set -- "$@" "$arg" + pass_arg_count=`expr $pass_arg_count + 1` + ;; + esac +done + +if [ $debugging = 1 ] +then + echo $dist_bin/run-mozilla.sh $script_args $dist_bin/$MOZILLA_BIN "$@" +fi +exec "$dist_bin/run-mozilla.sh" $script_args "$dist_bin/$MOZILLA_BIN" "$@" +# EOF. diff --git a/build/unix/print-failed-commands.sh b/build/unix/print-failed-commands.sh new file mode 100755 index 000000000..7f6b73d33 --- /dev/null +++ b/build/unix/print-failed-commands.sh @@ -0,0 +1,25 @@ +#!/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/. + +# +# Usage from makefile: +# ELOG = . $(topdir)/build/autoconf/print-failed-commands.sh +# $(ELOG) $(CC) $CFLAGS -o $@ $< +# +# This shell script is used by the build system to print out commands that fail +# to execute properly. It is designed to make the "make -s" command more +# useful. +# +# Note that in the example we are sourcing rather than execing the script. +# Since make already started a shell for us, we might as well use it rather +# than starting a new one. + +( exec "$@" ) || { + echo + echo "In the directory " `pwd` + echo "The following command failed to execute properly:" + echo "$@" + exit 1; +} diff --git a/build/unix/print-non-newline.sh b/build/unix/print-non-newline.sh new file mode 100755 index 000000000..5e0cf292d --- /dev/null +++ b/build/unix/print-non-newline.sh @@ -0,0 +1,35 @@ +#!/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/. + +# +# The purpose of this file is to find the files that do not end with a +# newline. Some compilers fail if the source files do not end with a +# newline. +# + +# +test_file=newline_test +test_dummy=newline_testee +inlist="$*" +broken_list= + +if test "$inlist" = ""; then + echo "Usage: $0 *.c *.cpp"; + exit 0; +fi + +echo "" > $test_file + +for f in $inlist; do + if test -f $f; then + tail -c 1 $f > $test_dummy + if ! `cmp -s $test_file $test_dummy`; then + broken_list="$broken_list $f" + fi + fi +done + +rm -f $test_file $test_dummy +echo $broken_list diff --git a/build/unix/rewrite_asan_dylib.py b/build/unix/rewrite_asan_dylib.py new file mode 100644 index 000000000..6e30374b7 --- /dev/null +++ b/build/unix/rewrite_asan_dylib.py @@ -0,0 +1,60 @@ +# 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 sys +import os +import subprocess +import shutil +from buildconfig import substs + +''' +Scans the given directories for binaries referencing the AddressSanitizer +runtime library, copies it to the main directory and rewrites binaries to not +reference it with absolute paths but with @executable_path instead. +''' + +# This is the dylib we're looking for +DYLIB_NAME='libclang_rt.asan_osx_dynamic.dylib' + +def scan_directory(path): + dylibCopied = False + + for root, subdirs, files in os.walk(path): + for filename in files: + filename = os.path.join(root, filename) + + # Skip all files that aren't either dylibs or executable + if not (filename.endswith('.dylib') or os.access(filename, os.X_OK)): + continue + + try: + otoolOut = subprocess.check_output([substs['OTOOL'], '-L', filename]) + except: + # Errors are expected on non-mach executables, ignore them and continue + continue + + for line in otoolOut.splitlines(): + if line.find(DYLIB_NAME) != -1: + absDylibPath = line.split()[0] + + # Don't try to rewrite binaries twice + if absDylibPath.find('@executable_path/') == 0: + continue + + if not dylibCopied: + # Copy the runtime once to the main directory, which is passed + # as the argument to this function. + shutil.copy(absDylibPath, path) + + # Now rewrite the library itself + subprocess.check_call(['install_name_tool', '-id', '@executable_path/' + DYLIB_NAME, os.path.join(path, DYLIB_NAME)]) + dylibCopied = True + + # Now use install_name_tool to rewrite the path in our binary + subprocess.check_call(['install_name_tool', '-change', absDylibPath, '@executable_path/' + DYLIB_NAME, filename]) + break + +if __name__ == '__main__': + for d in sys.argv[1:]: + scan_directory(d) diff --git a/build/unix/run-gprof.sh b/build/unix/run-gprof.sh new file mode 100644 index 000000000..794d146b9 --- /dev/null +++ b/build/unix/run-gprof.sh @@ -0,0 +1,17 @@ +#!/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/. + + +LD_LIBRARY_PATH=. +export LD_LIBRARY_PATH + +PROG=mozilla-bin +PLIBS="" + +for l in *.so components/*.so; do + PLIBS="$PLIBS -incobj $l" +done + +$ECHO /bin/gprof -L. -Lcomponents -all $PLIBS $PROG $PROG.hiout diff --git a/build/unix/run-hiprof.sh b/build/unix/run-hiprof.sh new file mode 100644 index 000000000..cb4ec851f --- /dev/null +++ b/build/unix/run-hiprof.sh @@ -0,0 +1,25 @@ +#!/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/. + + +LD_LIBRARY_PATH=. +export LD_LIBRARY_PATH + +PROG=mozilla-bin +PLIBS="-L." + +TOOL=hiprof + +for l in ./*.so components/*.so; do + PLIBS="$PLIBS -incobj $l" +done + +$ECHO atom $PROG -tool $TOOL -env threads -toolargs="-calltime -systime" -all $PLIBS + +cd components && ( + for f in lib*.so; do + mv ../$f.$PROG.$TOOL.threads . + done +) diff --git a/build/unix/run-mozilla.sh b/build/unix/run-mozilla.sh new file mode 100755 index 000000000..c053e60b1 --- /dev/null +++ b/build/unix/run-mozilla.sh @@ -0,0 +1,362 @@ +#!/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/. +cmdname=`basename "$0"` +MOZ_DIST_BIN=`dirname "$0"` +MOZ_DEFAULT_NAME="./${cmdname}-bin" +MOZ_APPRUNNER_NAME="./mozilla-bin" +MOZ_PROGRAM="" + +exitcode=1 +# +## +## Functions +## +########################################################################## +moz_usage() +{ +echo "Usage: ${cmdname} [options] [program]" +echo "" +echo " options:" +echo "" +echo " -g Run in debugger." +echo " --debug" +echo "" +echo " -d debugger Debugger to use." +echo " --debugger debugger" +echo "" +echo " -a debugger_args Arguments passed to [debugger]." +echo " --debugger-args debugger_args" +echo "" +echo " Examples:" +echo "" +echo " Run the mozilla-bin binary" +echo "" +echo " ${cmdname} mozilla-bin" +echo "" +echo " Debug the mozilla-bin binary in gdb" +echo "" +echo " ${cmdname} -g mozilla-bin -d gdb" +echo "" +echo " Run mozilla-bin under valgrind with arguments" +echo "" +echo " ${cmdname} -g -d valgrind -a '--tool=memcheck --leak-check=full' mozilla-bin" +echo "" + return 0 +} +########################################################################## +moz_bail() +{ + message=$1 + echo + echo "$cmdname: $message" + echo + exit 1 +} +########################################################################## +moz_test_binary() +{ + binary=$1 + if [ -f "$binary" ] + then + if [ -x "$binary" ] + then + return 1 + fi + fi + return 0 +} +########################################################################## +moz_get_debugger() +{ + debuggers="ddd gdb dbx bdb native-gdb" + debugger="notfound" + done="no" + for d in $debuggers + do + moz_test_binary /bin/which + if [ $? -eq 1 ] + then + dpath=`which ${d}` + else + dpath=`LC_MESSAGES=C type ${d} | awk '{print $3;}' | sed -e 's/\.$//'` + fi + if [ -x "$dpath" ] + then + debugger=$dpath + break + fi + done + echo $debugger + return 0 +} +########################################################################## +moz_run_program() +{ + prog=$MOZ_PROGRAM + ## + ## Make sure the program is executable + ## + if [ ! -x "$prog" ] + then + moz_bail "Cannot execute $prog." + fi + ## + ## Run the program + ## + exec "$prog" ${1+"$@"} + exitcode=$? +} +########################################################################## +moz_debug_program() +{ + prog=$MOZ_PROGRAM + ## + ## Make sure the program is executable + ## + if [ ! -x "$prog" ] + then + moz_bail "Cannot execute $prog." + fi + if [ -n "$moz_debugger" ] + then + moz_test_binary /bin/which + if [ $? -eq 1 ] + then + debugger=`which $moz_debugger` + else + debugger=`LC_MESSAGES=C type $moz_debugger | awk '{print $3;}' | sed -e 's/\.$//'` + fi + else + debugger=`moz_get_debugger` + fi + if [ -x "$debugger" ] + then +# If you are not using ddd, gdb and know of a way to convey the arguments +# over to the prog then add that here- Gagan Saksena 03/15/00 + case `basename $debugger` in + native-gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"} + exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"} + exitcode=$? + ;; + gdb) echo "$debugger $moz_debugger_args --args $prog" ${1+"$@"} + exec "$debugger" $moz_debugger_args --args "$prog" ${1+"$@"} + exitcode=$? + ;; + ddd) echo "$debugger $moz_debugger_args --gdb -- --args $prog" ${1+"$@"} + exec "$debugger" $moz_debugger_args --gdb -- --args "$prog" ${1+"$@"} + exitcode=$? + ;; + *) echo "$debugger $moz_debugger_args $prog ${1+"$@"}" + exec $debugger $moz_debugger_args "$prog" ${1+"$@"} + exitcode=$? + ;; + esac + else + moz_bail "Could not find a debugger on your system." + fi +} +########################################################################## +## +## Command line arg defaults +## +moz_debug=0 +moz_debugger="" +moz_debugger_args="" +# +## +## Parse the command line +## +while [ $# -gt 0 ] +do + case $1 in + -g | --debug) + moz_debug=1 + shift + ;; + -d | --debugger) + moz_debugger=$2; + if [ "${moz_debugger}" != "" ]; then + shift 2 + else + echo "-d requires an argument" + exit 1 + fi + ;; + -a | --debugger-args) + moz_debugger_args=$2; + if [ "${moz_debugger_args}" != "" ]; then + shift 2 + else + echo "-a requires an argument" + exit 1 + fi + ;; + *) + break; + ;; + esac +done +# +## +## Program name given in $1 +## +if [ $# -gt 0 ] +then + MOZ_PROGRAM=$1 + shift +fi +## +## Program not given, try to guess a default +## +if [ -z "$MOZ_PROGRAM" ] +then + ## + ## Try this script's name with '-bin' appended + ## + if [ -x "$MOZ_DEFAULT_NAME" ] + then + MOZ_PROGRAM=$MOZ_DEFAULT_NAME + ## + ## Try mozilla-bin + ## + elif [ -x "$MOZ_APPRUNNER_NAME" ] + then + MOZ_PROGRAM=$MOZ_APPRUNNER_NAME + fi +fi +# +# +## +## Make sure the program is executable +## +if [ ! -x "$MOZ_PROGRAM" ] +then + moz_bail "Cannot execute $MOZ_PROGRAM." +fi +# +## +## Set MOZILLA_FIVE_HOME +## +MOZILLA_FIVE_HOME=$MOZ_DIST_BIN + +if [ -z "$MRE_HOME" ]; then + MRE_HOME=$MOZILLA_FIVE_HOME +fi +## +## Set LD_LIBRARY_PATH +## +## On Solaris we use $ORIGIN (set in RUNPATH) instead of LD_LIBRARY_PATH +## to locate shared libraries. +## +## When a shared library is a symbolic link, $ORIGIN will be replaced with +## the real path (i.e., what the symbolic link points to) by the runtime +## linker. For example, if dist/bin/libxul.so is a symbolic link to +## toolkit/library/libxul.so, $ORIGIN will be "toolkit/library" instead of "dist/bin". +## So the runtime linker will use "toolkit/library" NOT "dist/bin" to locate the +## other shared libraries that libxul.so depends on. This only happens +## when a user (developer) tries to start firefox, thunderbird, or seamonkey +## under dist/bin. To solve the problem, we should rely on LD_LIBRARY_PATH +## to locate shared libraries. +## +## Note: +## We test $MOZ_DIST_BIN/*.so. If any of them is a symbolic link, +## we need to set LD_LIBRARY_PATH. +########################################################################## +moz_should_set_ld_library_path() +{ + [ `uname -s` != "SunOS" ] && return 0 + for sharedlib in $MOZ_DIST_BIN/*.so + do + [ -h $sharedlib ] && return 0 + done + return 1 +} +if moz_should_set_ld_library_path +then + LD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH:+":$LD_LIBRARY_PATH"} +fi + +if [ -n "$LD_LIBRARYN32_PATH" ] +then + LD_LIBRARYN32_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN32_PATH:+":$LD_LIBRARYN32_PATH"} +fi +if [ -n "$LD_LIBRARYN64_PATH" ] +then + LD_LIBRARYN64_PATH=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARYN64_PATH:+":$LD_LIBRARYN64_PATH"} +fi +if [ -n "$LD_LIBRARY_PATH_64" ]; then + LD_LIBRARY_PATH_64=${MOZ_DIST_BIN}:${MOZ_DIST_BIN}/plugins:${MRE_HOME}${LD_LIBRARY_PATH_64:+":$LD_LIBRARY_PATH_64"} +fi +# +# +## Set SHLIB_PATH for HPUX +SHLIB_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${SHLIB_PATH:+":$SHLIB_PATH"} +# +## Set LIBPATH for AIX +LIBPATH=${MOZ_DIST_BIN}:${MRE_HOME}${LIBPATH:+":$LIBPATH"} +# +## Set DYLD_LIBRARY_PATH for Mac OS X (Darwin) +DYLD_LIBRARY_PATH=${MOZ_DIST_BIN}:${MRE_HOME}${DYLD_LIBRARY_PATH:+":$DYLD_LIBRARY_PATH"} +# +## Solaris Xserver(Xsun) tuning - use shared memory transport if available +if [ "$XSUNTRANSPORT" = "" ] +then + XSUNTRANSPORT="shmem" + XSUNSMESIZE="512" + export XSUNTRANSPORT XSUNSMESIZE +fi + +# Disable Gnome crash dialog +GNOME_DISABLE_CRASH_DIALOG=1 +export GNOME_DISABLE_CRASH_DIALOG + +if [ "$moz_debug" -eq 1 ] +then + echo "MOZILLA_FIVE_HOME=$MOZILLA_FIVE_HOME" + echo " LD_LIBRARY_PATH=$LD_LIBRARY_PATH" + if [ -n "$LD_LIBRARYN32_PATH" ] + then + echo "LD_LIBRARYN32_PATH=$LD_LIBRARYN32_PATH" + fi + if [ -n "$LD_LIBRARYN64_PATH" ] + then + echo "LD_LIBRARYN64_PATH=$LD_LIBRARYN64_PATH" + fi + if [ -n "$LD_LIBRARY_PATH_64" ]; then + echo "LD_LIBRARY_PATH_64=$LD_LIBRARY_PATH_64" + fi + if [ -n "$DISPLAY" ]; then + echo "DISPLAY=$DISPLAY" + fi + if [ -n "$FONTCONFIG_PATH" ]; then + echo "FONTCONFIG_PATH=$FONTCONFIG_PATH" + fi + if [ -n "$MOZILLA_POSTSCRIPT_PRINTER_LIST" ]; then + echo "MOZILLA_POSTSCRIPT_PRINTER_LIST=$MOZILLA_POSTSCRIPT_PRINTER_LIST" + fi + echo "DYLD_LIBRARY_PATH=$DYLD_LIBRARY_PATH" + echo " LIBRARY_PATH=$LIBRARY_PATH" + echo " SHLIB_PATH=$SHLIB_PATH" + echo " LIBPATH=$LIBPATH" + echo " ADDON_PATH=$ADDON_PATH" + echo " MOZ_PROGRAM=$MOZ_PROGRAM" + echo " MOZ_TOOLKIT=$MOZ_TOOLKIT" + echo " moz_debug=$moz_debug" + echo " moz_debugger=$moz_debugger" + echo "moz_debugger_args=$moz_debugger_args" +fi +# +export MOZILLA_FIVE_HOME LD_LIBRARY_PATH +export SHLIB_PATH LIBPATH LIBRARY_PATH ADDON_PATH DYLD_LIBRARY_PATH + +if [ $moz_debug -eq 1 ] +then + moz_debug_program ${1+"$@"} +else + moz_run_program ${1+"$@"} +fi + +exit $exitcode diff --git a/build/unix/run-third.sh b/build/unix/run-third.sh new file mode 100644 index 000000000..cb2c66137 --- /dev/null +++ b/build/unix/run-third.sh @@ -0,0 +1,25 @@ +#!/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/. + + +LD_LIBRARY_PATH=. +export LD_LIBRARY_PATH + +PROG=mozilla-bin +PLIBS="-L." + +TOOL=third + +for l in ./*.so components/*.so; do + PLIBS="$PLIBS -incobj $l" +done + +$ECHO atom $PROG -tool $TOOL -env threads -g -all $PLIBS -toolargs="-leaks all -before NS_ShutdownXPCOM" + +cd components && ( + for f in lib*.so; do + mv ../$f.$PROG.$TOOL.threads . + done +) diff --git a/build/unix/stdc++compat/Makefile.in b/build/unix/stdc++compat/Makefile.in new file mode 100644 index 000000000..054c1c023 --- /dev/null +++ b/build/unix/stdc++compat/Makefile.in @@ -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/. + +ENABLE_CLANG_PLUGIN := + +include $(topsrcdir)/config/rules.mk diff --git a/build/unix/stdc++compat/moz.build b/build/unix/stdc++compat/moz.build new file mode 100644 index 000000000..01663f37d --- /dev/null +++ b/build/unix/stdc++compat/moz.build @@ -0,0 +1,24 @@ +# -*- Mode: python; 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/. + +if CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION']: + Library('stdc++compat') + SOURCES += ['stdc++compat.cpp'] + +if CONFIG['MOZ_LIBSTDCXX_HOST_VERSION']: + HostLibrary('host_stdc++compat') + HOST_SOURCES += [ + 'stdc++compat.cpp', + ] + +FORCE_STATIC_LIB = True + +NO_PGO = True + +DISABLE_STL_WRAPPING = True + +DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_TARGET_VERSION'] +HOST_DEFINES['MOZ_LIBSTDCXX_VERSION'] = CONFIG['MOZ_LIBSTDCXX_HOST_VERSION'] diff --git a/build/unix/stdc++compat/stdc++compat.cpp b/build/unix/stdc++compat/stdc++compat.cpp new file mode 100644 index 000000000..95a7a9afe --- /dev/null +++ b/build/unix/stdc++compat/stdc++compat.cpp @@ -0,0 +1,78 @@ +/* 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 <ostream> +#include <istream> +#include <string> +#include <stdarg.h> +#include <stdio.h> +#include <mozilla/Assertions.h> + +/* GLIBCXX_3.4.8 is from gcc 4.1.1 (111691) + GLIBCXX_3.4.9 is from gcc 4.2.0 (111690) + GLIBCXX_3.4.10 is from gcc 4.3.0 (126287) + GLIBCXX_3.4.11 is from gcc 4.4.0 (133006) + GLIBCXX_3.4.12 is from gcc 4.4.1 (147138) + GLIBCXX_3.4.13 is from gcc 4.4.2 (151127) + GLIBCXX_3.4.14 is from gcc 4.5.0 (151126) + GLIBCXX_3.4.15 is from gcc 4.6.0 (160071) + GLIBCXX_3.4.16 is from gcc 4.6.1 (172240) + GLIBCXX_3.4.17 is from gcc 4.7.0 (174383) + GLIBCXX_3.4.18 is from gcc 4.8.0 (190787) + GLIBCXX_3.4.19 is from gcc 4.8.1 (199309) + GLIBCXX_3.4.20 is from gcc 4.9.0 (199307) + GLIBCXX_3.4.21 is from gcc 5.0 (210290) + +This file adds the necessary compatibility tricks to avoid symbols with +version GLIBCXX_3.4.16 and bigger, keeping binary compatibility with +libstdc++ 4.6.1. + +*/ + +#define GLIBCXX_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c)) + +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 20) +namespace std { + + /* We shouldn't be throwing exceptions at all, but it sadly turns out + we call STL (inline) functions that do. */ + void __throw_out_of_range_fmt(char const* fmt, ...) + { + va_list ap; + char buf[1024]; // That should be big enough. + + va_start(ap, fmt); + vsnprintf(buf, sizeof(buf), fmt, ap); + buf[sizeof(buf) - 1] = 0; + va_end(ap); + + __throw_range_error(buf); + } + +} +#endif + +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 20) +/* Technically, this symbol is not in GLIBCXX_3.4.20, but in CXXABI_1.3.8, + but that's equivalent, version-wise. Those calls are added by the compiler + itself on `new Class[n]` calls. */ +extern "C" void +__cxa_throw_bad_array_new_length() +{ + MOZ_CRASH(); +} +#endif + +#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 21) +/* While we generally don't build with exceptions, we have some host tools + * that do use them. libstdc++ from GCC 5.0 added exception constructors with + * char const* argument. Older versions only have a constructor with + * std::string. */ +namespace std { + runtime_error::runtime_error(char const* s) + : runtime_error(std::string(s)) + { + } +} +#endif diff --git a/build/update-settings.ini b/build/update-settings.ini new file mode 100644 index 000000000..9bbd40aca --- /dev/null +++ b/build/update-settings.ini @@ -0,0 +1,11 @@ +#if 0 +; 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/. +#endif +; If you modify this file updates may fail. +; Do not modify this file. + +#filter substitution +[Settings] +ACCEPTED_MAR_CHANNEL_IDS=@ACCEPTED_MAR_CHANNEL_IDS@ diff --git a/build/upload.py b/build/upload.py new file mode 100644 index 000000000..c6bc10429 --- /dev/null +++ b/build/upload.py @@ -0,0 +1,375 @@ +#!/usr/bin/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/. +# +# When run directly, this script expects the following environment variables +# to be set: +# UPLOAD_HOST : host to upload files to +# UPLOAD_USER : username on that host +# and one of the following: +# UPLOAD_PATH : path on that host to put the files in +# UPLOAD_TO_TEMP : upload files to a new temporary directory +# +# If UPLOAD_HOST and UPLOAD_USER are not set, this script will simply write out +# the properties file. +# +# If UPLOAD_HOST is "localhost", then files are simply copied to UPLOAD_PATH. +# In this case, UPLOAD_TO_TEMP and POST_UPLOAD_CMD are not supported, and no +# properties are written out. +# +# And will use the following optional environment variables if set: +# UPLOAD_SSH_KEY : path to a ssh private key to use +# UPLOAD_PORT : port to use for ssh +# POST_UPLOAD_CMD: a commandline to run on the remote host after uploading. +# UPLOAD_PATH and the full paths of all files uploaded will +# be appended to the commandline. +# +# All files to be uploaded should be passed as commandline arguments to this +# script. The script takes one other parameter, --base-path, which you can use +# to indicate that files should be uploaded including their paths relative +# to the base path. + +import sys, os +import re +import json +import errno +import hashlib +import shutil +from optparse import OptionParser +from subprocess import ( + check_call, + check_output, + STDOUT, + CalledProcessError, +) +import concurrent.futures as futures +import redo + +def OptionalEnvironmentVariable(v): + """Return the value of the environment variable named v, or None + if it's unset (or empty).""" + if v in os.environ and os.environ[v] != "": + return os.environ[v] + return None + +def FixupMsysPath(path): + """MSYS helpfully translates absolute pathnames in environment variables + and commandline arguments into Windows native paths. This sucks if you're + trying to pass an absolute path on a remote server. This function attempts + to un-mangle such paths.""" + if 'OSTYPE' in os.environ and os.environ['OSTYPE'] == 'msys': + # sort of awful, find out where our shell is (should be in msys/bin) + # and strip the first part of that path out of the other path + if 'SHELL' in os.environ: + sh = os.environ['SHELL'] + msys = sh[:sh.find('/bin')] + if path.startswith(msys): + path = path[len(msys):] + return path + +def WindowsPathToMsysPath(path): + """Translate a Windows pathname to an MSYS pathname. + Necessary because we call out to ssh/scp, which are MSYS binaries + and expect MSYS paths.""" + # If we're not on Windows, or if we already have an MSYS path (starting + # with '/' instead of 'c:' or something), then just return. + if sys.platform != 'win32' or path.startswith('/'): + return path + (drive, path) = os.path.splitdrive(os.path.abspath(path)) + return "/" + drive[0] + path.replace('\\','/') + +def AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key): + """Given optional port and ssh key values, append valid OpenSSH + commandline arguments to the list cmdline if the values are not None.""" + if port is not None: + cmdline.append("-P%d" % port) + if ssh_key is not None: + # Don't interpret ~ paths - ssh can handle that on its own + if not ssh_key.startswith('~'): + ssh_key = WindowsPathToMsysPath(ssh_key) + cmdline.extend(["-o", "IdentityFile=%s" % ssh_key]) + # In case of an issue here we don't want to hang on a password prompt. + cmdline.extend(["-o", "BatchMode=yes"]) + +def DoSSHCommand(command, user, host, port=None, ssh_key=None): + """Execute command on user@host using ssh. Optionally use + port and ssh_key, if provided.""" + cmdline = ["ssh"] + AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key) + cmdline.extend(["%s@%s" % (user, host), command]) + + with redo.retrying(check_output, sleeptime=10) as f: + try: + output = f(cmdline, stderr=STDOUT).strip() + except CalledProcessError as e: + print "failed ssh command output:" + print '=' * 20 + print e.output + print '=' * 20 + raise + return output + + raise Exception("Command %s returned non-zero exit code" % cmdline) + +def DoSCPFile(file, remote_path, user, host, port=None, ssh_key=None, + log=False): + """Upload file to user@host:remote_path using scp. Optionally use + port and ssh_key, if provided.""" + if log: + print 'Uploading %s' % file + cmdline = ["scp"] + AppendOptionalArgsToSSHCommandline(cmdline, port, ssh_key) + cmdline.extend([WindowsPathToMsysPath(file), + "%s@%s:%s" % (user, host, remote_path)]) + with redo.retrying(check_call, sleeptime=10) as f: + f(cmdline) + return + + raise Exception("Command %s returned non-zero exit code" % cmdline) + +def GetBaseRelativePath(path, local_file, base_path): + """Given a remote path to upload to, a full path to a local file, and an + optional full path that is a base path of the local file, construct the + full remote path to place the file in. If base_path is not None, include + the relative path from base_path to file.""" + if base_path is None or not local_file.startswith(base_path): + # Hack to work around OSX uploading the i386 SDK from i386/dist. Both + # the i386 SDK and x86-64 SDK end up in the same directory this way. + if base_path.endswith('/x86_64/dist'): + return GetBaseRelativePath(path, local_file, base_path.replace('/x86_64/', '/i386/')) + return path + dir = os.path.dirname(local_file) + # strip base_path + extra slash and make it unixy + dir = dir[len(base_path)+1:].replace('\\','/') + return path + dir + +def GetFileHashAndSize(filename): + sha512Hash = 'UNKNOWN' + size = 'UNKNOWN' + + try: + # open in binary mode to make sure we get consistent results + # across all platforms + with open(filename, "rb") as f: + shaObj = hashlib.sha512(f.read()) + sha512Hash = shaObj.hexdigest() + + size = os.path.getsize(filename) + except: + raise Exception("Unable to get filesize/hash from file: %s" % filename) + + return (sha512Hash, size) + +def GetMarProperties(filename): + if not os.path.exists(filename): + return {} + (mar_hash, mar_size) = GetFileHashAndSize(filename) + return { + 'completeMarFilename': os.path.basename(filename), + 'completeMarSize': mar_size, + 'completeMarHash': mar_hash, + } + +def GetUrlProperties(output, package): + # let's create a switch case using name-spaces/dict + # rather than a long if/else with duplicate code + property_conditions = [ + # key: property name, value: condition + ('symbolsUrl', lambda m: m.endswith('crashreporter-symbols.zip') or + m.endswith('crashreporter-symbols-full.zip')), + ('testsUrl', lambda m: m.endswith(('tests.tar.bz2', 'tests.zip'))), + ('robocopApkUrl', lambda m: m.endswith('apk') and 'robocop' in m), + ('jsshellUrl', lambda m: 'jsshell-' in m and m.endswith('.zip')), + ('completeMarUrl', lambda m: m.endswith('.complete.mar')), + ('partialMarUrl', lambda m: m.endswith('.mar') and '.partial.' in m), + ('codeCoverageURL', lambda m: m.endswith('code-coverage-gcno.zip')), + ('sdkUrl', lambda m: m.endswith(('sdk.tar.bz2', 'sdk.zip'))), + ('testPackagesUrl', lambda m: m.endswith('test_packages.json')), + ('packageUrl', lambda m: m.endswith(package)), + ] + url_re = re.compile(r'''^(https?://.*?\.(?:tar\.bz2|dmg|zip|apk|rpm|mar|tar\.gz|json))$''') + properties = {} + + try: + for line in output.splitlines(): + m = url_re.match(line.strip()) + if m: + m = m.group(1) + for prop, condition in property_conditions: + if condition(m): + properties.update({prop: m}) + break + except IOError as e: + if e.errno != errno.ENOENT: + raise + properties = {prop: 'UNKNOWN' for prop, condition in property_conditions} + return properties + +def UploadFiles(user, host, path, files, verbose=False, port=None, ssh_key=None, base_path=None, upload_to_temp_dir=False, post_upload_command=None, package=None): + """Upload each file in the list files to user@host:path. Optionally pass + port and ssh_key to the ssh commands. If base_path is not None, upload + files including their path relative to base_path. If upload_to_temp_dir is + True files will be uploaded to a temporary directory on the remote server. + Generally, you should have a post upload command specified in these cases + that can move them around to their correct location(s). + If post_upload_command is not None, execute that command on the remote host + after uploading all files, passing it the upload path, and the full paths to + all files uploaded. + If verbose is True, print status updates while working.""" + if not host or not user: + return {} + if (not path and not upload_to_temp_dir) or (path and upload_to_temp_dir): + print "One (and only one of UPLOAD_PATH or UPLOAD_TO_TEMP must be " + \ + "defined." + sys.exit(1) + + if upload_to_temp_dir: + path = DoSSHCommand("mktemp -d", user, host, port=port, ssh_key=ssh_key) + if not path.endswith("/"): + path += "/" + if base_path is not None: + base_path = os.path.abspath(base_path) + remote_files = [] + properties = {} + + def get_remote_path(p): + return GetBaseRelativePath(path, os.path.abspath(p), base_path) + + try: + # Do a pass to find remote directories so we don't perform excessive + # scp calls. + remote_paths = set() + for file in files: + if not os.path.isfile(file): + raise IOError("File not found: %s" % file) + + remote_paths.add(get_remote_path(file)) + + # If we wanted to, we could reduce the remote paths if they are a parent + # of any entry. + for p in sorted(remote_paths): + DoSSHCommand("mkdir -p " + p, user, host, port=port, ssh_key=ssh_key) + + with futures.ThreadPoolExecutor(4) as e: + fs = [] + # Since we're uploading in parallel, the largest file should take + # the longest to upload. So start it first. + for file in sorted(files, key=os.path.getsize, reverse=True): + remote_path = get_remote_path(file) + fs.append(e.submit(DoSCPFile, file, remote_path, user, host, + port=port, ssh_key=ssh_key, log=verbose)) + remote_files.append(remote_path + '/' + os.path.basename(file)) + + # We need to call result() on the future otherwise exceptions could + # get swallowed. + for f in futures.as_completed(fs): + f.result() + + if post_upload_command is not None: + if verbose: + print "Running post-upload command: " + post_upload_command + file_list = '"' + '" "'.join(remote_files) + '"' + output = DoSSHCommand('%s "%s" %s' % (post_upload_command, path, file_list), user, host, port=port, ssh_key=ssh_key) + # We print since mozharness may parse URLs from the output stream. + print output + properties = GetUrlProperties(output, package) + finally: + if upload_to_temp_dir: + DoSSHCommand("rm -rf %s" % path, user, host, port=port, + ssh_key=ssh_key) + if verbose: + print "Upload complete" + return properties + +def CopyFilesLocally(path, files, verbose=False, base_path=None, package=None): + """Copy each file in the list of files to `path`. The `base_path` argument is treated + as it is by UploadFiles.""" + if not path.endswith("/"): + path += "/" + if base_path is not None: + base_path = os.path.abspath(base_path) + for file in files: + file = os.path.abspath(file) + if not os.path.isfile(file): + raise IOError("File not found: %s" % file) + # first ensure that path exists remotely + target_path = GetBaseRelativePath(path, file, base_path) + if not os.path.exists(target_path): + os.makedirs(target_path) + if verbose: + print "Copying " + file + " to " + target_path + shutil.copy(file, target_path) + +def WriteProperties(files, properties_file, url_properties, package): + properties = url_properties + for file in files: + if file.endswith('.complete.mar'): + properties.update(GetMarProperties(file)) + with open(properties_file, 'w') as outfile: + properties['packageFilename'] = package + properties['uploadFiles'] = [os.path.abspath(f) for f in files] + json.dump(properties, outfile, indent=4) + +if __name__ == '__main__': + host = OptionalEnvironmentVariable('UPLOAD_HOST') + user = OptionalEnvironmentVariable('UPLOAD_USER') + path = OptionalEnvironmentVariable('UPLOAD_PATH') + upload_to_temp_dir = OptionalEnvironmentVariable('UPLOAD_TO_TEMP') + port = OptionalEnvironmentVariable('UPLOAD_PORT') + if port is not None: + port = int(port) + key = OptionalEnvironmentVariable('UPLOAD_SSH_KEY') + post_upload_command = OptionalEnvironmentVariable('POST_UPLOAD_CMD') + + if sys.platform == 'win32': + if path is not None: + path = FixupMsysPath(path) + if post_upload_command is not None: + post_upload_command = FixupMsysPath(post_upload_command) + + parser = OptionParser(usage="usage: %prog [options] <files>") + parser.add_option("-b", "--base-path", + action="store", + help="Preserve file paths relative to this path when uploading. If unset, all files will be uploaded directly to UPLOAD_PATH.") + parser.add_option("--properties-file", + action="store", + help="Path to the properties file to store the upload properties.") + parser.add_option("--package", + action="store", + help="Name of the main package.") + (options, args) = parser.parse_args() + if len(args) < 1: + print "You must specify at least one file to upload" + sys.exit(1) + if not options.properties_file: + print "You must specify a --properties-file" + sys.exit(1) + + if host == "localhost": + if upload_to_temp_dir: + print "Cannot use UPLOAD_TO_TEMP with UPLOAD_HOST=localhost" + sys.exit(1) + if post_upload_command: + # POST_UPLOAD_COMMAND is difficult to extract from the mozharness + # scripts, so just ignore it until it's no longer used anywhere + print "Ignoring POST_UPLOAD_COMMAND with UPLOAD_HOST=localhost" + + try: + if host == "localhost": + CopyFilesLocally(path, args, base_path=options.base_path, + package=options.package, + verbose=True) + else: + + url_properties = UploadFiles(user, host, path, args, + base_path=options.base_path, port=port, ssh_key=key, + upload_to_temp_dir=upload_to_temp_dir, + post_upload_command=post_upload_command, + package=options.package, verbose=True) + + WriteProperties(args, options.properties_file, url_properties, options.package) + except IOError, (strerror): + print strerror + sys.exit(1) diff --git a/build/util/count_ctors.py b/build/util/count_ctors.py new file mode 100644 index 000000000..6a3a87068 --- /dev/null +++ b/build/util/count_ctors.py @@ -0,0 +1,64 @@ + +#!/usr/bin/python +import json +import re +import subprocess +import sys + +def count_ctors(filename): + proc = subprocess.Popen( + ['readelf', '-W', '-S', filename], stdout=subprocess.PIPE) + + # Some versions of ld produce both .init_array and .ctors. So we have + # to check for both. + n_init_array_ctors = 0 + have_init_array = False + n_ctors_ctors = 0 + have_ctors = False + + for line in proc.stdout: + f = line.split() + if len(f) != 11: + continue + # Don't try to int()-parse the header line for the section summaries. + if not re.match("\\[\\d+\\]", f[0]): + continue + section_name, contents, size, align = f[1], f[2], int(f[5], 16), int(f[10]) + if section_name == ".ctors" and contents == "PROGBITS": + have_ctors = True + # Subtract 2 for the uintptr_t(-1) header and the null terminator. + n_ctors_ctors = size / align - 2 + if section_name == ".init_array" and contents == "INIT_ARRAY": + have_init_array = True + n_init_array_ctors = size / align + + if have_init_array: + # Even if we have .ctors, we shouldn't have any constructors in .ctors. + # Complain if .ctors does not look how we expect it to. + if have_ctors and n_ctors_ctors != 0: + print >>sys.stderr, "Unexpected .ctors contents for", filename + sys.exit(1) + return n_init_array_ctors + if have_ctors: + return n_ctors_ctors + + # We didn't find anything; somebody switched initialization mechanisms on + # us, or the binary is completely busted. Complain either way. + print >>sys.stderr, "Couldn't find .init_array or .ctors in", filename + sys.exit(1) + +if __name__ == '__main__': + for f in sys.argv[1:]: + perfherder_data = { + "framework": {"name": "build_metrics"}, + "suites": [{ + "name": "compiler_metrics", + "subtests": [{ + "name": "num_constructors", + "value": count_ctors(f), + "alertThreshold": 0.25 + }]} + ] + } + print "PERFHERDER_DATA: %s" % json.dumps(perfherder_data) + diff --git a/build/util/hg.py b/build/util/hg.py new file mode 100644 index 000000000..c76c8ef03 --- /dev/null +++ b/build/util/hg.py @@ -0,0 +1,611 @@ +"""Functions for interacting with hg""" +import os +import re +import subprocess +from urlparse import urlsplit +from ConfigParser import RawConfigParser + +from util.commands import run_cmd, get_output, remove_path +from util.retry import retry + +import logging +log = logging.getLogger(__name__) + + +class DefaultShareBase: + pass +DefaultShareBase = DefaultShareBase() + + +class HgUtilError(Exception): + pass + + +def _make_absolute(repo): + if repo.startswith("file://"): + path = repo[len("file://"):] + repo = "file://%s" % os.path.abspath(path) + elif "://" not in repo: + repo = os.path.abspath(repo) + return repo + + +def make_hg_url(hgHost, repoPath, protocol='https', revision=None, + filename=None): + """construct a valid hg url from a base hg url (hg.mozilla.org), + repoPath, revision and possible filename""" + base = '%s://%s' % (protocol, hgHost) + repo = '/'.join(p.strip('/') for p in [base, repoPath]) + if not filename: + if not revision: + return repo + else: + return '/'.join([p.strip('/') for p in [repo, 'rev', revision]]) + else: + assert revision + return '/'.join([p.strip('/') for p in [repo, 'raw-file', revision, filename]]) + + +def get_repo_name(repo): + return repo.rstrip('/').split('/')[-1] + + +def get_repo_path(repo): + repo = _make_absolute(repo) + if repo.startswith("/"): + return repo.lstrip("/") + else: + return urlsplit(repo).path.lstrip("/") + + +def get_revision(path): + """Returns which revision directory `path` currently has checked out.""" + return get_output(['hg', 'parent', '--template', '{node|short}'], cwd=path) + + +def get_branch(path): + return get_output(['hg', 'branch'], cwd=path).strip() + + +def get_branches(path): + branches = [] + for line in get_output(['hg', 'branches', '-c'], cwd=path).splitlines(): + branches.append(line.split()[0]) + return branches + + +def is_hg_cset(rev): + """Retruns True if passed revision represents a valid HG revision + (long or short(er) 40 bit hex)""" + try: + int(rev, 16) + return True + except (TypeError, ValueError): + return False + + +def hg_ver(): + """Returns the current version of hg, as a tuple of + (major, minor, build)""" + ver_string = get_output(['hg', '-q', 'version']) + match = re.search("\(version ([0-9.]+)\)", ver_string) + if match: + bits = match.group(1).split(".") + if len(bits) < 3: + bits += (0,) + ver = tuple(int(b) for b in bits) + else: + ver = (0, 0, 0) + log.debug("Running hg version %s", ver) + return ver + + +def purge(dest): + """Purge the repository of all untracked and ignored files.""" + try: + run_cmd(['hg', '--config', 'extensions.purge=', 'purge', + '-a', '--all', dest], cwd=dest) + except subprocess.CalledProcessError, e: + log.debug('purge failed: %s' % e) + raise + + +def update(dest, branch=None, revision=None): + """Updates working copy `dest` to `branch` or `revision`. If neither is + set then the working copy will be updated to the latest revision on the + current branch. Local changes will be discarded.""" + # If we have a revision, switch to that + if revision is not None: + cmd = ['hg', 'update', '-C', '-r', revision] + run_cmd(cmd, cwd=dest) + else: + # Check & switch branch + local_branch = get_output(['hg', 'branch'], cwd=dest).strip() + + cmd = ['hg', 'update', '-C'] + + # If this is different, checkout the other branch + if branch and branch != local_branch: + cmd.append(branch) + + run_cmd(cmd, cwd=dest) + return get_revision(dest) + + +def clone(repo, dest, branch=None, revision=None, update_dest=True, + clone_by_rev=False, mirrors=None, bundles=None): + """Clones hg repo and places it at `dest`, replacing whatever else is + there. The working copy will be empty. + + If `revision` is set, only the specified revision and its ancestors will + be cloned. + + If `update_dest` is set, then `dest` will be updated to `revision` if + set, otherwise to `branch`, otherwise to the head of default. + + If `mirrors` is set, will try and clone from the mirrors before + cloning from `repo`. + + If `bundles` is set, will try and download the bundle first and + unbundle it. If successful, will pull in new revisions from mirrors or + the master repo. If unbundling fails, will fall back to doing a regular + clone from mirrors or the master repo. + + Regardless of how the repository ends up being cloned, the 'default' path + will point to `repo`. + """ + if os.path.exists(dest): + remove_path(dest) + + if bundles: + log.info("Attempting to initialize clone with bundles") + for bundle in bundles: + if os.path.exists(dest): + remove_path(dest) + init(dest) + log.info("Trying to use bundle %s", bundle) + try: + if not unbundle(bundle, dest): + remove_path(dest) + continue + adjust_paths(dest, default=repo) + # Now pull / update + return pull(repo, dest, update_dest=update_dest, + mirrors=mirrors, revision=revision, branch=branch) + except Exception: + remove_path(dest) + log.exception("Problem unbundling/pulling from %s", bundle) + continue + else: + log.info("Using bundles failed; falling back to clone") + + if mirrors: + log.info("Attempting to clone from mirrors") + for mirror in mirrors: + log.info("Cloning from %s", mirror) + try: + retval = clone(mirror, dest, branch, revision, + update_dest=update_dest, clone_by_rev=clone_by_rev) + adjust_paths(dest, default=repo) + return retval + except: + log.exception("Problem cloning from mirror %s", mirror) + continue + else: + log.info("Pulling from mirrors failed; falling back to %s", repo) + # We may have a partial repo here; mercurial() copes with that + # We need to make sure our paths are correct though + if os.path.exists(os.path.join(dest, '.hg')): + adjust_paths(dest, default=repo) + return mercurial(repo, dest, branch, revision, autoPurge=True, + update_dest=update_dest, clone_by_rev=clone_by_rev) + + cmd = ['hg', 'clone'] + if not update_dest: + cmd.append('-U') + + if clone_by_rev: + if revision: + cmd.extend(['-r', revision]) + elif branch: + # hg >= 1.6 supports -b branch for cloning + ver = hg_ver() + if ver >= (1, 6, 0): + cmd.extend(['-b', branch]) + + cmd.extend([repo, dest]) + run_cmd(cmd) + + if update_dest: + return update(dest, branch, revision) + + +def common_args(revision=None, branch=None, ssh_username=None, ssh_key=None): + """Fill in common hg arguments, encapsulating logic checks that depend on + mercurial versions and provided arguments""" + args = [] + if ssh_username or ssh_key: + opt = ['-e', 'ssh'] + if ssh_username: + opt[1] += ' -l %s' % ssh_username + if ssh_key: + opt[1] += ' -i %s' % ssh_key + args.extend(opt) + if revision: + args.extend(['-r', revision]) + elif branch: + if hg_ver() >= (1, 6, 0): + args.extend(['-b', branch]) + return args + + +def pull(repo, dest, update_dest=True, mirrors=None, **kwargs): + """Pulls changes from hg repo and places it in `dest`. + + If `update_dest` is set, then `dest` will be updated to `revision` if + set, otherwise to `branch`, otherwise to the head of default. + + If `mirrors` is set, will try and pull from the mirrors first before + `repo`.""" + + if mirrors: + for mirror in mirrors: + try: + return pull(mirror, dest, update_dest=update_dest, **kwargs) + except: + log.exception("Problem pulling from mirror %s", mirror) + continue + else: + log.info("Pulling from mirrors failed; falling back to %s", repo) + + # Convert repo to an absolute path if it's a local repository + repo = _make_absolute(repo) + cmd = ['hg', 'pull'] + # Don't pass -r to "hg pull", except when it's a valid HG revision. + # Pulling using tag names is dangerous: it uses the local .hgtags, so if + # the tag has moved on the remote side you won't pull the new revision the + # remote tag refers to. + pull_kwargs = kwargs.copy() + if 'revision' in pull_kwargs and \ + not is_hg_cset(pull_kwargs['revision']): + del pull_kwargs['revision'] + + cmd.extend(common_args(**pull_kwargs)) + + cmd.append(repo) + run_cmd(cmd, cwd=dest) + + if update_dest: + branch = None + if 'branch' in kwargs and kwargs['branch']: + branch = kwargs['branch'] + revision = None + if 'revision' in kwargs and kwargs['revision']: + revision = kwargs['revision'] + return update(dest, branch=branch, revision=revision) + +# Defines the places of attributes in the tuples returned by `out' +REVISION, BRANCH = 0, 1 + + +def out(src, remote, **kwargs): + """Check for outgoing changesets present in a repo""" + cmd = ['hg', '-q', 'out', '--template', '{node} {branches}\n'] + cmd.extend(common_args(**kwargs)) + cmd.append(remote) + if os.path.exists(src): + try: + revs = [] + for line in get_output(cmd, cwd=src).rstrip().split("\n"): + try: + rev, branch = line.split() + # Mercurial displays no branch at all if the revision is on + # "default" + except ValueError: + rev = line.rstrip() + branch = "default" + revs.append((rev, branch)) + return revs + except subprocess.CalledProcessError, inst: + # In some situations, some versions of Mercurial return "1" + # if no changes are found, so we need to ignore this return code + if inst.returncode == 1: + return [] + raise + + +def push(src, remote, push_new_branches=True, force=False, **kwargs): + cmd = ['hg', 'push'] + cmd.extend(common_args(**kwargs)) + if force: + cmd.append('-f') + if push_new_branches: + cmd.append('--new-branch') + cmd.append(remote) + run_cmd(cmd, cwd=src) + + +def mercurial(repo, dest, branch=None, revision=None, update_dest=True, + shareBase=DefaultShareBase, allowUnsharedLocalClones=False, + clone_by_rev=False, mirrors=None, bundles=None, autoPurge=False): + """Makes sure that `dest` is has `revision` or `branch` checked out from + `repo`. + + Do what it takes to make that happen, including possibly clobbering + dest. + + If allowUnsharedLocalClones is True and we're trying to use the share + extension but fail, then we will be able to clone from the shared repo to + our destination. If this is False, the default, then if we don't have the + share extension we will just clone from the remote repository. + + If `clone_by_rev` is True, use 'hg clone -r <rev>' instead of 'hg clone'. + This is slower, but useful when cloning repos with lots of heads. + + If `mirrors` is set, will try and use the mirrors before `repo`. + + If `bundles` is set, will try and download the bundle first and + unbundle it instead of doing a full clone. If successful, will pull in + new revisions from mirrors or the master repo. If unbundling fails, will + fall back to doing a regular clone from mirrors or the master repo. + """ + dest = os.path.abspath(dest) + if shareBase is DefaultShareBase: + shareBase = os.environ.get("HG_SHARE_BASE_DIR", None) + + log.info("Reporting hg version in use") + cmd = ['hg', '-q', 'version'] + run_cmd(cmd, cwd='.') + + if shareBase: + # Check that 'hg share' works + try: + log.info("Checking if share extension works") + output = get_output(['hg', 'help', 'share'], dont_log=True) + if 'no commands defined' in output: + # Share extension is enabled, but not functional + log.info("Disabling sharing since share extension doesn't seem to work (1)") + shareBase = None + elif 'unknown command' in output: + # Share extension is disabled + log.info("Disabling sharing since share extension doesn't seem to work (2)") + shareBase = None + except subprocess.CalledProcessError: + # The command failed, so disable sharing + log.info("Disabling sharing since share extension doesn't seem to work (3)") + shareBase = None + + # Check that our default path is correct + if os.path.exists(os.path.join(dest, '.hg')): + hgpath = path(dest, "default") + + # Make sure that our default path is correct + if hgpath != _make_absolute(repo): + log.info("hg path isn't correct (%s should be %s); clobbering", + hgpath, _make_absolute(repo)) + remove_path(dest) + + # If the working directory already exists and isn't using share we update + # the working directory directly from the repo, ignoring the sharing + # settings + if os.path.exists(dest): + if not os.path.exists(os.path.join(dest, ".hg")): + log.warning("%s doesn't appear to be a valid hg directory; clobbering", dest) + remove_path(dest) + elif not os.path.exists(os.path.join(dest, ".hg", "sharedpath")): + try: + if autoPurge: + purge(dest) + return pull(repo, dest, update_dest=update_dest, branch=branch, + revision=revision, + mirrors=mirrors) + except subprocess.CalledProcessError: + log.warning("Error pulling changes into %s from %s; clobbering", dest, repo) + log.debug("Exception:", exc_info=True) + remove_path(dest) + + # If that fails for any reason, and sharing is requested, we'll try to + # update the shared repository, and then update the working directory from + # that. + if shareBase: + sharedRepo = os.path.join(shareBase, get_repo_path(repo)) + dest_sharedPath = os.path.join(dest, '.hg', 'sharedpath') + + if os.path.exists(sharedRepo): + hgpath = path(sharedRepo, "default") + + # Make sure that our default path is correct + if hgpath != _make_absolute(repo): + log.info("hg path isn't correct (%s should be %s); clobbering", + hgpath, _make_absolute(repo)) + # we need to clobber both the shared checkout and the dest, + # since hgrc needs to be in both places + remove_path(sharedRepo) + remove_path(dest) + + if os.path.exists(dest_sharedPath): + # Make sure that the sharedpath points to sharedRepo + dest_sharedPath_data = os.path.normpath( + open(dest_sharedPath).read()) + norm_sharedRepo = os.path.normpath(os.path.join(sharedRepo, '.hg')) + if dest_sharedPath_data != norm_sharedRepo: + # Clobber! + log.info("We're currently shared from %s, but are being requested to pull from %s (%s); clobbering", + dest_sharedPath_data, repo, norm_sharedRepo) + remove_path(dest) + + try: + log.info("Updating shared repo") + mercurial(repo, sharedRepo, branch=branch, revision=revision, + update_dest=False, shareBase=None, clone_by_rev=clone_by_rev, + mirrors=mirrors, bundles=bundles, autoPurge=False) + if os.path.exists(dest): + if autoPurge: + purge(dest) + return update(dest, branch=branch, revision=revision) + + try: + log.info("Trying to share %s to %s", sharedRepo, dest) + return share(sharedRepo, dest, branch=branch, revision=revision) + except subprocess.CalledProcessError: + if not allowUnsharedLocalClones: + # Re-raise the exception so it gets caught below. + # We'll then clobber dest, and clone from original repo + raise + + log.warning("Error calling hg share from %s to %s;" + "falling back to normal clone from shared repo", + sharedRepo, dest) + # Do a full local clone first, and then update to the + # revision we want + # This lets us use hardlinks for the local clone if the OS + # supports it + clone(sharedRepo, dest, update_dest=False, + mirrors=mirrors, bundles=bundles) + return update(dest, branch=branch, revision=revision) + except subprocess.CalledProcessError: + log.warning( + "Error updating %s from sharedRepo (%s): ", dest, sharedRepo) + log.debug("Exception:", exc_info=True) + remove_path(dest) + # end if shareBase + + if not os.path.exists(os.path.dirname(dest)): + os.makedirs(os.path.dirname(dest)) + + # Share isn't available or has failed, clone directly from the source + return clone(repo, dest, branch, revision, + update_dest=update_dest, mirrors=mirrors, + bundles=bundles, clone_by_rev=clone_by_rev) + + +def apply_and_push(localrepo, remote, changer, max_attempts=10, + ssh_username=None, ssh_key=None, force=False): + """This function calls `changer' to make changes to the repo, and tries + its hardest to get them to the origin repo. `changer' must be a + callable object that receives two arguments: the directory of the local + repository, and the attempt number. This function will push ALL + changesets missing from remote.""" + assert callable(changer) + branch = get_branch(localrepo) + changer(localrepo, 1) + for n in range(1, max_attempts + 1): + new_revs = [] + try: + new_revs = out(src=localrepo, remote=remote, + ssh_username=ssh_username, + ssh_key=ssh_key) + if len(new_revs) < 1: + raise HgUtilError("No revs to push") + push(src=localrepo, remote=remote, ssh_username=ssh_username, + ssh_key=ssh_key, force=force) + return + except subprocess.CalledProcessError, e: + log.debug("Hit error when trying to push: %s" % str(e)) + if n == max_attempts: + log.debug("Tried %d times, giving up" % max_attempts) + for r in reversed(new_revs): + run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n', + r[REVISION]], cwd=localrepo) + raise HgUtilError("Failed to push") + pull(remote, localrepo, update_dest=False, + ssh_username=ssh_username, ssh_key=ssh_key) + # After we successfully rebase or strip away heads the push is + # is attempted again at the start of the loop + try: + run_cmd(['hg', '--config', 'ui.merge=internal:merge', + 'rebase'], cwd=localrepo) + except subprocess.CalledProcessError, e: + log.debug("Failed to rebase: %s" % str(e)) + update(localrepo, branch=branch) + for r in reversed(new_revs): + run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n', + r[REVISION]], cwd=localrepo) + changer(localrepo, n + 1) + + +def share(source, dest, branch=None, revision=None): + """Creates a new working directory in "dest" that shares history with + "source" using Mercurial's share extension""" + run_cmd(['hg', 'share', '-U', source, dest]) + return update(dest, branch=branch, revision=revision) + + +def cleanOutgoingRevs(reponame, remote, username, sshKey): + outgoingRevs = retry(out, kwargs=dict(src=reponame, remote=remote, + ssh_username=username, + ssh_key=sshKey)) + for r in reversed(outgoingRevs): + run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n', + r[REVISION]], cwd=reponame) + + +def path(src, name='default'): + """Returns the remote path associated with "name" """ + try: + return get_output(['hg', 'path', name], cwd=src).strip() + except subprocess.CalledProcessError: + return None + + +def init(dest): + """Initializes an empty repo in `dest`""" + run_cmd(['hg', 'init', dest]) + + +def unbundle(bundle, dest): + """Unbundles the bundle located at `bundle` into `dest`. + + `bundle` can be a local file or remote url.""" + try: + get_output(['hg', 'unbundle', bundle], cwd=dest, include_stderr=True) + return True + except subprocess.CalledProcessError: + return False + + +def adjust_paths(dest, **paths): + """Adjusts paths in `dest`/.hg/hgrc so that names in `paths` are set to + paths[name]. + + Note that any comments in the hgrc will be lost if changes are made to the + file.""" + hgrc = os.path.join(dest, '.hg', 'hgrc') + config = RawConfigParser() + config.read(hgrc) + + if not config.has_section('paths'): + config.add_section('paths') + + changed = False + for path_name, path_value in paths.items(): + if (not config.has_option('paths', path_name) or + config.get('paths', path_name) != path_value): + changed = True + config.set('paths', path_name, path_value) + + if changed: + config.write(open(hgrc, 'w')) + + +def commit(dest, msg, user=None): + cmd = ['hg', 'commit', '-m', msg] + if user: + cmd.extend(['-u', user]) + run_cmd(cmd, cwd=dest) + return get_revision(dest) + + +def tag(dest, tags, user=None, msg=None, rev=None, force=None): + cmd = ['hg', 'tag'] + if user: + cmd.extend(['-u', user]) + if msg: + cmd.extend(['-m', msg]) + if rev: + cmd.extend(['-r', rev]) + if force: + cmd.append('-f') + cmd.extend(tags) + run_cmd(cmd, cwd=dest) + return get_revision(dest) diff --git a/build/valgrind/__init__.py b/build/valgrind/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/valgrind/__init__.py diff --git a/build/valgrind/cross-architecture.sup b/build/valgrind/cross-architecture.sup new file mode 100644 index 000000000..1e9d7ab35 --- /dev/null +++ b/build/valgrind/cross-architecture.sup @@ -0,0 +1,124 @@ +# Full list is tracked through meta bug 793882 + +#################### +# Intended leaks # +#################### + +{ + PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793534.) + Memcheck:Leak + ... + fun:_ZL9SaveToEnvPKc + ... +} +{ + PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793549.) + Memcheck:Leak + ... + fun:_ZL13SaveWordToEnvPKcRK19nsACString_internal + ... +} +{ + PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 944133.) + Memcheck:Leak + ... + fun:_ZN13CrashReporter14SetRestartArgsEiPPc + ... +} +{ + PR_SetEnv requires its argument to be leaked, but does not appear on stacks. (See bug 793548.) + Memcheck:Leak + fun:malloc + ... + fun:_Z12ToNewCStringRK19nsACString_internal + fun:_ZN13CrashReporter14SetupExtraDataEP7nsIFileRK19nsACString_internal + ... +} +{ + We purposely leak the StatisticsReporter object + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + fun:operator new + fun:_Z21XRE_CreateStatsObjectv + ... +} + +#################################### +# Leaks in third party libraries # +#################################### + +{ + See bug 793535 + Memcheck:Leak + fun:calloc + fun:_dlerror_run + fun:dlsym + ... +} +{ + See bug 793611 + Memcheck:Leak + fun:memalign + fun:tls_get_addr_tail + fun:__tls_get_addr + ... +} +{ + See bug 793611 + Memcheck:Leak + fun:memalign + fun:tls_get_addr_tail + fun:___tls_get_addr + ... +} + +################# +# Other leaks # +################# + +{ + Bug 794369 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + ... + fun:_ZN20mozJSComponentLoader10LoadModuleERN7mozilla12FileLocationE + ... +} +{ + Bug 794370 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + ... + fun:_ZN22nsComponentManagerImpl15RegisterFactoryERK4nsIDPKcS4_P10nsIFactory + ... +} +{ + Bug 794372 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + ... + fun:_ZN22nsComponentManagerImpl22RegisterCIDEntryLockedEPKN7mozilla6Module8CIDEntryEPNS_11KnownModuleE + ... +} +{ + Bug 794374 + Memcheck:Leak + fun:malloc + fun:moz_xmalloc + ... + fun:_ZN22nsComponentManagerImpl17ManifestComponentERNS_25ManifestProcessingContextEiPKPc + ... +} +{ + Bug 1017112 + Memcheck:Leak + fun:malloc + ... + fun:PK11_InitPin + fun:_ZN11nsPK11Token12InitPasswordEPKDs + ... +} diff --git a/build/valgrind/i386-redhat-linux-gnu.sup b/build/valgrind/i386-redhat-linux-gnu.sup new file mode 100644 index 000000000..c1d39cd24 --- /dev/null +++ b/build/valgrind/i386-redhat-linux-gnu.sup @@ -0,0 +1,63 @@ +# Full list is tracked through meta bug 793882 + +#################################### +# Leaks in third party libraries # +#################################### + +{ + Bug 793537 + Memcheck:Leak + ... + obj:/usr/lib/libpango-1.0.so.0.2800.1 + ... +} +{ + Bug 793598 + Memcheck:Leak + ... + obj:/lib/libdbus-1.so.3.4.0 + ... +} +{ + Bug 793600 + Memcheck:Leak + fun:realloc + obj:/usr/lib/libfontconfig.so.1.4.4 + ... + fun:FcDefaultSubstitute + fun:_ZN17gfxPangoFontGroup11MakeFontSetEP14_PangoLanguagedP9nsAutoRefI10_FcPatternE + ... +} +{ + Bug 794366 + Memcheck:Leak + ... + obj:/usr/lib/libgtk-x11-2.0.so.0.1800.9 + ... +} +{ + Bug 794368 + Memcheck:Leak + ... + obj:/usr/lib/libXrandr.so.2.2.0 + ... +} +{ + Bug 794373 + Memcheck:Leak + ... + obj:/lib/libgobject-2.0.so.0.2200.5 + ... +} + +################################### +# Leaks in short lived precesses # +################################### + +{ + Bug 984196 + Memcheck:Leak + ... + fun:glxtest + ... +} diff --git a/build/valgrind/mach_commands.py b/build/valgrind/mach_commands.py new file mode 100644 index 000000000..ba2575247 --- /dev/null +++ b/build/valgrind/mach_commands.py @@ -0,0 +1,175 @@ +# 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 absolute_import, unicode_literals + +import logging +import os +import subprocess + +from mach.decorators import ( + Command, + CommandArgument, + CommandProvider, +) +from mozbuild.base import ( + MachCommandBase, + MachCommandConditions as conditions, +) + + +def is_valgrind_build(cls): + '''Must be a build with --enable-valgrind and --disable-jemalloc.''' + defines = cls.config_environment.defines + return 'MOZ_VALGRIND' in defines and 'MOZ_MEMORY' not in defines + + +@CommandProvider +class MachCommands(MachCommandBase): + ''' + Run Valgrind tests. + ''' + def __init__(self, context): + MachCommandBase.__init__(self, context) + + @Command('valgrind-test', category='testing', + conditions=[conditions.is_firefox, is_valgrind_build], + description='Run the Valgrind test job (memory-related errors).') + @CommandArgument('--suppressions', default=[], action='append', + metavar='FILENAME', + help='Specify a suppression file for Valgrind to use. Use ' + '--suppression multiple times to specify multiple suppression ' + 'files.') + def valgrind_test(self, suppressions): + import json + import sys + import tempfile + + from mozbuild.base import MozbuildObject + from mozfile import TemporaryDirectory + from mozhttpd import MozHttpd + from mozprofile import FirefoxProfile, Preferences + from mozprofile.permissions import ServerLocations + from mozrunner import FirefoxRunner + from mozrunner.utils import findInPath + from valgrind.output_handler import OutputHandler + + build_dir = os.path.join(self.topsrcdir, 'build') + + # XXX: currently we just use the PGO inputs for Valgrind runs. This may + # change in the future. + httpd = MozHttpd(docroot=os.path.join(build_dir, 'pgo')) + httpd.start(block=False) + + with TemporaryDirectory() as profilePath: + #TODO: refactor this into mozprofile + prefpath = os.path.join(self.topsrcdir, 'testing', 'profiles', 'prefs_general.js') + prefs = {} + prefs.update(Preferences.read_prefs(prefpath)) + interpolation = { 'server': '%s:%d' % httpd.httpd.server_address, + 'OOP': 'false'} + prefs = json.loads(json.dumps(prefs) % interpolation) + for pref in prefs: + prefs[pref] = Preferences.cast(prefs[pref]) + + quitter = os.path.join(self.topsrcdir, 'tools', 'quitter', 'quitter@mozilla.org.xpi') + + locations = ServerLocations() + locations.add_host(host='127.0.0.1', + port=httpd.httpd.server_port, + options='primary') + + profile = FirefoxProfile(profile=profilePath, + preferences=prefs, + addons=[quitter], + locations=locations) + + firefox_args = [httpd.get_url()] + + env = os.environ.copy() + env['G_SLICE'] = 'always-malloc' + env['MOZ_CC_RUN_DURING_SHUTDOWN'] = '1' + env['MOZ_CRASHREPORTER_NO_REPORT'] = '1' + env['XPCOM_DEBUG_BREAK'] = 'warn' + + env.update(self.extra_environment_variables) + + outputHandler = OutputHandler(self.log) + kp_kwargs = {'processOutputLine': [outputHandler]} + + valgrind = 'valgrind' + if not os.path.exists(valgrind): + valgrind = findInPath(valgrind) + + valgrind_args = [ + valgrind, + '--smc-check=all-non-file', + '--vex-iropt-register-updates=allregs-at-mem-access', + '--gen-suppressions=all', + '--num-callers=36', + '--leak-check=full', + '--show-possibly-lost=no', + '--track-origins=yes', + '--trace-children=yes', + '-v', # Enable verbosity to get the list of used suppressions + # Avoid excessive delays in the presence of spinlocks. + # See bug 1309851. + '--fair-sched=yes', + ] + + for s in suppressions: + valgrind_args.append('--suppressions=' + s) + + supps_dir = os.path.join(build_dir, 'valgrind') + supps_file1 = os.path.join(supps_dir, 'cross-architecture.sup') + valgrind_args.append('--suppressions=' + supps_file1) + + # MACHTYPE is an odd bash-only environment variable that doesn't + # show up in os.environ, so we have to get it another way. + machtype = subprocess.check_output(['bash', '-c', 'echo $MACHTYPE']).rstrip() + supps_file2 = os.path.join(supps_dir, machtype + '.sup') + if os.path.isfile(supps_file2): + valgrind_args.append('--suppressions=' + supps_file2) + + exitcode = None + timeout = 1800 + try: + runner = FirefoxRunner(profile=profile, + binary=self.get_binary_path(), + cmdargs=firefox_args, + env=env, + process_args=kp_kwargs) + runner.start(debug_args=valgrind_args) + exitcode = runner.wait(timeout=timeout) + + finally: + errs = outputHandler.error_count + supps = outputHandler.suppression_count + if errs != supps: + status = 1 # turns the TBPL job orange + self.log(logging.ERROR, 'valgrind-fail-parsing', + {'errs': errs, 'supps': supps}, + 'TEST-UNEXPECTED-FAIL | valgrind-test | error parsing: {errs} errors seen, but {supps} generated suppressions seen') + + elif errs == 0: + status = 0 + self.log(logging.INFO, 'valgrind-pass', {}, + 'TEST-PASS | valgrind-test | valgrind found no errors') + else: + status = 1 # turns the TBPL job orange + # We've already printed details of the errors. + + if exitcode == None: + status = 2 # turns the TBPL job red + self.log(logging.ERROR, 'valgrind-fail-timeout', + {'timeout': timeout}, + 'TEST-UNEXPECTED-FAIL | valgrind-test | Valgrind timed out (reached {timeout} second limit)') + elif exitcode != 0: + status = 2 # turns the TBPL job red + self.log(logging.ERROR, 'valgrind-fail-errors', {}, + 'TEST-UNEXPECTED-FAIL | valgrind-test | non-zero exit code from Valgrind') + + httpd.stop() + + return status diff --git a/build/valgrind/output_handler.py b/build/valgrind/output_handler.py new file mode 100644 index 000000000..df24ff28b --- /dev/null +++ b/build/valgrind/output_handler.py @@ -0,0 +1,116 @@ +# 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 logging +import re + +class OutputHandler(object): + ''' + A class for handling Valgrind output. + + Valgrind errors look like this: + + ==60741== 40 (24 direct, 16 indirect) bytes in 1 blocks are definitely lost in loss record 2,746 of 5,235 + ==60741== at 0x4C26B43: calloc (vg_replace_malloc.c:593) + ==60741== by 0x63AEF65: PR_Calloc (prmem.c:443) + ==60741== by 0x69F236E: PORT_ZAlloc_Util (secport.c:117) + ==60741== by 0x69F1336: SECITEM_AllocItem_Util (secitem.c:28) + ==60741== by 0xA04280B: ffi_call_unix64 (in /builds/slave/m-in-l64-valgrind-000000000000/objdir/toolkit/library/libxul.so) + ==60741== by 0xA042443: ffi_call (ffi64.c:485) + + For each such error, this class extracts most or all of the first (error + kind) line, plus the function name in each of the first few stack entries. + With this data it constructs and prints a TEST-UNEXPECTED-FAIL message that + TBPL will highlight. + + It buffers these lines from which text is extracted so that the + TEST-UNEXPECTED-FAIL message can be printed before the full error. + + Parsing the Valgrind output isn't ideal, and it may break in the future if + Valgrind changes the format of the messages, or introduces new error kinds. + To protect against this, we also count how many lines containing + "<insert_a_suppression_name_here>" are seen. Thanks to the use of + --gen-suppressions=yes, exactly one of these lines is present per error. If + the count of these lines doesn't match the error count found during + parsing, then the parsing has missed one or more errors and we can fail + appropriately. + ''' + + def __init__(self, logger): + # The regexps in this list match all of Valgrind's errors. Note that + # Valgrind is English-only, so we don't have to worry about + # localization. + self.logger = logger + self.re_error = \ + r'==\d+== (' + \ + r'(Use of uninitialised value of size \d+)|' + \ + r'(Conditional jump or move depends on uninitialised value\(s\))|' + \ + r'(Syscall param .* contains uninitialised byte\(s\))|' + \ + r'(Syscall param .* points to (unaddressable|uninitialised) byte\(s\))|' + \ + r'((Unaddressable|Uninitialised) byte\(s\) found during client check request)|' + \ + r'(Invalid free\(\) / delete / delete\[\] / realloc\(\))|' + \ + r'(Mismatched free\(\) / delete / delete \[\])|' + \ + r'(Invalid (read|write) of size \d+)|' + \ + r'(Jump to the invalid address stated on the next line)|' + \ + r'(Source and destination overlap in .*)|' + \ + r'(.* bytes in .* blocks are .* lost)' + \ + r')' + # Match identifer chars, plus ':' for namespaces, and '\?' in order to + # match "???" which Valgrind sometimes produces. + self.re_stack_entry = r'^==\d+==.*0x[A-Z0-9]+: ([A-Za-z0-9_:\?]+)' + self.re_suppression = r' *<insert_a_suppression_name_here>' + self.error_count = 0 + self.suppression_count = 0 + self.number_of_stack_entries_to_get = 0 + self.curr_error = None + self.curr_location = None + self.buffered_lines = None + + def log(self, line): + self.logger(logging.INFO, 'valgrind-output', {'line': line}, '{line}') + + def __call__(self, line): + if self.number_of_stack_entries_to_get == 0: + # Look for the start of a Valgrind error. + m = re.search(self.re_error, line) + if m: + self.error_count += 1 + self.number_of_stack_entries_to_get = 4 + self.curr_error = m.group(1) + self.curr_location = "" + self.buffered_lines = [line] + else: + self.log(line) + + else: + # We've recently found a Valgrind error, and are now extracting + # details from the first few stack entries. + self.buffered_lines.append(line) + m = re.match(self.re_stack_entry, line) + if m: + self.curr_location += m.group(1) + else: + self.curr_location += '?!?' + + self.number_of_stack_entries_to_get -= 1 + if self.number_of_stack_entries_to_get != 0: + self.curr_location += ' / ' + else: + # We've finished getting the first few stack entries. Print the + # failure message and the buffered lines, and then reset state. + self.logger(logging.ERROR, 'valgrind-error-msg', + {'error': self.curr_error, + 'location': self.curr_location}, + 'TEST-UNEXPECTED-FAIL | valgrind-test | {error} at {location}') + for b in self.buffered_lines: + self.log(b) + self.curr_error = None + self.curr_location = None + self.buffered_lines = None + + if re.match(self.re_suppression, line): + self.suppression_count += 1 + diff --git a/build/valgrind/valgrind.sh b/build/valgrind/valgrind.sh new file mode 100755 index 000000000..c46f18876 --- /dev/null +++ b/build/valgrind/valgrind.sh @@ -0,0 +1,45 @@ +#!/bin/bash +set -e +set -x + +srcdir=$PWD/src +objdir=${MOZ_OBJDIR-objdir} + +# If the objdir is a relative path, it is relative to the srcdir. +case "$objdir" in + /*) + ;; + *) + objdir="$srcdir/$objdir" + ;; +esac + +if [ ! -d $objdir ]; then + mkdir $objdir +fi +cd $objdir + +if [ "`uname -m`" = "x86_64" ]; then + _arch=64 +else + _arch=32 +fi + +TOOLTOOL_MANIFEST=browser/config/tooltool-manifests/linux${_arch}/releng.manifest +TOOLTOOL_SERVER=https://api.pub.build.mozilla.org/tooltool/ +(cd $srcdir; python /builds/tooltool.py --url $TOOLTOOL_SERVER --overwrite -m $TOOLTOOL_MANIFEST fetch ${TOOLTOOL_CACHE:+ -c ${TOOLTOOL_CACHE}}) || exit 2 + +# Note: an exit code of 2 turns the job red on TBPL. +MOZCONFIG=$srcdir/browser/config/mozconfigs/linux${_arch}/valgrind make -f $srcdir/client.mk configure || exit 2 +make -j4 || exit 2 +make package || exit 2 + +# We need to set MOZBUILD_STATE_PATH so that |mach| skips its first-run +# initialization step and actually runs the |valgrind-test| command. +export MOZBUILD_STATE_PATH=. + +# |mach valgrind-test|'s exit code will be 1 (which turns the job orange on +# TBPL) if Valgrind finds errors, and 2 (which turns the job red) if something +# else goes wrong, such as Valgrind crashing. +python2.7 $srcdir/mach valgrind-test +exit $? diff --git a/build/valgrind/x86_64-redhat-linux-gnu.sup b/build/valgrind/x86_64-redhat-linux-gnu.sup new file mode 100644 index 000000000..d253a3a4e --- /dev/null +++ b/build/valgrind/x86_64-redhat-linux-gnu.sup @@ -0,0 +1,210 @@ +# Full list is tracked through meta bug 793882 + +#################################### +# Leaks in third party libraries # +#################################### + +{ + Bug 793537 + Memcheck:Leak + ... + obj:/usr/lib64/libpango-1.0.so.0.2800.1 + ... +} +{ + Bug 793598 + Memcheck:Leak + ... + obj:/lib64/libdbus-1.so.3.4.0 + ... +} +{ + Bug 793600 + Memcheck:Leak + fun:realloc + obj:/usr/lib64/libfontconfig.so.1.4.4 + ... + fun:FcDefaultSubstitute + fun:_ZN17gfxPangoFontGroup11MakeFontSetEP14_PangoLanguagedP9nsAutoRefI10_FcPatternE + ... +} +# Fontconfig is going fancy with its cache structure and that confuses valgrind. +# https://bugs.freedesktop.org/show_bug.cgi?id=8215 +# https://bugs.freedesktop.org/show_bug.cgi?id=8428 +{ + Bug 1187649 + Memcheck:Leak + match-leak-kinds: definite + fun:realloc + fun:FcPatternObjectInsertElt + ... +} +# With older versions of fontconfig (e.g. 2.8.0 on taskcluster systems), +# there's an uninitialized memory usage and leak when loading app fonts. +{ + Bug 1231701 + Memcheck:Param + write(buf) + ... + fun:FcDirCacheWrite + fun:FcDirCacheScan + fun:FcConfigAddDirList + fun:FcConfigAppFontAddDir + ... +} +{ + Bug 1231701 + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:FcDirScanConfig + fun:FcDirCacheScan + fun:FcConfigAddDirList + fun:FcConfigAppFontAddDir + ... +} +# Leaks due to either Gtk+3 or cairo, but Gecko is not directly involved with +# those cairo interactions. One suspected cause is Gecko not closing the +# display to work around a bug in old Gtk+3 versions. See also bug 1228724. +{ + Bug 1187649 + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:_cairo_freelist_alloc + fun:_cairo_xlib_display_queue_resource + fun:_cairo_xlib_surface_finish + ... +} +# The following leak is deep in Gtk+3, and it doesn't seem we're doing +# anything wrong on our end with the container objects. This suppression +# is purposefully verbose so as to avoid catching actual leaks due to +# Gecko code. +# Note: valgrind doesn't support more than 24 elements in a suppression stack. +{ + Bug 1187649 + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_list_prepend + fun:gtk_combo_box_get_path_for_child + fun:gtk_container_get_path_for_child + fun:gtk_widget_get_path + fun:_gtk_widget_update_path + fun:reset_style_recurse + fun:gtk_widget_reset_style + fun:gtk_widget_set_parent + fun:gtk_combo_box_add + fun:g_cclosure_marshal_VOID__OBJECTv + fun:_g_closure_invoke_va + fun:g_signal_emit_valist + fun:g_signal_emit + fun:gtk_combo_box_constructor + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + fun:_ZN13nsLookAndFeel4InitEv + ... +} +# set_color() in gtkstyle.c of GTK version 3.4.4 only can leak GdkRGBA +# allocations when the theme has transparent colors: +# https://git.gnome.org/browse/gtk+/tree/gtk/deprecated/gtkstyle.c?h=3.4.4#n676 +{ + Bug 1250704 + Memcheck:Leak + match-leak-kinds: definite + fun:malloc + fun:g_malloc + fun:g_slice_alloc + fun:g_slice_copy + fun:boxed_proxy_lcopy_value + fun:gtk_style_context_get_valist + fun:gtk_style_context_get + fun:set_color + fun:gtk_style_update_from_context + fun:gtk_style_constructed + fun:g_object_newv + fun:g_object_new_valist + fun:g_object_new + ... +} +{ + Bug 794366 + Memcheck:Leak + ... + obj:/usr/lib64/libgtk-x11-2.0.so.0.1800.9 + ... +} +{ + Bug 794368 + Memcheck:Leak + ... + obj:/usr/lib64/libXrandr.so.2.2.0 + ... +} +{ + Bug 794373 + Memcheck:Leak + ... + obj:/lib64/libgobject-2.0.so.0.2200.5 + ... +} +{ + Bug 966673 + Memcheck:Leak + fun:malloc + obj:/lib64/libresolv-2.12.so + ... + fun:gaih_inet + fun:getaddrinfo + fun:PR_GetAddrInfoByName + ... +} +{ + Bug 979242 + Memcheck:Leak + fun:calloc + fun:xcb_connect_to_fd + fun:xcb_connect_to_display_with_auth_info + fun:_XConnectXCB + fun:XOpenDisplay + fun:gdk_display_open + ... +} + +################################### +# Leaks in short lived precesses # +################################### + +{ + Bug 984196 + Memcheck:Leak + ... + fun:glxtest + ... +} + +######################################### +# Uninitialised value false positives # +######################################### + +# This concerns a false positive pertaining to Memcheck's overly- +# conservative instrumentation of CPUID. See bug 1288618 comments +# 119 through 127. +{ + Bug 1288618 comments 119 through 127 + Memcheck:Cond + fun:_ZN6SkOptsL4initEv + fun:sk_once_no_arg_adaptor +} + +{ + Bug 1288618 comments 119 through 127 part 2 + Memcheck:Cond + fun:__get_cpuid + fun:cpuid + fun:_ZN6SkOptsL4initEv + fun:sk_once_no_arg_adaptor +} diff --git a/build/variables.py b/build/variables.py new file mode 100644 index 000000000..7761e6096 --- /dev/null +++ b/build/variables.py @@ -0,0 +1,106 @@ +# 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 os +import subprocess +import sys +from datetime import datetime + +SOURCESTAMP_FILENAME = 'sourcestamp.txt' + +def buildid_header(output): + buildid = os.environ.get('MOZ_BUILD_DATE') + if buildid and len(buildid) != 14: + print('Ignoring invalid MOZ_BUILD_DATE: %s' % buildid, file=sys.stderr) + buildid = None + if not buildid: + buildid = datetime.now().strftime('%Y%m%d%H%M%S') + output.write("#define MOZ_BUILDID %s\n" % buildid) + + +def get_program_output(*command): + try: + with open(os.devnull) as stderr: + return subprocess.check_output(command, stderr=stderr) + except: + return '' + + +def get_hg_info(workdir): + repo = get_program_output('hg', '-R', workdir, 'path', 'default') + if repo: + repo = repo.strip() + if repo.startswith('ssh://'): + repo = 'https://' + repo[6:] + repo = repo.rstrip('/') + + changeset = get_hg_changeset(workdir) + + return repo, changeset + + +def get_hg_changeset(path): + return get_program_output('hg', '-R', path, 'parent', '--template={node}') + +def get_info_from_sourcestamp(sourcestamp_path): + """Read the repository and changelog information from the sourcestamp + file. This assumes that the file exists and returns the results as a list + (either strings or None in case of error). + """ + + # Load the content of the file. + lines = None + with open(sourcestamp_path) as f: + lines = f.read().splitlines() + + # Parse the repo and the changeset. The sourcestamp file is supposed to + # contain two lines: the first is the build id and the second is the source + # URL. + if len(lines) != 2 or not lines[1].startswith('http'): + # Just return if the file doesn't contain what we expect. + return None, None + + # Return the repo and the changeset. + return lines[1].split('/rev/') + +def source_repo_header(output): + # We allow the source repo and changeset to be specified via the + # environment (see configure) + import buildconfig + repo = buildconfig.substs.get('MOZ_SOURCE_REPO') + changeset = buildconfig.substs.get('MOZ_SOURCE_CHANGESET') + source = '' + + if not repo: + sourcestamp_path = os.path.join(buildconfig.topsrcdir, SOURCESTAMP_FILENAME) + if os.path.exists(os.path.join(buildconfig.topsrcdir, '.hg')): + repo, changeset = get_hg_info(buildconfig.topsrcdir) + elif os.path.exists(sourcestamp_path): + repo, changeset = get_info_from_sourcestamp(sourcestamp_path) + elif not changeset: + changeset = get_hg_changeset(buildconfig.topsrcdir) + if not changeset: + raise Exception('could not resolve changeset; ' + 'try setting MOZ_SOURCE_CHANGESET') + + if changeset: + output.write('#define MOZ_SOURCE_STAMP %s\n' % changeset) + + if repo and buildconfig.substs.get('MOZ_INCLUDE_SOURCE_INFO'): + source = '%s/rev/%s' % (repo, changeset) + output.write('#define MOZ_SOURCE_REPO %s\n' % repo) + output.write('#define MOZ_SOURCE_URL %s\n' % source) + + +def main(args): + if (len(args)): + func = globals().get(args[0]) + if func: + return func(sys.stdout, *args[1:]) + + +if __name__ == '__main__': + sys.exit(main(sys.argv[1:])) diff --git a/build/virtualenv_packages.txt b/build/virtualenv_packages.txt new file mode 100644 index 000000000..0c4c50236 --- /dev/null +++ b/build/virtualenv_packages.txt @@ -0,0 +1,42 @@ +altgraph.pth:python/altgraph +marionette_driver.pth:testing/marionette/client +marionette_harness.pth:testing/marionette/harness +browsermobproxy.pth:testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py +wptserve.pth:testing/web-platform/tests/tools/wptserve +blessings.pth:python/blessings +configobj.pth:python/configobj +jsmin.pth:python/jsmin +mach.pth:python/mach +mozbuild.pth:python/mozbuild +mozversioncontrol.pth:python/mozversioncontrol +mozlint.pth:python/mozlint +pymake.pth:build/pymake +optional:setup.py:python/psutil:build_ext:--inplace +optional:psutil.pth:python/psutil +which.pth:python/which +ply.pth:other-licenses/ply/ +macholib.pth:python/macholib +mock.pth:python/mock-1.0.0 +py.pth:python/py +pytest.pth:python/pytest +mozilla.pth:build +mozilla.pth:config +mozilla.pth:xpcom/typelib/xpt/tools +mozilla.pth:dom/bindings +mozilla.pth:dom/bindings/parser +mozilla.pth:layout/tools/reftest +moztreedocs.pth:tools/docs +packages.txt:testing/mozbase/packages.txt +objdir:build +gyp.pth:media/webrtc/trunk/tools/gyp/pylib +pyasn1.pth:python/pyasn1 +pyasn1_modules.pth:python/pyasn1-modules +bitstring.pth:python/bitstring +redo.pth:python/redo +requests.pth:python/requests +rsa.pth:python/rsa +futures.pth:python/futures +ecc.pth:python/PyECC +xpcshell.pth:testing/xpcshell +pyyaml.pth:python/pyyaml/lib +pytoml.pth:python/pytoml diff --git a/build/win32/Makefile.in b/build/win32/Makefile.in new file mode 100644 index 000000000..c96099d8f --- /dev/null +++ b/build/win32/Makefile.in @@ -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/. + +include $(topsrcdir)/config/rules.mk + +# run the binscope tool to make sure the binary and all libraries +# are using all available Windows OS-level security mechanisms +# Don't do this in clang-cl since it doesn't support debug information yet. +ifndef CLANG_CL +check:: + $(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/$(MOZ_APP_NAME)$(BIN_SUFFIX) $(DIST)/crashreporter-symbols/ + $(PYTHON) $(srcdir)/autobinscope.py $(DIST)/bin/plugin-container.exe $(DIST)/crashreporter-symbols/ +endif diff --git a/build/win32/__init__.py b/build/win32/__init__.py new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/build/win32/__init__.py diff --git a/build/win32/autobinscope.py b/build/win32/autobinscope.py new file mode 100644 index 000000000..fdba68e0c --- /dev/null +++ b/build/win32/autobinscope.py @@ -0,0 +1,75 @@ +#!/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/. + +# run Microsoft's Binscope tool (http://www.microsoft.com/download/en/details.aspx?id=11910) +# against a fresh Windows build. output a 'binscope.log' file with full details +# of the run and appropriate strings to integrate with the buildbots + +# from the docs : "The error code returned when running under the command line is equal +# to the number of failures the tool reported plus the number of errors. BinScope will return +# 0 only if there are no errors or failures." + +# the symbol dir should point to the symbol dir hierarchy created +# via running make buildsymbols in a windows build's objdir + +import sys +import subprocess +import os + +BINSCOPE_OUTPUT_LOGFILE = r".\binscope_xml_output.log" + +# usage +if len(sys.argv) < 3: + print """usage : autobinscope.by path_to_binary path_to_symbols [log_file_path]" + log_file_path is optional, log will be written to .\binscope_xml_output.log by default""" + sys.exit(0) + +binary_path = sys.argv[1] +symbol_path = sys.argv[2] + +if len(sys.argv) == 4: + log_file_path = sys.argv[3] +else: + log_file_path = BINSCOPE_OUTPUT_LOGFILE + +# execute binscope against the binary, using the BINSCOPE environment +# variable as the path to binscope.exe +try: + binscope_path = os.environ['BINSCOPE'] +except KeyError: + print "BINSCOPE environment variable is not set, can't check DEP/ASLR etc. status." + sys.exit(0) + +try: + proc = subprocess.Popen([binscope_path, "/target", binary_path, + "/output", log_file_path, "/sympath", symbol_path, + "/c", "ATLVersionCheck", "/c", "ATLVulnCheck", "/c", "SharedSectionCheck", "/c", "APTCACheck", "/c", "NXCheck", + "/c", "GSCheck", "/c", "GSFriendlyInitCheck", + "/c", "CompilerVersionCheck", "/c", "SafeSEHCheck", "/c", "SNCheck", + "/c", "DBCheck"], stdout=subprocess.PIPE) + +except WindowsError, (errno, strerror): + if errno != 2 and errno != 3: + print "Unexpected error ! \nError " + str(errno) + " : " + strerror + "\nExiting !\n" + sys.exit(0) + else: + print "Could not locate binscope at location : %s\n" % binscope_path + print "Binscope wasn't installed or the BINSCOPE env variable wasn't set correctly, skipping this check and exiting..." + sys.exit(0) + +proc.wait() + +output = proc.communicate()[0] + +# is this a PASS or a FAIL ? +if proc.returncode != 0: + print "Error count: %d" % proc.returncode + print "TEST-UNEXPECTED-FAIL | autobinscope.py | %s is missing a needed Windows protection, such as /GS or ASLR" % binary_path + logfile = open(log_file_path, "r") + for line in logfile: + print(line), +else: + print "TEST-PASS | autobinscope.py | %s succeeded" % binary_path diff --git a/build/win32/crashinject.cpp b/build/win32/crashinject.cpp new file mode 100644 index 000000000..472e15a12 --- /dev/null +++ b/build/win32/crashinject.cpp @@ -0,0 +1,96 @@ +/* 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/. */ + +/* + * Given a PID, this program attempts to inject a DLL into the process + * with that PID. The DLL it attempts to inject, "crashinjectdll.dll", + * must exist alongside this exe. The DLL will then crash the process. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <windows.h> + +int main(int argc, char** argv) +{ + if (argc != 2) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + int pid = atoi(argv[1]); + if (pid <= 0) { + fprintf(stderr, "Usage: crashinject <PID>\n"); + return 1; + } + + // find our DLL to inject + wchar_t filename[_MAX_PATH]; + if (GetModuleFileNameW(nullptr, filename, + sizeof(filename) / sizeof(wchar_t)) == 0) + return 1; + + wchar_t* slash = wcsrchr(filename, L'\\'); + if (slash == nullptr) + return 1; + + slash++; + wcscpy(slash, L"crashinjectdll.dll"); + + // now find our target process + HANDLE targetProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_WRITE | PROCESS_CREATE_THREAD | PROCESS_QUERY_INFORMATION, + FALSE, + pid); + if (targetProc == nullptr) { + fprintf(stderr, "Error %d opening target process\n", GetLastError()); + return 1; + } + + /* + * This is sort of insane, but we're implementing a technique described here: + * http://www.codeproject.com/KB/threads/winspy.aspx#section_2 + * + * The gist is to use CreateRemoteThread to create a thread in the other + * process, but cheat and make the thread function kernel32!LoadLibrary, + * so that the only remote data we have to pass to the other process + * is the path to the library we want to load. The library we're loading + * will then do its dirty work inside the other process. + */ + HMODULE hKernel32 = GetModuleHandleW(L"Kernel32"); + // allocate some memory to hold the path in the remote process + void* pLibRemote = VirtualAllocEx(targetProc, nullptr, sizeof(filename), + MEM_COMMIT, PAGE_READWRITE); + if (pLibRemote == nullptr) { + fprintf(stderr, "Error %d in VirtualAllocEx\n", GetLastError()); + CloseHandle(targetProc); + return 1; + } + + if (!WriteProcessMemory(targetProc, pLibRemote, (void*)filename, + sizeof(filename), nullptr)) { + fprintf(stderr, "Error %d in WriteProcessMemory\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + // Now create a thread in the target process that will load our DLL + HANDLE hThread = CreateRemoteThread( + targetProc, nullptr, 0, + (LPTHREAD_START_ROUTINE)GetProcAddress(hKernel32, + "LoadLibraryW"), + pLibRemote, 0, nullptr); + if (hThread == nullptr) { + fprintf(stderr, "Error %d in CreateRemoteThread\n", GetLastError()); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + return 1; + } + WaitForSingleObject(hThread, INFINITE); + // Cleanup, not that it's going to matter at this point + CloseHandle(hThread); + VirtualFreeEx(targetProc, pLibRemote, sizeof(filename), MEM_RELEASE); + CloseHandle(targetProc); + + return 0; +} diff --git a/build/win32/crashinjectdll/crashinjectdll.cpp b/build/win32/crashinjectdll/crashinjectdll.cpp new file mode 100644 index 000000000..09ab9fdba --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.cpp @@ -0,0 +1,38 @@ +/* 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 <stdio.h> +#include <windows.h> + +// make sure we only ever spawn one thread +DWORD tid = -1; + +DWORD WINAPI CrashingThread( + LPVOID lpParameter +) +{ + // not a very friendly DLL + volatile int* x = (int *)0x0; + *x = 1; + return 0; +} + +BOOL WINAPI DllMain( + HANDLE hinstDLL, + DWORD dwReason, + LPVOID lpvReserved +) +{ + if (tid == -1) + // we have to crash on another thread because LoadLibrary() will + // catch memory access errors and return failure to the calling process + CreateThread( + nullptr, // default security attributes + 0, // use default stack size + CrashingThread, // thread function name + nullptr, // argument to thread function + 0, // use default creation flags + &tid); // returns the thread identifier + return TRUE; +} diff --git a/build/win32/crashinjectdll/crashinjectdll.def b/build/win32/crashinjectdll/crashinjectdll.def new file mode 100644 index 000000000..d1ea5602d --- /dev/null +++ b/build/win32/crashinjectdll/crashinjectdll.def @@ -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/. + +LIBRARY crashinjectdll +EXPORTS + DllMain diff --git a/build/win32/crashinjectdll/moz.build b/build/win32/crashinjectdll/moz.build new file mode 100644 index 000000000..06817aa96 --- /dev/null +++ b/build/win32/crashinjectdll/moz.build @@ -0,0 +1,15 @@ +# -*- Mode: python; 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/. + +SOURCES += [ + 'crashinjectdll.cpp', +] + +SharedLibrary('crashinjectdll') + +DEFFILE = SRCDIR + '/crashinjectdll.def' + +USE_STATIC_LIBS = True diff --git a/build/win32/dumpenv4python.pl b/build/win32/dumpenv4python.pl new file mode 100644 index 000000000..34a1135a1 --- /dev/null +++ b/build/win32/dumpenv4python.pl @@ -0,0 +1,19 @@ +# 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/. + +# See build/autoconf/hooks.m4 + +use Data::Dumper; + +$Data::Dumper::Terse = 1; +$Data::Dumper::Indent = 0; + +# We can't use perl hashes because Mozilla-Build's perl is 5.6, and perl +# 5.6's Data::Dumper doesn't have Pair to change ' => ' into ' : '. +@data = ( + ['env', [map { [$_, $ENV{$_}] } keys %ENV]], + ['args', \@ARGV], +); + +print Dumper \@data; diff --git a/build/win32/moz.build b/build/win32/moz.build new file mode 100644 index 000000000..cd5110ac6 --- /dev/null +++ b/build/win32/moz.build @@ -0,0 +1,28 @@ +# -*- Mode: python; 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/. + +TEST_DIRS += ['crashinjectdll'] + +if CONFIG['ENABLE_TESTS']: + Program('crashinject') + SOURCES += [ + 'crashinject.cpp', + ] + USE_STATIC_LIBS = True + +NO_PGO = True + +if CONFIG['WIN32_REDIST_DIR'] and CONFIG['COMPILE_ENVIRONMENT']: + for f in ['MSVC_C_RUNTIME_DLL', 'MSVC_CXX_RUNTIME_DLL']: + FINAL_TARGET_FILES += [ + '%%%s/%s' % (CONFIG['WIN32_REDIST_DIR'], CONFIG[f]) + ] + +if CONFIG['WIN_UCRT_REDIST_DIR'] and CONFIG['COMPILE_ENVIRONMENT']: + for f in ['api-ms-win-*.dll', 'ucrtbase.dll']: + FINAL_TARGET_FILES += [ + '%%%s/%s' % (CONFIG['WIN_UCRT_REDIST_DIR'], f) + ] diff --git a/build/win32/mozconfig.vs-latest b/build/win32/mozconfig.vs-latest new file mode 100644 index 000000000..9c8726a8d --- /dev/null +++ b/build/win32/mozconfig.vs-latest @@ -0,0 +1 @@ +. $topsrcdir/build/win32/mozconfig.vs2015-win64 diff --git a/build/win32/mozconfig.vs2015-win64 b/build/win32/mozconfig.vs2015-win64 new file mode 100644 index 000000000..b81afa681 --- /dev/null +++ b/build/win32/mozconfig.vs2015-win64 @@ -0,0 +1,25 @@ +if [ -z "${VSPATH}" ]; then + TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} + VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u3" +fi + +VSWINPATH="$(cd ${VSPATH} && pwd -W)" + +export WINDOWSSDKDIR="${VSWINPATH}/SDK" +export WIN32_REDIST_DIR="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT" +export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x86" + +export PATH="${VSPATH}/VC/bin/amd64_x86:${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x86:${VSPATH}/SDK/bin/x64:${VSPATH}/DIA SDK/bin:${PATH}" +export PATH="${VSPATH}/VC/redist/x86/Microsoft.VC140.CRT:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x86:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${PATH}" + +export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.14393.0/ucrt:${VSPATH}/SDK/Include/10.0.14393.0/shared:${VSPATH}/SDK/Include/10.0.14393.0/um:${VSPATH}/SDK/Include/10.0.14393.0/winrt:${VSPATH}/DIA SDK/include" +export LIB="${VSPATH}/VC/lib:${VSPATH}/VC/atlmfc/lib:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x86:${VSPATH}/SDK/lib/10.0.14393.0/um/x86:${VSPATH}/DIA SDK/lib" + +. $topsrcdir/build/mozconfig.vs-common + +mk_export_correct_style WINDOWSSDKDIR +mk_export_correct_style INCLUDE +mk_export_correct_style LIB +mk_export_correct_style PATH +mk_export_correct_style WIN32_REDIST_DIR +mk_export_correct_style WIN_UCRT_REDIST_DIR diff --git a/build/win32/pgomerge.py b/build/win32/pgomerge.py new file mode 100755 index 000000000..313d66870 --- /dev/null +++ b/build/win32/pgomerge.py @@ -0,0 +1,44 @@ +#!/usr/bin/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/. + +# Usage: pgomerge.py <binary basename> <dist/bin> +# Gathers .pgc files from dist/bin and merges them into +# $PWD/$basename.pgd using pgomgr, then deletes them. +# No errors if any of these files don't exist. + +import sys, os, os.path, subprocess +if not sys.platform == "win32": + raise Exception("This script was only meant for Windows.") + +def MergePGOFiles(basename, pgddir, pgcdir): + """Merge pgc files produced from an instrumented binary + into the pgd file for the second pass of profile-guided optimization + with MSVC. |basename| is the name of the DLL or EXE without the + extension. |pgddir| is the path that contains <basename>.pgd + (should be the objdir it was built in). |pgcdir| is the path + containing basename!N.pgc files, which is probably dist/bin. + Calls pgomgr to merge each pgc file into the pgd, then deletes + the pgc files.""" + if not os.path.isdir(pgddir) or not os.path.isdir(pgcdir): + return + pgdfile = os.path.abspath(os.path.join(pgddir, basename + ".pgd")) + if not os.path.isfile(pgdfile): + return + for file in os.listdir(pgcdir): + if file.startswith(basename+"!") and file.endswith(".pgc"): + try: + pgcfile = os.path.normpath(os.path.join(pgcdir, file)) + subprocess.call(['pgomgr', '-merge', + pgcfile, + pgdfile]) + os.remove(pgcfile) + except OSError: + pass + +if __name__ == '__main__': + if len(sys.argv) != 3: + print >>sys.stderr, "Usage: pgomerge.py <binary basename> <dist/bin>" + sys.exit(1) + MergePGOFiles(sys.argv[1], os.getcwd(), sys.argv[2]) diff --git a/build/win32/procmem.py b/build/win32/procmem.py new file mode 100644 index 000000000..8376d07ea --- /dev/null +++ b/build/win32/procmem.py @@ -0,0 +1,48 @@ +# 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 os, sys, ctypes, ctypes.wintypes + +class VM_COUNTERS(ctypes.Structure): + _fields_ = [("PeakVirtualSize", ctypes.wintypes.ULONG), + ("VirtualSize", ctypes.wintypes.ULONG), + ("PageFaultCount", ctypes.wintypes.ULONG), + ("PeakWorkingSetSize", ctypes.wintypes.ULONG), + ("WorkingSetSize", ctypes.wintypes.ULONG), + ("QuotaPeakPagedPoolUsage", ctypes.wintypes.ULONG), + ("QuotaPagedPoolUsage", ctypes.wintypes.ULONG), + ("QuotaPeakNonPagedPoolUsage", ctypes.wintypes.ULONG), + ("QuotaNonPagedPoolUsage", ctypes.wintypes.ULONG), + ("PagefileUsage", ctypes.wintypes.ULONG), + ("PeakPagefileUsage", ctypes.wintypes.ULONG) + ] + +def get_vmsize(handle): + """ + Return (peak_virtual_size, virtual_size) for the process |handle|. + """ + ProcessVmCounters = 3 + vmc = VM_COUNTERS() + if ctypes.windll.ntdll.NtQueryInformationProcess(int(handle), + ProcessVmCounters, + ctypes.byref(vmc), + ctypes.sizeof(vmc), + None) == 0: + return (vmc.PeakVirtualSize, vmc.VirtualSize) + + return (-1, -1) + +if __name__ == '__main__': + PROCESS_QUERY_INFORMATION = 0x0400 + for pid in sys.argv[1:]: + handle = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, + 0, # no inherit + int(pid)) + if handle: + print "Process %s:" % pid + vsize, peak_vsize = get_vmsize(handle) + print "peak vsize: %d" % peak_vsize + ctypes.windll.kernel32.CloseHandle(handle) + else: + print "Couldn't open process %s" % pid diff --git a/build/win64/mozconfig.vs-latest b/build/win64/mozconfig.vs-latest new file mode 100644 index 000000000..3470d4ace --- /dev/null +++ b/build/win64/mozconfig.vs-latest @@ -0,0 +1 @@ +. $topsrcdir/build/win64/mozconfig.vs2015 diff --git a/build/win64/mozconfig.vs2015 b/build/win64/mozconfig.vs2015 new file mode 100644 index 000000000..e81a00064 --- /dev/null +++ b/build/win64/mozconfig.vs2015 @@ -0,0 +1,24 @@ +if [ -z "${VSPATH}" ]; then + TOOLTOOL_DIR=${TOOLTOOL_DIR:-$topsrcdir} + VSPATH="$(cd ${TOOLTOOL_DIR} && pwd)/vs2015u3" +fi + +VSWINPATH="$(cd ${VSPATH} && pwd -W)" + +export WINDOWSSDKDIR="${VSWINPATH}/SDK" +export WIN32_REDIST_DIR=${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT +export WIN_UCRT_REDIST_DIR="${VSPATH}/SDK/Redist/ucrt/DLLs/x64" + +export PATH="${VSPATH}/VC/bin/amd64:${VSPATH}/VC/bin:${VSPATH}/SDK/bin/x64:${VSPATH}/VC/redist/x64/Microsoft.VC140.CRT:${VSPATH}/SDK/Redist/ucrt/DLLs/x64:${VSPATH}/DIA SDK/bin/amd64:${PATH}" + +export INCLUDE="${VSPATH}/VC/include:${VSPATH}/VC/atlmfc/include:${VSPATH}/SDK/Include/10.0.14393.0/ucrt:${VSPATH}/SDK/Include/10.0.14393.0/shared:${VSPATH}/SDK/Include/10.0.14393.0/um:${VSPATH}/SDK/Include/10.0.14393.0/winrt:${VSPATH}/DIA SDK/include" +export LIB="${VSPATH}/VC/lib/amd64:${VSPATH}/VC/atlmfc/lib/amd64:${VSPATH}/SDK/lib/10.0.14393.0/ucrt/x64:${VSPATH}/SDK/lib/10.0.14393.0/um/x64:${VSPATH}/DIA SDK/lib/amd64" + +. $topsrcdir/build/mozconfig.vs-common + +mk_export_correct_style WINDOWSSDKDIR +mk_export_correct_style INCLUDE +mk_export_correct_style LIB +mk_export_correct_style PATH +mk_export_correct_style WIN32_REDIST_DIR +mk_export_correct_style WIN_UCRT_REDIST_DIR diff --git a/build/windows_toolchain.py b/build/windows_toolchain.py new file mode 100644 index 000000000..22e656eff --- /dev/null +++ b/build/windows_toolchain.py @@ -0,0 +1,250 @@ +#!/usr/bin/env python2.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/. + +# This script is used to create and manipulate archives containing +# files necessary to build Firefox on Windows (referred to as the +# "Windows toolchain"). +# +# When updating behavior of this script, remember to update the docs +# in ``build/docs/toolchains.rst``. + +from __future__ import absolute_import, unicode_literals + +import hashlib +import os +import sys + +from mozpack.files import ( + FileFinder, +) +from mozpack.mozjar import ( + JarWriter, +) +import mozpack.path as mozpath + + +# mozpack.match patterns for files under "Microsoft Visual Studio 14.0". +VS_PATTERNS = [ + { + 'pattern': 'DIA SDK/bin/**', + 'ignore': ( + 'DIA SDK/bin/arm/**', + ), + }, + { + 'pattern': 'DIA SDK/idl/**', + }, + { + 'pattern': 'DIA SDK/include/**', + }, + { + 'pattern': 'DIA SDK/lib/**', + 'ignore': ( + 'DIA SDK/lib/arm/**', + ), + }, + # ATL is needed by Breakpad. + { + 'pattern': 'VC/atlmfc/include/**', + }, + { + 'pattern': 'VC/atlmfc/lib/atls.*', + }, + { + 'pattern': 'VC/atlmfc/lib/amd64/atls.*', + }, + { + 'pattern': 'VC/bin/**', + # We only care about compiling on amd64 for amd64 or x86 targets. + 'ignore': ( + 'VC/bin/amd64_arm/**', + 'VC/bin/arm/**', + 'VC/bin/x86_arm/**', + 'VC/bin/x86_amd64/**', + ), + }, + { + 'pattern': 'VC/include/**', + }, + { + 'pattern': 'VC/lib/**', + 'ignore': ( + 'VC/lib/arm/**', + 'VC/lib/onecore/**', + 'VC/lib/store/**', + ), + }, + { + 'pattern': 'VC/redist/x64/Microsoft.VC140.CRT/**', + }, + { + 'pattern': 'VC/redist/x86/Microsoft.VC140.CRT/**', + }, +] + +SDK_RELEASE = '10.0.14393.0' + +# Files from the Windows 10 SDK to install. +SDK_PATTERNS = [ + { + 'pattern': 'bin/x64/**', + }, + { + 'pattern': 'Include/%s/**' % SDK_RELEASE, + }, + { + 'pattern': 'Lib/%s/ucrt/x64/**' % SDK_RELEASE, + }, + { + 'pattern': 'Lib/%s/ucrt/x86/**' % SDK_RELEASE, + }, + { + 'pattern': 'Lib/%s/um/x64/**' % SDK_RELEASE, + }, + { + 'pattern': 'Lib/%s/um/x86/**' % SDK_RELEASE, + }, + { + 'pattern': 'Redist/D3D/**', + }, + { + 'pattern': 'Redist/ucrt/DLLs/x64/**', + }, + { + 'pattern': 'Redist/ucrt/DLLs/x86/**', + }, +] + + +def find_vs_paths(): + """Resolve source locations of files. + + Returns a 2-tuple of (Visual Studio Path, SDK Path). + """ + pf = os.environ.get('ProgramFiles(x86)') + if not pf: + raise Exception('No "ProgramFiles(x86)" environment variable. ' + 'Not running on 64-bit Windows?') + + vs_path = os.path.join(pf, 'Microsoft Visual Studio 14.0') + if not os.path.exists(vs_path): + raise Exception('%s does not exist; Visual Studio 2015 not installed?' % + vs_path) + + sdk_path = os.path.join(pf, 'Windows Kits', '10') + if not os.path.exists(sdk_path): + raise Exception('%s does not exist; Windows 10 SDK not installed?' % + sdk_path) + + return vs_path, sdk_path + + +def resolve_files(): + """Resolve the files that constitute a standalone toolchain. + + This is a generator of (dest path, file) where the destination + path is relative and the file instance is a BaseFile from mozpack. + """ + vs_path, sdk_path = find_vs_paths() + + for entry in VS_PATTERNS: + finder = FileFinder(vs_path, find_executables=False, + ignore=entry.get('ignore', [])) + for p, f in finder.find(entry['pattern']): + assert p.startswith(('VC/', 'DIA SDK/')) + + yield p.encode('utf-8'), f + + for entry in SDK_PATTERNS: + finder = FileFinder(sdk_path, find_executables=False, + ignore=entry.get('ignore', [])) + for p, f in finder.find(entry['pattern']): + relpath = 'SDK/%s' % p + + yield relpath.encode('utf-8'), f + + +def resolve_files_and_hash(manifest): + """Resolve files and hash their data. + + This is a generator of 3-tuples of (relpath, data, mode). + + As data is read, the manifest is populated with metadata. + Keys are set to the relative file path. Values are 2-tuples + of (data length, sha-256). + """ + assert manifest == {} + for p, f in resolve_files(): + data = f.read() + + sha256 = hashlib.sha256() + sha256.update(data) + manifest[p] = (len(data), sha256.hexdigest()) + + yield p, data, f.mode + + +def format_manifest(manifest): + """Return formatted SHA-256 manifests as a byte strings.""" + sha256_lines = [] + for path, (length, sha256) in sorted(manifest.items()): + sha256_lines.append(b'%s\t%d\t%s' % (sha256, length, path)) + + # Trailing newline. + sha256_lines.append(b'') + + return b'\n'.join(sha256_lines) + + +def write_zip(zip_path, prefix=None): + """Write toolchain data to a zip file.""" + if isinstance(prefix, unicode): + prefix = prefix.encode('utf-8') + + with JarWriter(file=zip_path, optimize=False, compress=5) as zip: + manifest = {} + for p, data, mode in resolve_files_and_hash(manifest): + print(p) + if prefix: + p = mozpath.join(prefix, p) + + zip.add(p, data, mode=mode) + + sha256_manifest = format_manifest(manifest) + + sdk_path = b'SDK_VERSION' + sha256_path = b'MANIFEST.SHA256' + if prefix: + sdk_path = mozpath.join(prefix, sdk_path) + sha256_path = mozpath.join(prefix, sha256_path) + + zip.add(sdk_path, SDK_RELEASE.encode('utf-8')) + zip.add(sha256_path, sha256_manifest) + + +if __name__ == '__main__': + if len(sys.argv) != 3: + print('usage: %s create-zip <path-prefix>' % sys.argv[0]) + sys.exit(1) + + assert sys.argv[1] == 'create-zip' + prefix = os.path.basename(sys.argv[2]) + destzip = '%s.zip' % sys.argv[2] + write_zip(destzip, prefix=prefix) + + sha1 = hashlib.sha1() + sha256 = hashlib.sha256() + sha512 = hashlib.sha512() + + with open(destzip, 'rb') as fh: + data = fh.read() + sha1.update(data) + sha256.update(data) + sha512.update(data) + + print('Hashes of %s (size=%d)' % (destzip, len(data))) + print('SHA-1: %s' % sha1.hexdigest()) + print('SHA-256: %s' % sha256.hexdigest()) + print('SHA-512: %s' % sha512.hexdigest()) |