mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Merge the last PGO-green inbound changeset to m-c.
This commit is contained in:
commit
fa9c462002
@ -70,9 +70,9 @@ libs:: $(_TEST_FILES)
|
||||
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
|
||||
|
||||
check::
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON_PATH) $(PLY_INCLUDE) \
|
||||
$(srcdir)/../parser/runtests.py
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) $(srcdir)/../parser/runtests.py
|
||||
|
||||
check-interactive:
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON_PATH) $(PLY_INCLUDE) \
|
||||
$(srcdir)/../parser/runtests.py -q
|
||||
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
|
||||
$(PLY_INCLUDE) $(srcdir)/../parser/runtests.py -q
|
||||
|
@ -14,7 +14,7 @@ if (DEBUG) {
|
||||
debug = function (s) {}
|
||||
}
|
||||
|
||||
const Cu = Components.utils;
|
||||
const Cu = Components.utils;
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
@ -76,7 +76,7 @@ ContactDB.prototype = {
|
||||
} else if (currVersion == 1) {
|
||||
debug("upgrade 1");
|
||||
|
||||
// Create a new scheme for the tel field. We move from an array of tel-numbers to an array of
|
||||
// Create a new scheme for the tel field. We move from an array of tel-numbers to an array of
|
||||
// ContactTelephone.
|
||||
if (!objectStore) {
|
||||
objectStore = aTransaction.objectStore(STORE_NAME);
|
||||
@ -85,7 +85,7 @@ ContactDB.prototype = {
|
||||
objectStore.deleteIndex("tel");
|
||||
|
||||
// Upgrade existing tel field in the DB.
|
||||
objectStore.openCursor().onsuccess = function(event) {
|
||||
objectStore.openCursor().onsuccess = function(event) {
|
||||
let cursor = event.target.result;
|
||||
if (cursor) {
|
||||
debug("upgrade tel1: " + JSON.stringify(cursor.value));
|
||||
@ -95,7 +95,7 @@ ContactDB.prototype = {
|
||||
cursor.update(cursor.value);
|
||||
debug("upgrade tel2: " + JSON.stringify(cursor.value));
|
||||
cursor.continue();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Create new searchable indexes.
|
||||
@ -155,7 +155,7 @@ ContactDB.prototype = {
|
||||
for (let i = 0; i <= aContact.properties[field].length; i++) {
|
||||
if (aContact.properties[field][i]) {
|
||||
if (field == "tel") {
|
||||
// Special case telephone number.
|
||||
// Special case telephone number.
|
||||
// "+1-234-567" should also be found with 1234, 234-56, 23456
|
||||
|
||||
// Chop off the first characters
|
||||
@ -308,21 +308,21 @@ ContactDB.prototype = {
|
||||
let request;
|
||||
if (key == "id") {
|
||||
// store.get would return an object and not an array
|
||||
request = store.getAll(options.filterValue);
|
||||
request = store.mozGetAll(options.filterValue);
|
||||
} else if (key == "category") {
|
||||
let index = store.index(key);
|
||||
request = index.getAll(options.filterValue, limit);
|
||||
request = index.mozGetAll(options.filterValue, limit);
|
||||
} else if (options.filterOp == "equals") {
|
||||
debug("Getting index: " + key);
|
||||
// case sensitive
|
||||
let index = store.index(key);
|
||||
request = index.getAll(options.filterValue, limit);
|
||||
request = index.mozGetAll(options.filterValue, limit);
|
||||
} else {
|
||||
// not case sensitive
|
||||
let tmp = options.filterValue.toLowerCase();
|
||||
let range = this._global.IDBKeyRange.bound(tmp, tmp + "\uFFFF");
|
||||
let index = store.index(key + "LowerCase");
|
||||
request = index.getAll(range, limit);
|
||||
request = index.mozGetAll(range, limit);
|
||||
}
|
||||
if (!txn.result)
|
||||
txn.result = {};
|
||||
@ -341,7 +341,7 @@ ContactDB.prototype = {
|
||||
txn.result = {};
|
||||
// Sorting functions takes care of limit if set.
|
||||
let limit = options.sortBy === 'undefined' ? options.filterLimit : null;
|
||||
store.getAll(null, limit).onsuccess = function (event) {
|
||||
store.mozGetAll(null, limit).onsuccess = function (event) {
|
||||
debug("Request successful. Record count:", event.target.result.length);
|
||||
for (let i in event.target.result)
|
||||
txn.result[event.target.result[i].id] = this.makeExport(event.target.result[i]);
|
||||
|
@ -379,7 +379,7 @@ SmsDatabaseService.prototype = {
|
||||
requestId, Ci.nsISmsRequestManager.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
let request = store.getAll(messageId);
|
||||
let request = store.mozGetAll(messageId);
|
||||
|
||||
txn.oncomplete = function oncomplete() {
|
||||
if (DEBUG) debug("Transaction " + txn + " completed.");
|
||||
|
@ -238,17 +238,6 @@ ArrayBufferObject::allocateSlots(JSContext *cx, uint32_t size, uint8_t *contents
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
DelegateObject(JSContext *cx, HandleObject obj)
|
||||
{
|
||||
if (!obj->getPrivate()) {
|
||||
JSObject *delegate = NewObjectWithGivenProto(cx, &ObjectClass, obj->getProto(), NULL);
|
||||
obj->setPrivate(delegate);
|
||||
return delegate;
|
||||
}
|
||||
return static_cast<JSObject*>(obj->getPrivate());
|
||||
}
|
||||
|
||||
JSObject *
|
||||
ArrayBufferObject::create(JSContext *cx, uint32_t nbytes, uint8_t *contents)
|
||||
{
|
||||
@ -318,7 +307,7 @@ ArrayBufferObject::obj_lookupGeneric(JSContext *cx, HandleObject obj, HandleId i
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
|
||||
@ -360,7 +349,7 @@ JSBool
|
||||
ArrayBufferObject::obj_lookupElement(JSContext *cx, HandleObject obj, uint32_t index,
|
||||
JSObject **objp, JSProperty **propp)
|
||||
{
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
|
||||
@ -404,7 +393,7 @@ ArrayBufferObject::obj_defineGeneric(JSContext *cx, HandleObject obj, HandleId i
|
||||
|
||||
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
|
||||
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::DefineGeneric(cx, delegate, id, v, getter, setter, attrs);
|
||||
@ -425,7 +414,7 @@ ArrayBufferObject::obj_defineElement(JSContext *cx, HandleObject obj, uint32_t i
|
||||
{
|
||||
AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
|
||||
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::DefineElement(cx, delegate, index, v, getter, setter, attrs);
|
||||
@ -451,7 +440,7 @@ ArrayBufferObject::obj_getGeneric(JSContext *cx, HandleObject obj, HandleObject
|
||||
return true;
|
||||
}
|
||||
|
||||
nobj = DelegateObject(cx, nobj);
|
||||
nobj = ArrayBufferDelegate(cx, nobj);
|
||||
if (!nobj)
|
||||
return false;
|
||||
return baseops::GetProperty(cx, nobj, receiver, id, vp);
|
||||
@ -477,7 +466,7 @@ ArrayBufferObject::obj_getProperty(JSContext *cx, HandleObject obj,
|
||||
return true;
|
||||
}
|
||||
|
||||
nobj = DelegateObject(cx, nobj);
|
||||
nobj = ArrayBufferDelegate(cx, nobj);
|
||||
if (!nobj)
|
||||
return false;
|
||||
Rooted<jsid> id(cx, NameToId(name));
|
||||
@ -489,7 +478,7 @@ ArrayBufferObject::obj_getElement(JSContext *cx, HandleObject obj,
|
||||
HandleObject receiver, uint32_t index, Value *vp)
|
||||
{
|
||||
RootedObject buffer(cx, getArrayBuffer(obj));
|
||||
RootedObject delegate(cx, DelegateObject(cx, buffer));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, buffer));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::GetElement(cx, delegate, receiver, index, vp);
|
||||
@ -500,7 +489,7 @@ ArrayBufferObject::obj_getElementIfPresent(JSContext *cx, HandleObject obj, Hand
|
||||
uint32_t index, Value *vp, bool *present)
|
||||
{
|
||||
RootedObject buffer(cx, getArrayBuffer(obj));
|
||||
RootedObject delegate(cx, DelegateObject(cx, buffer));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, buffer));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return delegate->getElementIfPresent(cx, receiver, index, vp, present);
|
||||
@ -520,7 +509,7 @@ ArrayBufferObject::obj_setGeneric(JSContext *cx, HandleObject obj, HandleId id,
|
||||
if (JSID_IS_ATOM(id, cx->runtime->atomState.byteLengthAtom))
|
||||
return true;
|
||||
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
|
||||
@ -582,7 +571,7 @@ JSBool
|
||||
ArrayBufferObject::obj_setElement(JSContext *cx, HandleObject obj,
|
||||
uint32_t index, Value *vp, JSBool strict)
|
||||
{
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
|
||||
@ -606,7 +595,7 @@ ArrayBufferObject::obj_getGenericAttributes(JSContext *cx, HandleObject obj,
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::GetAttributes(cx, delegate, id, attrsp);
|
||||
@ -624,7 +613,7 @@ JSBool
|
||||
ArrayBufferObject::obj_getElementAttributes(JSContext *cx, HandleObject obj,
|
||||
uint32_t index, unsigned *attrsp)
|
||||
{
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::GetElementAttributes(cx, delegate, index, attrsp);
|
||||
@ -648,7 +637,7 @@ ArrayBufferObject::obj_setGenericAttributes(JSContext *cx, HandleObject obj,
|
||||
return false;
|
||||
}
|
||||
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::SetAttributes(cx, delegate, id, attrsp);
|
||||
@ -666,7 +655,7 @@ JSBool
|
||||
ArrayBufferObject::obj_setElementAttributes(JSContext *cx, HandleObject obj,
|
||||
uint32_t index, unsigned *attrsp)
|
||||
{
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::SetElementAttributes(cx, delegate, index, attrsp);
|
||||
@ -689,7 +678,7 @@ ArrayBufferObject::obj_deleteProperty(JSContext *cx, HandleObject obj,
|
||||
return true;
|
||||
}
|
||||
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::DeleteProperty(cx, delegate, name, rval, strict);
|
||||
@ -699,7 +688,7 @@ JSBool
|
||||
ArrayBufferObject::obj_deleteElement(JSContext *cx, HandleObject obj,
|
||||
uint32_t index, Value *rval, JSBool strict)
|
||||
{
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::DeleteElement(cx, delegate, index, rval, strict);
|
||||
@ -709,7 +698,7 @@ JSBool
|
||||
ArrayBufferObject::obj_deleteSpecial(JSContext *cx, HandleObject obj,
|
||||
HandleSpecialId sid, Value *rval, JSBool strict)
|
||||
{
|
||||
RootedObject delegate(cx, DelegateObject(cx, obj));
|
||||
RootedObject delegate(cx, ArrayBufferDelegate(cx, obj));
|
||||
if (!delegate)
|
||||
return false;
|
||||
return baseops::DeleteSpecial(cx, delegate, sid, rval, strict);
|
||||
|
@ -449,8 +449,8 @@ DenseElementsHeader::defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint3
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSObject *
|
||||
ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
|
||||
JSObject *
|
||||
js::ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj)
|
||||
{
|
||||
MOZ_ASSERT(obj->hasClass(&ArrayBufferClass));
|
||||
if (obj->getPrivate())
|
||||
|
@ -1320,6 +1320,9 @@ Downcast(Handle<ObjectImpl*> obj)
|
||||
return Handle<JSObject*>::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address()));
|
||||
}
|
||||
|
||||
extern JSObject *
|
||||
ArrayBufferDelegate(JSContext *cx, Handle<ObjectImpl*> obj);
|
||||
|
||||
/* Generic [[GetOwnProperty]] method. */
|
||||
bool
|
||||
GetOwnElement(JSContext *cx, Handle<ObjectImpl*> obj, uint32_t index, unsigned resolveFlags,
|
||||
|
@ -25,7 +25,7 @@ namespace js {
|
||||
* and saved versions. If deserialization fails, the data should be
|
||||
* invalidated if possible.
|
||||
*/
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 118);
|
||||
static const uint32_t XDR_BYTECODE_VERSION = uint32_t(0xb973c0de - 119);
|
||||
|
||||
class XDRBuffer {
|
||||
public:
|
||||
|
@ -246,6 +246,8 @@ public class Tabs implements GeckoEventListener {
|
||||
selectTab(tab.getId());
|
||||
if (message.getBoolean("delayLoad"))
|
||||
tab.setState(Tab.STATE_DELAYED);
|
||||
if (message.getBoolean("desktopMode"))
|
||||
tab.setDesktopMode(true);
|
||||
} else if (event.equals("Tab:Close")) {
|
||||
Tab tab = getTab(message.getInt("tabID"));
|
||||
closeTab(tab);
|
||||
|
@ -2083,7 +2083,6 @@ function Tab(aURL, aParams) {
|
||||
this.browser = null;
|
||||
this.id = 0;
|
||||
this.showProgress = true;
|
||||
this.create(aURL, aParams);
|
||||
this._zoom = 1.0;
|
||||
this._drawZoom = 1.0;
|
||||
this.userScrollPos = { x: 0, y: 0 };
|
||||
@ -2093,6 +2092,8 @@ function Tab(aURL, aParams) {
|
||||
this.clickToPlayPluginsActivated = false;
|
||||
this.desktopMode = false;
|
||||
this.originalURI = null;
|
||||
|
||||
this.create(aURL, aParams);
|
||||
}
|
||||
|
||||
Tab.prototype = {
|
||||
@ -2122,6 +2123,7 @@ Tab.prototype = {
|
||||
} catch (e) {}
|
||||
|
||||
this.id = ++gTabIDFactory;
|
||||
this.desktopMode = ("desktopMode" in aParams) ? aParams.desktopMode : false;
|
||||
|
||||
let message = {
|
||||
gecko: {
|
||||
@ -2132,7 +2134,8 @@ Tab.prototype = {
|
||||
external: ("external" in aParams) ? aParams.external : false,
|
||||
selected: ("selected" in aParams) ? aParams.selected : true,
|
||||
title: aParams.title || aURL,
|
||||
delayLoad: aParams.delayLoad || false
|
||||
delayLoad: aParams.delayLoad || false,
|
||||
desktopMode: this.desktopMode
|
||||
}
|
||||
};
|
||||
sendMessageToJava(message);
|
||||
|
@ -405,7 +405,7 @@ SessionStore.prototype = {
|
||||
let data = { entries: entries, index: index };
|
||||
|
||||
delete aBrowser.__SS_data;
|
||||
this._collectTabData(aBrowser, data);
|
||||
this._collectTabData(aWindow, aBrowser, data);
|
||||
this.saveStateNow();
|
||||
}
|
||||
|
||||
@ -478,7 +478,7 @@ SessionStore.prototype = {
|
||||
return data;
|
||||
},
|
||||
|
||||
_collectTabData: function ss__collectTabData(aBrowser, aHistory) {
|
||||
_collectTabData: function ss__collectTabData(aWindow, aBrowser, aHistory) {
|
||||
// If this browser is being restored, skip any session save activity
|
||||
if (aBrowser.__SS_restore)
|
||||
return;
|
||||
@ -489,6 +489,7 @@ SessionStore.prototype = {
|
||||
tabData.entries = aHistory.entries;
|
||||
tabData.index = aHistory.index;
|
||||
tabData.attributes = { image: aBrowser.mIconURL };
|
||||
tabData.desktopMode = aWindow.BrowserApp.getTabForBrowser(aBrowser).desktopMode;
|
||||
|
||||
aBrowser.__SS_data = tabData;
|
||||
},
|
||||
@ -948,7 +949,12 @@ SessionStore.prototype = {
|
||||
let entry = tabData.entries[tabData.index - 1];
|
||||
|
||||
// Add a tab, but don't load the URL until we need to
|
||||
let params = { selected: isSelected, delayLoad: true, title: entry.title };
|
||||
let params = {
|
||||
selected: isSelected,
|
||||
delayLoad: true,
|
||||
title: entry.title,
|
||||
desktopMode: tabData.desktopMode == true
|
||||
};
|
||||
let tab = window.BrowserApp.addTab(entry.url, params);
|
||||
|
||||
if (isSelected) {
|
||||
|
@ -1 +1 @@
|
||||
http://hg.mozilla.org/projects/addon-sdk/archive/8d6150ab4903.tar.bz2
|
||||
http://hg.mozilla.org/projects/addon-sdk/archive/ff6864587606.tar.bz2
|
||||
|
@ -1,7 +1,3 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
Universal manifests for Mozilla test harnesses
|
||||
|
||||
# What is ManifestDestiny?
|
||||
|
@ -1,5 +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/.
|
||||
# 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 manifestparser import *
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/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/.
|
||||
# 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/.
|
||||
|
||||
"""
|
||||
Mozilla universal manifest parser
|
||||
@ -695,8 +696,8 @@ class TestManifest(ManifestParser):
|
||||
def filter(self, values, tests):
|
||||
"""
|
||||
filter on a specific list tag, e.g.:
|
||||
run-if = os == 'win' || os == 'linux'
|
||||
skip-if = os == 'mac'
|
||||
run-if.os = win linux
|
||||
skip-if.os = mac
|
||||
"""
|
||||
|
||||
# tags:
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/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/.
|
||||
# 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 real details are in manifestparser.py; this is just a front-end
|
||||
# BUT use this file when you want to distribute to python!
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/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/.
|
||||
# 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/.
|
||||
|
||||
"""tests for ManifestDestiny"""
|
||||
|
||||
|
@ -36,8 +36,6 @@ Test equality::
|
||||
True
|
||||
>>> parse("false == false")
|
||||
True
|
||||
>>> parse("false == false")
|
||||
True
|
||||
>>> parse("1 == 1")
|
||||
True
|
||||
>>> parse("100 == 100")
|
||||
|
@ -1,5 +1 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
basic python webserver, tested with talos
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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 mozhttpd import MozHttpd, Request, RequestHandler, main
|
||||
from handlers import json_response
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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/.
|
||||
|
||||
try:
|
||||
import json
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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 socket
|
||||
|
@ -1,8 +1,8 @@
|
||||
#!/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/.
|
||||
# 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 BaseHTTPServer
|
||||
import SimpleHTTPServer
|
||||
@ -16,6 +16,7 @@ import os
|
||||
import urllib
|
||||
import urlparse
|
||||
import re
|
||||
import iface
|
||||
from SocketServer import ThreadingMixIn
|
||||
|
||||
class EasyServer(ThreadingMixIn, BaseHTTPServer.HTTPServer):
|
||||
@ -224,27 +225,35 @@ class MozHttpd(object):
|
||||
|
||||
|
||||
def main(args=sys.argv[1:]):
|
||||
|
||||
|
||||
# parse command line options
|
||||
from optparse import OptionParser
|
||||
parser = OptionParser()
|
||||
parser.add_option('-p', '--port', dest='port',
|
||||
parser.add_option('-p', '--port', dest='port',
|
||||
type="int", default=8888,
|
||||
help="port to run the server on [DEFAULT: %default]")
|
||||
parser.add_option('-H', '--host', dest='host',
|
||||
default='127.0.0.1',
|
||||
help="host [DEFAULT: %default]")
|
||||
parser.add_option('-i', '--external-ip', action="store_true",
|
||||
dest='external_ip', default=False,
|
||||
help="find and use external ip for host")
|
||||
parser.add_option('-d', '--docroot', dest='docroot',
|
||||
default=os.getcwd(),
|
||||
help="directory to serve files from [DEFAULT: %default]")
|
||||
options, args = parser.parse_args(args)
|
||||
if args:
|
||||
parser.error("mozhttpd does not take any arguments")
|
||||
parser.print_help()
|
||||
parser.exit()
|
||||
|
||||
if options.external_ip:
|
||||
host = iface.get_lan_ip()
|
||||
else:
|
||||
host = options.host
|
||||
|
||||
# create the server
|
||||
kwargs = options.__dict__.copy()
|
||||
server = MozHttpd(**kwargs)
|
||||
server = MozHttpd(host=host, port=options.port, docroot=options.docroot)
|
||||
|
||||
print "Serving '%s' at %s:%s" % (server.docroot, server.host, server.port)
|
||||
server.start(block=True)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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
|
||||
from setuptools import setup, find_packages
|
||||
@ -11,7 +11,7 @@ try:
|
||||
except IOError:
|
||||
description = None
|
||||
|
||||
version = '0.2'
|
||||
version = '0.3'
|
||||
|
||||
deps = []
|
||||
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/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/.
|
||||
# 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 mozhttpd
|
||||
import urllib2
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/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/.
|
||||
# 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 mozhttpd
|
||||
import urllib2
|
||||
|
@ -1,7 +1,3 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
Throughout [mozmill](https://developer.mozilla.org/en/Mozmill)
|
||||
and other Mozilla python code, checking the underlying
|
||||
platform is done in many different ways. The various checks needed
|
||||
|
@ -1,5 +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/.
|
||||
# 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 mozinfo import *
|
||||
|
@ -1,8 +1,8 @@
|
||||
#!/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/.
|
||||
# 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/.
|
||||
|
||||
"""
|
||||
file for interface to transform introspected system information to a format
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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
|
||||
|
@ -1,37 +1,58 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
[Mozinstall](https://github.com/mozilla/mozbase/tree/master/mozinstall)
|
||||
is a python package for installing Mozilla applications on various platforms.
|
||||
[Mozinstall](https://github.com/mozilla/mozbase/tree/master/mozinstall) is a
|
||||
python package for installing and uninstalling Mozilla applications on
|
||||
various platforms.
|
||||
|
||||
For example, depending on the platform, Firefox can be distributed as a
|
||||
zip, tar.bz2, exe or dmg file or cloned from a repository. Mozinstall takes the
|
||||
hassle out of extracting and/or running these files and for convenience returns
|
||||
the full path to the application's binary in the install directory. In the case
|
||||
that mozinstall is invoked from the command line, the binary path will be
|
||||
printed to stdout.
|
||||
zip, tar.bz2, exe, or dmg file or cloned from a repository. Mozinstall takes
|
||||
the hassle out of extracting and/or running these files and for convenience
|
||||
returns the full path to the install directory. In the case that mozinstall
|
||||
is invoked from the command line, the binary path will be printed to stdout.
|
||||
|
||||
To remove an installed application the uninstaller can be used. It requires
|
||||
the installation path of the application and will remove all the installed
|
||||
files. On Windows the uninstaller will be tried first.
|
||||
|
||||
# Usage
|
||||
Mozinstall can be used as API or via the CLI commands.
|
||||
|
||||
For command line options run mozinstall --help
|
||||
|
||||
Mozinstall's main function is the install method
|
||||
## API
|
||||
An application can be installed by running the commands below. The install
|
||||
method will return the installation path of the application.
|
||||
|
||||
import mozinstall
|
||||
mozinstall.install('path_to_install_file', dest='path_to_install_folder')
|
||||
path = mozinstall.install(%installer%, %install_folder%)
|
||||
|
||||
The dest parameter defaults to the directory in which the install file is located.
|
||||
The install method accepts a third parameter called apps which tells mozinstall which
|
||||
binary to search for. By default it will search for 'firefox', 'thunderbird' and 'fennec'
|
||||
so unless you are installing a different application, this parameter is unnecessary.
|
||||
To retrieve the real binary call get_binary with the path and
|
||||
the application name as arguments:
|
||||
|
||||
mozinstall.get_binary(path, 'firefox')
|
||||
|
||||
If the application is not needed anymore the uninstaller will remove all
|
||||
traces from the system:
|
||||
|
||||
mozinstall.uninstall(path)
|
||||
|
||||
## CLI
|
||||
The installer can also be used as a command line tool:
|
||||
|
||||
$ mozinstall -d firefox %installer%
|
||||
|
||||
Whereby the directory option is optional and will default to the current
|
||||
working directory. If the installation was successful the path to the
|
||||
binary will be printed to stdout.
|
||||
|
||||
Also the uninstaller can be called via the command line:
|
||||
|
||||
$ mozuninstall %install_path%
|
||||
|
||||
# Error Handling
|
||||
|
||||
Mozinstall throws two different types of exceptions:
|
||||
Mozinstall throws different types of exceptions:
|
||||
|
||||
- mozinstall.InvalidSource is thrown when the source is not a recognized file type (zip, exe, tar.bz2, tar.gz, dmg)
|
||||
- mozinstall.InstallError is thrown when the installation fails for any reason. A traceback is provided.
|
||||
- mozinstall.InvalidBinary is thrown when the binary cannot be found.
|
||||
- mozinstall.InvalidSource is thrown when the source is not a recognized file type (zip, exe, tar.bz2, tar.gz, dmg).
|
||||
|
||||
|
||||
# Dependencies
|
||||
|
||||
|
@ -1,4 +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/.
|
||||
# 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 mozinstall import *
|
||||
|
@ -1,201 +1,370 @@
|
||||
#!/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/.
|
||||
# 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 optparse import OptionParser
|
||||
"""Module to handle the installation and uninstallation of Gecko based
|
||||
applications across platforms.
|
||||
|
||||
"""
|
||||
import mozinfo
|
||||
import subprocess
|
||||
import zipfile
|
||||
import tarfile
|
||||
import sys
|
||||
from optparse import OptionParser
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
import tarfile
|
||||
import time
|
||||
import zipfile
|
||||
|
||||
_default_apps = ["firefox",
|
||||
"thunderbird",
|
||||
"fennec"]
|
||||
if mozinfo.isMac:
|
||||
from plistlib import readPlist
|
||||
|
||||
|
||||
TIMEOUT_UNINSTALL = 60
|
||||
|
||||
|
||||
class InstallError(Exception):
|
||||
"""Thrown when installation fails. Includes traceback if available."""
|
||||
|
||||
|
||||
class InvalidBinary(Exception):
|
||||
"""Thrown when the binary cannot be found after the installation."""
|
||||
|
||||
|
||||
class InvalidSource(Exception):
|
||||
"""Thrown when the specified source is not a recognized file type.
|
||||
|
||||
Supported types:
|
||||
Linux: tar.gz, tar.bz2
|
||||
Mac: dmg
|
||||
Windows: zip, exe
|
||||
|
||||
def install(src, dest=None, apps=_default_apps):
|
||||
"""
|
||||
Installs a zip, exe, tar.gz, tar.bz2 or dmg file
|
||||
src - the path to the install file
|
||||
dest - the path to install to [default is os.path.dirname(src)]
|
||||
returns - the full path to the binary in the installed folder
|
||||
or None if the binary cannot be found
|
||||
|
||||
|
||||
class UninstallError(Exception):
|
||||
"""Thrown when uninstallation fails. Includes traceback if available."""
|
||||
|
||||
|
||||
def get_binary(path, app_name):
|
||||
"""Find the binary in the specified path, and return its path. If binary is
|
||||
not found throw an InvalidBinary exception.
|
||||
|
||||
Arguments:
|
||||
path -- the path within to search for the binary
|
||||
app_name -- application binary without file extension to look for
|
||||
|
||||
"""
|
||||
binary = None
|
||||
|
||||
# On OS X we can get the real binary from the app bundle
|
||||
if mozinfo.isMac:
|
||||
plist = '%s/Contents/Info.plist' % path
|
||||
assert os.path.isfile(plist), '"%s" has not been found.' % plist
|
||||
|
||||
binary = os.path.join(path, 'Contents/MacOS/',
|
||||
readPlist(plist)['CFBundleExecutable'])
|
||||
|
||||
else:
|
||||
app_name = app_name.lower()
|
||||
|
||||
if mozinfo.isWin:
|
||||
app_name = app_name + '.exe'
|
||||
|
||||
for root, dirs, files in os.walk(path):
|
||||
for filename in files:
|
||||
# os.access evaluates to False for some reason, so not using it
|
||||
if filename.lower() == app_name:
|
||||
binary = os.path.realpath(os.path.join(root, filename))
|
||||
break
|
||||
|
||||
if not binary:
|
||||
# The expected binary has not been found. Make sure we clean the
|
||||
# install folder to remove any traces
|
||||
shutil.rmtree(path)
|
||||
|
||||
raise InvalidBinary('"%s" does not contain a valid binary.' % path)
|
||||
|
||||
return binary
|
||||
|
||||
|
||||
def install(src, dest):
|
||||
"""Install a zip, exe, tar.gz, tar.bz2 or dmg file, and return the path of
|
||||
the installation folder.
|
||||
|
||||
Arguments:
|
||||
src -- the path to the install file
|
||||
dest -- the path to install to (to ensure we do not overwrite any existent
|
||||
files the folder should not exist yet)
|
||||
|
||||
"""
|
||||
src = os.path.realpath(src)
|
||||
assert(os.path.isfile(src))
|
||||
if not dest:
|
||||
dest = os.path.dirname(src)
|
||||
dest = os.path.realpath(dest)
|
||||
|
||||
if not is_installer(src):
|
||||
raise InvalidSource(src + ' is not a recognized file type ' +
|
||||
'(zip, exe, tar.gz, tar.bz2 or dmg)')
|
||||
|
||||
if not os.path.exists(dest):
|
||||
os.makedirs(dest)
|
||||
|
||||
trbk = None
|
||||
try:
|
||||
install_dir = None
|
||||
if zipfile.is_zipfile(src) or tarfile.is_tarfile(src):
|
||||
install_dir = _extract(src, dest)[0]
|
||||
elif mozinfo.isMac and src.lower().endswith(".dmg"):
|
||||
elif src.lower().endswith('.dmg'):
|
||||
install_dir = _install_dmg(src, dest)
|
||||
elif mozinfo.isWin and os.access(src, os.X_OK):
|
||||
elif src.lower().endswith('.exe'):
|
||||
install_dir = _install_exe(src, dest)
|
||||
else:
|
||||
raise InvalidSource(src + " is not a recognized file type " +
|
||||
"(zip, exe, tar.gz, tar.bz2 or dmg)")
|
||||
except InvalidSource, e:
|
||||
raise
|
||||
|
||||
return install_dir
|
||||
|
||||
except Exception, e:
|
||||
cls, exc, trbk = sys.exc_info()
|
||||
install_error = InstallError("Failed to install %s" % src)
|
||||
raise install_error.__class__, install_error, trbk
|
||||
error = InstallError('Failed to install "%s"' % src)
|
||||
raise InstallError, error, trbk
|
||||
|
||||
finally:
|
||||
# trbk won't get GC'ed due to circular reference
|
||||
# http://docs.python.org/library/sys.html#sys.exc_info
|
||||
del trbk
|
||||
|
||||
if install_dir:
|
||||
return get_binary(install_dir, apps=apps)
|
||||
|
||||
def get_binary(path, apps=_default_apps):
|
||||
def is_installer(src):
|
||||
"""Tests if the given file is a valid installer package.
|
||||
|
||||
Supported types:
|
||||
Linux: tar.gz, tar.bz2
|
||||
Mac: dmg
|
||||
Windows: zip, exe
|
||||
|
||||
Arguments:
|
||||
src -- the path to the install file
|
||||
|
||||
"""
|
||||
Finds the binary in the specified path
|
||||
path - the path within which to search for the binary
|
||||
returns - the full path to the binary in the folder
|
||||
or None if the binary cannot be found
|
||||
src = os.path.realpath(src)
|
||||
|
||||
if not os.path.isfile(src):
|
||||
return False
|
||||
|
||||
if mozinfo.isLinux:
|
||||
return tarfile.is_tarfile(src)
|
||||
elif mozinfo.isMac:
|
||||
return src.lower().endswith('.dmg')
|
||||
elif mozinfo.isWin:
|
||||
return src.lower().endswith('.exe') or zipfile.is_zipfile(src)
|
||||
|
||||
|
||||
def uninstall(install_folder):
|
||||
"""Uninstalls the application in the specified path. If it has been
|
||||
installed via an installer on Windows, use the uninstaller first.
|
||||
|
||||
Arguments:
|
||||
install_folder -- the path of the installation folder
|
||||
|
||||
"""
|
||||
install_folder = os.path.realpath(install_folder)
|
||||
assert os.path.isdir(install_folder), \
|
||||
'installation folder "%s" exists.' % install_folder
|
||||
|
||||
# On Windows we have to use the uninstaller. If it's not available fallback
|
||||
# to the directory removal code
|
||||
if mozinfo.isWin:
|
||||
apps = [app + ".exe" for app in apps]
|
||||
for root, dirs, files in os.walk(path):
|
||||
for filename in files:
|
||||
# os.access evaluates to False for some reason, so not using it
|
||||
if filename in apps:
|
||||
return os.path.realpath(os.path.join(root, filename))
|
||||
uninstall_folder = '%s\uninstall' % install_folder
|
||||
log_file = '%s\uninstall.log' % uninstall_folder
|
||||
|
||||
if os.path.isfile(log_file):
|
||||
trbk = None
|
||||
try:
|
||||
cmdArgs = ['%s\uninstall\helper.exe' % install_folder, '/S']
|
||||
result = subprocess.call(cmdArgs)
|
||||
if not result is 0:
|
||||
raise Exception('Execution of uninstaller failed.')
|
||||
|
||||
# The uninstaller spawns another process so the subprocess call
|
||||
# returns immediately. We have to wait until the uninstall
|
||||
# folder has been removed or until we run into a timeout.
|
||||
end_time = time.time() + TIMEOUT_UNINSTALL
|
||||
while os.path.exists(uninstall_folder):
|
||||
time.sleep(1)
|
||||
|
||||
if time.time() > end_time:
|
||||
raise Exception('Failure removing uninstall folder.')
|
||||
|
||||
except Exception, e:
|
||||
cls, exc, trbk = sys.exc_info()
|
||||
error = UninstallError('Failed to uninstall %s' % install_folder)
|
||||
raise UninstallError, error, trbk
|
||||
|
||||
finally:
|
||||
# trbk won't get GC'ed due to circular reference
|
||||
# http://docs.python.org/library/sys.html#sys.exc_info
|
||||
del trbk
|
||||
|
||||
# Ensure that we remove any trace of the installation. Even the uninstaller
|
||||
# on Windows leaves files behind we have to explicitely remove.
|
||||
shutil.rmtree(install_folder)
|
||||
|
||||
|
||||
def _extract(src, dest):
|
||||
"""Extract a tar or zip file into the destination folder and return the
|
||||
application folder.
|
||||
|
||||
Arguments:
|
||||
src -- archive which has to be extracted
|
||||
dest -- the path to extract to
|
||||
|
||||
def _extract(path, extdir=None, delete=False):
|
||||
"""
|
||||
Takes in a tar or zip file and extracts it to extdir
|
||||
If extdir is not specified, extracts to os.path.dirname(path)
|
||||
If delete is set to True, deletes the bundle at path
|
||||
Returns the list of top level files that were extracted
|
||||
"""
|
||||
assert not os.path.isfile(extdir), "extdir cannot be a file"
|
||||
if extdir is None:
|
||||
extdir = os.path.dirname(path)
|
||||
elif not os.path.isdir(extdir):
|
||||
os.makedirs(extdir)
|
||||
if zipfile.is_zipfile(path):
|
||||
bundle = zipfile.ZipFile(path)
|
||||
if zipfile.is_zipfile(src):
|
||||
bundle = zipfile.ZipFile(src)
|
||||
namelist = bundle.namelist()
|
||||
|
||||
if hasattr(bundle, 'extractall'):
|
||||
bundle.extractall(path=extdir)
|
||||
# zipfile.extractall doesn't exist in Python 2.5
|
||||
# zipfile.extractall doesn't exist in Python 2.5
|
||||
bundle.extractall(path=dest)
|
||||
else:
|
||||
for name in namelist:
|
||||
filename = os.path.realpath(os.path.join(extdir, name))
|
||||
if name.endswith("/"):
|
||||
filename = os.path.realpath(os.path.join(dest, name))
|
||||
if name.endswith('/'):
|
||||
os.makedirs(filename)
|
||||
else:
|
||||
path = os.path.dirname(filename)
|
||||
if not os.path.isdir(path):
|
||||
os.makedirs(path)
|
||||
dest = open(filename, "wb")
|
||||
dest = open(filename, 'wb')
|
||||
dest.write(bundle.read(name))
|
||||
dest.close()
|
||||
elif tarfile.is_tarfile(path):
|
||||
bundle = tarfile.open(path)
|
||||
|
||||
elif tarfile.is_tarfile(src):
|
||||
bundle = tarfile.open(src)
|
||||
namelist = bundle.getnames()
|
||||
|
||||
if hasattr(bundle, 'extractall'):
|
||||
bundle.extractall(path=extdir)
|
||||
# tarfile.extractall doesn't exist in Python 2.4
|
||||
# tarfile.extractall doesn't exist in Python 2.4
|
||||
bundle.extractall(path=dest)
|
||||
else:
|
||||
for name in namelist:
|
||||
bundle.extract(name, path=extdir)
|
||||
bundle.extract(name, path=dest)
|
||||
else:
|
||||
return
|
||||
|
||||
bundle.close()
|
||||
if delete:
|
||||
os.remove(path)
|
||||
|
||||
# namelist returns paths with forward slashes even in windows
|
||||
top_level_files = [os.path.join(extdir, name) for name in namelist
|
||||
top_level_files = [os.path.join(dest, name) for name in namelist
|
||||
if len(name.rstrip('/').split('/')) == 1]
|
||||
|
||||
# namelist doesn't include folders, append these to the list
|
||||
for name in namelist:
|
||||
root = os.path.join(extdir, name[:name.find('/')])
|
||||
root = os.path.join(dest, name[:name.find('/')])
|
||||
if root not in top_level_files:
|
||||
top_level_files.append(root)
|
||||
|
||||
return top_level_files
|
||||
|
||||
|
||||
def _install_dmg(src, dest):
|
||||
proc = subprocess.Popen("hdiutil attach " + src,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE)
|
||||
"""Extract a dmg file into the destination folder and return the
|
||||
application folder.
|
||||
|
||||
Arguments:
|
||||
src -- DMG image which has to be extracted
|
||||
dest -- the path to extract to
|
||||
|
||||
"""
|
||||
try:
|
||||
proc = subprocess.Popen('hdiutil attach %s' % src,
|
||||
shell=True,
|
||||
stdout=subprocess.PIPE)
|
||||
|
||||
for data in proc.communicate()[0].split():
|
||||
if data.find("/Volumes/") != -1:
|
||||
if data.find('/Volumes/') != -1:
|
||||
appDir = data
|
||||
break
|
||||
|
||||
for appFile in os.listdir(appDir):
|
||||
if appFile.endswith(".app"):
|
||||
appName = appFile
|
||||
break
|
||||
if appFile.endswith('.app'):
|
||||
appName = appFile
|
||||
break
|
||||
|
||||
mounted_path = os.path.join(appDir, appName)
|
||||
|
||||
dest = os.path.join(dest, appName)
|
||||
assert not os.path.isfile(dest)
|
||||
if not os.path.isdir(dest):
|
||||
os.makedirs(dest)
|
||||
subprocess.call("cp -r " +
|
||||
os.path.join(appDir,appName, "*") + " " + dest,
|
||||
shell=True)
|
||||
|
||||
# copytree() would fail if dest already exists.
|
||||
if os.path.exists(dest):
|
||||
raise InstallError('App bundle "%s" already exists.' % dest)
|
||||
|
||||
shutil.copytree(mounted_path, dest, False)
|
||||
|
||||
finally:
|
||||
subprocess.call("hdiutil detach " + appDir + " -quiet",
|
||||
subprocess.call('hdiutil detach %s -quiet' % appDir,
|
||||
shell=True)
|
||||
|
||||
return dest
|
||||
|
||||
|
||||
def _install_exe(src, dest):
|
||||
"""Run the MSI installer to silently install the application into the
|
||||
destination folder. Return the folder path.
|
||||
|
||||
Arguments:
|
||||
src -- MSI installer to be executed
|
||||
dest -- the path to install to
|
||||
|
||||
"""
|
||||
# The installer doesn't automatically create a sub folder. Lets guess the
|
||||
# best name from the src file name
|
||||
filename = os.path.basename(src)
|
||||
dest = os.path.join(dest, filename.split('.')[0])
|
||||
|
||||
# possibly gets around UAC in vista (still need to run as administrator)
|
||||
os.environ['__compat_layer'] = "RunAsInvoker"
|
||||
cmd = [src, "/S", "/D=" + os.path.realpath(dest)]
|
||||
subprocess.call(cmd)
|
||||
os.environ['__compat_layer'] = 'RunAsInvoker'
|
||||
cmd = [src, '/S', '/D=%s' % os.path.realpath(dest)]
|
||||
|
||||
# As long as we support Python 2.4 check_call will not be available.
|
||||
result = subprocess.call(cmd)
|
||||
if not result is 0:
|
||||
raise Exception('Execution of installer failed.')
|
||||
|
||||
return dest
|
||||
|
||||
def cli(argv=sys.argv[1:]):
|
||||
parser = OptionParser()
|
||||
parser.add_option("-s", "--source",
|
||||
dest="src",
|
||||
help="Path to installation file. "
|
||||
"Accepts: zip, exe, tar.bz2, tar.gz, and dmg")
|
||||
parser.add_option("-d", "--destination",
|
||||
dest="dest",
|
||||
default=None,
|
||||
help="[optional] Directory to install application into")
|
||||
parser.add_option("--app", dest="app",
|
||||
action="append",
|
||||
default=_default_apps,
|
||||
help="[optional] Application being installed. "
|
||||
"Should be lowercase, e.g: "
|
||||
"firefox, fennec, thunderbird, etc.")
|
||||
|
||||
def install_cli(argv=sys.argv[1:]):
|
||||
parser = OptionParser(usage="usage: %prog [options] installer")
|
||||
parser.add_option('-d', '--destination',
|
||||
dest='dest',
|
||||
default=os.getcwd(),
|
||||
help='Directory to install application into. '
|
||||
'[default: "%default"]')
|
||||
parser.add_option('--app', dest='app',
|
||||
default='firefox',
|
||||
help='Application being installed. [default: %default]')
|
||||
|
||||
(options, args) = parser.parse_args(argv)
|
||||
if not options.src or not os.path.exists(options.src):
|
||||
print "Error: must specify valid source"
|
||||
return 2
|
||||
if not len(args) == 1:
|
||||
parser.error('An installer file has to be specified.')
|
||||
|
||||
src = args[0]
|
||||
|
||||
# Run it
|
||||
if os.path.isdir(options.src):
|
||||
binary = get_binary(options.src, apps=options.app)
|
||||
if os.path.isdir(src):
|
||||
binary = get_binary(src, app_name=options.app)
|
||||
else:
|
||||
binary = install(options.src, dest=options.dest, apps=options.app)
|
||||
install_path = install(src, options.dest)
|
||||
binary = get_binary(install_path, app_name=options.app)
|
||||
|
||||
print binary
|
||||
|
||||
class InvalidSource(Exception):
|
||||
"""
|
||||
Thrown when the specified source is not a recognized
|
||||
file type (zip, exe, tar.gz, tar.bz2 or dmg)
|
||||
"""
|
||||
|
||||
class InstallError(Exception):
|
||||
"""
|
||||
Thrown when the installation fails. Includes traceback
|
||||
if available.
|
||||
"""
|
||||
def uninstall_cli(argv=sys.argv[1:]):
|
||||
parser = OptionParser(usage="usage: %prog install_path")
|
||||
|
||||
(options, args) = parser.parse_args(argv)
|
||||
if not len(args) == 1:
|
||||
parser.error('An installation path has to be specified.')
|
||||
|
||||
# Run it
|
||||
uninstall(argv[0])
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(cli())
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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
|
||||
from setuptools import setup, find_packages
|
||||
@ -11,27 +11,29 @@ try:
|
||||
except IOError:
|
||||
description = None
|
||||
|
||||
version = '0.3'
|
||||
version = '1.1'
|
||||
|
||||
deps = ['mozinfo']
|
||||
deps = ['mozinfo==0.3.3']
|
||||
|
||||
setup(name='mozInstall',
|
||||
version=version,
|
||||
description="This is a utility package for installing Mozilla applications on various platforms.",
|
||||
description="This is a utility package for installing and uninstalling "
|
||||
"Mozilla applications on various platforms.",
|
||||
long_description=description,
|
||||
# Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
classifiers=['Environment :: Console',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
|
||||
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
],
|
||||
keywords='mozilla',
|
||||
author='mdas',
|
||||
author_email='mdas@mozilla.com',
|
||||
author='Mozilla Automation and Tools team',
|
||||
author_email='tools@lists.mozilla.org',
|
||||
url='https://github.com/mozilla/mozbase',
|
||||
license='MPL',
|
||||
license='MPL 2.0',
|
||||
packages=find_packages(exclude=['legacy']),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
@ -39,6 +41,7 @@ setup(name='mozInstall',
|
||||
entry_points="""
|
||||
# -*- Entry points: -*-
|
||||
[console_scripts]
|
||||
mozinstall = mozinstall:cli
|
||||
mozinstall = mozinstall:install_cli
|
||||
mozuninstall = mozinstall:uninstall_cli
|
||||
""",
|
||||
)
|
||||
|
@ -1,7 +1,3 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
[Mozlog](https://github.com/mozilla/mozbase/tree/master/mozlog)
|
||||
is a python package intended to simplify and standardize logs in the Mozilla universe.
|
||||
It wraps around python's [logging](http://docs.python.org/library/logging.html)
|
||||
|
@ -1,4 +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/.
|
||||
# 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 logger import *
|
||||
|
@ -1,12 +1,13 @@
|
||||
# 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/.
|
||||
# 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 logging import getLogger as getSysLogger
|
||||
from logging import *
|
||||
# Some of the build slave environments don't see the following when doing
|
||||
# 'from logging import *'
|
||||
# see https://bugzilla.mozilla.org/show_bug.cgi?id=700415#c35
|
||||
from logging import getLoggerClass, addLevelName, setLoggerClass
|
||||
from logging import getLoggerClass, addLevelName, setLoggerClass, shutdown
|
||||
|
||||
_default_level = INFO
|
||||
_LoggerClass = getLoggerClass()
|
||||
|
@ -1,13 +1,13 @@
|
||||
# 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/.
|
||||
# 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 setuptools import setup, find_packages
|
||||
|
||||
PACKAGE_NAME = "mozlog"
|
||||
PACKAGE_VERSION = "1.0"
|
||||
PACKAGE_VERSION = "1.1"
|
||||
|
||||
desc = """Robust log handling specialized for logging in the Mozilla universe"""
|
||||
# take description from README
|
||||
|
@ -1,7 +1,3 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
[mozprocess](https://github.com/mozilla/mozbase/tree/master/mozprocess)
|
||||
provides python process management via an operating system
|
||||
and platform transparent interface to Mozilla platforms of interest.
|
||||
|
@ -1,5 +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/.
|
||||
# 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 processhandler import *
|
||||
|
@ -1,8 +1,8 @@
|
||||
#!/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/.
|
||||
# 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 mozinfo
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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 logging
|
||||
import mozinfo
|
||||
@ -111,14 +111,15 @@ class ProcessHandlerMixin(object):
|
||||
except BaseException, e:
|
||||
if getattr(e, "errno", None) != 3:
|
||||
# Error 3 is "no such process", which is ok
|
||||
print >> sys.stderr, "Could not kill process, could not find pid: %s" % self.pid
|
||||
print >> sys.stdout, "Could not kill process, could not find pid: %s, assuming it's already dead" % self.pid
|
||||
else:
|
||||
os.kill(self.pid, signal.SIGKILL)
|
||||
if self.returncode is None:
|
||||
self.returncode = subprocess.Popen._internal_poll(self)
|
||||
|
||||
self._cleanup()
|
||||
return self.returncode
|
||||
|
||||
return self.returncode
|
||||
|
||||
def wait(self):
|
||||
""" Popen.wait
|
||||
@ -365,7 +366,8 @@ falling back to not using job objects for managing child processes"""
|
||||
except:
|
||||
err = "IO Completion Port failed to signal process shutdown"
|
||||
# Either way, let's try to get this code
|
||||
self.returncode = winprocess.GetExitCodeProcess(self._handle)
|
||||
if self._handle:
|
||||
self.returncode = winprocess.GetExitCodeProcess(self._handle)
|
||||
self._cleanup()
|
||||
|
||||
if err is not None:
|
||||
@ -482,7 +484,7 @@ falling back to not using job objects for managing child processes"""
|
||||
cmd,
|
||||
args=None,
|
||||
cwd=None,
|
||||
env=os.environ.copy(),
|
||||
env=None,
|
||||
ignore_children = False,
|
||||
processOutputLine=(),
|
||||
onTimeout=(),
|
||||
@ -507,11 +509,14 @@ falling back to not using job objects for managing child processes"""
|
||||
self.cmd = cmd
|
||||
self.args = args
|
||||
self.cwd = cwd
|
||||
self.env = env
|
||||
self.didTimeout = False
|
||||
self._ignore_children = ignore_children
|
||||
self.keywordargs = kwargs
|
||||
|
||||
if env is None:
|
||||
env = os.environ.copy()
|
||||
self.env = env
|
||||
|
||||
# handlers
|
||||
self.processOutputLineHandlers = list(processOutputLine)
|
||||
self.onTimeoutHandlers = list(onTimeout)
|
||||
@ -623,7 +628,6 @@ falling back to not using job objects for managing child processes"""
|
||||
lineReadTimeout = timeout - (datetime.now() - self.startTime).seconds
|
||||
(line, self.didTimeout) = self.readWithTimeout(logsource, lineReadTimeout)
|
||||
|
||||
|
||||
if self.didTimeout:
|
||||
self.proc.kill()
|
||||
self.onTimeout()
|
||||
@ -668,7 +672,7 @@ falling back to not using job objects for managing child processes"""
|
||||
try:
|
||||
(r, w, e) = select.select([f], [], [], timeout)
|
||||
except:
|
||||
# TODO: return a blank line?
|
||||
# return a blank line
|
||||
return ('', True)
|
||||
|
||||
if len(r) == 0:
|
||||
@ -708,6 +712,7 @@ class LogOutput(object):
|
||||
if self.file is not None:
|
||||
self.file.close()
|
||||
|
||||
|
||||
### front end class with the default handlers
|
||||
|
||||
class ProcessHandler(ProcessHandlerMixin):
|
||||
@ -721,7 +726,11 @@ class ProcessHandler(ProcessHandlerMixin):
|
||||
appended to the given file.
|
||||
"""
|
||||
|
||||
kwargs.setdefault('processOutputLine', []).append(print_output)
|
||||
kwargs.setdefault('processOutputLine', [])
|
||||
|
||||
# Print to standard output only if no outputline provided
|
||||
if not kwargs['processOutputLine']:
|
||||
kwargs['processOutputLine'].append(print_output)
|
||||
|
||||
if logfile:
|
||||
logoutput = LogOutput(logfile)
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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 ctypes import c_void_p, POINTER, sizeof, Structure, windll, WinError, WINFUNCTYPE, addressof, c_size_t, c_ulong
|
||||
from ctypes.wintypes import BOOL, BYTE, DWORD, HANDLE, LARGE_INTEGER
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/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/.
|
||||
# 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 ctypes import sizeof, windll, addressof, c_wchar, create_unicode_buffer
|
||||
from ctypes.wintypes import DWORD, HANDLE
|
||||
|
@ -1,11 +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/.
|
||||
# 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
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
version = '0.1b2'
|
||||
PACKAGE_VERSION = '0.3'
|
||||
|
||||
# take description from README
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
@ -15,15 +15,22 @@ except (OSError, IOError):
|
||||
description = ''
|
||||
|
||||
setup(name='mozprocess',
|
||||
version=version,
|
||||
version=PACKAGE_VERSION,
|
||||
description="Mozilla-authored process handling",
|
||||
long_description=description,
|
||||
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
keywords='',
|
||||
author='Mozilla Automation and Testing Team',
|
||||
classifiers=['Environment :: Console',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
],
|
||||
keywords='mozilla',
|
||||
author='Mozilla Automation and Tools team',
|
||||
author_email='tools@lists.mozilla.com',
|
||||
url='https://github.com/mozilla/mozbase/tree/master/mozprocess',
|
||||
license='MPL',
|
||||
license='MPL 2.0',
|
||||
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/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/.
|
||||
# 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 subprocess
|
||||
|
@ -1,8 +1,6 @@
|
||||
#!/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/.
|
||||
# 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 subprocess
|
||||
|
@ -1,6 +1,6 @@
|
||||
/* 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/. */
|
||||
* 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 <stdlib.h>
|
||||
|
@ -1,7 +1,3 @@
|
||||
<!-- 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/. -->
|
||||
|
||||
[Mozprofile](https://github.com/mozilla/mozbase/tree/master/mozprofile)
|
||||
is a python tool for creating and managing profiles for Mozilla's
|
||||
applications (Firefox, Thunderbird, etc.). In addition to creating profiles,
|
||||
|
@ -1,6 +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/.
|
||||
# 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 profile import *
|
||||
from addons import *
|
||||
from cli import *
|
||||
|
@ -1,10 +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/.
|
||||
# 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 shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib2
|
||||
import zipfile
|
||||
@ -25,11 +24,14 @@ class AddonManager(object):
|
||||
profile - the path to the profile for which we install addons
|
||||
"""
|
||||
self.profile = profile
|
||||
self.installed_addons = []
|
||||
# keeps track of addons and manifests that were passed to install_addons
|
||||
self.addons = []
|
||||
self.manifests = []
|
||||
|
||||
# information needed for profile reset:
|
||||
# https://github.com/mozilla/mozbase/blob/270a857328b130860d1b1b512e23899557a3c8f7/mozprofile/mozprofile/profile.py#L93
|
||||
self.installed_addons = []
|
||||
self.installed_manifests = []
|
||||
|
||||
# addons that we've installed; needed for cleanup
|
||||
self._addon_dirs = []
|
||||
|
||||
def install_addons(self, addons=None, manifests=None):
|
||||
"""
|
||||
@ -41,6 +43,7 @@ class AddonManager(object):
|
||||
if addons:
|
||||
if isinstance(addons, basestring):
|
||||
addons = [addons]
|
||||
self.installed_addons.extend(addons)
|
||||
for addon in addons:
|
||||
self.install_from_path(addon)
|
||||
# install addon manifests
|
||||
@ -49,14 +52,13 @@ class AddonManager(object):
|
||||
manifests = [manifests]
|
||||
for manifest in manifests:
|
||||
self.install_from_manifest(manifest)
|
||||
|
||||
self.installed_manifests.extended(manifests)
|
||||
|
||||
def install_from_manifest(self, filepath):
|
||||
"""
|
||||
Installs addons from a manifest
|
||||
filepath - path to the manifest of addons to install
|
||||
"""
|
||||
self.manifests.append(filepath)
|
||||
manifest = ManifestParser()
|
||||
manifest.read(filepath)
|
||||
addons = manifest.get()
|
||||
@ -155,7 +157,6 @@ class AddonManager(object):
|
||||
- path: url, path to .xpi, or directory of addons
|
||||
- unpack: whether to unpack unless specified otherwise in the install.rdf
|
||||
"""
|
||||
self.addons.append(path)
|
||||
|
||||
# if the addon is a url, download it
|
||||
# note that this won't work with protocols urllib2 doesn't support
|
||||
@ -208,7 +209,7 @@ class AddonManager(object):
|
||||
shutil.copy(xpifile, addon_path + '.xpi')
|
||||
else:
|
||||
dir_util.copy_tree(addon, addon_path, preserve_symlinks=1)
|
||||
self.installed_addons.append(addon_path)
|
||||
self._addon_dirs.append(addon_path)
|
||||
|
||||
# remove the temporary directory, if any
|
||||
if tmpdir:
|
||||
@ -220,6 +221,6 @@ class AddonManager(object):
|
||||
|
||||
def clean_addons(self):
|
||||
"""Cleans up addons in the profile."""
|
||||
for addon in self.installed_addons:
|
||||
for addon in self._addon_dirs:
|
||||
if os.path.isdir(addon):
|
||||
dir_util.remove_tree(addon)
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/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/.
|
||||
# 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/.
|
||||
|
||||
"""
|
||||
Creates and/or modifies a Firefox profile.
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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/.
|
||||
|
||||
|
||||
"""
|
||||
@ -349,8 +349,12 @@ function FindProxyForURL(url, host)
|
||||
def clean_db(self):
|
||||
"""Removed permissions added by mozprofile."""
|
||||
|
||||
sqlite_file = os.path.join(self._profileDir, "permissions.sqlite")
|
||||
if not os.path.exists(sqlite_file):
|
||||
return
|
||||
|
||||
# Open database and create table
|
||||
permDB = sqlite3.connect(os.path.join(self._profileDir, "permissions.sqlite"))
|
||||
permDB = sqlite3.connect(sqlite_file)
|
||||
cursor = permDB.cursor();
|
||||
|
||||
# TODO: only delete values that we add, this would require sending in the full permissions object
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/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/.
|
||||
# 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/.
|
||||
|
||||
"""
|
||||
user preferences
|
||||
|
@ -1,6 +1,6 @@
|
||||
# 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/.
|
||||
# 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/.
|
||||
|
||||
__all__ = ['Profile', 'FirefoxProfile', 'ThunderbirdProfile']
|
||||
|
||||
@ -91,8 +91,8 @@ class Profile(object):
|
||||
else:
|
||||
profile = self.profile
|
||||
self.__init__(profile=profile,
|
||||
addons=self.addon_manager.addons,
|
||||
addon_manifests=self.addon_manager.manifests,
|
||||
addons=self.addon_manager.installed_addons,
|
||||
addon_manifests=self.addon_manager.installed_manifests,
|
||||
preferences=self._preferences,
|
||||
locations=self._locations,
|
||||
proxy = self._proxy)
|
||||
@ -170,6 +170,9 @@ class Profile(object):
|
||||
def clean_preferences(self):
|
||||
"""Removed preferences added by mozrunner."""
|
||||
for filename in self.written_prefs:
|
||||
if not os.path.exists(os.path.join(self.profile, filename)):
|
||||
# file has been deleted
|
||||
break
|
||||
while True:
|
||||
if not self.pop_preferences(filename):
|
||||
break
|
||||
@ -181,6 +184,7 @@ class Profile(object):
|
||||
process has not yet relinquished handles on files, so we do a wait/try
|
||||
construct and timeout if we can't get a clear road to deletion
|
||||
"""
|
||||
|
||||
try:
|
||||
from exceptions import WindowsError
|
||||
from time import sleep
|
||||
@ -202,7 +206,6 @@ class Profile(object):
|
||||
# We can't re-raise an error, so we'll hope the stuff above us will throw
|
||||
pass
|
||||
|
||||
|
||||
def cleanup(self):
|
||||
"""Cleanup operations for the profile."""
|
||||
if self.restore:
|
||||
|
@ -1,12 +1,12 @@
|
||||
# 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/.
|
||||
# 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 setuptools import setup, find_packages
|
||||
|
||||
version = '0.1'
|
||||
version = '0.4'
|
||||
|
||||
# we only support python 2 right now
|
||||
assert sys.version_info[0] == 2
|
||||
@ -32,14 +32,21 @@ except (OSError, IOError):
|
||||
|
||||
setup(name='mozprofile',
|
||||
version=version,
|
||||
description="handling of Mozilla XUL app profiles",
|
||||
description="Handling of Mozilla Gecko based application profiles",
|
||||
long_description=description,
|
||||
classifiers=[], # Get strings from http://pypi.python.org/pypi?%3Aaction=list_classifiers
|
||||
keywords='',
|
||||
author='Mozilla Automation + Testing Team',
|
||||
author_email='mozmill-dev@googlegroups.com',
|
||||
url='http://github.com/mozautomation/mozmill',
|
||||
license='MPL',
|
||||
classifiers=['Environment :: Console',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
],
|
||||
keywords='mozilla',
|
||||
author='Mozilla Automation and Tools team',
|
||||
author_email='tools@lists.mozilla.com',
|
||||
url='https://github.com/mozilla/mozbase/tree/master/mozprofile',
|
||||
license='MPL 2.0',
|
||||
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
|
||||
include_package_data=True,
|
||||
zip_safe=False,
|
||||
@ -49,4 +56,4 @@ setup(name='mozprofile',
|
||||
[console_scripts]
|
||||
mozprofile = mozprofile:cli
|
||||
""",
|
||||
)
|
||||
)
|
||||
|
46
testing/mozbase/mozprofile/tests/bug758250.py
Normal file
46
testing/mozbase/mozprofile/tests/bug758250.py
Normal file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import mozprofile
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
here = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
class Bug758250(unittest.TestCase):
|
||||
"""
|
||||
use of --profile in mozrunner just blows away addon sources:
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=758250
|
||||
"""
|
||||
|
||||
def test_profile_addon_cleanup(self):
|
||||
|
||||
# sanity check: the empty addon should be here
|
||||
empty = os.path.join(here, 'empty')
|
||||
self.assertTrue(os.path.exists(empty))
|
||||
self.assertTrue(os.path.isdir(empty))
|
||||
self.assertTrue(os.path.exists(os.path.join(empty, 'install.rdf')))
|
||||
|
||||
# because we are testing data loss, let's make sure we make a copy
|
||||
tmpdir = tempfile.mktemp()
|
||||
shutil.copytree(empty, tmpdir)
|
||||
self.assertTrue(os.path.exists(os.path.join(tmpdir, 'install.rdf')))
|
||||
|
||||
# make a starter profile
|
||||
profile = mozprofile.FirefoxProfile()
|
||||
path = profile.profile
|
||||
|
||||
# make a new profile based on the old
|
||||
newprofile = mozprofile.FirefoxProfile(profile=path, addons=[tmpdir])
|
||||
newprofile.cleanup()
|
||||
|
||||
# the source addon *should* still exist
|
||||
self.assertTrue(os.path.exists(tmpdir))
|
||||
self.assertTrue(os.path.exists(os.path.join(tmpdir, 'install.rdf')))
|
||||
|
||||
# remove vestiges
|
||||
shutil.rmtree(tmpdir)
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
20
testing/mozbase/mozprofile/tests/empty/install.rdf
Normal file
20
testing/mozbase/mozprofile/tests/empty/install.rdf
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>test-empty@quality.mozilla.org</em:id>
|
||||
<em:version>0.1</em:version>
|
||||
<em:name>Test Extension (empty)</em:name>
|
||||
<em:creator>Mozilla QA</em:creator>
|
||||
<em:homepageURL>http://quality.mozilla.org</em:homepageURL>
|
||||
<em:type>2</em:type>
|
||||
|
||||
<!-- Firefox -->
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>3.5.*</em:minVersion>
|
||||
<em:maxVersion>*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
@ -2,3 +2,4 @@
|
||||
[server_locations.py]
|
||||
[test_preferences.py]
|
||||
[permissions.py]
|
||||
[bug758250.py]
|
||||
|
@ -85,6 +85,31 @@ browser.startup.homepage = http://github.com/
|
||||
|
||||
# cleanup
|
||||
os.remove(name)
|
||||
|
||||
def test_reset_should_remove_added_prefs(self):
|
||||
"""Check that when we call reset the items we expect are updated"""
|
||||
|
||||
profile = Profile()
|
||||
prefs_file = os.path.join(profile.profile, 'user.js')
|
||||
|
||||
# we shouldn't have any initial preferences
|
||||
initial_prefs = Preferences.read_prefs(prefs_file)
|
||||
self.assertFalse(initial_prefs)
|
||||
initial_prefs = file(prefs_file).read().strip()
|
||||
self.assertFalse(initial_prefs)
|
||||
|
||||
# add some preferences
|
||||
prefs1 = [("mr.t.quotes", "i aint getting on no plane!")]
|
||||
profile.set_preferences(prefs1)
|
||||
self.assertEqual(prefs1, Preferences.read_prefs(prefs_file))
|
||||
lines = file(prefs_file).read().strip().splitlines()
|
||||
self.assertTrue('#MozRunner Prefs Start' in lines)
|
||||
self.assertTrue('#MozRunner Prefs End' in lines)
|
||||
|
||||
profile.reset()
|
||||
self.assertNotEqual(prefs1, \
|
||||
Preferences.read_prefs(os.path.join(profile.profile, 'user.js')),\
|
||||
"I pity the fool who left my pref")
|
||||
|
||||
def test_magic_markers(self):
|
||||
"""ensure our magic markers are working"""
|
||||
|
@ -1,13 +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/. -->
|
||||
|
||||
[mozrunner](https://github.com/mozilla/mozbase/tree/master/mozrunner)
|
||||
is a [python package](http://pypi.python.org/pypi/mozrunner)
|
||||
which handles running of Mozilla applications.
|
||||
mozrunner utilizes [mozprofile](/en/Mozprofile)
|
||||
mozrunner utilizes [mozprofile](https://github.com/mozilla/mozbase/tree/master/mozprofile)
|
||||
for managing application profiles
|
||||
and [mozprocess](/en/Mozprocess) for robust process control.
|
||||
and [mozprocess](https://github.com/mozilla/mozbase/tree/master/mozprocess) for robust process control.
|
||||
|
||||
mozrunner may be used from the command line or programmatically as an API.
|
||||
|
||||
@ -18,7 +14,7 @@ The `mozrunner` command will launch the application (specified by
|
||||
`--app`) from a binary specified with `-b` or as located on the `PATH`.
|
||||
|
||||
mozrunner takes the command line options from
|
||||
[mozprofile](/en/Mozprofile) for constructing the profile to be used by
|
||||
[mozprofile](https://github.com/mozilla/mozbase/tree/master/mozprofile) for constructing the profile to be used by
|
||||
the application.
|
||||
|
||||
Run `mozrunner --help` for detailed information on the command line
|
||||
|
@ -1,5 +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/.
|
||||
# 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 runner import *
|
||||
|
@ -1,6 +1,8 @@
|
||||
#!/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/.
|
||||
# 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/.
|
||||
|
||||
__all__ = ['Runner', 'ThunderbirdRunner', 'FirefoxRunner', 'runners', 'CLI', 'cli', 'package_metadata']
|
||||
|
||||
@ -8,24 +10,64 @@ import mozinfo
|
||||
import optparse
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import ConfigParser
|
||||
|
||||
from threading import Thread
|
||||
from utils import get_metadata_from_egg
|
||||
from utils import findInPath
|
||||
from mozprofile import *
|
||||
from mozprocess.processhandler import ProcessHandler
|
||||
|
||||
if mozinfo.isMac:
|
||||
from plistlib import readPlist
|
||||
|
||||
package_metadata = get_metadata_from_egg('mozrunner')
|
||||
|
||||
# Map of debugging programs to information about them
|
||||
# from http://mxr.mozilla.org/mozilla-central/source/build/automationutils.py#59
|
||||
debuggers = {'gdb': {'interactive': True,
|
||||
'args': ['-q', '--args'],},
|
||||
'valgrind': {'interactive': False,
|
||||
'args': ['--leak-check=full']}
|
||||
}
|
||||
|
||||
def debugger_arguments(debugger, arguments=None, interactive=None):
|
||||
"""
|
||||
finds debugger arguments from debugger given and defaults
|
||||
* debugger : debugger name or path to debugger
|
||||
* arguments : arguments to the debugger, or None to use defaults
|
||||
* interactive : whether the debugger should be run in interactive mode, or None to use default
|
||||
"""
|
||||
|
||||
# find debugger executable if not a file
|
||||
executable = debugger
|
||||
if not os.path.exists(executable):
|
||||
executable = findInPath(debugger)
|
||||
if executable is None:
|
||||
raise Exception("Path to '%s' not found" % debugger)
|
||||
|
||||
# if debugger not in dictionary of knowns return defaults
|
||||
dirname, debugger = os.path.split(debugger)
|
||||
if debugger not in debuggers:
|
||||
return ([executable] + (arguments or []), bool(interactive))
|
||||
|
||||
# otherwise use the dictionary values for arguments unless specified
|
||||
if arguments is None:
|
||||
arguments = debuggers[debugger].get('args', [])
|
||||
if interactive is None:
|
||||
interactive = debuggers[debugger].get('interactive', False)
|
||||
return ([executable] + arguments, interactive)
|
||||
|
||||
class Runner(object):
|
||||
"""Handles all running operations. Finds bins, runs and kills the process."""
|
||||
|
||||
profile_class = Profile # profile class to use by default
|
||||
|
||||
@classmethod
|
||||
def create(cls, binary, cmdargs=None, env=None, kp_kwargs=None, profile_args=None,
|
||||
clean_profile=True, process_class=ProcessHandler):
|
||||
def create(cls, binary=None, cmdargs=None, env=None, kp_kwargs=None, profile_args=None,
|
||||
clean_profile=True, process_class=ProcessHandler):
|
||||
profile = cls.profile_class(**(profile_args or {}))
|
||||
return cls(profile, binary=binary, cmdargs=cmdargs, env=env, kp_kwargs=kp_kwargs,
|
||||
clean_profile=clean_profile, process_class=process_class)
|
||||
@ -44,6 +86,13 @@ class Runner(object):
|
||||
if not os.path.exists(self.binary):
|
||||
raise OSError("Binary path does not exist: %s" % self.binary)
|
||||
|
||||
# allow Mac binaries to be specified as an app bundle
|
||||
plist = '%s/Contents/Info.plist' % self.binary
|
||||
if mozinfo.isMac and os.path.exists(plist):
|
||||
info = readPlist(plist)
|
||||
self.binary = os.path.join(self.binary, "Contents/MacOS/",
|
||||
info['CFBundleExecutable'])
|
||||
|
||||
self.cmdargs = cmdargs or []
|
||||
_cmdargs = [i for i in self.cmdargs
|
||||
if i != '-foreground']
|
||||
@ -102,8 +151,11 @@ class Runner(object):
|
||||
def is_running(self):
|
||||
return self.process_handler is not None
|
||||
|
||||
def start(self):
|
||||
"""Run self.command in the proper environment."""
|
||||
def start(self, debug_args=None, interactive=False):
|
||||
"""
|
||||
Run self.command in the proper environment.
|
||||
- debug_args: arguments for the debugger
|
||||
"""
|
||||
|
||||
# ensure you are stopped
|
||||
self.stop()
|
||||
@ -111,17 +163,35 @@ class Runner(object):
|
||||
# ensure the profile exists
|
||||
if not self.profile.exists():
|
||||
self.profile.reset()
|
||||
|
||||
assert self.profile.exists(), "%s : failure to reset profile" % self.__class__.__name__
|
||||
|
||||
cmd = self._wrap_command(self.command+self.cmdargs)
|
||||
# this run uses the managed processhandler
|
||||
self.process_handler = self.process_class(cmd, env=self.env, **self.kp_kwargs)
|
||||
self.process_handler.run()
|
||||
|
||||
# attach a debugger, if specified
|
||||
if debug_args:
|
||||
cmd = list(debug_args) + cmd
|
||||
|
||||
#
|
||||
if interactive:
|
||||
self.process_handler = subprocess.Popen(cmd, env=self.env)
|
||||
# TODO: other arguments
|
||||
else:
|
||||
# this run uses the managed processhandler
|
||||
self.process_handler = self.process_class(cmd, env=self.env, **self.kp_kwargs)
|
||||
self.process_handler.run()
|
||||
|
||||
# Spin a thread to handle reading the output
|
||||
self.outThread = OutputThread(self.process_handler)
|
||||
self.outThread.start()
|
||||
|
||||
def wait(self, timeout=None, outputTimeout=None):
|
||||
"""Wait for the app to exit."""
|
||||
if self.process_handler is None:
|
||||
return
|
||||
self.process_handler.waitForFinish(timeout=timeout, outputTimeout=outputTimeout)
|
||||
if isinstance(self.process_handler, subprocess.Popen):
|
||||
self.process_handler.wait()
|
||||
else:
|
||||
self.process_handler.waitForFinish(timeout=timeout, outputTimeout=outputTimeout)
|
||||
self.process_handler = None
|
||||
|
||||
def stop(self):
|
||||
@ -152,7 +222,7 @@ class Runner(object):
|
||||
if mozinfo.isMac and hasattr(platform, 'mac_ver') and \
|
||||
platform.mac_ver()[0][:4] < '10.6':
|
||||
return ["arch", "-arch", "i386"] + cmd
|
||||
return cmd
|
||||
return cmd
|
||||
|
||||
__del__ = cleanup
|
||||
|
||||
@ -170,21 +240,6 @@ class FirefoxRunner(Runner):
|
||||
|
||||
Runner.__init__(self, profile, binary, **kwargs)
|
||||
|
||||
# Find application version number
|
||||
appdir = os.path.dirname(os.path.realpath(self.binary))
|
||||
appini = ConfigParser.RawConfigParser()
|
||||
appini.read(os.path.join(appdir, 'application.ini'))
|
||||
# Version needs to be of the form 3.6 or 4.0b and not the whole string
|
||||
version = appini.get('App', 'Version').rstrip('0123456789pre').rstrip('.')
|
||||
|
||||
# Disable compatibility check. See:
|
||||
# - http://kb.mozillazine.org/Extensions.checkCompatibility
|
||||
# - https://bugzilla.mozilla.org/show_bug.cgi?id=659048
|
||||
preference = {'extensions.checkCompatibility.' + version: False,
|
||||
'extensions.checkCompatibility.nightly': False}
|
||||
self.profile.set_preferences(preference)
|
||||
|
||||
|
||||
class ThunderbirdRunner(Runner):
|
||||
"""Specialized Runner subclass for running Thunderbird"""
|
||||
profile_class = ThunderbirdProfile
|
||||
@ -192,6 +247,13 @@ class ThunderbirdRunner(Runner):
|
||||
runners = {'firefox': FirefoxRunner,
|
||||
'thunderbird': ThunderbirdRunner}
|
||||
|
||||
class OutputThread(Thread):
|
||||
def __init__(self, prochandler):
|
||||
Thread.__init__(self)
|
||||
self.ph = prochandler
|
||||
def run(self):
|
||||
self.ph.waitForFinish()
|
||||
|
||||
class CLI(MozProfileCLI):
|
||||
"""Command line interface."""
|
||||
|
||||
@ -239,6 +301,14 @@ class CLI(MozProfileCLI):
|
||||
parser.add_option('--app-arg', dest='appArgs',
|
||||
default=[], action='append',
|
||||
help="provides an argument to the test application")
|
||||
parser.add_option('--debugger', dest='debugger',
|
||||
help="run under a debugger, e.g. gdb or valgrind")
|
||||
parser.add_option('--debugger-args', dest='debugger_args',
|
||||
action='append', default=None,
|
||||
help="arguments to the debugger")
|
||||
parser.add_option('--interactive', dest='interactive',
|
||||
action='store_true',
|
||||
help="run the program interactively")
|
||||
if self.metadata:
|
||||
parser.add_option("--info", dest="info", default=False,
|
||||
action="store_true",
|
||||
@ -284,10 +354,24 @@ class CLI(MozProfileCLI):
|
||||
self.start(runner)
|
||||
runner.cleanup()
|
||||
|
||||
def debugger_arguments(self):
|
||||
"""
|
||||
returns a 2-tuple of debugger arguments:
|
||||
(debugger_arguments, interactive)
|
||||
"""
|
||||
debug_args = self.options.debugger_args
|
||||
interactive = self.options.interactive
|
||||
if self.options.debugger:
|
||||
debug_args, interactive = debugger_arguments(self.options.debugger)
|
||||
return debug_args, interactive
|
||||
|
||||
def start(self, runner):
|
||||
"""Starts the runner and waits for Firefox to exit or Keyboard Interrupt.
|
||||
Shoule be overwritten to provide custom running of the runner instance."""
|
||||
runner.start()
|
||||
|
||||
# attach a debugger if specified
|
||||
debug_args, interactive = self.debugger_arguments()
|
||||
runner.start(debug_args=debug_args, interactive=interactive)
|
||||
print 'Starting:', ' '.join(runner.command)
|
||||
try:
|
||||
runner.wait()
|
||||
|
@ -1,7 +1,6 @@
|
||||
#!/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/.
|
||||
# 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/.
|
||||
|
||||
"""
|
||||
utility functions for mozrunner
|
||||
|
@ -1,13 +1,13 @@
|
||||
# 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/.
|
||||
# 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 setuptools import setup, find_packages
|
||||
|
||||
PACKAGE_NAME = "mozrunner"
|
||||
PACKAGE_VERSION = "5.1"
|
||||
PACKAGE_VERSION = '5.7'
|
||||
|
||||
desc = """Reliable start/stop/configuration of Mozilla Applications (Firefox, Thunderbird, etc.)"""
|
||||
# take description from README
|
||||
@ -17,9 +17,9 @@ try:
|
||||
except (OSError, IOError):
|
||||
description = ''
|
||||
|
||||
deps = ['mozinfo',
|
||||
'mozprocess',
|
||||
'mozprofile >= 0.1',
|
||||
deps = ['mozinfo == 0.3.3',
|
||||
'mozprocess == 0.3',
|
||||
'mozprofile == 0.4',
|
||||
]
|
||||
|
||||
# we only support python 2 right now
|
||||
@ -29,23 +29,25 @@ setup(name=PACKAGE_NAME,
|
||||
version=PACKAGE_VERSION,
|
||||
description=desc,
|
||||
long_description=description,
|
||||
author='Mikeal Rogers, Mozilla',
|
||||
author_email='mikeal.rogers@gmail.com',
|
||||
url='http://github.com/mozautomation/mozmill',
|
||||
license='MPL 1.1/GPL 2.0/LGPL 2.1',
|
||||
classifiers=['Environment :: Console',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)',
|
||||
'Natural Language :: English',
|
||||
'Operating System :: OS Independent',
|
||||
'Programming Language :: Python',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
],
|
||||
keywords='mozilla',
|
||||
author='Mozilla Automation and Tools team',
|
||||
author_email='tools@lists.mozilla.com',
|
||||
url='https://github.com/mozilla/mozbase/tree/master/mozrunner',
|
||||
license='MPL 2.0',
|
||||
packages=find_packages(exclude=['legacy']),
|
||||
zip_safe=False,
|
||||
entry_points="""
|
||||
[console_scripts]
|
||||
mozrunner = mozrunner:cli
|
||||
""",
|
||||
platforms =['Any'],
|
||||
install_requires = deps,
|
||||
classifiers=['Development Status :: 4 - Beta',
|
||||
'Environment :: Console',
|
||||
'Intended Audience :: Developers',
|
||||
'License :: OSI Approved :: Mozilla Public License 1.1 (MPL 1.1)',
|
||||
'Operating System :: OS Independent',
|
||||
'Topic :: Software Development :: Libraries :: Python Modules',
|
||||
]
|
||||
)
|
||||
entry_points="""
|
||||
# -*- Entry points: -*-
|
||||
[console_scripts]
|
||||
mozrunner = mozrunner:cli
|
||||
""",
|
||||
)
|
||||
|
@ -1,7 +1,8 @@
|
||||
#!/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/.
|
||||
# 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 mozbase packages for development.
|
||||
@ -12,8 +13,6 @@ If no arguments are given, install all packages.
|
||||
See https://wiki.mozilla.org/Auto-tools/Projects/MozBase
|
||||
"""
|
||||
|
||||
# XXX note that currently directory names must equal package names
|
||||
|
||||
import pkg_resources
|
||||
import os
|
||||
import sys
|
||||
@ -149,13 +148,20 @@ def main(args=sys.argv[1:]):
|
||||
parser.exit()
|
||||
|
||||
# gather dependencies
|
||||
# TODO: version conflict checking
|
||||
deps = {}
|
||||
alldeps = {}
|
||||
mapping = {} # mapping from subdir name to package name
|
||||
# core dependencies
|
||||
for package in packages:
|
||||
key, value = dependencies(os.path.join(here, package))
|
||||
deps[key] = [sanitize_dependency(dep) for dep in value]
|
||||
mapping[package] = key
|
||||
|
||||
# keep track of all dependencies for non-mozbase packages
|
||||
for dep in value:
|
||||
alldeps[sanitize_dependency(dep)] = ''.join(dep.split())
|
||||
|
||||
# indirect dependencies
|
||||
flag = True
|
||||
while flag:
|
||||
@ -165,6 +171,9 @@ def main(args=sys.argv[1:]):
|
||||
if dep in all_packages and dep not in deps:
|
||||
key, value = dependencies(os.path.join(here, dep))
|
||||
deps[key] = [sanitize_dependency(dep) for dep in value]
|
||||
|
||||
for dep in value:
|
||||
alldeps[sanitize_dependency(dep)] = ''.join(dep.split())
|
||||
mapping[package] = key
|
||||
flag = True
|
||||
break
|
||||
@ -193,9 +202,20 @@ def main(args=sys.argv[1:]):
|
||||
print package
|
||||
parser.exit()
|
||||
|
||||
# install non-mozbase dependencies
|
||||
# (currently none on modern python)
|
||||
# these need to be installed separately and the --no-deps flag
|
||||
# subsequently used due to a bug in setuptools; see
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=759836
|
||||
pypi_deps = dict([(i, j) for i,j in alldeps.items()
|
||||
if i not in unrolled])
|
||||
for package, version in pypi_deps.items():
|
||||
# easy_install should be available since we rely on setuptools
|
||||
call(['easy_install', version])
|
||||
|
||||
# set up the packages for development
|
||||
for package in unrolled:
|
||||
call([sys.executable, 'setup.py', 'develop'],
|
||||
call([sys.executable, 'setup.py', 'develop', '--no-deps'],
|
||||
cwd=os.path.join(here, reverse_mapping[package]))
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -1,7 +1,3 @@
|
||||
; 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/.
|
||||
|
||||
# mozbase test manifest, in the format of
|
||||
# https://github.com/mozilla/mozbase/blob/master/manifestdestiny/README.txt
|
||||
|
||||
@ -11,3 +7,4 @@
|
||||
[include:mozprocess/tests/manifest.ini]
|
||||
[include:mozprofile/tests/manifest.ini]
|
||||
[include:mozhttpd/tests/manifest.ini]
|
||||
[include:mozdevice/tests/manifest.ini]
|
||||
|
@ -1,8 +1,4 @@
|
||||
#!/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 mozbase tests from a manifest,
|
||||
|
Loading…
Reference in New Issue
Block a user