From d7b2de8029239f6e11f9952f7e087240e34988bb Mon Sep 17 00:00:00 2001 From: David Rose Date: Mon, 12 Oct 2009 01:50:55 +0000 Subject: [PATCH] add audio libraries, and download transitive package requirements --- direct/src/p3d/AppRunner.py | 26 +++++++++- direct/src/p3d/PackageInfo.py | 15 ++++++ direct/src/p3d/PackageInstaller.py | 53 ++++++++++++++----- direct/src/p3d/Packager.py | 16 +++--- direct/src/p3d/panda3d.pdef | 36 +++++++++++++ direct/src/plugin/p3dInstance.cxx | 71 ++++++++++++++++++-------- direct/src/plugin/p3dInstance.h | 5 +- direct/src/plugin/p3dPackage.I | 15 ++++++ direct/src/plugin/p3dPackage.cxx | 81 ++++++++++++++++++++---------- direct/src/plugin/p3dPackage.h | 13 +++++ 10 files changed, 258 insertions(+), 73 deletions(-) diff --git a/direct/src/p3d/AppRunner.py b/direct/src/p3d/AppRunner.py index 74f75bfd88..fc2241730d 100644 --- a/direct/src/p3d/AppRunner.py +++ b/direct/src/p3d/AppRunner.py @@ -238,16 +238,39 @@ class AppRunner(DirectObject): packageName, version, hostUrl) return False + return self.__rInstallPackage(package, []) + + def __rInstallPackage(self, package, nested): + """ The recursive implementation of installPackage(). The new + parameter, nested, is a list of packages that we are + recursively calling this from, to avoid recursive loops. """ + if not package.downloadDescFile(self.http): return False + # Now that we've downloaded and read the desc file, we can + # install all of the required packages first. + nested = nested[:] + [self] + for packageName, version, host in package.requires: + if host.downloadContentsFile(self.http): + p2 = host.getPackage(packageName, version) + if not p2: + print "Couldn't find %s %s on %s" % (packageName, version, host.hostUrl) + else: + if p2 not in nested: + self.__rInstallPackage(p2, nested) + + # Now that all of the required packages are installed, carry + # on to download and install this package. if not package.downloadPackage(self.http): return False if not package.installPackage(self): return False - print "Package %s %s installed." % (packageName, version) + print "Package %s %s installed." % ( + package.packageName, package.packageVersion) + return True def getHostWithAlt(self, hostUrl): """ Returns a suitable HostInfo object for downloading @@ -290,7 +313,6 @@ class AppRunner(DirectObject): mapped host, which is the one we should actually download from, see getHostWithAlt(). """ - if hostUrl is None: hostUrl = PandaSystem.getPackageHostUrl() diff --git a/direct/src/p3d/PackageInfo.py b/direct/src/p3d/PackageInfo.py index 8eca0c58f9..ef2543d2d1 100644 --- a/direct/src/p3d/PackageInfo.py +++ b/direct/src/p3d/PackageInfo.py @@ -79,6 +79,7 @@ class PackageInfo: self.uncompressedArchive = None self.compressedArchive = None self.extracts = [] + self.requires = [] self.installPlans = None # This is updated during downloadPackage(). It is in the @@ -253,6 +254,7 @@ class PackageInfo: self.compressedArchive.loadXml(xcompressedArchive) # The list of files that should be extracted to disk. + self.extracts = [] xextract = xpackage.FirstChildElement('extract') while xextract: file = FileSpec() @@ -260,6 +262,19 @@ class PackageInfo: self.extracts.append(file) xextract = xextract.NextSiblingElement('extract') + # The list of additional packages that must be installed for + # this package to function properly. + self.requires = [] + xrequires = xpackage.FirstChildElement('requires') + while xrequires: + packageName = xrequires.Attribute('name') + version = xrequires.Attribute('version') + hostUrl = xrequires.Attribute('host') + if packageName and hostUrl: + host = self.host.appRunner.getHostWithAlt(hostUrl) + self.requires.append((packageName, version, host)) + xrequires = xrequires.NextSiblingElement('requires') + self.hasDescFile = True # Now that we've read the desc file, go ahead and use it to diff --git a/direct/src/p3d/PackageInstaller.py b/direct/src/p3d/PackageInstaller.py index 3961666195..c8f485267c 100644 --- a/direct/src/p3d/PackageInstaller.py +++ b/direct/src/p3d/PackageInstaller.py @@ -76,6 +76,13 @@ class PackageInstaller(DirectObject): # getDescFile(). self.downloadEffort = 0 + def __cmp__(self, pp): + """ Python comparision function. This makes all + PendingPackages withe same (packageName, version, host) + combination be deemed equivalent. """ + return cmp((self.packageName, self.version, self.host), + (pp.packageName, pp.version, pp.host)) + def getProgress(self): """ Returns the download progress of this package in the range 0..1. """ @@ -230,16 +237,37 @@ class PackageInstaller(DirectObject): self.packageLock.acquire() try: - self.packages.append(pp) - if not pp.checkDescFile(): - # Still need to download the desc file. - self.needsDescFile.append(pp) - if not self.descFileTask: - self.descFileTask = taskMgr.add( - self.__getDescFileTask, 'getDescFile', - taskChain = self.taskChain) + self.__internalAddPackage(pp) + finally: + self.packageLock.release() - elif not pp.package.hasPackage: + def __internalAddPackage(self, pp): + """ Adds the indicated "pending package" to the appropriate + list(s) for downloading and installing. Assumes packageLock + is already held.""" + + if pp in self.packages: + # Already added. + return + + self.packages.append(pp) + if not pp.checkDescFile(): + # Still need to download the desc file. + self.needsDescFile.append(pp) + if not self.descFileTask: + self.descFileTask = taskMgr.add( + self.__getDescFileTask, 'getDescFile', + taskChain = self.taskChain) + + else: + # The desc file is ready, which means so are the requirements. + for packageName, version, host in pp.package.requires: + pp2 = self.PendingPackage(packageName, version, host) + self.__internalAddPackage(pp2) + + # Now that we've added the required packages, add the + # package itself. + if not pp.package.hasPackage: # The desc file is good, but the package itself needs # to be downloaded. self.needsDownload.append(pp) @@ -247,9 +275,6 @@ class PackageInstaller(DirectObject): else: # The package is already fully downloaded. self.earlyDone.append(pp) - - finally: - self.packageLock.release() def donePackages(self): """ After calling addPackage() for each package to be @@ -494,6 +519,10 @@ class PackageInstaller(DirectObject): # This package is now ready to be downloaded. self.packageLock.acquire() try: + # Also add any packages required by this one. + for packageName, version, host in pp.package.requires: + pp2 = self.PendingPackage(packageName, version, host) + self.__internalAddPackage(pp2) self.needsDownload.append(pp) finally: self.packageLock.release() diff --git a/direct/src/p3d/Packager.py b/direct/src/p3d/Packager.py index 826b334148..ca628743d6 100644 --- a/direct/src/p3d/Packager.py +++ b/direct/src/p3d/Packager.py @@ -1545,15 +1545,15 @@ class Packager: def requirePackage(self, package): """ Indicates a dependency on the given package. This also implicitly requires all of the package's requirements - as well. """ + as well (though this transitive requirement happens at + runtime, not here at build time). """ - for p2 in package.requires + [package]: - if p2 not in self.requires: - self.requires.append(p2) - for filename in p2.targetFilenames.keys(): - self.skipFilenames[filename] = True - for moduleName, mdef in p2.moduleNames.items(): - self.skipModules[moduleName] = mdef + if package not in self.requires: + self.requires.append(package) + for filename in package.targetFilenames.keys(): + self.skipFilenames[filename] = True + for moduleName, mdef in package.moduleNames.items(): + self.skipModules[moduleName] = mdef # Packager constructor def __init__(self): diff --git a/direct/src/p3d/panda3d.pdef b/direct/src/p3d/panda3d.pdef index 7a85d1003b..33d926ecdf 100755 --- a/direct/src/p3d/panda3d.pdef +++ b/direct/src/p3d/panda3d.pdef @@ -99,7 +99,43 @@ aux-display tinydisplay plugin-path $PANDA3D_ROOT default-model-extension .bam """ + auxDisplays) + +class fmod(package): + # This package includes the FMod audio library. This is + # full-featured and robust, but it is closed-source and the + # licensing is cost-free only for non-commercial products. + config(display_name = "FMod audio library") + require('panda3d') + + file('libp3fmod_audio.dll') + + file('fmod.prc', extract = True, text = """ +plugin-path $FMOD_ROOT +audio-library-name p3fmod_audio +""") + +class openal(package): + # This package includes the OpenAL audio libraries. This is free + # in both senses, but there are some technical issues, especially + # on certain platforms. + + config(display_name = "OpenAL audio library") + require('panda3d') + + file('libp3openal_audio.dll') + + file('openal.prc', extract = True, text = """ +plugin-path $OPENAL_ROOT +audio-library-name p3openal_audio +""") + +class audio(package): + # This package includes the best audio library for the given platform. + if platform.startswith('osx'): + require('fmod') + else: + require('openal') class egg(package): # This package contains the code for reading and operating on egg diff --git a/direct/src/plugin/p3dInstance.cxx b/direct/src/plugin/p3dInstance.cxx index b77f9385cb..fedc53e4c6 100644 --- a/direct/src/plugin/p3dInstance.cxx +++ b/direct/src/plugin/p3dInstance.cxx @@ -85,6 +85,7 @@ P3DInstance(P3D_request_ready_func *func, _got_fparams = false; _got_wparams = false; _p3d_trusted = false; + _xpackage = NULL; _p3dcert_package = NULL; _fparams.set_tokens(tokens, num_tokens); @@ -224,6 +225,11 @@ P3DInstance:: delete _temp_p3d_filename; _temp_p3d_filename = NULL; } + + if (_xpackage != NULL) { + delete _xpackage; + _xpackage = NULL; + } #ifdef __APPLE__ if (_frame_timer != NULL) { @@ -761,6 +767,33 @@ handle_event(P3D_event_data event) { return retval; } +//////////////////////////////////////////////////////////////////// +// Function: P3DInstance::add_package +// Access: Public +// Description: Adds the package to the list of packages used by this +// instance. The instance will share responsibility for +// downloading the package with any of the other +// instances that use the same package. +//////////////////////////////////////////////////////////////////// +void P3DInstance:: +add_package(const string &name, const string &version, P3DHost *host) { + string alt_host = _fparams.lookup_token("alt_host"); + + // Look up in the p3d_info.xml file to see if this p3d file has + // a specific alt_host indication for this host_url. + string alt_host_url = find_alt_host_url(host->get_host_url(), alt_host); + if (!alt_host_url.empty()) { + // If it does, we go ahead and switch to that host now, + // instead of bothering to contact the original host. + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + host = inst_mgr->get_host(alt_host_url); + alt_host.clear(); + } + + P3DPackage *package = host->get_package(name, version, alt_host); + add_package(package); +} + //////////////////////////////////////////////////////////////////// // Function: P3DInstance::add_package // Access: Public @@ -1259,13 +1292,15 @@ scan_app_desc_file(TiXmlDocument *doc) { if (xpackage == NULL) { return; } + assert(_xpackage == NULL); + _xpackage = (TiXmlElement *)xpackage->Clone(); - const char *log_basename = xpackage->Attribute("log_basename"); + const char *log_basename = _xpackage->Attribute("log_basename"); if (log_basename != NULL) { _log_basename = log_basename; } - TiXmlElement *xconfig = xpackage->FirstChildElement("config"); + TiXmlElement *xconfig = _xpackage->FirstChildElement("config"); if (xconfig != NULL) { int hidden = 0; if (xconfig->QueryIntAttribute("hidden", &hidden) == TIXML_SUCCESS) { @@ -1308,9 +1343,7 @@ scan_app_desc_file(TiXmlDocument *doc) { _wparams.set_window_type(P3D_WT_hidden); } - string alt_host = _fparams.lookup_token("alt_host"); - - TiXmlElement *xrequires = xpackage->FirstChildElement("requires"); + TiXmlElement *xrequires = _xpackage->FirstChildElement("requires"); while (xrequires != NULL) { const char *name = xrequires->Attribute("name"); const char *host_url = xrequires->Attribute("host"); @@ -1320,20 +1353,7 @@ scan_app_desc_file(TiXmlDocument *doc) { version = ""; } P3DHost *host = inst_mgr->get_host(host_url); - string this_alt_host = alt_host; - - // Look up in the p3d_info.xml file to see if this p3d file has - // a specific alt_host indication for this host_url. - string alt_host_url = find_alt_host_url(xpackage, host_url, alt_host); - if (!alt_host_url.empty()) { - // If it does, we go ahead and switch to that host now, - // instead of bothering to contact the original host. - host = inst_mgr->get_host(alt_host_url); - this_alt_host.clear(); - } - - P3DPackage *package = host->get_package(name, version, this_alt_host); - add_package(package); + add_package(name, version, host); } xrequires = xrequires->NextSiblingElement("requires"); @@ -1348,9 +1368,8 @@ scan_app_desc_file(TiXmlDocument *doc) { // Returns empty string if there is no match. //////////////////////////////////////////////////////////////////// string P3DInstance:: -find_alt_host_url(TiXmlElement *xpackage, - const string &host_url, const string &alt_host) { - TiXmlElement *xhost = xpackage->FirstChildElement("host"); +find_alt_host_url(const string &host_url, const string &alt_host) { + TiXmlElement *xhost = _xpackage->FirstChildElement("host"); while (xhost != NULL) { const char *url = xhost->Attribute("url"); if (url != NULL && host_url == url) { @@ -1845,6 +1864,14 @@ report_package_info_ready(P3DPackage *package) { return; } + // Now that the package's info is ready, we know its set of required + // packages, and we can add these to the list. + P3DPackage::Requires::const_iterator ri; + for (ri = package->_requires.begin(); ri != package->_requires.end(); ++ri) { + const P3DPackage::RequiredPackage &rp = (*ri); + add_package(rp._package_name, rp._package_version, rp._host); + } + if (get_packages_info_ready()) { // All packages are ready to go. Let's start some download // action. diff --git a/direct/src/plugin/p3dInstance.h b/direct/src/plugin/p3dInstance.h index 17b090270b..5a75a855d6 100644 --- a/direct/src/plugin/p3dInstance.h +++ b/direct/src/plugin/p3dInstance.h @@ -90,6 +90,7 @@ public: inline P3D_request_ready_func *get_request_ready_func() const; + void add_package(const string &name, const string &version, P3DHost *host); void add_package(P3DPackage *package); bool get_packages_info_ready() const; bool get_packages_ready() const; @@ -154,8 +155,7 @@ private: void mark_p3d_untrusted(); void mark_p3d_trusted(); void scan_app_desc_file(TiXmlDocument *doc); - string find_alt_host_url(TiXmlElement *xpackage, - const string &host_url, const string &alt_host); + string find_alt_host_url(const string &host_url, const string &alt_host); void send_browser_script_object(); P3D_request *make_p3d_request(TiXmlElement *xrequest); @@ -218,6 +218,7 @@ private: P3DWindowParams _wparams; bool _p3d_trusted; + TiXmlElement *_xpackage; // For downloading the p3dcert authorization program. P3DPackage *_p3dcert_package; diff --git a/direct/src/plugin/p3dPackage.I b/direct/src/plugin/p3dPackage.I index 04c8948c65..18ed3f2491 100755 --- a/direct/src/plugin/p3dPackage.I +++ b/direct/src/plugin/p3dPackage.I @@ -202,3 +202,18 @@ inline void P3DPackage::InstallStep:: report_step_progress() { _package->report_progress(this); } + +//////////////////////////////////////////////////////////////////// +// Function: P3DPackage::RequiredPackage::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +inline P3DPackage::RequiredPackage:: +RequiredPackage(const string &package_name, const string &package_version, + P3DHost *host) : + _package_name(package_name), + _package_version(package_version), + _host(host) +{ +} + diff --git a/direct/src/plugin/p3dPackage.cxx b/direct/src/plugin/p3dPackage.cxx index 5d09d668bf..095857e941 100755 --- a/direct/src/plugin/p3dPackage.cxx +++ b/direct/src/plugin/p3dPackage.cxx @@ -169,8 +169,8 @@ get_formatted_name() const { //////////////////////////////////////////////////////////////////// // Function: P3DPackage::add_instance // Access: Public -// Description: Specifies an instance that may be responsible for -// downloading this package. +// Description: Specifies an instance that that will be using this +// package, and may be responsible for downloading it. //////////////////////////////////////////////////////////////////// void P3DPackage:: add_instance(P3DInstance *inst) { @@ -182,8 +182,9 @@ add_instance(P3DInstance *inst) { //////////////////////////////////////////////////////////////////// // Function: P3DPackage::remove_instance // Access: Public -// Description: Indicates that the given instance will no longer be -// responsible for downloading this package. +// Description: Indicates that the given instance is no longer +// interested in this package and will not be +// responsible for downloading it. //////////////////////////////////////////////////////////////////// void P3DPackage:: remove_instance(P3DInstance *inst) { @@ -567,26 +568,33 @@ desc_file_download_finished(bool success) { void P3DPackage:: got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) { TiXmlElement *xpackage = doc->FirstChildElement("package"); - TiXmlElement *xuncompressed_archive = NULL; - TiXmlElement *xcompressed_archive = NULL; - - if (xpackage != NULL) { - xpackage->Attribute("patch_version", &_patch_version); - - xuncompressed_archive = xpackage->FirstChildElement("uncompressed_archive"); - xcompressed_archive = xpackage->FirstChildElement("compressed_archive"); - - TiXmlElement *xconfig = xpackage->FirstChildElement("config"); - if (xconfig != NULL) { - const char *display_name_cstr = xconfig->Attribute("display_name"); - if (display_name_cstr != NULL) { - _package_display_name = display_name_cstr; - } - - // Save the config entry within this class for others to query. - _xconfig = (TiXmlElement *)xconfig->Clone(); + if (xpackage == NULL) { + nout << _package_name << " desc file contains no \n"; + if (!freshly_downloaded) { + download_desc_file(); + return; } + report_done(false); + return; } + + xpackage->Attribute("patch_version", &_patch_version); + + TiXmlElement *xconfig = xpackage->FirstChildElement("config"); + if (xconfig != NULL) { + const char *display_name_cstr = xconfig->Attribute("display_name"); + if (display_name_cstr != NULL) { + _package_display_name = display_name_cstr; + } + + // Save the config entry within this class for others to query. + _xconfig = (TiXmlElement *)xconfig->Clone(); + } + + TiXmlElement *xuncompressed_archive = + xpackage->FirstChildElement("uncompressed_archive"); + TiXmlElement *xcompressed_archive = + xpackage->FirstChildElement("compressed_archive"); if (xuncompressed_archive == NULL || xcompressed_archive == NULL) { // The desc file didn't include the archive file itself, weird. @@ -604,18 +612,37 @@ got_desc_file(TiXmlDocument *doc, bool freshly_downloaded) { // Now get all the extractable components. _unpack_size = 0; _extracts.clear(); - TiXmlElement *extract = xpackage->FirstChildElement("extract"); - while (extract != NULL) { + TiXmlElement *xextract = xpackage->FirstChildElement("extract"); + while (xextract != NULL) { FileSpec file; - file.load_xml(extract); + file.load_xml(xextract); _extracts.push_back(file); _unpack_size += file.get_size(); - extract = extract->NextSiblingElement("extract"); + xextract = xextract->NextSiblingElement("extract"); + } + + P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); + + // Get the required packages. + _requires.clear(); + TiXmlElement *xrequires = xpackage->FirstChildElement("requires"); + while (xrequires != NULL) { + const char *package_name = xrequires->Attribute("name"); + const char *version = xrequires->Attribute("version"); + const char *host_url = xrequires->Attribute("host"); + if (package_name != NULL && host_url != NULL) { + P3DHost *host = inst_mgr->get_host(host_url); + if (version == NULL) { + version = ""; + } + _requires.push_back(RequiredPackage(package_name, version, host)); + } + + xrequires = xrequires->NextSiblingElement("requires"); } // Get a list of all of the files in the directory, so we can remove // files that don't belong. - P3DInstanceManager *inst_mgr = P3DInstanceManager::get_global_ptr(); vector contents; inst_mgr->scan_directory_recursively(_package_dir, contents); diff --git a/direct/src/plugin/p3dPackage.h b/direct/src/plugin/p3dPackage.h index 9ee16e1698..4f5179aa99 100755 --- a/direct/src/plugin/p3dPackage.h +++ b/direct/src/plugin/p3dPackage.h @@ -211,6 +211,19 @@ private: bool is_extractable(FileSpec &file, const string &filename) const; +public: + class RequiredPackage { + public: + inline RequiredPackage(const string &package_name, + const string &package_version, + P3DHost *host); + string _package_name; + string _package_version; + P3DHost *_host; + }; + typedef vector Requires; + Requires _requires; + private: P3DHost *_host; int _host_contents_seq;