# # Copyright 2017 Pixar # # Licensed under the Apache License, Version 2.0 (the "Apache License") # with the following modification; you may not use this file except in # compliance with the Apache License and the following modification to it: # Section 6. Trademarks. is deleted and replaced with: # # 6. Trademarks. This License does not grant permission to use the trade # names, trademarks, service marks, or product names of the Licensor # and its affiliates, except as required to comply with Section 4(c) of # the License and to reproduce the content of the NOTICE file. # # You may obtain a copy of the Apache License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the Apache License with the above modification is # distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the Apache License for the specific # language governing permissions and limitations under the Apache License. # # Generate doxygen based docs for USD. from __future__ import print_function import sys, os, argparse, shutil, subprocess, tempfile, platform, stat # This finds all modules in the source area. For example, it will find ar, # usdGeom, etc. in the pxr source area, or prman etc. in the third_party # source area. def _getModules(sourceRoot): modules = [] topLevel = [] for p in os.listdir(sourceRoot): # Ignore any hidden directories if os.path.basename(p).startswith('.'): continue # add all library dirs, such as usdGeom path = os.path.join(sourceRoot, p) if os.path.isdir(path): topLevel.append(path) # add all plugin subdirs, such as usdAbc path = os.path.join(os.path.join(sourceRoot, p), 'plugin/') if os.path.isdir(path): topLevel.append(path) for t in topLevel: for p in os.listdir(t): if os.path.basename(p).startswith('.'): continue # Ignore any irrelevant directories elif os.path.basename(p).startswith('CMakeFiles'): continue elif os.path.basename(p).startswith('testenv'): continue path = os.path.join(os.path.join(sourceRoot, t), p) if os.path.isdir(path): modules.append(path) return modules def _generateDocs(pxrSourceRoot, thirdSourceRoot, pxrBuildRoot, installLoc, doxygenBin, dotBin): docsLoc = os.path.join(installLoc, 'src/modules') if not os.path.exists(docsLoc): os.makedirs(docsLoc) modules = _getModules(pxrSourceRoot) modules.extend(_getModules(thirdSourceRoot)) for mod in modules: target = os.path.join(docsLoc, os.path.basename(mod)) # This ensures that we get a fresh view of the source # on each build invocation. if os.path.exists(target): def _removeReadOnly(func, path, exc): try: os.chmod(path, stat.S_IWRITE) func(path) except Exception as exc: print("Failed to remove %s: %s" % (path, str(exc)), file=sys.stderr) shutil.rmtree(target, onerror=_removeReadOnly) shutil.copytree(mod, target) # We need to copy the namespace header separately because # its a generated file from CMake pxrMod = os.path.join(docsLoc, 'pxr') if not os.path.exists(pxrMod): os.makedirs(pxrMod) pxrHeaderSrc = os.path.join(pxrBuildRoot, 'include', 'pxr', 'pxr.h') pxrHeaderDest = os.path.join(pxrMod, 'pxr.h') if os.path.exists(pxrHeaderDest): os.remove(pxrHeaderDest) shutil.copyfile(pxrHeaderSrc, pxrHeaderDest) doxyConfigSrc = os.path.join(pxrBuildRoot, 'Doxyfile') doxyConfigDest = os.path.join(docsLoc, 'usd', 'Doxyfile') if os.path.exists(doxyConfigDest): os.remove(doxyConfigDest) shutil.copyfile(doxyConfigSrc, doxyConfigDest) os.chdir(installLoc) cmd = [doxygenBin, doxyConfigDest] proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) output = proc.communicate()[0] if proc.wait() != 0: print(output.replace('\r\n', '\n'), file=sys.stderr) sys.exit('Error: doxygen failed to complete; ' 'exit code %d.' % proc.returncode) def _checkPath(path, ident, perm): if not os.path.exists(path): sys.exit('Error: file path %s (arg=%s) ' 'does not exist, exiting.' % (path,ident)) elif not os.access(path, perm): permString = '-permission' if perm == os.W_OK: permString = 'write'+permString elif perm == os.R_OK: permString = 'read'+permString elif perm == os.X_OK: permString = 'execute'+permString sys.exit('Error: insufficient permission for path %s, ' '%s required.' % (path, permString)) def _generateInstallLoc(installRoot): installPath = os.path.join(installRoot, 'docs') if not os.path.exists(installPath): os.mkdir(installPath) return installPath if __name__ == "__main__": p = argparse.ArgumentParser(description="Generate USD documentation.") p.add_argument("source", help="The path to the pxr source root.") p.add_argument("third_source", help="The path to the third_party source root.") p.add_argument("build", help="The path to the build directory root.") p.add_argument("install", help="The install root for generated docs.") p.add_argument("doxygen", help="The path to the doxygen executable.") p.add_argument("dot", help="The path to the dot executable.") args = p.parse_args() # Ensure all paths exist first _checkPath(args.source, 'source', os.R_OK) _checkPath(args.third_source, 'third_source', os.R_OK) _checkPath(args.build, 'build', os.R_OK) _checkPath(args.install, 'install', os.W_OK) _checkPath(args.doxygen, 'doxygen', os.X_OK) _checkPath(args.dot, 'dot', os.X_OK) installPath = _generateInstallLoc(args.install) _generateDocs(args.source, args.third_source, args.build, installPath, args.doxygen, args.dot)