Panda3D

mayapath.cxx

00001 // Filename: mayapath.cxx
00002 // Created by:  drose (07Apr08)
00003 //
00004 ////////////////////////////////////////////////////////////////////
00005 //
00006 // PANDA 3D SOFTWARE
00007 // Copyright (c) Carnegie Mellon University.  All rights reserved.
00008 //
00009 // All use of this software is subject to the terms of the revised BSD
00010 // license.  You should have received a copy of this license along
00011 // with this source code in a file named "LICENSE."
00012 //
00013 ////////////////////////////////////////////////////////////////////
00014 
00015 // This program works as a stub to launch maya2egg, egg2maya, and
00016 // similar programs that invoke OpenMaya and require certain
00017 // environment variables to be set first.
00018 
00019 // It used to duplicate code in mayaWrapper.cxx, but now the
00020 // functionality for these two separate programs are unified here.
00021 
00022 // If MAYAVERSION is defined at the time this is compiled, then that
00023 // particular version of Maya is insisted upon, and the desired Maya
00024 // location is found in the Registry; otherwise, we require that
00025 // $MAYA_LOCATION be set at runtime and points to the desired Maya
00026 // installation.
00027 
00028 // If MAYAVERSION is defined and $MAYA_LOCATION is also set, then we
00029 // check that definition of $MAYA_LOCATION is reasonable, which we
00030 // define as pointing to the same version of OpenMaya.dll.  If so,
00031 // then we use the runtime $MAYA_LOCATION, allowing the user to
00032 // (slightly) override the runtime Maya directory.  If $MAYA_LOCATION
00033 // is set but points to a different version of OpenMaya.dll, we ignore
00034 // it altogether and replace it with our registry data, which allows
00035 // the user to have MAYA_LOCATION pointing to a different version of
00036 // Maya without interfering with this program.
00037 
00038 #include "dtoolbase.h"
00039 #include "filename.h"
00040 #include "globPattern.h"
00041 #include "dSearchPath.h"
00042 #include "executionEnvironment.h"
00043 #include "hashVal.h"
00044 #include <stdlib.h>
00045 
00046 #if defined(_WIN32)
00047 #define WIN32_LEAN_AND_MEAN
00048 #include <windows.h>
00049 #endif
00050 
00051 #define QUOTESTR(x) #x
00052 #define TOSTRING(x) QUOTESTR(x)
00053 
00054 #ifdef IS_OSX
00055 static const Filename openmaya_filename = "MacOS/libOpenMaya.dylib";
00056 #else
00057 static const Filename openmaya_filename = "bin/OpenMaya.so";
00058 #endif  // IS_OSX
00059 
00060 // Searches for python26.zip or whatever version it is.
00061 static Filename
00062 find_pyzip(const Filename &maya_location) {
00063   // This is where python26.zip appears on Windows.  Should it be in
00064   // other locations on other platforms?
00065   Filename dirname(maya_location, "bin");
00066 
00067   vector_string results;
00068   GlobPattern glob("python*.zip");
00069   if (glob.match_files(results, dirname) != 0) {
00070     return Filename(dirname, results[0]);
00071   }
00072 
00073   return Filename();
00074 }
00075 
00076 struct { char *ver, *key; } maya_versions[] = {
00077   { "MAYA6",    "6.0" },
00078   { "MAYA65",   "6.5" },
00079   { "MAYA7",    "7.0" },
00080   { "MAYA8",    "8.0" },
00081   { "MAYA85",   "8.5" },
00082   { "MAYA2008", "2008" },
00083   { "MAYA2009", "2009" },
00084   { "MAYA2010", "2010" },
00085   { "MAYA2011", "2011"},
00086   { "MAYA2012", "2012"},
00087   { 0, 0 },
00088 };
00089 
00090 static char *
00091 get_version_number(const char *ver) {
00092   for (int i=0; maya_versions[i].ver != 0; i++) {
00093     if (strcmp(maya_versions[i].ver, ver)==0) {
00094       return maya_versions[i].key;
00095     }
00096   }
00097   return 0;
00098 }
00099 
00100 #if defined(_WIN32)
00101 static void
00102 get_maya_location(const char *ver, string &loc) {
00103   char fullkey[1024];
00104   const char *developer;
00105   LONG res;
00106 
00107   for (int dev=0; dev<3; dev++) {
00108     switch (dev) {
00109     case 0: developer="Alias|Wavefront"; break;
00110     case 1: developer="Alias"; break;
00111     case 2: developer="Autodesk"; break;
00112     }
00113     sprintf(fullkey, "SOFTWARE\\%s\\Maya\\%s\\Setup\\InstallPath", developer, ver);
00114     for (int hive=0; hive<2; hive++) {
00115       HKEY hkey;
00116       res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | (hive ? 256:0), &hkey);
00117       if (res == ERROR_SUCCESS) {
00118         DWORD dtype; 
00119         DWORD size = 4096;
00120         char result[4096 + 1];
00121         res = RegQueryValueEx(hkey, "MAYA_INSTALL_LOCATION", NULL, &dtype, (LPBYTE)result, &size);
00122         if ((res == ERROR_SUCCESS)&&(dtype == REG_SZ)) {
00123           result[size] = 0;
00124           loc = result;
00125         }
00126         RegCloseKey(hkey);
00127       }
00128     }
00129   }
00130 }
00131 
00132 #elif defined(__APPLE__)
00133 static void
00134 get_maya_location(const char *ver, string &loc) {
00135   char mpath[64];
00136   sprintf(mpath, "/Applications/Autodesk/maya%s/Maya.app/Contents", ver);
00137   struct stat st;
00138   if(stat(mpath, &st) == 0) {
00139     loc = mpath;
00140   }
00141 }
00142 
00143 #else  // _WIN32
00144 static void
00145 get_maya_location(const char *ver, string &loc) {
00146   char mpath[64];
00147 #if __WORDSIZE == 64
00148   sprintf(mpath, "/usr/autodesk/maya%s-x64", ver);
00149 #else
00150   sprintf(mpath, "/usr/autodesk/maya%s", ver);
00151 #endif
00152   struct stat st;
00153   if(stat(mpath, &st) == 0) {
00154     loc = mpath;
00155   } else {
00156 #if __WORDSIZE == 64
00157     sprintf(mpath, "/usr/aw/maya%s-x64", ver);
00158 #else
00159     sprintf(mpath, "/usr/aw/maya%s", ver);
00160 #endif
00161     if(stat(mpath, &st) == 0) {
00162       loc = mpath;
00163     }
00164   }
00165 }
00166 
00167 #endif  // _WIN32
00168 
00169 
00170 int 
00171 main(int argc, char *argv[]) {
00172   // First, get the command line and append _bin, so we will actually
00173   // run maya2egg_bin.exe, egg2maya_bin.exe, etc.
00174   Filename command = Filename::from_os_specific(argv[0]);
00175   if (!command.is_fully_qualified()) {
00176     DSearchPath path;
00177     path.append_path(ExecutionEnvironment::get_environment_variable("PATH"));
00178 #ifdef _WIN32
00179     command.set_extension("exe");
00180 #endif
00181     command.resolve_filename(path);
00182   }
00183 
00184 #ifdef _WIN32
00185   if (command.get_extension() == "exe") {
00186     command.set_extension("");
00187   }
00188 #endif
00189 
00190   command = command.get_fullpath() + string("_bin");
00191 #ifdef _WIN32
00192   command.set_extension("exe");
00193 #endif
00194   string os_command = command.to_os_specific();
00195 
00196   // First start with $PANDA_MAYA_LOCATION.  If it is set, it
00197   // overrides everything else.
00198   Filename maya_location = Filename::expand_from("$PANDA_MAYA_LOCATION");
00199   if (!maya_location.empty()) {
00200     // Reset maya_location to its full long name, because Maya
00201     // requires this.
00202     maya_location.make_canonical();
00203     maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
00204 
00205   } else {
00206     // $PANDA_MAYA_LOCATION wasn't set, so check the normal locations.
00207     // First, we get the standard location, as a point of reference.
00208     Filename standard_maya_location;
00209 #ifdef MAYAVERSION
00210     const char *key = get_version_number(TOSTRING(MAYAVERSION));
00211     if (key == NULL) {
00212       cerr << "Unknown Maya version: " << TOSTRING(MAYAVERSION) << "\n";
00213     } else {
00214       string loc;
00215       get_maya_location(key, loc);
00216       if (loc.empty()) {
00217         cerr << "Cannot locate " << TOSTRING(MAYAVERSION) << ": it does not appear to be installed.\n";
00218       } else {
00219         standard_maya_location = Filename::from_os_specific(loc);
00220       }
00221     }
00222     if (!standard_maya_location.empty()) {
00223       // Reset standard_maya_location to its full long name, so we can
00224       // compare reliably to the given version.
00225       standard_maya_location.make_canonical();
00226       standard_maya_location = Filename::from_os_specific(standard_maya_location.to_os_long_name());
00227     }
00228 #endif  // MAYAVERSION
00229 
00230     // Now check if $MAYA_LOCATION is set.  If it is, and it's
00231     // consistent with the standard location, we respect it.
00232     maya_location = Filename::expand_from("$MAYA_LOCATION");
00233     if (!maya_location.empty()) {
00234       // Reset maya_location to its full long name, so we can compare
00235       // it reliably to the standard location; and also because Maya
00236       // requires this.
00237       maya_location.make_canonical();
00238       maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
00239     }
00240     
00241     if (maya_location.empty()) {
00242       // If it is not set, we use the standard version instead.
00243       maya_location = standard_maya_location;
00244 
00245     } else if (maya_location != standard_maya_location) {
00246       // If it *is* set, we verify that OpenMaya.dll matches the
00247       // standard version.
00248       Filename openmaya_given = Filename::dso_filename(Filename(maya_location, openmaya_filename));
00249       Filename openmaya_standard = Filename::dso_filename(Filename(standard_maya_location, openmaya_filename));
00250 
00251       if (openmaya_given != openmaya_standard) {
00252 #ifdef HAVE_OPENSSL
00253         // If we have OpenSSL, we can use it to check the md5 hashes of
00254         // the DLL.
00255         HashVal hash_given, hash_standard;
00256         if (!hash_standard.hash_file(openmaya_standard)) {
00257           // Couldn't read the standard file, so use the given one.
00258 
00259         } else {
00260           if (!hash_given.hash_file(openmaya_given)) {
00261             // Couldn't even read the given file; use the standard one
00262             // instead.
00263             maya_location = standard_maya_location;
00264 
00265           } else {
00266             if (hash_standard != hash_given) {
00267               // No match; it must be the wrong version.
00268               cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
00269               maya_location = standard_maya_location;
00270             } else {
00271               // The hash matches; keep the given MAYA_LOCATION setting.
00272             }
00273           }
00274         }
00275 #else // HAVE_OPENSSL
00276       // Without OpenSSL, just check the DLL filesize only.
00277         off_t size_given, size_standard;
00278         size_standard = openmaya_standard.get_file_size();
00279         if (size_standard == 0) {
00280           // Couldn't read the standard file, so use the given one.
00281 
00282         } else {
00283           size_given = openmaya_given.get_file_size();
00284           if (size_given == 0) {
00285             // Couldn't even read the given file; use the standard one
00286             // instead.
00287             maya_location = standard_maya_location;
00288 
00289           } else {
00290             if (size_standard != size_given) {
00291               // No match; it must be the wrong version.
00292               cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
00293               maya_location = standard_maya_location;
00294 
00295             } else {
00296               // The size matches; keep the given MAYA_LOCATION setting.
00297             }
00298           }
00299         }
00300 
00301 #endif  // HAVE_OPENSSL
00302       }
00303     }
00304   }
00305 
00306   if (maya_location.empty()) {
00307     cerr << "$MAYA_LOCATION is not set!\n";
00308     exit(1);
00309   }
00310 
00311   cerr << "MAYA_LOCATION: " << maya_location.to_os_specific() << endl;
00312   if (!maya_location.is_directory()) {
00313     cerr << "The directory referred to by $MAYA_LOCATION does not exist!\n";
00314     exit(1);
00315   }
00316   
00317   // Look for OpenMaya.dll as a sanity check.
00318   Filename openmaya = Filename::dso_filename(Filename(maya_location, openmaya_filename));
00319   if (!openmaya.is_regular_file()) {
00320     cerr << "Could not find $MAYA_LOCATION/" << Filename::dso_filename(openmaya_filename).to_os_specific() << "!\n";
00321     exit(1); 
00322   }
00323 
00324   // Re-set MAYA_LOCATION to its properly sanitized form.
00325   {
00326     string putenv_str = "MAYA_LOCATION=" + maya_location.to_os_specific();
00327     char *putenv_cstr = strdup(putenv_str.c_str());
00328     putenv(putenv_cstr);
00329   }
00330 
00331 #ifdef WIN32
00332   string sep = ";";
00333 #else
00334   string sep = ":";
00335 #endif
00336 
00337   // Now set PYTHONHOME & PYTHONPATH.  Maya2008 requires this to be
00338   // set and pointing within $MAYA_LOCATION, or it might get itself
00339   // confused with another Python installation (e.g. Panda's).
00340   Filename python = Filename(maya_location, "Python");
00341   if (python.is_directory()) {
00342     {
00343       string putenv_str = "PYTHONHOME=" + python.to_os_specific();
00344       char *putenv_cstr = strdup(putenv_str.c_str());
00345       putenv(putenv_cstr);
00346     }
00347     {
00348       string putenv_str = "PYTHONPATH=" + python.to_os_specific();
00349 
00350       Filename pyzip = find_pyzip(maya_location);
00351       if (!pyzip.empty() && pyzip.exists()) {
00352         putenv_str += sep;
00353         putenv_str += pyzip.to_os_specific();
00354       }
00355 
00356       Filename site_packages(python, "lib/site-packages");
00357       if (site_packages.is_directory()) {
00358         putenv_str += sep;
00359         putenv_str += site_packages.to_os_specific();
00360       }
00361 
00362       char *putenv_cstr = strdup(putenv_str.c_str());
00363       putenv(putenv_cstr);
00364     }
00365   }
00366 
00367   // Also put the Maya bin directory on the PATH.
00368   Filename bin = Filename(maya_location, "bin");
00369   if (bin.is_directory()) {
00370     char *path = getenv("PATH");
00371     if (path == NULL) {
00372       path = "";
00373     }
00374     string putenv_str = "PATH=" + bin.to_os_specific() + sep + path;
00375     char *putenv_cstr = strdup(putenv_str.c_str());
00376     putenv(putenv_cstr);
00377   }
00378 
00379 #ifdef IS_OSX
00380   // And on DYLD_LIBRARY_PATH.
00381   if (bin.is_directory()) {
00382     char *path = getenv("DYLD_LIBRARY_PATH");
00383     if (path == NULL) {
00384       path = "";
00385     }
00386     string sep = ":";
00387     string putenv_str = "DYLD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
00388     char *putenv_cstr = strdup(putenv_str.c_str());
00389     putenv(putenv_cstr);
00390   }
00391 
00392 #elif !defined(_WIN32)
00393   // Linux (or other non-Windows OS) gets it added to LD_LIBRARY_PATH.
00394   if (bin.is_directory()) {
00395     char *path = getenv("LD_LIBRARY_PATH");
00396     if (path == NULL) {
00397       path = "";
00398     }
00399     string sep = ":";
00400     string putenv_str = "LD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
00401     char *putenv_cstr = strdup(putenv_str.c_str());
00402     putenv(putenv_cstr);
00403   }
00404 
00405 #endif // IS_OSX
00406 
00407   // When this is set, Panda3D will try not to use any functions from the
00408   // CPython API.  This is necessary because Maya links with its own copy
00409   // of Python, which may be incompatible with ours.
00410   putenv("PANDA_INCOMPATIBLE_PYTHON=1");
00411 
00412   // Now that we have set up the environment variables properly, chain
00413   // to the actual maya2egg_bin (or whichever) executable.
00414 
00415 #ifdef _WIN32
00416   // Windows case.
00417   char *command_line = strdup(GetCommandLine());
00418   STARTUPINFO startup_info;
00419   PROCESS_INFORMATION process_info;
00420   GetStartupInfo(&startup_info);
00421   BOOL result = CreateProcess(os_command.c_str(),
00422                               command_line, 
00423                               NULL, NULL, true, 0,
00424                               NULL, NULL,
00425                               &startup_info,
00426                               &process_info);
00427   if (result) {
00428     WaitForSingleObject(process_info.hProcess, INFINITE);
00429     DWORD exit_code = 0;
00430 
00431     if (GetExitCodeProcess(process_info.hProcess, &exit_code)) {
00432       if (exit_code != 0) {
00433         cerr << "Program exited with status " << exit_code << "\n";
00434       }
00435     }
00436 
00437     CloseHandle(process_info.hProcess);
00438     CloseHandle(process_info.hThread);
00439     exit(exit_code);
00440   }
00441   cerr << "Couldn't execute " << command << ": " << GetLastError() << "\n";
00442 
00443 #else
00444   // Unix case.
00445   execvp(os_command.c_str(), argv);
00446 #endif
00447 
00448   // Couldn't execute for some reason.
00449   return 1;
00450 }
 All Classes Functions Variables Enumerations