Panda3D
 All Classes Functions Variables Enumerations
mayapath.cxx
1 // Filename: mayapath.cxx
2 // Created by: drose (07Apr08)
3 //
4 ////////////////////////////////////////////////////////////////////
5 //
6 // PANDA 3D SOFTWARE
7 // Copyright (c) Carnegie Mellon University. All rights reserved.
8 //
9 // All use of this software is subject to the terms of the revised BSD
10 // license. You should have received a copy of this license along
11 // with this source code in a file named "LICENSE."
12 //
13 ////////////////////////////////////////////////////////////////////
14 
15 // This program works as a stub to launch maya2egg, egg2maya, and
16 // similar programs that invoke OpenMaya and require certain
17 // environment variables to be set first.
18 
19 // It used to duplicate code in mayaWrapper.cxx, but now the
20 // functionality for these two separate programs are unified here.
21 
22 // If MAYAVERSION is defined at the time this is compiled, then that
23 // particular version of Maya is insisted upon, and the desired Maya
24 // location is found in the Registry; otherwise, we require that
25 // $MAYA_LOCATION be set at runtime and points to the desired Maya
26 // installation.
27 
28 // If MAYAVERSION is defined and $MAYA_LOCATION is also set, then we
29 // check that definition of $MAYA_LOCATION is reasonable, which we
30 // define as pointing to the same version of OpenMaya.dll. If so,
31 // then we use the runtime $MAYA_LOCATION, allowing the user to
32 // (slightly) override the runtime Maya directory. If $MAYA_LOCATION
33 // is set but points to a different version of OpenMaya.dll, we ignore
34 // it altogether and replace it with our registry data, which allows
35 // the user to have MAYA_LOCATION pointing to a different version of
36 // Maya without interfering with this program.
37 
38 #include "dtoolbase.h"
39 #include "filename.h"
40 #include "globPattern.h"
41 #include "dSearchPath.h"
42 #include "executionEnvironment.h"
43 #include "hashVal.h"
44 #include <stdlib.h>
45 
46 #if defined(_WIN32)
47 #define WIN32_LEAN_AND_MEAN
48 #include <windows.h>
49 #else
50 #include <sys/stat.h>
51 #endif
52 
53 #ifdef HAVE_PYTHON
54 #include "pystub.h"
55 #endif
56 
57 #define QUOTESTR(x) #x
58 #define TOSTRING(x) QUOTESTR(x)
59 
60 #if defined(_WIN32)
61 // Filename::dso_filename changes .so to .dll automatically.
62 static const Filename openmaya_filename = "bin/OpenMaya.so";
63 #elif defined(IS_OSX)
64 static const Filename openmaya_filename = "MacOS/libOpenMaya.dylib";
65 #else
66 static const Filename openmaya_filename = "lib/libOpenMaya.so";
67 #endif // _WIN32
68 
69 // Searches for python26.zip or whatever version it is.
70 static Filename
71 find_pyzip(const Filename &maya_location) {
72  // This is where python26.zip appears on Windows. Should it be in
73  // other locations on other platforms?
74  Filename dirname(maya_location, "bin");
75 
76  vector_string results;
77  GlobPattern glob("python*.zip");
78  if (glob.match_files(results, dirname) != 0) {
79  return Filename(dirname, results[0]);
80  }
81 
82  return Filename();
83 }
84 
85 struct MayaVerInfo {
86  const char *ver, *key;
87 };
88 
89 struct MayaVerInfo maya_versions[] = {
90  { "MAYA6", "6.0" },
91  { "MAYA65", "6.5" },
92  { "MAYA7", "7.0" },
93  { "MAYA8", "8.0" },
94  { "MAYA85", "8.5" },
95  { "MAYA2008", "2008"},
96  { "MAYA2009", "2009"},
97  { "MAYA2010", "2010"},
98  { "MAYA2011", "2011"},
99  { "MAYA2012", "2012"},
100  { "MAYA2013", "2013"},
101  { "MAYA20135", "2013.5"},
102  { "MAYA2014", "2014"},
103  { "MAYA2015", "2015"},
104  { "MAYA2016", "2016"},
105  { 0, 0 },
106 };
107 
108 static const char *
109 get_version_number(const char *ver) {
110  for (int i = 0; maya_versions[i].ver != 0; ++i) {
111  if (strcmp(maya_versions[i].ver, ver) == 0) {
112  return maya_versions[i].key;
113  }
114  }
115  return 0;
116 }
117 
118 #if defined(_WIN32)
119 static void
120 get_maya_location(const char *ver, string &loc) {
121  char fullkey[1024];
122  const char *developer;
123  LONG res;
124 
125  for (int dev=0; dev<3; dev++) {
126  switch (dev) {
127  case 0: developer="Alias|Wavefront"; break;
128  case 1: developer="Alias"; break;
129  case 2: developer="Autodesk"; break;
130  }
131  sprintf(fullkey, "SOFTWARE\\%s\\Maya\\%s\\Setup\\InstallPath", developer, ver);
132  for (int hive=0; hive<2; hive++) {
133  HKEY hkey;
134  res = RegOpenKeyEx(HKEY_LOCAL_MACHINE, fullkey, 0, KEY_READ | (hive ? 256:0), &hkey);
135  if (res == ERROR_SUCCESS) {
136  DWORD dtype;
137  DWORD size = 4096;
138  char result[4096 + 1];
139  res = RegQueryValueEx(hkey, "MAYA_INSTALL_LOCATION", NULL, &dtype, (LPBYTE)result, &size);
140  if ((res == ERROR_SUCCESS)&&(dtype == REG_SZ)) {
141  result[size] = 0;
142  loc = result;
143  }
144  RegCloseKey(hkey);
145  }
146  }
147  }
148 }
149 
150 #elif defined(__APPLE__)
151 static void
152 get_maya_location(const char *ver, string &loc) {
153  char mpath[64];
154  sprintf(mpath, "/Applications/Autodesk/maya%s/Maya.app/Contents", ver);
155  struct stat st;
156  if(stat(mpath, &st) == 0) {
157  loc = mpath;
158  }
159 }
160 
161 #else // _WIN32
162 static void
163 get_maya_location(const char *ver, string &loc) {
164  char mpath[64];
165 #if __WORDSIZE == 64
166  sprintf(mpath, "/usr/autodesk/maya%s-x64", ver);
167 #else
168  sprintf(mpath, "/usr/autodesk/maya%s", ver);
169 #endif
170  struct stat st;
171  if(stat(mpath, &st) == 0) {
172  loc = mpath;
173  } else {
174 #if __WORDSIZE == 64
175  sprintf(mpath, "/usr/aw/maya%s-x64", ver);
176 #else
177  sprintf(mpath, "/usr/aw/maya%s", ver);
178 #endif
179  if(stat(mpath, &st) == 0) {
180  loc = mpath;
181  }
182  }
183 }
184 
185 #endif // _WIN32
186 
187 
188 int
189 main(int argc, char *argv[]) {
190 #ifdef HAVE_PYTHON
191  // Force pystub to be linked in.
192  pystub();
193 #endif
194 
195  // First, get the command line and append _bin, so we will actually
196  // run maya2egg_bin.exe, egg2maya_bin.exe, etc.
197  Filename command = Filename::from_os_specific(argv[0]);
198  if (!command.is_fully_qualified()) {
199  DSearchPath path;
201 #ifdef _WIN32
202  command.set_extension("exe");
203 #endif
204  command.resolve_filename(path);
205  }
206 
207 #ifdef _WIN32
208  if (command.get_extension() == "exe") {
209  command.set_extension("");
210  }
211 #endif
212 
213  command = command.get_fullpath() + string("_bin");
214 #ifdef _WIN32
215  command.set_extension("exe");
216 #endif
217  string os_command = command.to_os_specific();
218 
219  // First start with $PANDA_MAYA_LOCATION. If it is set, it
220  // overrides everything else.
221  Filename maya_location = Filename::expand_from("$PANDA_MAYA_LOCATION");
222  if (!maya_location.empty()) {
223  // Reset maya_location to its full long name, because Maya
224  // requires this.
225  maya_location.make_canonical();
226  maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
227 
228  } else {
229  // $PANDA_MAYA_LOCATION wasn't set, so check the normal locations.
230  // First, we get the standard location, as a point of reference.
231  Filename standard_maya_location;
232 #ifdef MAYAVERSION
233  const char *key = get_version_number(TOSTRING(MAYAVERSION));
234  if (key == NULL) {
235  cerr << "Unknown Maya version: " << TOSTRING(MAYAVERSION) << "\n";
236  } else {
237  string loc;
238  get_maya_location(key, loc);
239  if (loc.empty()) {
240  cerr << "Cannot locate " << TOSTRING(MAYAVERSION) << ": it does not appear to be installed.\n";
241  } else {
242  standard_maya_location = Filename::from_os_specific(loc);
243  }
244  }
245  if (!standard_maya_location.empty()) {
246  // Reset standard_maya_location to its full long name, so we can
247  // compare reliably to the given version.
248  standard_maya_location.make_canonical();
249  standard_maya_location = Filename::from_os_specific(standard_maya_location.to_os_long_name());
250  }
251 #endif // MAYAVERSION
252 
253  // Now check if $MAYA_LOCATION is set. If it is, and it's
254  // consistent with the standard location, we respect it.
255  maya_location = Filename::expand_from("$MAYA_LOCATION");
256  if (!maya_location.empty()) {
257  // Reset maya_location to its full long name, so we can compare
258  // it reliably to the standard location; and also because Maya
259  // requires this.
260  maya_location.make_canonical();
261  maya_location = Filename::from_os_specific(maya_location.to_os_long_name());
262  }
263 
264  if (maya_location.empty()) {
265  // If it is not set, we use the standard version instead.
266  maya_location = standard_maya_location;
267 
268  } else if (maya_location != standard_maya_location) {
269  // If it *is* set, we verify that OpenMaya.dll matches the
270  // standard version.
271  Filename openmaya_given = Filename::dso_filename(Filename(maya_location, openmaya_filename));
272  Filename openmaya_standard = Filename::dso_filename(Filename(standard_maya_location, openmaya_filename));
273 
274  if (openmaya_given != openmaya_standard) {
275 #ifdef HAVE_OPENSSL
276  // If we have OpenSSL, we can use it to check the md5 hashes of
277  // the DLL.
278  HashVal hash_given, hash_standard;
279  if (!hash_standard.hash_file(openmaya_standard)) {
280  // Couldn't read the standard file, so use the given one.
281 
282  } else {
283  if (!hash_given.hash_file(openmaya_given)) {
284  // Couldn't even read the given file; use the standard one
285  // instead.
286  maya_location = standard_maya_location;
287 
288  } else {
289  if (hash_standard != hash_given) {
290  // No match; it must be the wrong version.
291  cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
292  maya_location = standard_maya_location;
293  } else {
294  // The hash matches; keep the given MAYA_LOCATION setting.
295  }
296  }
297  }
298 #else // HAVE_OPENSSL
299  // Without OpenSSL, just check the DLL filesize only.
300  off_t size_given, size_standard;
301  size_standard = openmaya_standard.get_file_size();
302  if (size_standard == 0) {
303  // Couldn't read the standard file, so use the given one.
304 
305  } else {
306  size_given = openmaya_given.get_file_size();
307  if (size_given == 0) {
308  // Couldn't even read the given file; use the standard one
309  // instead.
310  maya_location = standard_maya_location;
311 
312  } else {
313  if (size_standard != size_given) {
314  // No match; it must be the wrong version.
315  cerr << "$MAYA_LOCATION points to wrong version; using standard location instead.\n";
316  maya_location = standard_maya_location;
317 
318  } else {
319  // The size matches; keep the given MAYA_LOCATION setting.
320  }
321  }
322  }
323 
324 #endif // HAVE_OPENSSL
325  }
326  }
327  }
328 
329  if (maya_location.empty()) {
330  cerr << "$MAYA_LOCATION is not set!\n";
331  exit(1);
332  }
333 
334  cerr << "MAYA_LOCATION: " << maya_location.to_os_specific() << endl;
335  if (!maya_location.is_directory()) {
336  cerr << "The directory referred to by $MAYA_LOCATION does not exist!\n";
337  exit(1);
338  }
339 
340  // Look for OpenMaya.dll as a sanity check.
341  Filename openmaya = Filename::dso_filename(Filename(maya_location, openmaya_filename));
342  if (!openmaya.is_regular_file()) {
343  cerr << "Could not find $MAYA_LOCATION/" << Filename::dso_filename(openmaya_filename).to_os_specific() << "!\n";
344  exit(1);
345  }
346 
347  // Re-set MAYA_LOCATION to its properly sanitized form.
348  {
349  string putenv_str = "MAYA_LOCATION=" + maya_location.to_os_specific();
350  char *putenv_cstr = strdup(putenv_str.c_str());
351  putenv(putenv_cstr);
352  }
353 
354 #ifdef WIN32
355  string sep = ";";
356 #else
357  string sep = ":";
358 #endif
359 
360  // Now set PYTHONHOME & PYTHONPATH. Maya2008 requires this to be
361  // set and pointing within $MAYA_LOCATION, or it might get itself
362  // confused with another Python installation (e.g. Panda's).
363  Filename python = Filename(maya_location, "Python");
364  if (python.is_directory()) {
365  {
366  string putenv_str = "PYTHONHOME=" + python.to_os_specific();
367  char *putenv_cstr = strdup(putenv_str.c_str());
368  putenv(putenv_cstr);
369  }
370  {
371  string putenv_str = "PYTHONPATH=" + python.to_os_specific();
372 
373  Filename pyzip = find_pyzip(maya_location);
374  if (!pyzip.empty() && pyzip.exists()) {
375  putenv_str += sep;
376  putenv_str += pyzip.to_os_specific();
377  }
378 
379  Filename site_packages(python, "lib/site-packages");
380  if (site_packages.is_directory()) {
381  putenv_str += sep;
382  putenv_str += site_packages.to_os_specific();
383  }
384 
385  char *putenv_cstr = strdup(putenv_str.c_str());
386  putenv(putenv_cstr);
387  }
388  }
389 
390  // Also put the Maya bin directory on the PATH.
391 #ifdef IS_OSX
392  Filename bin = Filename(maya_location, "MacOS");
393 #else
394  Filename bin = Filename(maya_location, "bin");
395 #endif
396  if (bin.is_directory()) {
397  const char *path = getenv("PATH");
398  if (path == NULL) {
399  path = "";
400  }
401  string putenv_str = "PATH=" + bin.to_os_specific() + sep + path;
402  char *putenv_cstr = strdup(putenv_str.c_str());
403  putenv(putenv_cstr);
404  }
405 
406 #ifdef IS_OSX
407  // And on DYLD_LIBRARY_PATH.
408  if (bin.is_directory()) {
409  const char *path = getenv("DYLD_LIBRARY_PATH");
410  if (path == NULL) {
411  path = "";
412  }
413  string sep = ":";
414  string putenv_str = "DYLD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
415  char *putenv_cstr = strdup(putenv_str.c_str());
416  putenv(putenv_cstr);
417  }
418 
419  // We also have to give it a way to find the Maya frameworks.
420  Filename fw_dir = Filename(maya_location, "Frameworks");
421  if (fw_dir.is_directory()) {
422  const char *path = getenv("DYLD_FALLBACK_FRAMEWORK_PATH");
423  if (path == NULL) {
424  path = "";
425  }
426  string sep = ":";
427  string putenv_str = "DYLD_FALLBACK_FRAMEWORK_PATH=" + fw_dir.to_os_specific() + sep + path;
428  char *putenv_cstr = strdup(putenv_str.c_str());
429  putenv(putenv_cstr);
430  }
431 
432 #elif !defined(_WIN32)
433  // Linux (or other non-Windows OS) gets it added to LD_LIBRARY_PATH.
434  if (bin.is_directory()) {
435  const char *path = getenv("LD_LIBRARY_PATH");
436  if (path == NULL) {
437  path = "";
438  }
439  string sep = ":";
440  string putenv_str = "LD_LIBRARY_PATH=" + bin.to_os_specific() + sep + path;
441  char *putenv_cstr = strdup(putenv_str.c_str());
442  putenv(putenv_cstr);
443  }
444 
445 #endif // IS_OSX
446 
447  // When this is set, Panda3D will try not to use any functions from the
448  // CPython API. This is necessary because Maya links with its own copy
449  // of Python, which may be incompatible with ours.
450  putenv((char *)"PANDA_INCOMPATIBLE_PYTHON=1");
451 
452  // Now that we have set up the environment variables properly, chain
453  // to the actual maya2egg_bin (or whichever) executable.
454 
455 #ifdef _WIN32
456  // Windows case.
457  char *command_line = strdup(GetCommandLine());
458  STARTUPINFO startup_info;
459  PROCESS_INFORMATION process_info;
460  GetStartupInfo(&startup_info);
461  BOOL result = CreateProcess(os_command.c_str(),
462  command_line,
463  NULL, NULL, true, 0,
464  NULL, NULL,
465  &startup_info,
466  &process_info);
467  if (result) {
468  WaitForSingleObject(process_info.hProcess, INFINITE);
469  DWORD exit_code = 0;
470 
471  if (GetExitCodeProcess(process_info.hProcess, &exit_code)) {
472  if (exit_code != 0) {
473  cerr << "Program exited with status " << exit_code << "\n";
474  }
475  }
476 
477  CloseHandle(process_info.hProcess);
478  CloseHandle(process_info.hThread);
479  exit(exit_code);
480  }
481  cerr << "Couldn't execute " << command << ": " << GetLastError() << "\n";
482 
483 #else
484  // Unix case.
485  execvp(os_command.c_str(), argv);
486 #endif
487 
488  // Couldn't execute for some reason.
489  return 1;
490 }
string get_fullpath() const
Returns the entire filename: directory, basename, extension.
Definition: filename.I:398
void set_extension(const string &s)
Replaces the file extension.
Definition: filename.cxx:837
bool is_directory() const
Returns true if the filename exists and is a directory name, false otherwise.
Definition: filename.cxx:1456
bool make_canonical()
Converts this filename to a canonical name by replacing the directory part with the fully-qualified d...
Definition: filename.cxx:1072
Stores a 128-bit value that represents the hashed contents (typically MD5) of a file or buffer...
Definition: hashVal.h:32
bool is_fully_qualified() const
Returns true if the filename is fully qualified, e.g.
Definition: filename.I:682
static Filename expand_from(const string &user_string, Type type=T_general)
Returns the same thing as from_os_specific(), but embedded environment variable references (e...
Definition: filename.cxx:418
void append_path(const string &path, const string &separator=string())
Adds all of the directories listed in the search path to the end of the search list.
bool resolve_filename(const DSearchPath &searchpath, const string &default_extension=string())
Searches the given search path for the filename.
Definition: filename.cxx:1699
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:44
string to_os_long_name() const
This is the opposite of to_os_short_name(): it returns the &quot;long name&quot; of the filename, if the filename exists.
Definition: filename.cxx:1322
string to_os_specific() const
Converts the filename from our generic Unix-like convention (forward slashes starting with the root a...
Definition: filename.cxx:1196
static string get_environment_variable(const string &var)
Returns the definition of the indicated environment variable, or the empty string if the variable is ...
bool exists() const
Returns true if the filename exists on the disk, false otherwise.
Definition: filename.cxx:1356
streamsize get_file_size() const
Returns the size of the file in bytes, or 0 if there is an error.
Definition: filename.cxx:1665
This class stores a list of directories that can be searched, in order, to locate a particular file...
Definition: dSearchPath.h:32
bool is_regular_file() const
Returns true if the filename exists and is the name of a regular file (i.e.
Definition: filename.cxx:1389
string get_extension() const
Returns the file extension.
Definition: filename.I:477
This class can be used to test for string matches against standard Unix-shell filename globbing conve...
Definition: globPattern.h:37
static Filename from_os_specific(const string &os_specific, Type type=T_general)
This named constructor returns a Panda-style filename (that is, using forward slashes, and no drive letter) based on the supplied filename string that describes a filename in the local system conventions (for instance, on Windows, it may use backslashes or begin with a drive letter and a colon).
Definition: filename.cxx:332