Repository: huggingface/peft

Issue:
CUDA kernels from PEFT v0.11.0 breaks C++ compilation
### System Info

-

### Who can help?

-

### Information

- [ ] The official example scripts
- [ ] My own modified scripts

### Tasks

- [ ] An officially supported task in the `examples` folder
- [ ] My own task or dataset (give details below)

### Reproduction

As reported to us by @danielhanchen

> the new PEFT 0.11.0 release is breaking llama.cpp / C++ compilation. If you import PEFT, it just breaks C++ compilation - presumably its related to some scripting.
> Repro: PEFT 0.10.0 works: https://colab.research.google.com/drive/1vQ4_wUazxvf39wEeN6fxP58xHVaT3Mj8?usp=sharing
> PEFT 0.11.0 fails causing gcc to break after importing peft: https://colab.research.google.com/drive/1-NHOoRLISEyisuQqFgUR5L714Fe9sLij?usp=sharing

Ping @yfeng95 @Zeju1997 @YuliangXiu

### Expected behavior

We may have to remove the kernels in a patch release if there is no quick solution.


Discussion:
I made a repo to comment out BOFT for now - https://github.com/danielhanchen/peft

And repro which worked after comment it out: https://colab.research.google.com/drive/1Y_MdJnS73hIlR_t2DXgXCgqKVwXHPE82?usp=sharing
I manually added the below to every line and tried isolating the problem:
```python
def install_llama_cpp_blocking(use_cuda = True):
    import subprocess
    import os
    import psutil
    # https://github.com/ggerganov/llama.cpp/issues/7062
    # Weirdly GPU conversion for GGUF breaks??
    # use_cuda = "LLAMA_CUDA=1" if use_cuda else ""

    commands = [
        "git clone --recursive https://github.com/ggerganov/llama.cpp",
        "make clean -C llama.cpp",
        # https://github.com/ggerganov/llama.cpp/issues/7062
        # Weirdly GPU conversion for GGUF breaks??
        # f"{use_cuda} make all -j{psutil.cpu_count()*2} -C llama.cpp",
        f"make all -j{psutil.cpu_count()*2} -C llama.cpp",
        "pip install gguf protobuf",
    ]
    # if os.path.exists("llama.cpp"): return

    for command in commands:
        with subprocess.Popen(command, shell = True, stdout = subprocess.PIPE, stderr = subprocess.STDOUT, bufsize = 1) as sp:
            for line in sp.stdout:
                line = line.decode("utf-8", errors = "replace")
                if "undefined reference" in line:
                    raise RuntimeError("Failed compiling llama.cpp")
                # print(line, flush = True, end = "")
        pass
    pass
pass
```
Running this Python script reproduces the error on my machine:

```python
import os
import subprocess
from peft import PeftModelForCausalLM

os.chdir("/tmp/")

commands = [
    "git clone --recursive https://github.com/ggerganov/llama.cpp",
    "make clean -C llama.cpp",
    "make all -j4 -C llama.cpp",
    "echo $?",
]

for command in commands:
    with subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, bufsize=1) as sp:
        for line in sp.stdout:
            line = line.decode("utf-8", errors = "replace")
            print(line, end = "")
            if "undefined reference" in line:
                raise RuntimeError("Failed compiling llama.cpp")
    print(f"-------------- finished: {command} --------------")
print("done")
```
Commenting out these lines seems to fix it for me:

https://github.com/huggingface/peft/blob/ae1ae20b768d8bafc1b7660f4b8153033e684c32/src/peft/tuners/boft/layer.py#L34-L35

Resolving patch:
diff --git a/src/peft/tuners/boft/layer.py b/src/peft/tuners/boft/layer.py
--- a/src/peft/tuners/boft/layer.py
+++ b/src/peft/tuners/boft/layer.py
@@ -20,6 +20,7 @@
 import math
 import os
 import warnings
+from contextlib import contextmanager
 from typing import Any, Optional, Union
 
 import torch
@@ -31,13 +32,46 @@
 from peft.tuners.tuners_utils import BaseTunerLayer, check_adapters_to_merge
 
 
-os.environ["CC"] = "gcc"
-os.environ["CXX"] = "gcc"
-curr_dir = os.path.dirname(__file__)
-
 _FBD_CUDA = None
 
 
+# this function is a 1:1 copy from accelerate
+@contextmanager
+def patch_environment(**kwargs):
+    """
+    A context manager that will add each keyword argument passed to `os.environ` and remove them when exiting.
+
+    Will convert the values in `kwargs` to strings and upper-case all the keys.
+
+    Example:
+
+    ```python
+    >>> import os
+    >>> from accelerate.utils import patch_environment
+
+    >>> with patch_environment(FOO="bar"):
+    ...     print(os.environ["FOO"])  # prints "bar"
+    >>> print(os.environ["FOO"])  # raises KeyError
+    ```
+    """
+    existing_vars = {}
+    for key, value in kwargs.items():
+        key = key.upper()
+        if key in os.environ:
+            existing_vars[key] = os.environ[key]
+        os.environ[key] = str(value)
+
+    yield
+
+    for key in kwargs:
+        key = key.upper()
+        if key in existing_vars:
+            # restore previous value
+            os.environ[key] = existing_vars[key]
+        else:
+            os.environ.pop(key, None)
+
+
 def get_fbd_cuda():
     global _FBD_CUDA
 
@@ -47,14 +81,15 @@ def get_fbd_cuda():
     curr_dir = os.path.dirname(__file__)
     # need ninja to build the extension
     try:
-        fbd_cuda = load(
-            name="fbd_cuda",
-            sources=[f"{curr_dir}/fbd/fbd_cuda.cpp", f"{curr_dir}/fbd/fbd_cuda_kernel.cu"],
-            verbose=True,
-            # build_directory='/tmp/'  # for debugging
-        )
-        # extra_cuda_cflags = ['-std=c++14', '-ccbin=$$(which gcc-7)']) # cuda10.2 is not compatible with gcc9. Specify gcc 7
-        import fbd_cuda
+        with patch_environment(CC="gcc", CXX="gcc"):
+            fbd_cuda = load(
+                name="fbd_cuda",
+                sources=[f"{curr_dir}/fbd/fbd_cuda.cpp", f"{curr_dir}/fbd/fbd_cuda_kernel.cu"],
+                verbose=True,
+                # build_directory='/tmp/'  # for debugging
+            )
+            # extra_cuda_cflags = ['-std=c++14', '-ccbin=$$(which gcc-7)']) # cuda10.2 is not compatible with gcc9. Specify gcc 7
+            import fbd_cuda
     except Exception as e:
         warnings.warn(f"Failed to load the CUDA extension: {e}, check if ninja is available.")
         warnings.warn("Setting boft_n_butterfly_factor to 1 to speed up the finetuning process.")


Test patch: