示例#1
0
def main():
    parser = argparse.ArgumentParser(description="OpenLane Local Installer")
    parser.add_argument("--list-tools",
                        action='store_true',
                        help="List the tools and exit.")
    args = parser.parse_args()
    if args.list_tools:
        tools = Tool.from_metadata_yaml(
            open("./dependencies/tool_metadata.yml").read())
        for tool in tools.values():
            print(f"{tool.name}: {tool.version_string}")
    else:
        run_installer()
示例#2
0
def tool_list():
    from dependencies.tool import Tool
    tools = Tool.from_metadata_yaml(
        open("./dependencies/tool_metadata.yml").read())
    for tool in tools.values():
        print(f"{tool.name}: {tool.version_string}")
示例#3
0
    def run(self):
        from dependencies.tool import Tool
        from dependencies.get_tag import NoGitException, get_tag
        from dependencies.env_info import OSInfo

        try:
            import venv
        except ImportError:
            print(
                "Python venv does not appear to be installed, and is required for local installations.",
                file=sys.stderr)

        try:
            ol_version = get_tag()
        except NoGitException as e:
            print("Installing OpenLane locally requires a Git repository.",
                  file=sys.stderr)
            exit(-1)

        tools = Tool.from_metadata_yaml(
            open("./dependencies/tool_metadata.yml").read())
        if self.input_options(
                "RISK_ACKNOWLEDGED",
                "I affirm that I have read docs/source/local_installs.md and agree to the outlined risks.",
            ["n", "y"]) != "y":
            return

        print(
            textwrap.dedent(f"""\
            OpenLane Local Installer ALPHA

                Copyright 2021 Efabless Corporation. Available under the Apache License,
                Version 2.0.

                Ctrl+C at any time to quit.

                Note that this installer does *not* handle:
                - Installing OpenROAD to PATH

                You'll have to do these on your own. We hope that you understand the implications of this.

                This version of OpenLane was tested with this version of OpenRoad:

                    {tools["openroad_app"].version_string}
        """))

        install_dir = realpath("./install")

        sh("mkdir", "-p", install_dir, root="retry")

        home_perms = os.stat(os.getenv("HOME"))
        sh("chown",
           "-R",
           "%i:%i" % (home_perms.st_uid, home_perms.st_gid),
           install_dir,
           root="retry")

        os_list = ["other", "ubuntu-20.04", "centos-7", "arch", "macos"]

        # Try to determine user's OS
        set_default_os = lambda x: os_list.insert(
            0, os_list.pop(os_list.index(x)))

        os_info = OSInfo.get()

        if os_info.distro == "macOS":
            set_default_os("macos")

        if os_info.distro == "centos" and os_info.distro_version == "7":
            set_default_os("centos-7")

        if os_info.distro == "ubuntu" and os_info.distro_version == "20.04":
            set_default_os("ubuntu-20.04")

        if os_info.distro in ["manjaro", "arch"]:
            set_default_os("arch")

        os_pick = self.input_options("OS",
                                     "Which UNIX/Unix-like OS are you using?",
                                     os_list)

        gcc_bin = os.getenv("CC") or "gcc"
        gxx_bin = os.getenv("CXX") or "g++"
        try:
            if not os_pick in [
                    "centos-7", "macos"
            ]:  # The reason we ignore centos 7 and macos is that we're going to just use devtoolset-8/brew gcc anyway.
                all_output = ""
                try:
                    gcc_ver_output = subprocess.run([gcc_bin, "--version"],
                                                    stdout=subprocess.PIPE)
                    all_output += gcc_ver_output.stdout.decode("utf8")
                    gx_ver_output = subprocess.run([gxx_bin, "--version"],
                                                   stdout=subprocess.PIPE)
                    all_output += gx_ver_output.stdout.decode("utf8")
                except:
                    pass
                if "clang" in all_output:
                    print(
                        textwrap.dedent(f"""\
                        We've detected that you're using Clang as your default C or C++ compiler.
                        Unfortunately, Clang is not compatible with some of the tools being
                        installed.

                        You may continue this installation at your own risk, but we recommend
                        installing GCC.

                        You can specify a compiler to use explicitly by invoking this script as
                        follows, for example:

                            CC=/usr/local/bin/gcc-8 CXX=/usr/local/bin/g++-8 python3 {__file__}
                    """))
                    input(
                        "Press return if you understand the risk and wish to continue anyways >"
                    )
        except FileNotFoundError as e:
            print(e, "(set as either CC or CXX)")
            exit(os.EX_CONFIG)

        install_packages = "no"
        if os_pick != "other":
            install_packages = self.input_options(
                "INSTALL_PACKAGES",
                "Do you want to install dependencies using your package manager?",
                ["no", "yes"])
        if install_packages != "no":

            def cat_all(dir):
                result = ""
                for file in os.listdir(dir):
                    result += open(join(dir, file)).read()
                    result += "\n"
                return result

            if os_pick == "macos":
                brew_packages = cat_all(
                    join(openlane_dir, 'dependencies',
                         'macos')).strip().split("\n")

                sh("brew", "install", *brew_packages)
            if os_pick == "centos-7":
                yum_packages = cat_all(
                    join(openlane_dir, 'dependencies',
                         'centos-7')).strip().split("\n")

                sh("yum", "install", "-y", *yum_packages, root="retry")
            if os_pick == "arch":
                raw = cat_all(join(openlane_dir, 'dependencies',
                                   'arch')).strip().split("\n")

                arch_packages = []
                aur_packages = []

                for entry in raw:
                    if entry.strip() == "":
                        continue

                    if entry.startswith("https://"):
                        aur_packages.append(entry)
                    else:
                        arch_packages.append(entry)

                sh("pacman",
                   "-S",
                   "--noconfirm",
                   "--needed",
                   *arch_packages,
                   root="retry")

                temp_dir = tempfile.gettempdir()
                oaur_path = os.path.join(temp_dir, "openlane_aur")
                pathlib.Path(oaur_path).mkdir(parents=True, exist_ok=True)
                with chdir(oaur_path):
                    for package in aur_packages:
                        sh("rm", "-rf", "current")
                        sh("git", "clone", package, "current")
                        with chdir("current"):
                            sh("makepkg", "-si", "--noconfirm")
            if os_pick == "ubuntu-20.04":
                raw = cat_all(
                    join(openlane_dir, 'dependencies',
                         'ubuntu-20.04')).strip().split("\n")

                apt_packages = []
                apt_debs = []

                for entry in raw:
                    if entry.strip() == "":
                        continue

                    if entry.startswith("https://"):
                        apt_debs.append(entry)
                    else:
                        apt_packages.append(entry)
                sh("apt-get", "update", root="retry")
                sh("apt-get", "install", "-y", "curl", root="retry")
                for deb in apt_debs:
                    path = download(deb, "deb")
                    sh("apt-get", "install", "-y", "-f", path, root="retry")
                sh("apt-get", "install", "-y", *apt_packages, root="retry")

        print("To re-run with the same options: ")
        print(
            f"{' '.join(['%s=%s' % env for env in self.envs])} python3 {__file__}"
        )

        run_env = os.environ.copy()
        run_env["PREFIX"] = install_dir

        path_elements = ["$OL_INSTALL_DIR/venv/bin", "$OL_INSTALL_DIR/bin"]

        if os_pick == "centos-7":
            run_env["CC"] = "/opt/rh/devtoolset-8/root/usr/bin/gcc"
            run_env["CXX"] = "/opt/rh/devtoolset-8/root/usr/bin/g++"
            run_env[
                "PATH"] = f"/opt/rh/devtoolset-8/root/usr/bin:{os.getenv('PATH')}"
            run_env[
                "LD_LIBRARY_PATH"] = f"/opt/rh/devtoolset-8/root/usr/lib64:/opt/rh/devtoolset-8/root/usr/lib:/opt/rh/devtoolset-8/root/usr/lib64/dyninst:/opt/rh/devtoolset-8/root/usr/lib/dyninst:/opt/rh/devtoolset-8/root/usr/lib64:/opt/rh/devtoolset-8/root/usr/lib:{os.getenv('LD_LIBRARY_PATH')}"
            run_env[
                "CMAKE_INCLUDE_PATH"] = f"/usr/include/boost169:{os.getenv('CMAKE_INCLUDE_PATH')}"
            run_env[
                "CMAKE_LIBRARY_PATH"] = f"/lib64/boost169:{os.getenv('CMAKE_LIBRARY_PATH')}"
        elif os_pick == "macos":

            def get_prefix(tool):
                return subprocess.check_output(["brew", "--prefix",
                                                tool]).decode('utf8').strip()

            klayout_app_path = self.input_default(
                "KLAYOUT_MAC_APP",
                "Please input the path to klayout.app (0.27.3 or later): ",
                "/Applications/klayout.app")
            klayout_path_element = join(klayout_app_path, "Contents", "MacOS")

            run_env["CC"] = f"{get_prefix('gcc')}/bin/gcc-11"
            run_env["CXX"] = f"{get_prefix('gcc')}/bin/g++-11"
            run_env[
                "PATH"] = f"{get_prefix('swig@3')}/bin:{get_prefix('bison')}/bin:{get_prefix('flex')}/bin:{get_prefix('gnu-which')}/bin:{os.getenv('PATH')}"
            run_env[
                "MAGIC_CONFIG_OPTS"] = f"--with-tcl={get_prefix('tcl-tk')} --with-tk={get_prefix('tcl-tk')}"
            run_env[
                "READLINE_CXXFLAGS"] = f"CXXFLAGS=-L{get_prefix('readline')}/lib"

            path_elements.append(f"{klayout_path_element}")
            path_elements.append(f"{get_prefix('gnu-sed')}/libexec/gnubin")
            path_elements.append(f"{get_prefix('bash')}/bin")
        else:
            run_env["CC"] = gcc_bin
            self.envs.append(("CC", gcc_bin))
            run_env["CXX"] = gxx_bin
            self.envs.append(("CXX", gxx_bin))

        def copy(f):
            sh("rm", "-rf", f)
            sh("cp", "-r", join(openlane_dir, f), f)

        def install():
            print("Copying files...")
            for folder in ["bin", "lib", "share", "build", "dependencies"]:
                sh("mkdir", "-p", folder)

            print("Building Python virtual environment...")
            venv_builder = venv.EnvBuilder(clear=True, with_pip=True)
            venv_builder.create("./venv")

            subprocess.run([
                "bash", "-c", f"""
                    source ./venv/bin/activate
                    python3 -m pip install --upgrade -r ../dependencies/python/precompile_time.txt
                    python3 -m pip install --upgrade -r ../dependencies/python/compile_time.txt
                    python3 -m pip install --upgrade -r ../dependencies/python/run_time.txt
                """
            ])

            print("Installing dependencies...")
            with chdir("build"):
                for folder in ["repos", "versions"]:
                    sh("mkdir", "-p", folder)

                skip_tools = re.compile(
                    os.getenv("SKIP_TOOLS") or "Unmatchable")
                for tool in tools.values():
                    if not tool.in_install:
                        continue
                    if skip_tools.match(tool.name) is not None:
                        continue
                    installed_version = ""
                    version_path = f"versions/{tool.name}"
                    try:
                        installed_version = open(version_path).read()
                    except:
                        pass
                    if installed_version == tool.version_string and os.getenv(
                            "FORCE_REINSTALL") != "1":
                        print(
                            f"{tool.version_string} already installed, skipping..."
                        )
                        continue

                    print(f"Installing {tool.name}...")

                    with chdir("repos"):
                        if not exists(tool.name):
                            sh("git", "clone", tool.repo, tool.name)

                        with chdir(tool.name):
                            sh("git", "fetch")
                            sh("git", "submodule", "update", "--init")
                            sh("git", "checkout", tool.commit)
                            subprocess.run([
                                "bash", "-c", f"""\
                                    set -e
                                    source {install_dir}/venv/bin/activate
                                    {tool.build_script}
                                """
                            ],
                                           env=run_env,
                                           check=True)

                    with open(version_path, "w") as f:
                        f.write(tool.version_string)

            path_elements.reverse()
            with open("env.tcl", "w") as f:
                f.write(
                    textwrap.dedent(f"""\
                set OL_INSTALL_DIR [file dirname [file normalize [info script]]]

                set ::env(OPENLANE_LOCAL_INSTALL) 1
                set ::env(OL_INSTALL_DIR) "$OL_INSTALL_DIR"
                set ::env(PATH) "{":".join(path_elements)}:$::env(PATH)"
                set ::env(VIRTUAL_ENV) "$OL_INSTALL_DIR/venv"
                """))

            with open("installed_version", "w") as f:
                f.write(ol_version)

        with chdir(install_dir):
            install()

        print("Done.")
        print(
            f"To invoke Openlane from now on, invoke ./flow.tcl from the OpenLane root without the Makefile."
        )
示例#4
0
def run_installer():
    ol_version = get_tag()
    tools = Tool.from_metadata_yaml(
        open("./dependencies/tool_metadata.yml").read())
    if input_options(
            "RISK_ACKNOWLEDGED",
            "I affirm that I have read docs/source/local_installs.md and agree to the outlined risks.",
        ["n", "y"]) != "y":
        return

    print(f"""\
OpenLane Local Installer ALPHA

    Copyright 2021 Efabless Corporation. Available under the Apache License,
    Version 2.0.

    Ctrl+C at any time to quit.

    Note that this installer does *not* handle:
    - Installing OpenROAD to PATH

    You'll have to do these on your own. We hope that you understand the implications of this.

    This version of OpenLane was tested with this version of OpenRoad:

        {tools["openroad_app"].version_string}
    """)

    install_dir = input_default("INSTALL_DIR",
                                "Where do you want to install Openlane?",
                                "/usr/local/opt/openlane")

    sh("mkdir", "-p", install_dir, root="retry")

    home_perms = os.stat(os.getenv("HOME"))
    sh("chown",
       "-R",
       "%i:%i" % (home_perms.st_uid, home_perms.st_gid),
       install_dir,
       root="retry")

    pip_action = input_options("PIP_DEPS", "Install PIP dependencies?",
                               ["no", "system", "user"])
    if pip_action != "no":
        python_directory = join(openlane_dir, "dependencies", "python")
        requirements_files = [
            os.path.join(python_directory, file)
            for file in os.listdir(python_directory)
        ]
        final_list = []
        for file in requirements_files:
            final_list.append("-r")
            final_list.append(file)

        sh("python3",
           "-m",
           "pip",
           "install",
           *final_list,
           root=pip_action == "system")

    os_list = ["other", "ubuntu-20.04", "centos7", "macos"]

    # Try to determine user's OS
    set_default_os = lambda x: os_list.insert(0, os_list.pop(os_list.index(x)))
    uname = subprocess.check_output(["uname", "-a"]).decode("utf8").strip()
    if "Darwin" in uname:
        set_default_os("macos")

    if "Linux" in uname:
        try:
            os_release = open("/etc/os-release").read()

            config = {}
            for line in os_release.split("\n"):
                key, value = line.split("=")
                value = value.strip('"')

                config[key] = value

            id = config["ID"]
            version = config["VERSION_ID"]

            if id == "centos" and version == "7":
                set_default_os("centos7")

            if id == "ubuntu" and version == "20.04":
                set_default_os("ubuntu-20.04")
        except:
            pass  # non-systemd

    os_pick = input_options("OS", "Which UNIX/Unix-like OS are you using?",
                            os_list)

    gcc_bin = os.getenv("CC") or "gcc"
    gxx_bin = os.getenv("CXX") or "g++"
    try:
        if not os_pick in [
                "centos7", "macos"
        ]:  # The reason we ignore centos7 and macos is that we're going to just use devtoolset-8/brew gcc anyway.
            gcc_ver_output = subprocess.run([gcc_bin, "--version"],
                                            stdout=subprocess.PIPE)
            gx_ver_output = subprocess.run([gxx_bin, "--version"],
                                           stdout=subprocess.PIPE)
            if "clang" in gcc_ver_output.stdout.decode(
                    "utf-8") + gx_ver_output.stdout.decode("utf-8"):
                print(f"""
    We've detected that you're using Clang as your default C or C++ compiler.
    Unfortunately, Clang is not compatible with some of the tools being
    installed.

    You may continue this installation at your own risk, but we recommend
    installing GCC.

    You can specify a compiler to use explicitly by invoking this script as
    follows, for example:

        CC=/usr/local/bin/gcc-8 CXX=/usr/local/bin/g++-8 python3 {__file__}

            """)
                input(
                    "Press return if you understand the risk and wish to continue anyways >"
                )
    except FileNotFoundError as e:
        print(e, "(set as either CC or CXX)")
        exit(os.EX_CONFIG)

    install_packages = "no"
    if os_pick != "other":
        install_packages = input_options(
            "INSTALL_PACKAGES",
            "Do you want to install packages for development?", ["no", "yes"])
    if install_packages != "no":

        def cat_all(dir):
            result = ""
            for file in os.listdir(dir):
                result += open(join(dir, file)).read()
                result += "\n"
            return result

        if os_pick == "macos":
            brew_packages = cat_all(join(openlane_dir, 'dependencies',
                                         'macos')).strip().split("\n")

            sh("brew", "install", *brew_packages)
        if os_pick == "centos7":
            yum_packages = cat_all(
                join(openlane_dir, 'dependencies',
                     'centos7')).strip().split("\n")

            sh("yum", "install", "-y", *yum_packages)
        if os_pick == "ubuntu-20.04":
            raw = cat_all(join(openlane_dir, 'dependencies',
                               'ubuntu-20.04')).strip().split("\n")

            apt_packages = []
            apt_debs = []

            for entry in raw:
                if entry.startswith("https://"):
                    apt_debs.append(entry)
                else:
                    apt_packages.append(entry)
            sh("apt-get", "update", root=True)
            sh("apt-get", "install", "-y", "curl", root=True)
            for deb in apt_debs:
                path = download(deb, "deb")
                sh("apt-get", "install", "-y", "-f", path, root=True)
            sh("apt-get", "install", "-y", *apt_packages, root=True)

    print("To re-run with the same options: ")
    print(f"{' '.join(['%s=%s' % env for env in envs])} python3 {__file__}")

    run_env = os.environ.copy()
    run_env["PREFIX"] = install_dir

    path_elements = ["$PATH", "$OL_DIR/bin"]

    if os_pick == "centos7":
        run_env["CC"] = "/opt/rh/devtoolset-8/root/usr/bin/gcc"
        run_env["CXX"] = "/opt/rh/devtoolset-8/root/usr/bin/g++"
        run_env[
            "PATH"] = f"/opt/rh/devtoolset-8/root/usr/bin:{os.getenv('PATH')}"
        run_env[
            "LD_LIBRARY_PATH"] = f"/opt/rh/devtoolset-8/root/usr/lib64:/opt/rh/devtoolset-8/root/usr/lib:/opt/rh/devtoolset-8/root/usr/lib64/dyninst:/opt/rh/devtoolset-8/root/usr/lib/dyninst:/opt/rh/devtoolset-8/root/usr/lib64:/opt/rh/devtoolset-8/root/usr/lib:{os.getenv('LD_LIBRARY_PATH')}"
        run_env[
            "CMAKE_INCLUDE_PATH"] = f"/usr/include/boost169:{os.getenv('CMAKE_INCLUDE_PATH')}"
        run_env[
            "CMAKE_LIBRARY_PATH"] = f"/lib64/boost169:{os.getenv('CMAKE_LIBRARY_PATH')}"
    elif os_pick == "macos":

        def get_prefix(tool):
            return subprocess.check_output(["brew", "--prefix",
                                            tool]).decode('utf8').strip()

        klayout_app_path = input_default(
            "KLAYOUT_MAC_APP",
            "Please input the path to klayout.app (0.27.3 or later): ",
            "/Applications/klayout.app")
        klayout_path_element = join(klayout_app_path, "Contents", "MacOS")

        run_env["CC"] = f"{get_prefix('gcc')}/bin/gcc-11"
        run_env["CXX"] = f"{get_prefix('gcc')}/bin/g++-11"
        run_env[
            "PATH"] = f"{get_prefix('swig@3')}/bin:{get_prefix('bison')}/bin:{get_prefix('flex')}/bin:{get_prefix('gnu-which')}/bin:{os.getenv('PATH')}"
        run_env[
            "MAGIC_CONFIG_OPTS"] = f"--with-tcl={get_prefix('tcl-tk')} --with-tk={get_prefix('tcl-tk')}"
        run_env[
            "READLINE_CXXFLAGS"] = f"CXXFLAGS=-L{get_prefix('readline')}/lib"

        path_elements.append(f"{klayout_path_element}")
        path_elements.append(f"{get_prefix('gnu-sed')}/libexec/gnubin")
        path_elements.append(f"{get_prefix('bash')}/bin")
    else:
        run_env["CC"] = gcc_bin
        envs.append(("CC", gcc_bin))
        run_env["CXX"] = gxx_bin
        envs.append(("CXX", gxx_bin))

    def copy(f):
        sh("rm", "-rf", f)
        sh("cp", "-r", join(openlane_dir, f), f)

    def install():
        print("Copying files...")
        for folder in ["bin", "lib", "share", "build", "dependencies"]:
            sh("mkdir", "-p", folder)
        copy("configuration")
        copy("scripts")
        copy("flow.tcl")
        copy("generate_reports.py")
        copy("dependencies/")

        print("Installing dependencies...")
        with chdir("build"):
            for folder in ["repos", "versions"]:
                sh("mkdir", "-p", folder)

            skip_tools = (os.getenv("SKIP_TOOLS") or "").split(':')
            print(skip_tools)
            for tool in tools.values():
                if not tool.in_install:
                    continue
                if tool.name in skip_tools:
                    continue
                installed_version = ""
                version_path = f"versions/{tool.name}"
                try:
                    installed_version = open(version_path).read()
                except:
                    pass
                if installed_version == tool.version_string:
                    print(
                        f"{tool.version_string} already installed, skipping..."
                    )
                    continue

                print(f"Installing {tool.name}...")

                with chdir("repos"):
                    if not exists(tool.name):
                        sh("git", "clone", tool.repo, tool.name)

                    with chdir(tool.name):
                        sh("git", "fetch")
                        sh("git", "submodule", "update", "--init")
                        sh("git", "checkout", tool.commit)
                        subprocess.run(["bash", "-c", tool.build_script],
                                       env=run_env,
                                       check=True)

                with open(version_path, "w") as f:
                    f.write(tool.version_string)

        path_elements.reverse()
        with open("openlane", "w") as f:
            f.write(f"""#!/bin/bash
            OL_DIR="$(dirname "$(test -L "$0" && readlink "$0" || echo "$0")")"

            export PATH={":".join(path_elements)}

            FLOW_TCL=${{FLOW_TCL:-$OL_DIR/flow.tcl}}
            FLOW_TCL=$(realpath $FLOW_TCL)

            tclsh $FLOW_TCL $@
            """)
        sh("chmod", "+x", "./openlane")

        with open("installed_version", "w") as f:
            f.write(ol_version)

    with chdir(install_dir):
        install()

    print("Done.")
    print(
        f"To invoke Openlane from now on, invoke {install_dir}/openlane then pass on the same options you would flow.tcl."
    )