wip: host support

This commit is contained in:
David Rose 2009-08-21 15:47:39 +00:00
parent 494d61083e
commit ae8c460b1b
22 changed files with 749 additions and 387 deletions

View File

@ -32,6 +32,7 @@
p3dFileDownload.h p3dFileDownload.I \
p3dFileParams.h p3dFileParams.I \
p3dFloatObject.h \
p3dHost.h p3dHost.I \
p3dInstance.h p3dInstance.I \
p3dInstanceManager.h p3dInstanceManager.I \
p3dIntObject.h \
@ -62,6 +63,7 @@
p3dFileDownload.cxx \
p3dFileParams.cxx \
p3dFloatObject.cxx \
p3dHost.cxx \
p3dInstance.cxx \
p3dInstanceManager.cxx \
p3dIntObject.cxx \

View File

@ -24,6 +24,17 @@ get_filename() const {
return _filename;
}
////////////////////////////////////////////////////////////////////
// Function: FileSpec::set_filename
// Access: Private
// Description: Changes the relative path to this file on disk,
// within the package root directory.
////////////////////////////////////////////////////////////////////
inline void FileSpec::
set_filename(const string &filename) {
_filename = filename;
}
////////////////////////////////////////////////////////////////////
// Function: FileSpec::get_pathname
// Access: Private

View File

@ -32,6 +32,7 @@ public:
void load_xml(TiXmlElement *element);
inline const string &get_filename() const;
inline void set_filename(const string &filename);
inline string get_pathname(const string &package_dir) const;
inline size_t get_size() const;

View File

@ -0,0 +1,73 @@
// Filename: p3dHost.I
// Created by: drose (21Aug09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////
// Function: P3DHost::get_host_dir
// Access: Public
// Description: Returns the local directory into which files
// downloaded from this host will be installed.
////////////////////////////////////////////////////////////////////
inline const string &P3DHost::
get_host_dir() const {
return _host_dir;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::get_host_url
// Access: Public
// Description: Returns the root URL of this particular host, as
// passed from the package file. This is a unique
// string that identifies each host.
////////////////////////////////////////////////////////////////////
inline const string &P3DHost::
get_host_url() const {
return _host_url;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::get_host_url_prefix
// Access: Public
// Description: Returns the root URL of this host, for constructing
// full URL sequences. This is the same as
// get_host_url(), except it is guaranteed to end in a
// slash character.
////////////////////////////////////////////////////////////////////
inline const string &P3DHost::
get_host_url_prefix() const {
return _host_url_prefix;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::get_descriptive_name
// Access: Public
// Description: Returns the descriptive name provided for this host,
// if any. This will be available after
// read_contents_file() has been called.
////////////////////////////////////////////////////////////////////
inline const string &P3DHost::
get_descriptive_name() const {
return _descriptive_name;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::has_contents_file
// Access: Public
// Description: Returns true if a contents.xml file has been
// successfully read for this host, false otherwise.
////////////////////////////////////////////////////////////////////
inline bool P3DHost::
has_contents_file() const {
return (_xcontents != NULL);
}

View File

@ -0,0 +1,265 @@
// Filename: p3dHost.cxx
// Created by: drose (21Aug09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#include "p3dHost.h"
#include "p3dInstanceManager.h"
#include "p3dPackage.h"
////////////////////////////////////////////////////////////////////
// Function: P3DHost::Constructor
// Access: Private
// Description: Use P3DInstanceManager::get_host() to construct a new
// P3DHost.
////////////////////////////////////////////////////////////////////
P3DHost::
P3DHost(P3DInstanceManager *inst_mgr, const string &host_url) :
_host_url(host_url)
{
_host_dir = inst_mgr->get_root_dir();
_host_dir += "/host"; // TODO.
// Ensure that the download URL ends with a slash.
_host_url_prefix = _host_url;
if (!_host_url_prefix.empty() && _host_url_prefix[_host_url_prefix.size() - 1] != '/') {
_host_url_prefix += "/";
}
_xcontents = NULL;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::Destructor
// Access: Private
// Description:
////////////////////////////////////////////////////////////////////
P3DHost::
~P3DHost() {
if (_xcontents != NULL) {
delete _xcontents;
}
Packages::iterator pi;
for (pi = _packages.begin(); pi != _packages.end(); ++pi) {
delete (*pi).second;
}
_packages.clear();
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::read_contents_file
// Access: Public
// Description: Reads the contents.xml file in the indicated
// filename. On success, copies the contents.xml file
// into the standard location (if it's not there
// already).
//
// Returns true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DHost::
read_contents_file(const string &contents_filename) {
TiXmlDocument doc(contents_filename.c_str());
if (!doc.LoadFile()) {
return false;
}
TiXmlElement *xcontents = doc.FirstChildElement("contents");
if (xcontents == NULL) {
return false;
}
if (_xcontents != NULL) {
delete _xcontents;
}
_xcontents = (TiXmlElement *)xcontents->Clone();
string standard_filename = _host_dir + "/contents.xml";
if (standardize_filename(standard_filename) !=
standardize_filename(contents_filename)) {
copy_file(contents_filename, standard_filename);
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::get_package
// Access: Public
// Description: Returns a (possibly shared) pointer to the indicated
// package.
////////////////////////////////////////////////////////////////////
P3DPackage *P3DHost::
get_package(const string &package_name, const string &package_version) {
string key = package_name + "_" + package_version;
Packages::iterator pi = _packages.find(key);
if (pi != _packages.end()) {
return (*pi).second;
}
P3DPackage *package =
new P3DPackage(this, package_name, package_version);
bool inserted = _packages.insert(Packages::value_type(key, package)).second;
assert(inserted);
return package;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::get_package_desc_file
// Access: Public
// Description: Fills the indicated FileSpec with the hash
// information for the package's desc file, and also
// determines the package's platform. Returns true if
// successful, false if the package is unknown. This
// requires has_contents_file() to return true in order
// to be successful.
////////////////////////////////////////////////////////////////////
bool P3DHost::
get_package_desc_file(FileSpec &desc_file, // out
string &package_platform, // out
const string &package_name, // in
const string &package_version) { // in
if (_xcontents == NULL) {
return false;
}
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
// Scan the contents data for the indicated package. First, we look
// for a platform-specific version.
TiXmlElement *xpackage = _xcontents->FirstChildElement("package");
while (xpackage != NULL) {
const char *name = xpackage->Attribute("name");
const char *platform = xpackage->Attribute("platform");
const char *version = xpackage->Attribute("version");
if (name != NULL && platform != NULL && version != NULL &&
package_name == name &&
inst_mgr->get_platform() == platform &&
package_version == version) {
// Here's the matching package definition.
desc_file.load_xml(xpackage);
package_platform = platform;
return true;
}
xpackage = xpackage->NextSiblingElement("package");
}
// Look again, this time looking for a non-platform-specific version.
xpackage = _xcontents->FirstChildElement("package");
while (xpackage != NULL) {
const char *name = xpackage->Attribute("name");
const char *platform = xpackage->Attribute("platform");
const char *version = xpackage->Attribute("version");
if (platform == NULL) {
platform = "";
}
if (name != NULL && version != NULL &&
package_name == name &&
*platform == '\0' &&
package_version == version) {
// Here's the matching package definition.
desc_file.load_xml(xpackage);
package_platform = platform;
return true;
}
xpackage = xpackage->NextSiblingElement("package");
}
// Couldn't find the named package.
return false;
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::standardize_filename
// Access: Private, Static
// Description: Attempts to change the filename into some standard
// form for comparison with other filenames. On a
// case-insensitive filesystem, this converts the
// filename to lowercase. On Windows, it further
// replaces forward slashes with backslashes.
////////////////////////////////////////////////////////////////////
string P3DHost::
standardize_filename(const string &filename) {
#if defined(_WIN32) || defined(__APPLE__)
string new_filename;
for (string::const_iterator si = filename.begin();
si != filename.end();
++si) {
char ch = *si;
#ifdef _WIN32
if (ch == '/') {
ch = '\\';
}
#endif // _WIN32
new_filename += tolower(ch);
}
return new_filename;
#else // _WIN32 || __APPLE__
return filename;
#endif // _WIN32 || __APPLE__
}
////////////////////////////////////////////////////////////////////
// Function: P3DHost::copy_file
// Access: Private, Static
// Description: Copies the data in the file named by from_filename
// into the file named by to_filename.
////////////////////////////////////////////////////////////////////
bool P3DHost::
copy_file(const string &from_filename, const string &to_filename) {
ifstream in(from_filename.c_str(), ios::in | ios::binary);
// Copy to a temporary file first, in case (a) the filenames
// actually refer to the same file, or (b) in case we have different
// processes writing to the same file, and (c) to prevent
// partially overwriting the file should something go wrong.
ostringstream strm;
strm << to_filename << ".t";
#ifdef _WIN32
strm << GetCurrentProcessId() << "_" << GetCurrentThreadId();
#else
strm << getpid();
#endif
string temp_filename = strm.str();
ofstream out(temp_filename.c_str(), ios::out | ios::binary);
static const size_t buffer_size = 4096;
char buffer[buffer_size];
in.read(buffer, buffer_size);
size_t count = in.gcount();
while (count != 0) {
out.write(buffer, count);
if (out.fail()) {
unlink(temp_filename.c_str());
return false;
}
in.read(buffer, buffer_size);
count = in.gcount();
}
out.close();
if (!in.eof()) {
unlink(temp_filename.c_str());
return false;
}
if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
return true;
}
unlink(temp_filename.c_str());
return false;
}

View File

@ -0,0 +1,71 @@
// Filename: p3dHost.h
// Created by: drose (21Aug09)
//
////////////////////////////////////////////////////////////////////
//
// PANDA 3D SOFTWARE
// Copyright (c) Carnegie Mellon University. All rights reserved.
//
// All use of this software is subject to the terms of the revised BSD
// license. You should have received a copy of this license along
// with this source code in a file named "LICENSE."
//
////////////////////////////////////////////////////////////////////
#ifndef P3DHOST_H
#define P3DHOST_H
#include "p3d_plugin_common.h"
#include <map>
class FileSpec;
class P3DInstanceManager;
class P3DPackage;
////////////////////////////////////////////////////////////////////
// Class : P3DHost
// Description : Represents a particular download host serving up
// Panda3D packages.
////////////////////////////////////////////////////////////////////
class P3DHost {
private:
P3DHost(P3DInstanceManager *inst_mgr, const string &host_url);
~P3DHost();
public:
inline const string &get_host_dir() const;
inline const string &get_host_url() const;
inline const string &get_host_url_prefix() const;
inline const string &get_descriptive_name() const;
inline bool has_contents_file() const;
bool read_contents_file(const string &contents_filename);
P3DPackage *get_package(const string &package_name,
const string &package_version);
bool get_package_desc_file(FileSpec &desc_file,
string &package_platform,
const string &package_name,
const string &package_version);
private:
static string standardize_filename(const string &filename);
static bool copy_file(const string &from_filename, const string &to_filename);
private:
string _host_dir;
string _host_url;
string _host_url_prefix;
string _descriptive_name;
TiXmlElement *_xcontents;
typedef map<string, P3DPackage *> Packages;
Packages _packages;
friend class P3DInstanceManager;
};
#include "p3dHost.I"
#endif

View File

@ -884,12 +884,14 @@ scan_app_desc_file(TiXmlDocument *doc) {
TiXmlElement *xrequires = xpackage->FirstChildElement("requires");
while (xrequires != NULL) {
const char *name = xrequires->Attribute("name");
if (name != NULL) {
const char *host_url = xrequires->Attribute("host");
if (name != NULL && host_url != NULL) {
const char *version = xrequires->Attribute("version");
if (version == NULL) {
version = "";
}
P3DPackage *package = inst_mgr->get_package(name, version);
P3DHost *host = inst_mgr->get_host(host_url);
P3DPackage *package = host->get_package(name, version);
add_package(package);
}
@ -1177,9 +1179,10 @@ make_splash_window() {
if (!_fparams.has_token("splash_img")) {
// No specific splash image is specified; get the default splash
// image.
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
splash_image_url = inst_mgr->get_download_url();
splash_image_url += "coreapi/splash.jpg";
if (_panda3d != NULL) {
splash_image_url = _panda3d->get_host()->get_host_url_prefix();
splash_image_url += "coreapi/splash.jpg";
}
}
if (splash_image_url.empty()) {

View File

@ -36,18 +36,6 @@ get_root_dir() const {
return _root_dir;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_download_url
// Access: Public
// Description: Returns the URL of the download server. All
// downloadable files will be retrieved from various
// subdirectories of this URL root.
////////////////////////////////////////////////////////////////////
inline const string &P3DInstanceManager::
get_download_url() const {
return _download_url;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_platform
// Access: Public
@ -74,17 +62,6 @@ get_log_directory() const {
return _log_directory;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::has_contents_file
// Access: Public
// Description: Returns true if a contents.xml file has been
// successfully read, false otherwise.
////////////////////////////////////////////////////////////////////
inline bool P3DInstanceManager::
has_contents_file() const {
return (_xcontents != NULL);
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_num_instances
// Access: Public

View File

@ -15,7 +15,7 @@
#include "p3dInstanceManager.h"
#include "p3dInstance.h"
#include "p3dSession.h"
#include "p3dPackage.h"
#include "p3dHost.h"
#include "p3d_plugin_config.h"
#include "p3dWinSplashWindow.h"
#include "p3dUndefinedObject.h"
@ -51,7 +51,6 @@ P3DInstanceManager() {
_is_initialized = false;
_next_temp_filename_counter = 0;
_unique_id = 0;
_xcontents = NULL;
_notify_thread_continue = false;
_started_notify_thread = false;
@ -103,13 +102,15 @@ P3DInstanceManager::
sigaction(SIGPIPE, &_old_sigpipe, NULL);
#endif // _WIN32
if (_xcontents != NULL) {
delete _xcontents;
}
assert(_instances.empty());
assert(_sessions.empty());
Hosts::iterator hi;
for (hi = _hosts.begin(); hi != _hosts.end(); ++hi) {
delete (*hi).second;
}
_hosts.clear();
// Delete any remaining temporary files.
TempFilenames::iterator ti;
for (ti = _temp_filenames.begin(); ti != _temp_filenames.end(); ++ti) {
@ -145,7 +146,7 @@ P3DInstanceManager::
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::initialize
// Access: Public
// Description: Called by the host at application startup. It
// Description: Called by the plugin host at application startup. It
// returns true if the DLL is successfully initialized,
// false if it should be immediately shut down and
// redownloaded.
@ -156,12 +157,7 @@ initialize(const string &contents_filename, const string &download_url,
const string &log_basename) {
_root_dir = find_root_dir();
_download_url = download_url;
#ifdef P3D_PLUGIN_DOWNLOAD
if (_download_url.empty()) {
_download_url = P3D_PLUGIN_DOWNLOAD;
}
#endif
_platform = platform;
if (_platform.empty()) {
_platform = DTOOL_PLATFORM;
@ -197,7 +193,7 @@ initialize(const string &contents_filename, const string &download_url,
}
delete[] buffer_2;
// Also make sure the directory actually exists.
// And make sure the directory actually exists.
mkdir_complete(_temp_directory, nout);
#else
@ -216,11 +212,6 @@ initialize(const string &contents_filename, const string &download_url,
}
#endif
// Ensure that the download URL ends with a slash.
if (!_download_url.empty() && _download_url[_download_url.size() - 1] != '/') {
_download_url += "/";
}
// Ensure that the temp directory ends with a slash.
if (!_temp_directory.empty() && _temp_directory[_temp_directory.size() - 1] != '/') {
#ifdef _WIN32
@ -251,8 +242,6 @@ initialize(const string &contents_filename, const string &download_url,
}
nout << "_root_dir = " << _root_dir
<< ", contents = " << contents_filename
<< ", download = " << _download_url
<< ", platform = " << _platform
<< "\n";
@ -263,9 +252,11 @@ initialize(const string &contents_filename, const string &download_url,
_is_initialized = true;
// Attempt to read the supplied contents.xml file.
if (!contents_filename.empty()) {
if (!read_contents_file(contents_filename)) {
if (!download_url.empty() && !contents_filename.empty()) {
// Attempt to pre-read the supplied contents.xml file, to avoid an
// unnecessary download later.
P3DHost *host = get_host(download_url);
if (!host->read_contents_file(contents_filename)) {
nout << "Couldn't read " << contents_filename << "\n";
}
}
@ -273,42 +264,6 @@ initialize(const string &contents_filename, const string &download_url,
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::read_contents_file
// Access: Public
// Description: Reads the contents.xml file in the indicated
// filename. On success, copies the contents.xml file
// into the standard location (if it's not there
// already).
//
// Returns true on success, false on failure.
////////////////////////////////////////////////////////////////////
bool P3DInstanceManager::
read_contents_file(const string &contents_filename) {
TiXmlDocument doc(contents_filename.c_str());
if (!doc.LoadFile()) {
return false;
}
TiXmlElement *xcontents = doc.FirstChildElement("contents");
if (xcontents == NULL) {
return false;
}
if (_xcontents != NULL) {
delete _xcontents;
}
_xcontents = (TiXmlElement *)xcontents->Clone();
string standard_filename = _root_dir + "/contents.xml";
if (standardize_filename(standard_filename) !=
standardize_filename(contents_filename)) {
copy_file(contents_filename, standard_filename);
}
return true;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::create_instance
// Access: Public
@ -473,67 +428,23 @@ wait_request() {
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_package
// Function: P3DInstanceManager::get_host
// Access: Public
// Description: Returns a (possibly shared) pointer to the indicated
// package.
// download host.
////////////////////////////////////////////////////////////////////
P3DPackage *P3DInstanceManager::
get_package(const string &package_name, const string &package_version) {
string package_platform = get_platform();
string key = package_name + "_" + package_platform + "_" + package_version;
Packages::iterator pi = _packages.find(key);
if (pi != _packages.end()) {
P3DHost *P3DInstanceManager::
get_host(const string &host_url) {
Hosts::iterator pi = _hosts.find(host_url);
if (pi != _hosts.end()) {
return (*pi).second;
}
P3DPackage *package =
new P3DPackage(package_name, package_platform, package_version);
bool inserted = _packages.insert(Packages::value_type(key, package)).second;
P3DHost *host = new P3DHost(this, host_url);
bool inserted = _hosts.insert(Hosts::value_type(host_url, host)).second;
assert(inserted);
return package;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::get_package_desc_file
// Access: Public
// Description: Fills the indicated FileSpec with the hash
// information for the package's desc file. Returns
// true if successful, false if the package is unknown.
// This requires has_contents_file() to return true in
// order to be successful.
////////////////////////////////////////////////////////////////////
bool P3DInstanceManager::
get_package_desc_file(FileSpec &desc_file,
const string &package_name,
const string &package_version) {
if (_xcontents == NULL) {
return false;
}
string package_platform = get_platform();
// Scan the contents data for the indicated package.
TiXmlElement *xpackage = _xcontents->FirstChildElement("package");
while (xpackage != NULL) {
const char *name = xpackage->Attribute("name");
const char *platform = xpackage->Attribute("platform");
const char *version = xpackage->Attribute("version");
if (name != NULL && platform != NULL && version != NULL &&
package_name == name &&
package_platform == platform &&
package_version == version) {
// Here's the matching package definition.
desc_file.load_xml(xpackage);
return true;
}
xpackage = xpackage->NextSiblingElement("package");
}
// Couldn't find the named package.
return false;
return host;
}
////////////////////////////////////////////////////////////////////
@ -698,89 +609,6 @@ delete_global_ptr() {
}
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::standardize_filename
// Access: Private, Static
// Description: Attempts to change the filename into some standard
// form for comparison with other filenames. On a
// case-insensitive filesystem, this converts the
// filename to lowercase. On Windows, it further
// replaces forward slashes with backslashes.
////////////////////////////////////////////////////////////////////
string P3DInstanceManager::
standardize_filename(const string &filename) {
#if defined(_WIN32) || defined(__APPLE__)
string new_filename;
for (string::const_iterator si = filename.begin();
si != filename.end();
++si) {
char ch = *si;
#ifdef _WIN32
if (ch == '/') {
ch = '\\';
}
#endif // _WIN32
new_filename += tolower(ch);
}
return new_filename;
#else // _WIN32 || __APPLE__
return filename;
#endif // _WIN32 || __APPLE__
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::copy_file
// Access: Private, Static
// Description: Copies the data in the file named by from_filename
// into the file named by to_filename.
////////////////////////////////////////////////////////////////////
bool P3DInstanceManager::
copy_file(const string &from_filename, const string &to_filename) {
ifstream in(from_filename.c_str(), ios::in | ios::binary);
// Copy to a temporary file first, in case (a) the filenames
// actually refer to the same file, or (b) in case we have different
// processes writing to the same file, and (c) to prevent
// partially overwriting the file should something go wrong.
ostringstream strm;
strm << to_filename << ".t";
#ifdef _WIN32
strm << GetCurrentProcessId() << "_" << GetCurrentThreadId();
#else
strm << getpid();
#endif
string temp_filename = strm.str();
ofstream out(temp_filename.c_str(), ios::out | ios::binary);
static const size_t buffer_size = 4096;
char buffer[buffer_size];
in.read(buffer, buffer_size);
size_t count = in.gcount();
while (count != 0) {
out.write(buffer, count);
if (out.fail()) {
unlink(temp_filename.c_str());
return false;
}
in.read(buffer, buffer_size);
count = in.gcount();
}
out.close();
if (!in.eof()) {
unlink(temp_filename.c_str());
return false;
}
if (rename(temp_filename.c_str(), to_filename.c_str()) == 0) {
return true;
}
unlink(temp_filename.c_str());
return false;
}
////////////////////////////////////////////////////////////////////
// Function: P3DInstanceManager::nt_thread_run
// Access: Private
@ -791,12 +619,12 @@ nt_thread_run() {
// The notify thread exists because we need to be able to send
// asynchronous notifications of request events. These request
// events were detected in the various read threads associated with
// each session, but we can't call back into the host space from the
// read thread, since if the host immediately response to a callback
// by calling back into the p3d_plugin space, now we have our read
// thread doing stuff in here that's not related to the read thread.
// Even worse, some of the things it might need to do might require
// a separate read thread to be running!
// each session, but we can't call back into the plugin host space
// from the read thread, since if the host immediately response to a
// callback by calling back into the p3d_plugin space, now we have
// our read thread doing stuff in here that's not related to the
// read thread. Even worse, some of the things it might need to do
// might require a separate read thread to be running!
_notify_ready.acquire();
while (_notify_thread_continue) {

View File

@ -28,7 +28,7 @@
class P3DInstance;
class P3DSession;
class P3DPackage;
class P3DHost;
class FileSpec;
class TiXmlElement;
@ -52,13 +52,9 @@ public:
inline bool is_initialized() const;
inline const string &get_root_dir() const;
inline const string &get_download_url() const;
inline const string &get_platform() const;
inline const string &get_log_directory() const;
inline bool has_contents_file() const;
bool read_contents_file(const string &contents_filename);
P3DInstance *
create_instance(P3D_request_ready_func *func,
const P3D_token tokens[], size_t num_tokens,
@ -74,11 +70,7 @@ public:
P3DInstance *check_request();
void wait_request();
P3DPackage *get_package(const string &package_name,
const string &package_version);
bool get_package_desc_file(FileSpec &desc_file,
const string &package_name,
const string &package_version);
P3DHost *get_host(const string &host_url);
inline int get_num_instances() const;
@ -97,10 +89,6 @@ public:
static P3DInstanceManager *get_global_ptr();
static void delete_global_ptr();
private:
static string standardize_filename(const string &filename);
static bool copy_file(const string &from_filename, const string &to_filename);
private:
// The notify thread. This thread runs only for the purpose of
// generating asynchronous notifications of requests, to callers who
@ -111,15 +99,12 @@ private:
private:
bool _is_initialized;
string _root_dir;
string _download_url;
string _platform;
string _log_directory;
string _log_basename;
string _log_pathname;
string _temp_directory;
TiXmlElement *_xcontents;
P3D_object *_undefined_object;
P3D_object *_none_object;
P3D_object *_true_object;
@ -131,8 +116,8 @@ private:
typedef map<string, P3DSession *> Sessions;
Sessions _sessions;
typedef map<string, P3DPackage *> Packages;
Packages _packages;
typedef map<string, P3DHost *> Hosts;
Hosts _hosts;
typedef set<string> TempFilenames;
TempFilenames _temp_filenames;

View File

@ -81,6 +81,17 @@ get_failed() const {
return _failed;
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::get_host
// Access: Public
// Description: Returns the host server which offers this package for
// download.
////////////////////////////////////////////////////////////////////
inline P3DHost *P3DPackage::
get_host() const {
return _host;
}
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::get_package_dir
// Access: Public

View File

@ -36,21 +36,14 @@ static const double extract_portion = 0.05;
// Description:
////////////////////////////////////////////////////////////////////
P3DPackage::
P3DPackage(const string &package_name,
const string &package_platform,
P3DPackage(P3DHost *host, const string &package_name,
const string &package_version) :
_host(host),
_package_name(package_name),
_package_platform(package_platform),
_package_version(package_version)
{
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
_package_fullname = _package_name;
_package_dir = inst_mgr->get_root_dir() + string("/packages/") + _package_name;
if (!_package_platform.empty()) {
_package_fullname += string("_") + _package_platform;
_package_dir += string("/") + _package_platform;
}
_package_dir = _host->get_host_dir() + string("/packages/") + _package_name;
_package_fullname += string("_") + _package_version;
_package_dir += string("/") + _package_version;
@ -169,16 +162,14 @@ begin_info_download() {
////////////////////////////////////////////////////////////////////
void P3DPackage::
download_contents_file() {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (inst_mgr->has_contents_file()) {
if (_host->has_contents_file()) {
// We've already got a contents.xml file; go straight to the
// package desc file.
download_desc_file();
return;
}
string url = inst_mgr->get_download_url();
string url = _host->get_host_url_prefix();
url += "contents.xml";
// Download contents.xml to a temporary filename first, in case
@ -196,15 +187,13 @@ download_contents_file() {
////////////////////////////////////////////////////////////////////
void P3DPackage::
contents_file_download_finished(bool success) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
if (!inst_mgr->has_contents_file()) {
if (!success || !inst_mgr->read_contents_file(_temp_contents_file->get_filename())) {
if (!_host->has_contents_file()) {
if (!success || !_host->read_contents_file(_temp_contents_file->get_filename())) {
nout << "Couldn't read " << *_temp_contents_file << "\n";
// Maybe we can read an already-downloaded contents.xml file.
string standard_filename = inst_mgr->get_root_dir() + "/contents.xml";
if (!inst_mgr->read_contents_file(standard_filename)) {
string standard_filename = _host->get_host_dir() + "/contents.xml";
if (!_host->read_contents_file(standard_filename)) {
// Couldn't even read that. Fail.
report_done(false);
delete _temp_contents_file;
@ -225,27 +214,31 @@ contents_file_download_finished(bool success) {
////////////////////////////////////////////////////////////////////
// Function: P3DPackage::download_desc_file
// Access: Private
// Description: Starts downloading the desc file for the package.
// Description: Starts downloading the desc file for the package, if
// it's needed; or read to local version if it's fresh
// enough.
////////////////////////////////////////////////////////////////////
void P3DPackage::
download_desc_file() {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
// Attempt to check the desc file for freshness. If it already
// exists, and is consistent with the server contents file, we don't
// need to re-download it.
string root_dir = inst_mgr->get_root_dir() + "/packages";
FileSpec desc_file;
if (!inst_mgr->get_package_desc_file(desc_file, _package_name, _package_version)) {
if (!_host->get_package_desc_file(desc_file, _package_platform,
_package_name, _package_version)) {
nout << "Couldn't find package " << _package_fullname
<< " in contents file.\n";
return;
}
} else if (desc_file.get_pathname(root_dir) != _desc_file_pathname) {
nout << "Wrong pathname for desc file: "
<< desc_file.get_pathname(root_dir)
<< " instead of " << _desc_file_pathname << "\n";
// The desc file might have a different path on the host server than
// it has locally, because we strip out the platform locally.
// Adjust desc_file to point to the local file.
string url_filename = desc_file.get_filename();
desc_file.set_filename(_desc_file_basename);
assert (desc_file.get_pathname(_package_dir) == _desc_file_pathname);
} else if (!desc_file.full_verify(root_dir)) {
if (!desc_file.full_verify(_package_dir)) {
nout << _desc_file_pathname << " is stale.\n";
} else {
@ -258,13 +251,8 @@ download_desc_file() {
}
// The desc file is not current. Go download it.
string url = inst_mgr->get_download_url();
url += _package_name;
if (!_package_platform.empty()) {
url += "/" + _package_platform;
}
url += "/" + _package_version;
url += "/" + _desc_file_basename;
string url = _host->get_host_url_prefix();
url += url_filename;
start_download(DT_desc_file, url, _desc_file_pathname, false);
}
@ -415,8 +403,7 @@ begin_data_download() {
////////////////////////////////////////////////////////////////////
void P3DPackage::
download_compressed_archive(bool allow_partial) {
P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr();
string url = inst_mgr->get_download_url();
string url = _host->get_host_url_prefix();
url += _package_name;
if (!_package_platform.empty()) {
url += "/" + _package_platform;

View File

@ -20,6 +20,7 @@
#include "fileSpec.h"
#include "get_tinyxml.h"
class P3DHost;
class P3DInstance;
class P3DTemporaryFile;
@ -31,23 +32,26 @@ class P3DTemporaryFile;
// runtime, which consists of a bunch of dll's
// downloaded in a single tar file, is a package.
//
// The plugin is responsible for managing these packages
// on disk, downloading new versions when needed, and
// removing stale versions to limit disk space waste.
// The core API is responsible for managing these
// packages on disk, downloading new versions when
// needed, and removing stale versions to limit disk
// space waste.
////////////////////////////////////////////////////////////////////
class P3DPackage {
public:
P3DPackage(const string &package_name,
const string &package_platform,
private:
P3DPackage(P3DHost *host,
const string &package_name,
const string &package_version);
~P3DPackage();
public:
inline bool get_info_ready() const;
inline size_t get_download_size() const;
inline void activate_download();
inline bool get_ready() const;
inline bool get_failed() const;
inline P3DHost *get_host() const;
inline const string &get_package_dir() const;
inline const string &get_package_name() const;
inline const string &get_package_version() const;
@ -101,6 +105,8 @@ private:
bool is_extractable(const string &filename) const;
private:
P3DHost *_host;
string _package_name;
string _package_version;
string _package_platform;
@ -132,6 +138,8 @@ private:
friend class Download;
friend class P3DMultifileReader;
friend class P3DHost;
};
#include "p3dPackage.I"

View File

@ -18,7 +18,7 @@
// There is only one P3DPythonRun object in any given process space.
// Makes the statics easier to deal with, and we don't need multiple
// instances of this think.
// instances of this thing.
P3DPythonRun *P3DPythonRun::_global_ptr = NULL;
////////////////////////////////////////////////////////////////////

View File

@ -7,6 +7,7 @@
#include "p3dFileDownload.cxx"
#include "p3dFileParams.cxx"
#include "p3dFloatObject.cxx"
#include "p3dHost.cxx"
#include "p3dInstance.cxx"
#include "p3dInstanceManager.cxx"
#include "p3dIntObject.cxx"

View File

@ -6,10 +6,9 @@
/********************************** DO NOT EDIT ****************************/
/* The URL that is the root of the download server that this plugin
should contact. The nppanda3d.dll file should be found at this
location; as well as the contents.xml file that defines where the
various Panda3D packages will be found. */
#$[]define P3D_PLUGIN_DOWNLOAD "$[P3D_PLUGIN_DOWNLOAD]$[if $[notdir $[P3D_PLUGIN_DOWNLOAD]],/]"
should contact. The contents.xml file that defines this particular
"coreapi" package should be found at this location. */
#$[]define PANDA_PACKAGE_HOST_URL "$[PANDA_PACKAGE_HOST_URL]"
/* The filename(s) to generate output to when the plugin is running.
For debugging purposes only. */

View File

@ -108,7 +108,7 @@ void PPInstance::
begin() {
if (!is_plugin_loaded()) {
// Go download the contents file, so we can download the core DLL.
string url = P3D_PLUGIN_DOWNLOAD;
string url = PANDA_PACKAGE_HOST_URL;
if (!url.empty() && url[url.length() - 1] != '/') {
url += '/';
}
@ -837,7 +837,7 @@ get_core_api(TiXmlElement *xplugin) {
} else {
// The DLL file needs to be downloaded. Go get it.
string url = P3D_PLUGIN_DOWNLOAD;
string url = PANDA_PACKAGE_HOST_URL;
if (!url.empty() && url[url.length() - 1] != '/') {
url += '/';
}

View File

@ -64,7 +64,7 @@ run(int argc, char *argv[]) {
const char *optstr = "+mu:p:ft:s:o:l:h";
bool allow_multiple = false;
string download_url = P3D_PLUGIN_DOWNLOAD;
string download_url = PANDA_PACKAGE_HOST_URL;
string this_platform = DTOOL_PLATFORM;
bool force_download = false;
@ -752,7 +752,7 @@ usage() {
<< " -u url\n"
<< " Specify the URL of the Panda3D download server. The default is\n"
<< " \"" << P3D_PLUGIN_DOWNLOAD << "\" .\n\n"
<< " \"" << PANDA_PACKAGE_HOST_URL << "\" .\n\n"
<< " -p platform\n"
<< " Specify the platform to masquerade as. The default is \""

View File

@ -133,8 +133,9 @@ class Packager:
def __init__(self, packageName, packager):
self.packageName = packageName
self.packager = packager
self.version = None
self.platform = None
self.version = None
self.host = None
self.p3dApplication = False
self.compressionLevel = 0
self.importedMapsDir = 'imported_maps'
@ -672,6 +673,7 @@ class Packager:
xrequires.SetAttribute('name', package.packageName)
if package.version:
xrequires.SetAttribute('version', package.version)
xrequires.SetAttribute('host', package.host)
xpackage.InsertEndChild(xrequires)
doc.InsertEndChild(xpackage)
@ -699,6 +701,9 @@ class Packager:
raise PackagerError, message
def writeDescFile(self):
""" Makes the package.xml file that describes the package
and its contents, for download. """
packageDescFullpath = Filename(self.packager.installDir, self.packageDesc)
doc = TiXmlDocument(packageDescFullpath.toOsSpecific())
decl = TiXmlDeclaration("1.0", "utf-8", "")
@ -724,6 +729,7 @@ class Packager:
xrequires.SetAttribute('platform', package.platform)
if package.version:
xrequires.SetAttribute('version', package.version)
xrequires.SetAttribute('host', package.host)
xpackage.InsertEndChild(xrequires)
xuncompressedArchive = self.getFileSpec(
@ -743,6 +749,10 @@ class Packager:
doc.SaveFile()
def writeImportDescFile(self):
""" Makes the package_import.xml file that describes the
package and its contents, for other packages and
applications that may wish to "require" this one. """
packageImportDescFullpath = Filename(self.packager.installDir, self.packageImportDesc)
doc = TiXmlDocument(packageImportDescFullpath.toOsSpecific())
decl = TiXmlDeclaration("1.0", "utf-8", "")
@ -754,6 +764,7 @@ class Packager:
xpackage.SetAttribute('platform', self.platform)
if self.version:
xpackage.SetAttribute('version', self.version)
xpackage.SetAttribute('host', self.host)
for package in self.requires:
xrequires = TiXmlElement('requires')
@ -762,6 +773,7 @@ class Packager:
xrequires.SetAttribute('platform', package.platform)
if package.version:
xrequires.SetAttribute('version', package.version)
xrequires.SetAttribute('host', package.host)
xpackage.InsertEndChild(xrequires)
self.components.sort()
@ -785,6 +797,7 @@ class Packager:
self.packageName = xpackage.Attribute('name')
self.platform = xpackage.Attribute('platform')
self.version = xpackage.Attribute('version')
self.host = xpackage.Attribute('host')
self.requires = []
xrequires = xpackage.FirstChildElement('requires')
@ -792,8 +805,11 @@ class Packager:
packageName = xrequires.Attribute('name')
platform = xrequires.Attribute('platform')
version = xrequires.Attribute('version')
host = xrequires.Attribute('host')
if packageName:
package = self.packager.findPackage(packageName, platform = platform, version = version, requires = self.requires)
package = self.packager.findPackage(
packageName, platform = platform, version = version,
host = host, requires = self.requires)
if package:
self.requires.append(package)
xrequires = xrequires.NextSiblingElement('requires')
@ -1012,9 +1028,12 @@ class Packager:
self.installDir = None
self.persistDir = None
# A search list of directories and/or URL's to search for
# installed packages. We query it from a config variable
# initially, but we may also be extending it at runtime.
# The download URL at which these packages will eventually be
# hosted. This may also be changed with the "host" command.
self.host = PandaSystem.getPackageHostUrl()
self.hostDescriptiveName = None
# A search list for previously-built local packages.
self.installSearch = ConfigVariableSearchPath('pdef-path')
# The system PATH, for searching dll's and exe's.
@ -1195,23 +1214,8 @@ class Packager:
# We must have an actual install directory.
assert(self.installDir)
## # If the persist dir names an empty or nonexistent directory,
## # we will be generating a brand new publish with no previous
## # patches.
## self.persistDir.makeDir()
## # Within the persist dir, we make a temporary holding dir for
## # generating multifiles.
## self.mfTempDir = Filename(self.persistDir, Filename('mftemp/'))
## self.mfTempDir.makeDir()
## # We also need a temporary holding dir for squeezing py files.
## self.pyzTempDir = Filename(self.persistDir, Filename('pyz/'))
## self.pyzTempDir.makeDir()
## # Change to the persist directory so the temp files will be
## # created there
## os.chdir(self.persistDir.toOsSpecific())
if not PandaSystem.getPackageVersionString() or not PandaSystem.getPackageHostUrl():
raise PackagerError, 'This script must be run using a version of Panda3D that has been built\nfor distribution. Try using ppackage.p3d or packp3d.p3d instead.'
def __expandVariable(self, line, p):
""" Given that line[p] is a dollar sign beginning a variable
@ -1441,6 +1445,32 @@ class Packager:
value = ExecutionEnvironment.expandString(value.strip())
ExecutionEnvironment.setEnvironmentVariable(variable, value)
def parse_host(self, words):
"""
host "url" ["descriptive name"]
"""
hostDescriptiveName = None
try:
if len(words) == 2:
command, host = words
else:
command, host, hostDescriptiveName = words
except ValueError:
raise ArgumentNumber
if self.currentPackage:
self.currentPackage.host = host
else:
# Outside of a package, the "host" command specifies the
# host for all future packages.
self.host = host
# The descriptive name, if specified, is kept until the end,
# where it may be passed to make_contents by ppackage.py.
if hostDescriptiveName:
self.hostDescriptiveName = hostDescriptiveName
def parse_model_path(self, words):
"""
model_path directory
@ -1469,10 +1499,10 @@ class Packager:
def parse_begin_package(self, words):
"""
begin_package packageName [version=v]
begin_package packageName [version=v] [host=host]
"""
args = self.__parseArgs(words, ['version'])
args = self.__parseArgs(words, ['version', 'host'])
try:
command, packageName = words
@ -1480,8 +1510,10 @@ class Packager:
raise ArgumentNumber
version = args.get('version', None)
host = args.get('host', None)
self.beginPackage(packageName, version = version, p3dApplication = False)
self.beginPackage(packageName, version = version, host = host,
p3dApplication = False)
def parse_end_package(self, words):
"""
@ -1539,10 +1571,10 @@ class Packager:
def parse_require(self, words):
"""
require packageName [version=v]
require packageName [version=v] [host=url]
"""
args = self.__parseArgs(words, ['version'])
args = self.__parseArgs(words, ['version', 'host'])
try:
command, packageName = words
@ -1550,7 +1582,8 @@ class Packager:
raise ArgumentError
version = args.get('version', None)
self.require(packageName, version = version)
host = args.get('host', None)
self.require(packageName, version = version, host = host)
def parse_module(self, words):
"""
@ -1798,7 +1831,8 @@ class Packager:
del words[-1]
def beginPackage(self, packageName, version = None, p3dApplication = False):
def beginPackage(self, packageName, version = None, host = None,
p3dApplication = False):
""" Begins a new package specification. packageName is the
basename of the package. Follow this with a number of calls
to file() etc., and close the package with endPackage(). """
@ -1806,20 +1840,33 @@ class Packager:
if self.currentPackage:
raise PackagerError, 'unmatched end_package %s' % (self.currentPackage.packageName)
# A special case for the Panda3D package. We enforce that the
# version number matches what we've been compiled with.
if host is None and not p3dApplication:
# Every package that doesn't specify otherwise uses the
# current download host.
host = self.host
# A special case when building the "panda3d" package. We
# enforce that the version number matches what we've been
# compiled with.
if packageName == 'panda3d':
if version is None:
version = PandaSystem.getPackageVersionString()
else:
if version != PandaSystem.getPackageVersionString():
message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (version, PandaSystem.getPackageVersionString())
raise PackageError, message
if host is None:
host = PandaSystem.getPackageHostUrl()
if version != PandaSystem.getPackageVersionString():
message = 'mismatched Panda3D version: requested %s, but Panda3D is built as %s' % (version, PandaSystem.getPackageVersionString())
raise PackageError, message
if host != PandaSystem.getPackageHostUrl():
message = 'mismatched Panda3D host: requested %s, but Panda3D is built as %s' % (host, PandaSystem.getPackageHostUrl())
raise PackageError, message
package = self.Package(packageName, self)
self.currentPackage = package
package.version = version
package.host = host
package.p3dApplication = p3dApplication
if package.p3dApplication:
@ -1855,7 +1902,7 @@ class Packager:
self.currentPackage = None
def findPackage(self, packageName, platform = None, version = None,
requires = None):
host = None, requires = None):
""" Searches for the named package from a previous publish
operation along the install search path.
@ -1872,25 +1919,32 @@ class Packager:
platform = self.platform
# Is it a package we already have resident?
package = self.packages.get((packageName, platform, version), None)
package = self.packages.get((packageName, platform, version, host), None)
if package:
return package
# Look on the searchlist.
for dirname in self.installSearch.getDirectories():
package = self.__scanPackageDir(dirname, packageName, platform, version, requires = requires)
package = self.__scanPackageDir(dirname, packageName, platform, version, host, requires = requires)
if not package:
package = self.__scanPackageDir(dirname, packageName, None, version, requires = requires)
package = self.__scanPackageDir(dirname, packageName, None, version, host, requires = requires)
if package:
package = self.packages.setdefault((package.packageName, package.platform, package.version), package)
self.packages[(packageName, platform, version)] = package
return package
break
if not package:
# Query the indicated host.
package = self.__findPackageOnHost(packageName, platform, version, host, requires = requires)
if package:
package = self.packages.setdefault((package.packageName, package.platform, package.version, package.host), package)
self.packages[(packageName, platform, version, host)] = package
return package
return None
def __scanPackageDir(self, rootDir, packageName, platform, version,
requires = None):
host, requires = None):
""" Scans a directory on disk, looking for *_import.xml files
that match the indicated packageName and optional version. If a
suitable xml file is found, reads it and returns the assocated
@ -1917,6 +1971,9 @@ class Packager:
packageDir = Filename(packageDir, '*')
basename += '_%s' % ('*')
# Actually, the host means little for this search, since we're
# only looking in a local directory at this point.
basename += '_import.xml'
filename = Filename(packageDir, basename)
filelist = glob.glob(filename.toOsSpecific())
@ -1934,6 +1991,10 @@ class Packager:
return None
def __findPackageOnHost(self, packageName, platform, version, host, requires = None):
# TODO.
return None
def __sortPackageImportFilelist(self, filelist):
""" Given a list of *_import.xml filenames, sorts them in
reverse order by version, so that the highest-numbered
@ -1968,7 +2029,8 @@ class Packager:
while p < len(version) and version[p] in string.digits:
w += version[p]
p += 1
words.append(int(w))
if w:
words.append(int(w))
return tuple(words)
@ -2024,7 +2086,7 @@ class Packager:
self.currentPackage.configs[variable] = value
def require(self, packageName, version = None):
def require(self, packageName, version = None, host = None):
""" Indicates a dependency on the named package, supplied as
a name.
@ -2035,13 +2097,17 @@ class Packager:
if not self.currentPackage:
raise OutsideOfPackageError
# A special case for the Panda3D package. We enforce that the
# version number matches what we've been compiled with.
# A special case when requiring the "panda3d" package. We
# supply the version number what we've been compiled with as a
# default.
if packageName == 'panda3d':
if version is None:
version = PandaSystem.getPackageVersionString()
if host is None:
host = PandaSystem.getPackageHostUrl()
package = self.findPackage(packageName, version = version, requires = self.currentPackage.requires)
package = self.findPackage(packageName, version = version, host = host,
requires = self.currentPackage.requires)
if not package:
message = 'Unknown package %s, version "%s"' % (packageName, version)
raise PackagerError, message
@ -2059,20 +2125,14 @@ class Packager:
if not self.currentPackage:
raise OutsideOfPackageError
# A special case for the Panda3D package. We enforce that the
# version number matches what we've been compiled with.
# A special case when requiring the "panda3d" package. We
# complain if the version number doesn't match what we've been
# compiled with.
if package.packageName == 'panda3d':
if package.version != PandaSystem.getPackageVersionString():
if not PandaSystem.getPackageVersionString():
# We haven't been compiled with any particular
# version of Panda. This is a warning, not an
# error.
print "Warning: requiring panda3d version %s, which may or may not match the current build of Panda. Recommend that you use only the official Panda3D build for making distributable applications to ensure compatibility." % (package.version)
else:
# This particular version of Panda doesn't match
# the requested version. Again, a warning, not an
# error.
print "Warning: requiring panda3d version %s, which does not match the current build of Panda, which is version %s." % (package, PandaSystem.getPackageVersionString())
print "Warning: requiring panda3d version %s, which does not match the current build of Panda, which is version %s." % (package, PandaSystem.getPackageVersionString())
elif package.host != PandaSystem.getPackageHostUrl():
print "Warning: requiring panda3d host %s, which does not match the current build of Panda, which is host %s." % (package, PandaSystem.getPackageHostUrl())
self.currentPackage.requirePackage(package)

View File

@ -12,17 +12,24 @@ make_contents.py [opts]
Options:
-d stage_dir
-i install_dir
The full path to a local directory that contains the
ready-to-be-published files, as populated by one or more
iterations of the ppackage script. It is the user's
responsibility to copy this directory structure to a server.
Specify the staging directory. This is a temporary directory on
the local machine that contains a copy of the web server
contents. The default is the current directory.
-n "host descriptive name"
Specifies a descriptive name of the download server that will
host these contents. This name may be presented to the user when
managing installed packages. If this option is omitted, the name
is unchanged from the previous pass.
"""
import sys
import getopt
import os
import types
try:
import hashlib
@ -56,6 +63,7 @@ class FileSpec:
class ContentsMaker:
def __init__(self):
self.installDir = None
self.hostDescriptiveName = None
def build(self):
if not self.installDir:
@ -67,20 +75,56 @@ class ContentsMaker:
if not self.packages:
raise ArgumentError, "No packages found."
# Now write the contents.xml file.
contentsFileBasename = 'contents.xml'
contentsFilePathname = os.path.join(self.installDir, contentsFileBasename)
contentsLine = None
if self.hostDescriptiveName is not None:
if self.hostDescriptiveName:
contentsLine = '<contents descriptive_name="%s">' % (
self.quoteString(self.hostDescriptiveName))
else:
contentsLine = self.readContentsLine(contentsFilePathname)
if not contentsLine:
contentsLine = '<contents>'
# Now write the contents.xml file.
f = open(contentsFilePathname, 'w')
print >> f, '<?xml version="1.0" ?>'
print >> f, '<?xml version="1.0" encoding="utf-8" ?>'
print >> f, ''
print >> f, '<contents>'
print >> f, contentsLine
for type, packageName, packagePlatform, packageVersion, file in self.packages:
print >> f, ' <%s name="%s" platform="%s" version="%s" %s />' % (
type, packageName, packagePlatform or '', packageVersion, file.getParams())
print >> f, '</contents>'
f.close()
def readContentsLine(self, contentsFilePathname):
""" Reads the previous iteration of contents.xml to get the
previous top-level contents line, which contains the
hostDescriptiveName. """
try:
f = open(contentsFilePathname, 'r')
except OSError:
return None
for line in f.readlines():
if line.startswith('<contents'):
return line.rstrip()
return None
def quoteString(self, str):
""" Correctly quotes a string for embedding in the xml file. """
if isinstance(str, types.UnicodeType):
str = str.encode('utf-8')
str = str.replace('&', '&amp;')
str = str.replace('"', '&quot;')
str = str.replace('\'', '&apos;')
str = str.replace('<', '&lt;')
str = str.replace('>', '&gt;')
return str
def scanDirectory(self):
""" Walks through all the files in the stage directory and
looks for the package directory xml files. """
@ -139,13 +183,16 @@ class ContentsMaker:
def makeContents(args):
opts, args = getopt.getopt(args, 'd:h')
opts, args = getopt.getopt(args, 'i:n:h')
cm = ContentsMaker()
cm.installDir = '.'
for option, value in opts:
if option == '-d':
if option == '-i':
cm.installDir = value
elif option == '-n':
cm.hostDescriptiveName = value
elif option == '-h':
print __doc__

View File

@ -119,15 +119,21 @@ def makePackedApp(args):
packager.installDir = appDir
getModelPath().appendDirectory(root)
packager.setup()
packager.beginPackage(appBase, p3dApplication = True)
for requireName in requires:
packager.require(requireName)
packager.dir(root)
packager.mainModule(mainModule)
packager.endPackage(appBase, p3dApplication = True)
try:
packager.setup()
packager.beginPackage(appBase, p3dApplication = True)
for requireName in requires:
packager.require(requireName)
packager.dir(root)
packager.mainModule(mainModule)
packager.endPackage(appBase, p3dApplication = True)
except Packager.PackagerError:
# Just print the error message and exit gracefully.
inst = sys.exc_info()[1]
print inst.args[0]
sys.exit(1)
def main(appRunner):
""" This function is called when this module is invoked as

View File

@ -38,12 +38,13 @@ Options:
ready-to-be-published files into. This directory structure may
contain multiple different packages from multiple different
invocations of this script. It is the user's responsibility to
copy this directory structure to a web host where it may be
downloaded by the client.
copy this directory structure to a server, which will have the
URL specified by -u, below.
-s search_dir
Additional directories to search for previously-built packages.
This option may be repeated as necessary.
This option may be repeated as necessary. These directories may
also be specified with the pdef-path Config.prc variable.
-d persist_dir
The full path to a local directory that retains persistant state
@ -54,6 +55,21 @@ Options:
empty, patches will not be created for this publish; but the
directory structure will be populated for the next publish.
-u host_url
Specifies the URL to the download server that will eventually
host these packages (that is, the public URL of the install
directory). This may also be overridden with a "host" command
appearing within the pdef file. This is used for packages only;
it is ignored for p3d applications, which are not specific to a
particular host.
-n "host descriptive name"
Specifies a descriptive name of the download server named by -u.
This name may be presented to the user when managing installed
packages. This may also be overridden with a "host" command
appearing within the pdef file. This information is written to
the contents.xml file at the top of the install directory.
-p platform
Specify the platform to masquerade as. The default is whatever
platform Panda has been built for. It is probably unwise to set
@ -82,7 +98,7 @@ def usage(code, msg = ''):
packager = Packager.Packager()
try:
opts, args = getopt.getopt(sys.argv[1:], 'i:s:d:p:Hh')
opts, args = getopt.getopt(sys.argv[1:], 'i:s:d:p:u:n:Hh')
except getopt.error, msg:
usage(1, msg)
@ -95,6 +111,10 @@ for opt, arg in opts:
packager.persistDir = Filename.fromOsSpecific(arg)
elif opt == '-p':
packager.platform = arg
elif opt == '-u':
package.host = arg
elif opt == '-n':
package.hostDescriptiveName = arg
elif opt == '-h':
usage(0)
@ -117,8 +137,14 @@ if not packager.installDir:
packager.installDir = Filename('install')
packager.installSearch.prependDirectory(packager.installDir)
packager.setup()
packages = packager.readPackageDef(packageDef)
try:
packager.setup()
packages = packager.readPackageDef(packageDef)
except Packager.PackagerError:
# Just print the error message and exit gracefully.
inst = sys.exc_info()[1]
print inst.args[0]
sys.exit(1)
# Look to see if we built any true packages, or if all of them were
# p3d files.
@ -133,5 +159,6 @@ if anyPackages:
# the root of the install directory.
cm = make_contents.ContentsMaker()
cm.installDir = packager.installDir.toOsSpecific()
cm.hostDescriptiveName = packager.hostDescriptiveName
cm.build()