]> git.proxmox.com Git - mirror_edk2.git/blobdiff - BaseTools/Scripts/PackageDocumentTools/packagedoc_cli.py
BaseTools: Add PackageDocumentTools into Scripts folder
[mirror_edk2.git] / BaseTools / Scripts / PackageDocumentTools / packagedoc_cli.py
diff --git a/BaseTools/Scripts/PackageDocumentTools/packagedoc_cli.py b/BaseTools/Scripts/PackageDocumentTools/packagedoc_cli.py
new file mode 100644 (file)
index 0000000..92ee699
--- /dev/null
@@ -0,0 +1,429 @@
+## @file\r
+# This module provide command line entry for generating package document!\r
+#\r
+# Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials are licensed and made available\r
+# under the terms and conditions of the BSD License which accompanies this\r
+# distribution. The full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+\r
+import os, sys, logging, traceback, subprocess\r
+from optparse import OptionParser\r
+\r
+import plugins.EdkPlugins.edk2.model.baseobject as baseobject\r
+import plugins.EdkPlugins.edk2.model.doxygengen as doxygengen\r
+\r
+gArchMarcoDict = {'ALL'      : 'MDE_CPU_IA32 MDE_CPU_X64 MDE_CPU_EBC MDE_CPU_IPF _MSC_EXTENSIONS __GNUC__ __INTEL_COMPILER',\r
+                  'IA32_MSFT': 'MDE_CPU_IA32 _MSC_EXTENSIONS',\r
+                  'IA32_GNU' : 'MDE_CPU_IA32 __GNUC__',\r
+                  'X64_MSFT' : 'MDE_CPU_X64 _MSC_EXTENSIONS  ASM_PFX= OPTIONAL= ',\r
+                  'X64_GNU'  : 'MDE_CPU_X64 __GNUC__  ASM_PFX= OPTIONAL= ',\r
+                  'IPF_MSFT' : 'MDE_CPU_IPF _MSC_EXTENSIONS  ASM_PFX= OPTIONAL= ',\r
+                  'IPF_GNU'  : 'MDE_CPU_IPF __GNUC__  ASM_PFX= OPTIONAL= ',\r
+                  'EBC_INTEL': 'MDE_CPU_EBC __INTEL_COMPILER  ASM_PFX= OPTIONAL= '}\r
+\r
+def parseCmdArgs():\r
+    parser = OptionParser(version="Package Document Generation Tools - Version 0.1")\r
+    parser.add_option('-w', '--workspace', action='store', type='string', dest='WorkspacePath',\r
+                      help='Specify workspace absolute path. For example: c:\\tianocore')\r
+    parser.add_option('-p', '--decfile', action='store', dest='PackagePath',\r
+                      help='Specify the absolute path for package DEC file. For example: c:\\tianocore\\MdePkg\\MdePkg.dec')\r
+    parser.add_option('-x', '--doxygen', action='store', dest='DoxygenPath',\r
+                      help='Specify the absolute path of doxygen tools installation. For example: C:\\Program Files\\doxygen\bin\doxygen.exe')\r
+    parser.add_option('-o', '--output', action='store', dest='OutputPath',\r
+                      help='Specify the document output path. For example: c:\\docoutput')\r
+    parser.add_option('-a', '--arch', action='store', dest='Arch', choices=gArchMarcoDict.keys(),\r
+                      help='Specify the architecture used in preprocess package\'s source. For example: -a IA32_MSFT')\r
+    parser.add_option('-m', '--mode', action='store', dest='DocumentMode', choices=['CHM', 'HTML'],\r
+                      help='Specify the document mode from : CHM or HTML')\r
+    parser.add_option('-i', '--includeonly', action='store_true', dest='IncludeOnly',\r
+                      help='Only generate document for package\'s public interfaces produced by include folder. ')\r
+    parser.add_option('-c', '--htmlworkshop', dest='HtmlWorkshopPath',\r
+                      help='Specify the absolute path for Microsoft HTML Workshop\'s hhc.exe file. For example: C:\\Program Files\\HTML Help Workshop\\hhc.exe')\r
+    (options, args) = parser.parse_args()\r
+\r
+    # validate the options\r
+    errors = []\r
+    if options.WorkspacePath == None:\r
+        errors.append('- Please specify workspace path via option -w!')\r
+    elif not os.path.exists(options.WorkspacePath):\r
+        errors.append("- Invalid workspace path %s! The workspace path should be exist in absolute path!" % options.WorkspacePath)\r
+\r
+    if options.PackagePath == None:\r
+        errors.append('- Please specify package DEC file path via option -p!')\r
+    elif not os.path.exists(options.PackagePath):\r
+        errors.append("- Invalid package's DEC file path %s! The DEC path should be exist in absolute path!" % options.PackagePath)\r
+\r
+    default = "C:\\Program Files\\doxygen\\bin\\doxygen.exe"\r
+    if options.DoxygenPath == None:\r
+        if os.path.exists(default):\r
+            print "Warning: Assume doxygen tool is installed at %s. If not, please specify via -x" % default\r
+            options.DoxygenPath = default\r
+        else:\r
+            errors.append('- Please specify the path of doxygen tool installation via option -x! or install it in default path %s' % default)\r
+    elif not os.path.exists(options.DoxygenPath):\r
+        errors.append("- Invalid doxygen tool path %s! The doxygen tool path should be exist in absolute path!" % options.DoxygenPath)\r
+\r
+    if options.OutputPath != None:\r
+        if not os.path.exists(options.OutputPath):\r
+            # create output\r
+            try:\r
+                os.makedirs(options.OutputPath)\r
+            except:\r
+                errors.append('- Fail to create the output directory %s' % options.OutputPath)\r
+    else:\r
+        if options.PackagePath != None and os.path.exists(options.PackagePath):\r
+            dirpath = os.path.dirname(options.PackagePath)\r
+            default = os.path.join (dirpath, "Document")\r
+            print 'Warning: Assume document output at %s. If not, please specify via option -o' % default\r
+            options.OutputPath = default\r
+            if not os.path.exists(default):\r
+                try:\r
+                    os.makedirs(default)\r
+                except:\r
+                    errors.append('- Fail to create default output directory %s! Please specify document output diretory via option -o' % default)\r
+        else:\r
+            errors.append('- Please specify document output path via option -o!')\r
+\r
+    if options.Arch == None:\r
+        options.Arch = 'ALL'\r
+        print "Warning: Assume arch is \"ALL\". If not, specify via -a"\r
+\r
+    if options.DocumentMode == None:\r
+        options.DocumentMode = "HTML"\r
+        print "Warning: Assume document mode is \"HTML\". If not, specify via -m"\r
+\r
+    if options.IncludeOnly == None:\r
+        options.IncludeOnly = False\r
+        print "Warning: Assume generate package document for all package\'s source including publich interfaces and implementation libraries and modules."\r
+\r
+    if options.DocumentMode.lower() == 'chm':\r
+        default = "C:\\Program Files\\HTML Help Workshop\\hhc.exe"\r
+        if options.HtmlWorkshopPath == None:\r
+            if os.path.exists(default):\r
+                print 'Warning: Assume the installation path of Microsoft HTML Workshop is %s. If not, specify via option -c.' % default\r
+                options.HtmlWorkshopPath = default\r
+            else:\r
+                errors.append('- Please specify the installation path of Microsoft HTML Workshop via option -c!')\r
+        elif not os.path.exists(options.HtmlWorkshopPath):\r
+            errors.append('- The installation path of Microsoft HTML Workshop %s does not exists. ' % options.HtmlWorkshopPath)\r
+\r
+    if len(errors) != 0:\r
+        print '\n'\r
+        parser.error('Fail to start due to following reasons: \n%s' %'\n'.join(errors))\r
+    return (options.WorkspacePath, options.PackagePath, options.DoxygenPath, options.OutputPath,\r
+            options.Arch, options.DocumentMode, options.IncludeOnly, options.HtmlWorkshopPath)\r
+\r
+def createPackageObject(wsPath, pkgPath):\r
+    try:\r
+        pkgObj = baseobject.Package(None, wsPath)\r
+        pkgObj.Load(pkgPath)\r
+    except:\r
+        logging.getLogger().error ('Fail to create package object!')\r
+        return None\r
+\r
+    return pkgObj\r
+\r
+def callbackLogMessage(msg, level):\r
+    print msg.strip()\r
+\r
+def callbackCreateDoxygenProcess(doxPath, configPath):\r
+    if sys.platform == 'win32':\r
+        cmd = '"%s" %s' % (doxPath, configPath)\r
+    else:\r
+        cmd = '%s %s' % (doxPath, configPath)\r
+    print cmd\r
+    subprocess.call(cmd, shell=True)\r
+\r
+\r
+def DocumentFixup(outPath, arch):\r
+    # find BASE_LIBRARY_JUMP_BUFFER structure reference page\r
+\r
+    print '\n    >>> Start fixup document \n'\r
+\r
+    for root, dirs, files in os.walk(outPath):\r
+        for dir in dirs:\r
+            if dir.lower() in ['.svn', '_svn', 'cvs']:\r
+                dirs.remove(dir)\r
+        for file in files:\r
+            if not file.lower().endswith('.html'): continue\r
+            fullpath = os.path.join(outPath, root, file)\r
+            try:\r
+                f = open(fullpath, 'r')\r
+                text = f.read()\r
+                f.close()\r
+            except:\r
+                logging.getLogger().error('\nFail to open file %s\n' % fullpath)\r
+                continue\r
+            if arch.lower() == 'all':\r
+                if text.find('BASE_LIBRARY_JUMP_BUFFER Struct Reference') != -1:\r
+                    FixPageBASE_LIBRARY_JUMP_BUFFER(fullpath, text)\r
+                if text.find('MdePkg/Include/Library/BaseLib.h File Reference') != -1:\r
+                    FixPageBaseLib(fullpath, text)\r
+                if text.find('IA32_IDT_GATE_DESCRIPTOR Union Reference') != -1:\r
+                    FixPageIA32_IDT_GATE_DESCRIPTOR(fullpath, text)\r
+            if text.find('MdePkg/Include/Library/UefiDriverEntryPoint.h File Reference') != -1:\r
+                FixPageUefiDriverEntryPoint(fullpath, text)\r
+            if text.find('MdePkg/Include/Library/UefiApplicationEntryPoint.h File Reference') != -1:\r
+                FixPageUefiApplicationEntryPoint(fullpath, text)\r
+\r
+    print '    >>> Finish all document fixing up! \n'\r
+\r
+def FixPageBaseLib(path, text):\r
+    print '    >>> Fixup BaseLib file page at file %s \n' % path\r
+    lines = text.split('\n')\r
+    lastBaseJumpIndex = -1\r
+    lastIdtGateDescriptor = -1\r
+    for index in range(len(lines) - 1, -1, -1):\r
+        line = lines[index]\r
+        if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;4          </td>':\r
+            lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;4&nbsp;[IA32]    </td>'\r
+        if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;0x10          </td>':\r
+            lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;0x10&nbsp;[IPF]   </td>'\r
+        if line.strip() == '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;8          </td>':\r
+            lines[index] = '<td class="memname">#define BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT&nbsp;&nbsp;&nbsp;9&nbsp;[EBC, x64]   </td>'\r
+        if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;4') != -1:\r
+            lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;4',\r
+                                 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;4&nbsp;[IA32]')\r
+        if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;0x10') != -1:\r
+            lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;0x10',\r
+                                 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;0x10&nbsp;[IPF]')\r
+        if line.find('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;8') != -1:\r
+            lines[index] = lines[index].replace('BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;8',\r
+                                 'BASE_LIBRARY_JUMP_BUFFER_ALIGNMENT</a>&nbsp;&nbsp;&nbsp;8&nbsp;[x64, EBC]')\r
+        if line.find('>BASE_LIBRARY_JUMP_BUFFER</a>') != -1:\r
+            if lastBaseJumpIndex != -1:\r
+                del lines[lastBaseJumpIndex]\r
+            lastBaseJumpIndex = index\r
+        if line.find('>IA32_IDT_GATE_DESCRIPTOR</a></td>') != -1:\r
+            if lastIdtGateDescriptor != -1:\r
+                del lines[lastIdtGateDescriptor]\r
+            lastIdtGateDescriptor = index\r
+    try:\r
+        f = open(path, 'w')\r
+        f.write('\n'.join(lines))\r
+        f.close()\r
+    except:\r
+        logging.getLogger().error("     <<< Fail to fixup file %s\n" % path)\r
+        return\r
+    print "    <<< Finish to fixup file %s\n" % path\r
+\r
+def FixPageIA32_IDT_GATE_DESCRIPTOR(path, text):\r
+    print '    >>> Fixup structure reference IA32_IDT_GATE_DESCRIPTOR at file %s \n' % path\r
+    lines = text.split('\n')\r
+    for index in range(len(lines) - 1, -1, -1):\r
+        line = lines[index].strip()\r
+        if line.find('struct {</td>') != -1 and lines[index - 2].find('>Uint64</a></td>') != -1:\r
+            lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')\r
+        if line.find('struct {</td>') != -1 and lines[index - 1].find('Data Fields') != -1:\r
+            lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')\r
+    try:\r
+        f = open(path, 'w')\r
+        f.write('\n'.join(lines))\r
+        f.close()\r
+    except:\r
+        logging.getLogger().error("     <<< Fail to fixup file %s\n" % path)\r
+        return\r
+    print "    <<< Finish to fixup file %s\n" % path\r
+\r
+def FixPageBASE_LIBRARY_JUMP_BUFFER(path, text):\r
+    print '    >>> Fixup structure reference BASE_LIBRARY_JUMP_BUFFER at file %s \n' % path\r
+    lines = text.split('\n')\r
+    bInDetail = True\r
+    bNeedRemove = False\r
+    for index in range(len(lines) - 1, -1, -1):\r
+        line = lines[index]\r
+        if line.find('Detailed Description') != -1:\r
+            bInDetail = False\r
+        if line.startswith('EBC context buffer used by') and lines[index - 1].startswith('x64 context buffer'):\r
+            lines[index] = "IA32/IPF/X64/" + line\r
+            bNeedRemove  = True\r
+        if line.startswith("x64 context buffer") or line.startswith('IPF context buffer used by') or \\r
+           line.startswith('IA32 context buffer used by'):\r
+            if bNeedRemove:\r
+                lines.remove(line)\r
+        if line.find('>R0</a>') != -1 and not bInDetail:\r
+            if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>':\r
+                lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For EBC</h2></td></tr>')\r
+        if line.find('>Rbx</a>') != -1 and not bInDetail:\r
+            if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>':\r
+                lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For X64</h2></td></tr>')\r
+        if line.find('>F2</a>') != -1 and not bInDetail:\r
+            if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>':\r
+                lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For IPF</h2></td></tr>')\r
+        if line.find('>Ebx</a>') != -1 and not bInDetail:\r
+            if lines[index - 1] != '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>':\r
+                lines.insert(index, '<tr><td colspan="2"><br><h2>Data Fields For IA32</h2></td></tr>')\r
+    try:\r
+        f = open(path, 'w')\r
+        f.write('\n'.join(lines))\r
+        f.close()\r
+    except:\r
+        logging.getLogger().error("     <<< Fail to fixup file %s" % path)\r
+        return\r
+    print "    <<< Finish to fixup file %s\n" % path\r
+\r
+def FixPageUefiDriverEntryPoint(path, text):\r
+    print '    >>> Fixup file reference MdePkg/Include/Library/UefiDriverEntryPoint.h at file %s \n' % path\r
+    lines = text.split('\n')\r
+    bInModuleEntry = False\r
+    bInEfiMain     = False\r
+    ModuleEntryDlCount  = 0\r
+    ModuleEntryDelStart = 0\r
+    ModuleEntryDelEnd   = 0\r
+    EfiMainDlCount      = 0\r
+    EfiMainDelStart     = 0\r
+    EfiMainDelEnd       = 0\r
+\r
+    for index in range(len(lines)):\r
+        line = lines[index].strip()\r
+        if line.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint           </td>') != -1:\r
+            bInModuleEntry = True\r
+        if line.find('EFI_STATUS</a> EFIAPI EfiMain           </td>') != -1:\r
+            bInEfiMain = True\r
+        if line.startswith('<p>References <a'):\r
+            if bInModuleEntry:\r
+                ModuleEntryDelEnd = index - 1\r
+                bInModuleEntry = False\r
+            elif bInEfiMain:\r
+                EfiMainDelEnd = index - 1\r
+                bInEfiMain = False\r
+        if bInModuleEntry:\r
+            if line.startswith('</dl>'):\r
+                ModuleEntryDlCount = ModuleEntryDlCount + 1\r
+            if ModuleEntryDlCount == 1:\r
+                ModuleEntryDelStart = index + 1\r
+        if bInEfiMain:\r
+            if line.startswith('</dl>'):\r
+                EfiMainDlCount = EfiMainDlCount + 1\r
+            if EfiMainDlCount == 1:\r
+                EfiMainDelStart = index + 1\r
+\r
+    if EfiMainDelEnd > EfiMainDelStart:\r
+        for index in range(EfiMainDelEnd, EfiMainDelStart, -1):\r
+            del lines[index]\r
+    if ModuleEntryDelEnd > ModuleEntryDelStart:\r
+        for index in range(ModuleEntryDelEnd, ModuleEntryDelStart, -1):\r
+            del lines[index]\r
+\r
+    try:\r
+        f = open(path, 'w')\r
+        f.write('\n'.join(lines))\r
+        f.close()\r
+    except:\r
+        logging.getLogger().error("     <<< Fail to fixup file %s" % path)\r
+        return\r
+    print "    <<< Finish to fixup file %s\n" % path\r
+\r
+\r
+def FixPageUefiApplicationEntryPoint(path, text):\r
+    print '    >>> Fixup file reference MdePkg/Include/Library/UefiApplicationEntryPoint.h at file %s \n' % path\r
+    lines = text.split('\n')\r
+    bInModuleEntry = False\r
+    bInEfiMain     = False\r
+    ModuleEntryDlCount  = 0\r
+    ModuleEntryDelStart = 0\r
+    ModuleEntryDelEnd   = 0\r
+    EfiMainDlCount      = 0\r
+    EfiMainDelStart     = 0\r
+    EfiMainDelEnd       = 0\r
+\r
+    for index in range(len(lines)):\r
+        line = lines[index].strip()\r
+        if line.find('EFI_STATUS</a> EFIAPI _ModuleEntryPoint           </td>') != -1:\r
+            bInModuleEntry = True\r
+        if line.find('EFI_STATUS</a> EFIAPI EfiMain           </td>') != -1:\r
+            bInEfiMain = True\r
+        if line.startswith('<p>References <a'):\r
+            if bInModuleEntry:\r
+                ModuleEntryDelEnd = index - 1\r
+                bInModuleEntry = False\r
+            elif bInEfiMain:\r
+                EfiMainDelEnd = index - 1\r
+                bInEfiMain = False\r
+        if bInModuleEntry:\r
+            if line.startswith('</dl>'):\r
+                ModuleEntryDlCount = ModuleEntryDlCount + 1\r
+            if ModuleEntryDlCount == 1:\r
+                ModuleEntryDelStart = index + 1\r
+        if bInEfiMain:\r
+            if line.startswith('</dl>'):\r
+                EfiMainDlCount = EfiMainDlCount + 1\r
+            if EfiMainDlCount == 1:\r
+                EfiMainDelStart = index + 1\r
+\r
+    if EfiMainDelEnd > EfiMainDelStart:\r
+        for index in range(EfiMainDelEnd, EfiMainDelStart, -1):\r
+            del lines[index]\r
+    if ModuleEntryDelEnd > ModuleEntryDelStart:\r
+        for index in range(ModuleEntryDelEnd, ModuleEntryDelStart, -1):\r
+            del lines[index]\r
+\r
+    try:\r
+        f = open(path, 'w')\r
+        f.write('\n'.join(lines))\r
+        f.close()\r
+    except:\r
+        logging.getLogger().error("     <<< Fail to fixup file %s" % path)\r
+        return\r
+    print "    <<< Finish to fixup file %s\n" % path\r
+\r
+if __name__ == '__main__':\r
+    wspath, pkgpath, doxpath, outpath, archtag, docmode, isinc, hwpath = parseCmdArgs()\r
+\r
+    # configure logging system\r
+    logfilepath = os.path.join(outpath, 'log.txt')\r
+    logging.basicConfig(format='%(levelname)-8s %(message)s', level=logging.DEBUG)\r
+\r
+    # create package model object firstly\r
+    pkgObj = createPackageObject(wspath, pkgpath)\r
+    if pkgObj == None:\r
+        sys.exit(-1)\r
+\r
+    # create doxygen action model\r
+    arch    = None\r
+    tooltag = None\r
+    if archtag.lower() != 'all':\r
+        arch = archtag.split('_')[0]\r
+        tooltag = archtag.split('_')[1]\r
+    else:\r
+        arch    = 'all'\r
+        tooltag = 'all'\r
+\r
+    # preprocess package and call doxygen\r
+    try:\r
+        action = doxygengen.PackageDocumentAction(doxpath,\r
+                                                  hwpath,\r
+                                                  outpath,\r
+                                                  pkgObj,\r
+                                                  docmode,\r
+                                                  callbackLogMessage,\r
+                                                  arch,\r
+                                                  tooltag,\r
+                                                  isinc,\r
+                                                  True)\r
+        action.RegisterCallbackDoxygenProcess(callbackCreateDoxygenProcess)\r
+        action.Generate()\r
+    except:\r
+        message = traceback.format_exception(*sys.exc_info())\r
+        logging.getLogger().error('Fail to create doxygen action! \n%s' % ''.join(message))\r
+        sys.exit(-1)\r
+\r
+    DocumentFixup(outpath, arch)\r
+\r
+    # generate CHM is necessary\r
+    if docmode.lower() == 'chm':\r
+        indexpath = os.path.join(outpath, 'html', 'index.hhp')\r
+        if sys.platform == 'win32':\r
+            cmd = '"%s" %s' % (hwpath, indexpath)\r
+        else:\r
+            cmd = '%s %s' % (hwpath, indexpath)\r
+        subprocess.call(cmd)\r
+        print '\nFinish to generate package document! Please open %s for review' % os.path.join(outpath, 'html', 'index.chm')\r
+    else:\r
+        print '\nFinish to generate package document! Please open %s for review' % os.path.join(outpath, 'html', 'index.html')\r