Repository: miurahr/aqtinstall

Issue:
download qt5_xxxx_src_doc_examples
can add feature to download: qt5_xxxx_src_doc_examples
thanks a lot.

Discussion:

Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index cba4746..d13fd35 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -14,12 +14,16 @@ Current changes
 Added
 -----
 
+* New subcommand doc/src/example to install each components.(#137, 138)
+
 Changed
 -------
 
 Fixed
 -----
 
+* Show help when launched without any argument (#136)
+
 Deprecated
 ----------
 
diff --git a/README.rst b/README.rst
index 02da9f7..cfc276a 100644
--- a/README.rst
+++ b/README.rst
@@ -144,7 +144,23 @@ Example: Installing Android (armv7) Qt 5.10.2:
     aqt install 5.10.2 linux android android_armv7
 
 
-Example: Install Install FrameWork(IFW):
+Example: Install examples, doc and source:
+
+.. code-block:: bash
+
+    C:\ aqt examples 5.15.0 windows desktop -m qtcharts qtnetworkauth
+    C:\ aqt doc 5.15.0 windows desktop -m qtcharts qtnetworkauth
+    C:\ aqt src 5.15.0 windows desktop
+
+
+Example: Install Web Assembly
+
+.. code-block:: bash
+
+    aqt install 5.15.0 linux desktop wasm_32
+
+
+Example: Install an Install FrameWork(IFW):
 
 .. code-block:: bash
 
@@ -159,11 +175,12 @@ Example: Install vcredist:
     C:\ .\Qt\Tools\vcredist\vcredist_msvc2019_x64.exe /norestart /q
 
 
-Example: Install OpenSSL:
+Example: Install MinGW on Windows
 
 .. code-block:: bash
 
-    C:\ aqt tool windows tools_openssl 1.1.1-1 qt.tools.openssl.win_x64
+    C:\ aqt tool -O c:\Qt windows tools_mingw 8.1.0-1-202004170606 qt.tools.win64_mingw810w
+    c:\ set PATH=C:\Qt\Tools\mingw810_64\bin
 
 
 Example: Show help message
diff --git a/aqt/archives.py b/aqt/archives.py
index 3b60e7d..c9d31e1 100644
--- a/aqt/archives.py
+++ b/aqt/archives.py
@@ -104,7 +104,7 @@ class QtArchives:
         target_packages.append("qt.{}.{}".format(qt_ver_num, self.arch))
         target_packages.extend(self.mod_list)
         self._download_update_xml(update_xml_url)
-        self._parse_update_xml(target_packages, archive_url)
+        self._parse_update_xml(archive_url, target_packages)
 
     def _download_update_xml(self, update_xml_url):
         try:
@@ -121,7 +121,7 @@ class QtArchives:
                                                                                 r.status_code, r.reason))
                 raise ArchiveDownloadError("Download error!")
 
-    def _parse_update_xml(self, target_packages, archive_url):
+    def _parse_update_xml(self, archive_url, target_packages):
         try:
             self.update_xml = ElementTree.fromstring(self.update_xml_text)
         except ElementTree.ParseError as perror:
@@ -163,6 +163,38 @@ class QtArchives:
         return TargetConfig(self.version, self.target, self.arch, self.os_name)
 
 
+class SrcDocExamplesArchives(QtArchives):
+    """Hold doc/src/example archive package list."""
+
+    def __init__(self, flavor, os_name, target, version, subarchives=None,
+                 modules=None, mirror=None, logging=None, all_extra=False):
+        self.flavor = flavor
+        self.target = target
+        self.os_name = os_name
+        super(SrcDocExamplesArchives, self).__init__(os_name, target, version, self.flavor, subarchives=subarchives,
+                                                     modules=modules, mirror=mirror, logging=logging,
+                                                     all_extra=all_extra)
+
+    def _get_archives(self, qt_ver_num):
+        archive_path = "{0}{1}{2}/qt5_{3}{4}".format(self.os_name,
+                                                     '_x86/' if self.os_name == 'windows' else '_x64/',
+                                                     self.target, qt_ver_num, '_src_doc_examples/')
+        archive_url = "{0}{1}".format(self.base, archive_path)
+        update_xml_url = "{0}/Updates.xml".format(archive_url)
+        target_packages = []
+        target_packages.append("qt.qt5.{}.{}".format(qt_ver_num, self.flavor))
+        target_packages.extend(self.mod_list)
+        self._download_update_xml(update_xml_url)
+        self._parse_update_xml(archive_url, target_packages)
+
+    def get_target_config(self) -> TargetConfig:
+        """Get target configuration.
+
+        :return tuple of three parameter, "Tools", target and arch
+        """
+        return TargetConfig("src_doc_examples", self.target, self.arch, self.os_name)
+
+
 class ToolArchives(QtArchives):
     """Hold tool archive package list
         when installing mingw tool, argument would be
@@ -183,28 +215,33 @@ class ToolArchives(QtArchives):
             archive_url = self.base + self.os_name + '_x64/' + self.target + '/'
         update_xml_url = "{0}{1}/Updates.xml".format(archive_url, self.tool_name)
         self._download_update_xml(update_xml_url)  # call super method.
-        self._parse_tool_update_xml(archive_url)
-
-    def _parse_tool_update_xml(self, archive_url):
-        self.update_xml = ElementTree.fromstring(self.update_xml_text)
-        for packageupdate in self.update_xml.iter("PackageUpdate"):
-            name = packageupdate.find("Name").text
-            downloadable_archives = packageupdate.find("DownloadableArchives").text.split(", ")
-            full_version = packageupdate.find("Version").text
-            if full_version != self.version:
-                continue
-            # FIXME: rules
-            # if "-" in full_version:
-            #    split_version = full_version.split("-")
-            #    named_version = split_version[0] + "-" + split_version[1]
-            # else:
-            #    named_version = full_version
-            named_version = full_version
-            package_desc = packageupdate.find("Description").text
-            for archive in downloadable_archives:
-                package_url = archive_url + self.tool_name + "/" + name + "/" + named_version + archive
-                self.archives.append(QtPackage(name, package_url, archive, package_desc,
-                                               has_mirror=(self.mirror is not None)))
+        self._parse_update_xml(archive_url, [])
+
+    def _parse_update_xml(self, archive_url, target_packages):
+        try:
+            self.update_xml = ElementTree.fromstring(self.update_xml_text)
+        except ElementTree.ParseError as perror:
+            self.logger.error("Downloaded metadata is corrupted. {}".format(perror))
+            raise ArchiveListError("Downloaded metadata is corrupted.")
+        else:
+            for packageupdate in self.update_xml.iter("PackageUpdate"):
+                name = packageupdate.find("Name").text
+                downloadable_archives = packageupdate.find("DownloadableArchives").text.split(", ")
+                full_version = packageupdate.find("Version").text
+                if full_version != self.version:
+                    continue
+                # FIXME: rules
+                # if "-" in full_version:
+                #    split_version = full_version.split("-")
+                #    named_version = split_version[0] + "-" + split_version[1]
+                # else:
+                #    named_version = full_version
+                named_version = full_version
+                package_desc = packageupdate.find("Description").text
+                for archive in downloadable_archives:
+                    package_url = archive_url + self.tool_name + "/" + name + "/" + named_version + archive
+                    self.archives.append(QtPackage(name, package_url, archive, package_desc,
+                                                   has_mirror=(self.mirror is not None)))
 
     def get_target_config(self) -> TargetConfig:
         """Get target configuration.
diff --git a/aqt/cli.py b/aqt/cli.py
index 3913ee3..bb1c445 100644
--- a/aqt/cli.py
+++ b/aqt/cli.py
@@ -31,7 +31,7 @@ import time
 from packaging.version import Version, parse
 
 from aqt.archives import (ArchiveDownloadError, ArchiveListError, QtArchives,
-                          ToolArchives)
+                          SrcDocExamplesArchives, ToolArchives)
 from aqt.installer import QtInstaller
 from aqt.settings import Settings
 
@@ -115,6 +115,14 @@ class Cli():
             return False
         return all([m in available for m in modules])
 
+    def _run_common_part(self, output_dir=None, mirror=None):
+        self.show_aqt_version()
+        if output_dir is not None:
+            output_dir = os.path.normpath(output_dir)
+        if not self._check_mirror(mirror):
+            self.parser.print_help()
+            exit(1)
+
     def run_install(self, args):
         start_time = time.perf_counter()
         arch = args.arch
@@ -127,12 +135,7 @@ class Cli():
         sevenzip = self._set_sevenzip(args)
         mirror = args.base
         archives = args.archives
-        self.show_aqt_version()
-        if output_dir is not None:
-            output_dir = os.path.normpath(output_dir)
-        if not self._check_mirror(mirror):
-            self.parser.print_help()
-            exit(1)
+        self._run_common_part(output_dir, mirror)
         if not self._check_qt_arg_versions(qt_version):
             self.logger.warning("Specified Qt version is unknown: {}.".format(qt_version))
         if not self._check_qt_arg_combination(qt_version, os_name, target, arch):
@@ -149,8 +152,50 @@ class Cli():
         else:
             installer = QtInstaller(qt_archives, logging=self.logger, command=sevenzip, target_dir=output_dir)
             installer.install()
+            installer.finalize()
+        self.logger.info("Finished installation")
         self.logger.info("Time elasped: {time:.8f} second".format(time=time.perf_counter() - start_time))
 
+    def _run_src_doc_examples(self, flavor, args):
+        start_time = time.perf_counter()
+        target = args.target
+        os_name = args.host
+        qt_version = args.qt_version
+        output_dir = args.outputdir
+        mirror = args.base
+        sevenzip = self._set_sevenzip(args)
+        if flavor in ['doc', 'examples']:
+            modules = args.modules
+            archives = args.archives
+        else:
+            modules = None
+            archives = None
+        self._run_common_part(output_dir, mirror)
+        all_extra = True if modules is not None and 'all' in modules else False
+        if not self._check_qt_arg_versions(qt_version):
+            self.logger.warning("Specified Qt version is unknown: {}.".format(qt_version))
+        try:
+            srcdocexamples_archives = SrcDocExamplesArchives(flavor, os_name, target, qt_version, subarchives=archives,
+                                                             modules=modules, mirror=mirror, logging=self.logger,
+                                                             all_extra=all_extra)
+        except ArchiveDownloadError or ArchiveListError:
+            exit(1)
+        else:
+            installer = QtInstaller(srcdocexamples_archives, logging=self.logger, command=sevenzip,
+                                    target_dir=output_dir)
+            installer.install()
+        self.logger.info("Finished installation")
+        self.logger.info("Time elasped: {time:.8f} second".format(time=time.perf_counter() - start_time))
+
+    def run_src(self, args):
+        self._run_src_doc_examples('src', args)
+
+    def run_examples(self, args):
+        self._run_src_doc_examples('examples', args)
+
+    def run_doc(self, args):
+        self._run_src_doc_examples('doc', args)
+
     def run_tool(self, args):
         start_time = time.perf_counter()
         arch = args.arch
@@ -160,12 +205,17 @@ class Cli():
         sevenzip = self._set_sevenzip(args)
         version = args.version
         mirror = args.base
-        self.show_aqt_version()
-        self._check_mirror(mirror)
+        self._run_common_part(output_dir, mirror)
         if not self._check_tools_arg_combination(os_name, tool_name, arch):
             self.logger.warning("Specified target combination is not valid: {} {} {}".format(os_name, tool_name, arch))
-        QtInstaller(ToolArchives(os_name, tool_name, version, arch, mirror=mirror, logging=self.logger),
-                    logging=self.logger, command=sevenzip, target_dir=output_dir).install()
+        try:
+            tool_archives = ToolArchives(os_name, tool_name, version, arch, mirror=mirror, logging=self.logger)
+        except ArchiveDownloadError or ArchiveListError:
+            exit(1)
+        else:
+            installer = QtInstaller(tool_archives, logging=self.logger, command=sevenzip, target_dir=output_dir)
+            installer.install()
+        self.logger.info("Finished installation")
         self.logger.info("Time elasped: {time:.8f} second".format(time=time.perf_counter() - start_time))
 
     def run_list(self, args):
@@ -184,6 +234,25 @@ class Cli():
         self.logger.info("aqtinstall({}) v{} on Python {} [{} {}]".format(module_name, dist.version,
                                                                           py_version, py_impl, py_build))
 
+    def _set_common_options(self, subparser):
+        subparser.add_argument('-O', '--outputdir', nargs='?',
+                               help='Target output directory(default current directory)')
+        subparser.add_argument('-b', '--base', nargs='?',
+                               help="Specify mirror base url such as http://mirrors.ocf.berkeley.edu/qt/, "
+                                    "where 'online' folder exist.")
+        subparser.add_argument('-E', '--external', nargs='?', help='Specify external 7zip command path.')
+        subparser.add_argument('--internal', action='store_true', help='Use internal extractor.')
+
+    def _set_module_options(self, subparser):
+        subparser.add_argument('-m', '--modules', nargs='*', help="Specify extra modules to install")
+        subparser.add_argument('--archives', nargs='*',
+                               help="Specify subset modules to install(Default: all standard modules).")
+
+    def _set_common_argument(self, subparser):
+        subparser.add_argument("qt_version", help="Qt version in the format of \"5.X.Y\"")
+        subparser.add_argument('host', choices=['linux', 'mac', 'windows'], help="host os name")
+        subparser.add_argument('target', choices=['desktop', 'winrt', 'android', 'ios'], help="target sdk")
+
     def _create_parser(self):
         parser = argparse.ArgumentParser(prog='aqt', description='Installer for Qt SDK.',
                                          formatter_class=argparse.RawTextHelpFormatter, add_help=True)
@@ -194,9 +263,8 @@ class Cli():
                                            help='subcommand for aqt Qt installer')
         install_parser = subparsers.add_parser('install')
         install_parser.set_defaults(func=self.run_install)
-        install_parser.add_argument("qt_version", help="Qt version in the format of \"5.X.Y\"")
-        install_parser.add_argument('host', choices=['linux', 'mac', 'windows'], help="host os name")
-        install_parser.add_argument('target', choices=['desktop', 'winrt', 'android', 'ios'], help="target sdk")
+        self._set_common_argument(install_parser)
+        self._set_common_options(install_parser)
         install_parser.add_argument('arch', nargs='?', help="\ntarget linux/desktop: gcc_64, wasm_32"
                                     "\ntarget mac/desktop:   clang_64, wasm_32"
                                     "\ntarget mac/ios:       ios"
@@ -213,28 +281,33 @@ class Cli():
                                     "\nandroid:              Qt 5.14:          android (optional)"
                                     "\n                      Qt 5.13 or below: android_x86_64, android_arm64_v8a"
                                     "\n                                        android_x86, android_armv7")
-        install_parser.add_argument('-m', '--modules', nargs='*', help="Specify extra modules to install")
-        install_parser.add_argument('--archives', nargs='*',
-                                    help="Specify subset modules to install(Default: all standard modules).")
-        install_parser.add_argument('-O', '--outputdir', nargs='?',
-                                    help='Target output directory(default current directory)')
-        install_parser.add_argument('-b', '--base', nargs='?',
-                                    help="Specify mirror base url such as http://mirrors.ocf.berkeley.edu/qt/, "
-                                         "where 'online' folder exist.")
-        install_parser.add_argument('-E', '--external', nargs='?', help='Specify external 7zip command path.')
+        self._set_module_options(install_parser)
+        #
+        doc_parser = subparsers.add_parser('doc')
+        doc_parser.set_defaults(func=self.run_doc)
+        self._set_common_argument(doc_parser)
+        self._set_common_options(doc_parser)
+        self._set_module_options(doc_parser)
+        #
+        examples_parser = subparsers.add_parser('examples')
+        examples_parser.set_defaults(func=self.run_examples)
+        self._set_common_argument(examples_parser)
+        self._set_common_options(examples_parser)
+        self._set_module_options(examples_parser)
+        #
+        src_parser = subparsers.add_parser('src')
+        src_parser.set_defaults(func=self.run_src)
+        self._set_common_argument(src_parser)
+        self._set_common_options(src_parser)
+        #
         tools_parser = subparsers.add_parser('tool')
         tools_parser.set_defaults(func=self.run_tool)
         tools_parser.add_argument('host', choices=['linux', 'mac', 'windows'], help="host os name")
         tools_parser.add_argument('tool_name', help="Name of tool such as tools_ifw, tools_mingw")
         tools_parser.add_argument("version", help="Tool version in the format of \"4.1.2\"")
         tools_parser.add_argument('arch', help="Name of full tool name such as qt.tools.ifw.31")
-        tools_parser.add_argument('-O', '--outputdir', nargs='?',
-                                  help='Target output directory(default current directory)')
-        tools_parser.add_argument('-b', '--base', nargs='?',
-                                  help="Specify mirror base url such as http://mirrors.ocf.berkeley.edu/qt/, "
-                                       "where 'online' folder exist.")
-        tools_parser.add_argument('-E', '--external', nargs='?', help='Specify external 7zip command path.')
-        tools_parser.add_argument('--internal', action='store_true', help='Use internal extractor.')
+        self._set_common_options(tools_parser)
+        #
         list_parser = subparsers.add_parser('list')
         list_parser.set_defaults(func=self.run_list)
         list_parser.add_argument("qt_version", help="Qt version in the format of \"5.X.Y\"")
diff --git a/aqt/installer.py b/aqt/installer.py
index 9e9b411..57bb2af 100644
--- a/aqt/installer.py
+++ b/aqt/installer.py
@@ -163,14 +163,11 @@ class QtInstaller:
             if len(not_done) > 0:
                 self.logger.error("Installation error detected.")
                 exit(1)
-        # finalize
+
+    def finalize(self):
         target = self.qt_archives.get_target_config()
-        if target.version == "Tools":
-            pass
-        else:
-            self.make_conf_files(target.version, target.arch)
-            prefix = pathlib.Path(self.base_dir) / target.version / target.arch
-            updater = Updater(prefix, self.logger)
-            if versiontuple(target.version) < (5, 14, 2):
-                updater.patch_qt(target)
-        self.logger.info("Finished installation")
+        self.make_conf_files(target.version, target.arch)
+        prefix = pathlib.Path(self.base_dir) / target.version / target.arch
+        updater = Updater(prefix, self.logger)
+        if versiontuple(target.version) < (5, 14, 2):
+            updater.patch_qt(target)
diff --git a/azure-pipelines.yml b/azure-pipelines.yml
index c1a7e0b..a246743 100644
--- a/azure-pipelines.yml
+++ b/azure-pipelines.yml
@@ -87,3 +87,42 @@ jobs:
     vmImage: 'ubuntu-16.04'
   steps:
     - template: ci/steps.yml
+
+- job: LinuxSrc
+  displayName: Linux (source)
+  variables:
+    PYTHON_VERSION: '3.7'
+    QT_VERSION: 5.15.0
+    HOST: linux
+    TARGET: desktop
+    TOOL_NAME: src
+  pool:
+    vmImage: 'ubuntu-16.04'
+  steps:
+    - template: ci/steps.yml
+
+- job: LinuxExamples
+  displayName: Linux (Examples)
+  variables:
+    PYTHON_VERSION: '3.7'
+    QT_VERSION: 5.15.0
+    HOST: linux
+    TARGET: desktop
+    TOOL_NAME: examples
+  pool:
+    vmImage: 'ubuntu-16.04'
+  steps:
+    - template: ci/steps.yml
+
+- job: LinuxDoc
+  displayName: Linux (doc)
+  variables:
+    PYTHON_VERSION: '3.7'
+    QT_VERSION: 5.15.0
+    HOST: linux
+    TARGET: desktop
+    TOOL_NAME: doc
+  pool:
+    vmImage: 'ubuntu-16.04'
+  steps:
+    - template: ci/steps.yml
diff --git a/ci/steps.yml b/ci/steps.yml
index 1557bf7..7377ef6 100644
--- a/ci/steps.yml
+++ b/ci/steps.yml
@@ -70,6 +70,42 @@ steps:
       AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini
     condition: ne(variables['SUBARCHIVES'], '')
     displayName: Run Aqt (sub arhives option)
+  - bash: |
+      number=$RANDOM
+      let "number %= 30"
+      sleep $number
+      mkdir Qt
+      cd Qt
+      python -m aqt src $(QT_VERSION) $(HOST) $(TARGET)
+    workingDirectory: $(Build.BinariesDirectory)
+    env:
+      AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini
+    condition: eq(variables['TOOL_NAME'], 'src')
+    displayName: Run Aqt (source)
+  - bash: |
+      number=$RANDOM
+      let "number %= 30"
+      sleep $number
+      mkdir Qt
+      cd Qt
+      python -m aqt examples $(QT_VERSION) $(HOST) $(TARGET)
+    workingDirectory: $(Build.BinariesDirectory)
+    env:
+      AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini
+    condition: eq(variables['TOOL_NAME'], 'examples')
+    displayName: Run Aqt (examples)
+  - bash: |
+      number=$RANDOM
+      let "number %= 30"
+      sleep $number
+      mkdir Qt
+      cd Qt
+      python -m aqt doc $(QT_VERSION) $(HOST) $(TARGET)
+    workingDirectory: $(Build.BinariesDirectory)
+    env:
+      AQT_CONFIG: $(Build.SourcesDirectory)/ci/settings.ini
+    condition: eq(variables['TOOL_NAME'], 'doc')
+    displayName: Run Aqt (doc)
 
   # Test installation environments
   ##----------------------------------------------------
@@ -126,7 +162,7 @@ steps:
     displayName: Detect toolchain for Windows and update PATH
   - script: |
       sudo apt-get install libgl1-mesa-dev
-    condition: and(eq( variables['TARGET'], 'desktop'), eq(variables['Agent.OS'], 'Linux'))
+    condition: and(eq( variables['TARGET'], 'desktop'), eq(variables['Agent.OS'], 'Linux'), eq(variables['TOOL_NAME'], ''))
     displayName: install test dependency for Linux
 
   # no modules
@@ -168,7 +204,7 @@ steps:
       export PATH=$(Build.BinariesDirectory)/Qt/$(QT_VERSION)/$(ARCHDIR)/bin:$PATH
       qmake $(Build.BinariesDirectory)/tests/helloworld
       make
-    condition: and(eq( variables['TARGET'], 'desktop' ), ne( variables['ARCH'], 'wasm_32' ), or(eq(variables['Agent.OS'], 'Linux'), eq(variables['Agent.OS'], 'Darwin')), eq(variables['MODULE'], ''))
+    condition: and(eq( variables['TARGET'], 'desktop' ), ne( variables['ARCH'], 'wasm_32' ), or(eq(variables['Agent.OS'], 'Linux'), eq(variables['Agent.OS'], 'Darwin')), eq(variables['MODULE'], ''), eq(variables['TOOL_NAME'], ''))
     displayName: Build test with qmake for Linux and macOS w/o extra module
   # modules
   # TODO: current PSCX release does not support VSVER 2019
@@ -190,7 +226,7 @@ steps:
       export PATH=$(Build.BinariesDirectory)/Qt/$(QT_VERSION)/$(ARCHDIR)/bin:$PATH
       qmake $(Build.BinariesDirectory)/tests/redditclient
       make
-    condition: and(eq( variables['TARGET'], 'desktop' ), or(eq(variables['Agent.OS'], 'Linux'), eq(variables['Agent.OS'], 'Darwin')), ne(variables['MODULE'], ''))
+    condition: and(eq( variables['TARGET'], 'desktop'), or(eq(variables['Agent.OS'], 'Linux'), eq(variables['Agent.OS'], 'Darwin')), ne(variables['MODULE'], ''))
     displayName: Build test with qmake for Linux and macOS with extra module
 
   ##----------------------------------------------------
@@ -219,3 +255,4 @@ steps:
     workingDirectory: $(Build.BinariesDirectory)
     condition: and(eq( variables['ARCH'], 'wasm_32' ), or(eq(variables['Agent.OS'], 'Linux'), eq(variables['Agent.OS'], 'Darwin')))
     displayName: 'Build WebAssembler sample project'
+
diff --git a/docs/cli.rst b/docs/cli.rst
index b8efb44..6353e9f 100644
--- a/docs/cli.rst
+++ b/docs/cli.rst
@@ -68,3 +68,29 @@ long options may be truncated to the shortest unambiguous abbreviation.
 
     -m qtcharts qtdatavis3d qtlottie qtnetworkauth qtpurchasing qtquicktimeline qtscript qtvirtualkeyboard qtwebglplugin
 
+
+.. option:: --archives <list of archives>
+
+    [Advanced] specify subset of archives to limit installed archvies.
+
+
+.. option:: src <Qt version> <target OS> <target variant>
+
+    install Qt sources specified version and target.
+
+
+.. option:: doc <Qt version> <target OS> <target variant>
+
+    install Qt documents specified version and target.
+
+
+.. option:: examples <Qt version> <target OS> <target variant>
+
+    install Qt examples specified version and target.
+
+
+.. option:: tools <target OS> <target tool name> <target tool version> <tool variant name>
+
+    install tools specified. tool name may be 'tools_openssl_x64', 'tools_ninja', 'tools_ifw', 'tools_cmake'
+    and tool variants name may be 'qt.tools.openssl.gcc_64', 'qt.tools.ninja',  'qt.tools.ifw.32', 'qt.tools.cmake'.
+    You may need to looking for version number at  https://download.qt.io/online/qtsdkrepository/


Test patch:
diff --git a/tests/test_cli.py b/tests/test_cli.py
index 29bb0e4..7885915 100644
--- a/tests/test_cli.py
+++ b/tests/test_cli.py
@@ -3,7 +3,7 @@ import aqt
 
 def test_cli_help(capsys):
     expected = "".join(["usage: aqt [-h] [--logging-conf LOGGING_CONF] [--logger LOGGER]\n",
-                        "           {install,tool,list,help} ...\n",
+                        "           {install,doc,examples,src,tool,list,help} ...\n",
                         "\n",
                         "Installer for Qt SDK.\n",
                         "\n",
@@ -16,7 +16,7 @@ def test_cli_help(capsys):
                         "subcommands:\n",
                         "  Valid subcommands\n",
                         "\n",
-                        "  {install,tool,list,help}\n",
+                        "  {install,doc,examples,src,tool,list,help}\n",
                         "                        subcommand for aqt Qt installer\n"])
     cli = aqt.cli.Cli()
     cli.run(["help"])
@@ -56,7 +56,7 @@ def test_cli_check_mirror():
 
 def test_cli_launch_with_no_argument(capsys):
     expected = "".join(["usage: aqt [-h] [--logging-conf LOGGING_CONF] [--logger LOGGER]\n",
-                        "           {install,tool,list,help} ...\n",
+                        "           {install,doc,examples,src,tool,list,help} ...\n",
                         "\n",
                         "Installer for Qt SDK.\n",
                         "\n",
@@ -69,7 +69,7 @@ def test_cli_launch_with_no_argument(capsys):
                         "subcommands:\n",
                         "  Valid subcommands\n",
                         "\n",
-                        "  {install,tool,list,help}\n",
+                        "  {install,doc,examples,src,tool,list,help}\n",
                         "                        subcommand for aqt Qt installer\n"])
     cli = aqt.cli.Cli()
     cli.run([])