Repository: easybuilders/easybuild-framework

Issue:
Support for buildininstalldir=True and multiple compilations
Easybuild always deletes the install dir, if one attempts to combine several compilations and buildinstalldir = True inside the same easyconfig.

Example
```
buildininstalldir=True
configopts = [
    " configopts1 ",
    " configopts2",
    " configopts3",
]
```

The installation process will perform the installation using `configopts1`, then using`configopts2`, then `configopts3`. But the `installdir` will only contain the build that used `configopts3`. 

Even if I set to the easyblock
```
def install_step(self):
    ...
    self.cfg["keeppreviousinstall"] = True
    self.cfg["cleanupoldbuild"] = False
    self.cfg["cleanupoldinstall"] = False
```



Discussion:

Resolving patch:
diff --git a/easybuild/framework/easyblock.py b/easybuild/framework/easyblock.py
--- a/easybuild/framework/easyblock.py
+++ b/easybuild/framework/easyblock.py
@@ -858,8 +858,12 @@ def make_builddir(self):
             # avoid cleanup after installation
             self.cfg['cleanupoldinstall'] = False
 
-        # always make build dir
-        self.make_dir(self.builddir, self.cfg['cleanupoldbuild'])
+        # always make build dir,
+        # unless we're building in installation directory and we iterating over a list of (pre)config/build/installopts,
+        # otherwise we wipe the already partially populated installation directory,
+        # see https://github.com/easybuilders/easybuild-framework/issues/2556
+        if not (self.build_in_installdir and self.iter_idx > 0):
+            self.make_dir(self.builddir, self.cfg['cleanupoldbuild'])
 
         trace_msg("build dir: %s" % self.builddir)
 


Test patch:
diff --git a/test/framework/easyblock.py b/test/framework/easyblock.py
--- a/test/framework/easyblock.py
+++ b/test/framework/easyblock.py
@@ -837,6 +837,7 @@ def test_gen_dirs(self):
         builddir = eb.builddir
         eb.gen_builddir()
         self.assertEqual(builddir, eb.builddir)
+
         eb.cfg['cleanupoldbuild'] = True
         eb.gen_builddir()
         self.assertEqual(builddir, eb.builddir)
@@ -854,6 +855,66 @@ def test_gen_dirs(self):
         sys.stdout = stdoutorig
         eb.close_log()
 
+    def test_make_builddir(self):
+        """Test make_dir method."""
+        self.contents = '\n'.join([
+            'easyblock = "ConfigureMake"',
+            "name = 'pi'",
+            "version = '3.14'",
+            "homepage = 'http://example.com'",
+            "description = 'test easyconfig'",
+            "toolchain = {'name': 'dummy', 'version': 'dummy'}",
+        ])
+        self.writeEC()
+
+        eb = EasyBlock(EasyConfig(self.eb_file))
+        eb.gen_builddir()
+
+        # by default, make_builddir will re-create the build directory (i.e. remove existing & re-create)
+        eb.make_builddir()
+        builddir = eb.builddir
+        testfile = os.path.join(builddir, 'test123', 'foobar.txt')
+        write_file(testfile, 'test123')
+        self.assertTrue(os.path.exists(testfile))
+
+        eb.make_builddir()
+        self.assertEqual(builddir, eb.builddir)
+        # file is gone because directory was removed and re-created
+        self.assertFalse(os.path.exists(testfile))
+        self.assertFalse(os.path.exists(os.path.dirname(testfile)))
+        self.assertEqual(os.listdir(eb.builddir), [])
+
+        # make sure that build directory does *not* get re-created when we're building in installation directory
+        # and we're iterating over a list of (pre)config/build/installopts
+        eb.build_in_installdir = True
+        eb.make_builddir()
+        # also need to create install directory since build dir == install dir
+        eb.make_installdir()
+        builddir = eb.builddir
+        testfile = os.path.join(builddir, 'test123', 'foobar.txt')
+        write_file(testfile, 'test123')
+        self.assertTrue(os.path.exists(testfile))
+        self.assertEqual(os.listdir(eb.builddir), ['test123'])
+        self.assertEqual(os.listdir(os.path.join(eb.builddir, 'test123')), ['foobar.txt'])
+
+        # with iteration count > 0, build directory is not re-created because of build-in-installdir
+        eb.iter_idx = 1
+        eb.make_builddir()
+        eb.make_installdir()
+        self.assertEqual(builddir, eb.builddir)
+        self.assertTrue(os.path.exists(testfile))
+        self.assertEqual(os.listdir(eb.builddir), ['test123'])
+        self.assertEqual(os.listdir(os.path.join(eb.builddir, 'test123')), ['foobar.txt'])
+
+        # resetting iteration index to 0 results in re-creating build directory
+        eb.iter_idx = 0
+        eb.make_builddir()
+        eb.make_installdir()
+        self.assertEqual(builddir, eb.builddir)
+        self.assertFalse(os.path.exists(testfile))
+        self.assertFalse(os.path.exists(os.path.dirname(testfile)))
+        self.assertEqual(os.listdir(eb.builddir), [])
+
     def test_get_easyblock_instance(self):
         """Test get_easyblock_instance function."""
         from easybuild.easyblocks.toy import EB_toy