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