From a8d644556e76f9c736b3b2c86b7277e3b24ae87b Mon Sep 17 00:00:00 2001
From: JasonJiazhiZhang <21229070+JasonJiazhiZhang@users.noreply.github.com>
Date: Wed, 5 Jun 2019 10:42:44 -0700
Subject: [PATCH] Enable optional habitat_baselines installation (#94)

* add extras_require in setup.py

* add exclude in setup.py for baselines

* add pytest as requirement

* Implement accessing setup() params within custom commands
---
 README.md                                    |  6 +-
 habitat_baselines/README.md                  |  7 +++
 habitat_baselines/rl/requirements.txt        |  1 +
 habitat_baselines/slambased/requirements.txt |  0
 setup.py                                     | 59 +++++++++++++++++++-
 5 files changed, 69 insertions(+), 4 deletions(-)
 create mode 100644 habitat_baselines/rl/requirements.txt
 create mode 100644 habitat_baselines/slambased/requirements.txt

diff --git a/README.md b/README.md
index 143012e77..a7e66fa9f 100644
--- a/README.md
+++ b/README.md
@@ -54,7 +54,11 @@ If you use the Habitat platform in your research, please cite the following [tec
 cd habitat-api
 pip install -e .
 ```
-
+The command above will install only habitat core API. To include habitat_baselines along with all additional requirements, use the command below instead:
+```bash
+cd habitat-api
+python setup.py develop --all # install habitat and habitat_baselines
+```
 2. Install `habitat-sim` from [github repo](https://github.com/facebookresearch/habitat-sim).
 
 3. Download the [test scenes data](http://dl.fbaipublicfiles.com/habitat/habitat-test-scenes.zip) and extract `data` folder in zip to `habitat-api/data/` where `habitat-api/` is the github repository folder.
diff --git a/habitat_baselines/README.md b/habitat_baselines/README.md
index 2229a8f7c..57a2a7298 100644
--- a/habitat_baselines/README.md
+++ b/habitat_baselines/README.md
@@ -1,5 +1,12 @@
 baselines
 ==============================
+### Installation
+
+The `habitat_baselines` sub-package is NOT included upon installation by default. To install `habitat_baselines`, use the following command instead:
+```bash
+python setup.py develop --all
+```
+This will also install additional requirements for each sub-module in `habitat_baselines/`, which are specified in `requirements.txt` files located in the sub-module directory.
 
 
 ### Reinforcement Learning (RL)
diff --git a/habitat_baselines/rl/requirements.txt b/habitat_baselines/rl/requirements.txt
new file mode 100644
index 000000000..c6c6cdd78
--- /dev/null
+++ b/habitat_baselines/rl/requirements.txt
@@ -0,0 +1 @@
+torch=1.1.0
diff --git a/habitat_baselines/slambased/requirements.txt b/habitat_baselines/slambased/requirements.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/setup.py b/setup.py
index f3b2e5d5b..bec05a1f7 100644
--- a/setup.py
+++ b/setup.py
@@ -4,10 +4,12 @@
 # This source code is licensed under the MIT license found in the
 # LICENSE file in the root directory of this source tree.
 
+import glob
 import os.path
 import sys
-
 import setuptools
+from setuptools.command.develop import develop as DefaultDevelopCommand
+from setuptools.command.install import install as DefaultInstallCommand
 
 sys.path.insert(0, os.path.join(os.path.dirname(__file__), "habitat"))
 from version import VERSION  # noqa
@@ -26,13 +28,63 @@ DESCRIPTION = "habitat: a suite for embodied agent tasks and benchmarks"
 LONG_DESCRIPTION = readme
 AUTHOR = "Facebook AI Research"
 LICENSE = license
-REQUIREMENTS = (reqs.strip().split("\n"),)
+REQUIREMENTS = reqs.strip().split("\n")
+BASELINE_PATH = ["habitat_baselines", "habitat_baselines.*"]
+DEFAULT_EXCLUSION = ["test", "examples"]
+FULL_REQUIREMENTS = set()
+# collect requirements.txt file in all subdirectories
+for file_name in glob.glob("**/requirements.txt", recursive=True):
+    with open(file_name) as f:
+        reqs = f.read()
+        FULL_REQUIREMENTS.update(reqs.strip().split("\n"))
+
+
+class OptionedCommand:
+    """
+    Generic Command class that takes extra user options and modifies
+    arguments in setuptools.setup() accordingly.
+    Though OptionedCommand inherits directly from object, it assumes
+    inheritance from DefaultDevelopCommand or DefaultInstallCommand, as it
+    overrides methods from those two classes.
+    """
+
+    user_options = [("all", None, "include habitat_baselines in installation")]
+
+    def initialize_options(self):
+        super().initialize_options()
+        self.all = None
+
+    def run(self):
+        if not self.all:  # install core only
+            DEFAULT_EXCLUSION.extend(BASELINE_PATH)
+            self.distribution.packages = setuptools.find_packages(
+                exclude=DEFAULT_EXCLUSION
+            )
+            # self.distribution accesses arguments of setup() in main()
+        else:  # install all except test and examples
+            self.distribution.install_requires = FULL_REQUIREMENTS
+        super().run()
+
+
+class InstallCommand(OptionedCommand, DefaultInstallCommand):
+    user_options = (
+        getattr(DefaultInstallCommand, "user_options", [])
+        + OptionedCommand.user_options
+    )
+
+
+class DevelopCommand(OptionedCommand, DefaultDevelopCommand):
+    user_options = (
+        getattr(DefaultDevelopCommand, "user_options", [])
+        + OptionedCommand.user_options
+    )
+
 
 if __name__ == "__main__":
     setuptools.setup(
         name=DISTNAME,
         install_requires=REQUIREMENTS,
-        packages=setuptools.find_packages(),
+        packages=setuptools.find_packages(exclude=DEFAULT_EXCLUSION),
         version=VERSION,
         description=DESCRIPTION,
         long_description=LONG_DESCRIPTION,
@@ -41,4 +93,5 @@ if __name__ == "__main__":
         setup_requires=["pytest-runner"],
         tests_require=["pytest"],
         include_package_data=True,
+        cmdclass={"install": InstallCommand, "develop": DevelopCommand},
     )
-- 
GitLab