22
33from os import environ , system
44from pathlib import Path
5+ from shutil import which
56from sys import executable , platform as sys_platform
67from sysconfig import get_path
78from typing import List , Literal , Optional
1516 "HatchCppBuildPlan" ,
1617)
1718
18- Platform = Literal ["linux " , "darwin" , "win32 " ]
19+ BuildType = Literal ["debug " , "release " ]
1920CompilerToolchain = Literal ["gcc" , "clang" , "msvc" ]
21+ Language = Literal ["c" , "c++" ]
22+ Platform = Literal ["linux" , "darwin" , "win32" ]
2023PlatformDefaults = {
21- "linux" : {"CC" : "gcc" , "CXX" : "g++" },
22- "darwin" : {"CC" : "clang" , "CXX" : "clang++" },
23- "win32" : {"CC" : "cl" , "CXX" : "cl" },
24+ "linux" : {"CC" : "gcc" , "CXX" : "g++" , "LD" : "ld" },
25+ "darwin" : {"CC" : "clang" , "CXX" : "clang++" , "LD" : "ld" },
26+ "win32" : {"CC" : "cl" , "CXX" : "cl" , "LD" : "link" },
2427}
2528
2629
@@ -29,7 +32,7 @@ class HatchCppLibrary(BaseModel):
2932
3033 name : str
3134 sources : List [str ]
32-
35+ language : Language = "c++"
3336 include_dirs : List [str ] = Field (default_factory = list , alias = "include-dirs" )
3437 library_dirs : List [str ] = Field (default_factory = list , alias = "library-dirs" )
3538 libraries : List [str ] = Field (default_factory = list )
@@ -46,6 +49,7 @@ class HatchCppLibrary(BaseModel):
4649class HatchCppPlatform (BaseModel ):
4750 cc : str
4851 cxx : str
52+ ld : str
4953 platform : Platform
5054 toolchain : CompilerToolchain
5155
@@ -54,6 +58,7 @@ def default() -> HatchCppPlatform:
5458 platform = environ .get ("HATCH_CPP_PLATFORM" , sys_platform )
5559 CC = environ .get ("CC" , PlatformDefaults [platform ]["CC" ])
5660 CXX = environ .get ("CXX" , PlatformDefaults [platform ]["CXX" ])
61+ LD = environ .get ("LD" , PlatformDefaults [platform ]["LD" ])
5762 if "gcc" in CC and "g++" in CXX :
5863 toolchain = "gcc"
5964 elif "clang" in CC and "clang++" in CXX :
@@ -62,34 +67,35 @@ def default() -> HatchCppPlatform:
6267 toolchain = "msvc"
6368 else :
6469 raise Exception (f"Unrecognized toolchain: { CC } , { CXX } " )
65- return HatchCppPlatform (cc = CC , cxx = CXX , platform = platform , toolchain = toolchain )
6670
67- def get_compile_flags (self , library : HatchCppLibrary ) -> str :
71+ # Customizations
72+ if which ("ccache" ) and not environ .get ("HATCH_CPP_DISABLE_CCACHE" ):
73+ CC = f"ccache { CC } "
74+ CXX = f"ccache { CXX } "
75+
76+ # https://github.com/rui314/mold/issues/647
77+ # if which("ld.mold"):
78+ # LD = which("ld.mold")
79+ # elif which("ld.lld"):
80+ # LD = which("ld.lld")
81+ return HatchCppPlatform (cc = CC , cxx = CXX , ld = LD , platform = platform , toolchain = toolchain )
82+
83+ def get_compile_flags (self , library : HatchCppLibrary , build_type : BuildType = "release" ) -> str :
6884 flags = ""
6985 if self .toolchain == "gcc" :
7086 flags = f"-I{ get_path ('include' )} "
7187 flags += " " + " " .join (f"-I{ d } " for d in library .include_dirs )
72- flags += " -fPIC -shared "
88+ flags += " -fPIC"
7389 flags += " " + " " .join (library .extra_compile_args )
74- flags += " " + " " .join (library .extra_link_args )
75- flags += " " + " " .join (library .extra_objects )
76- flags += " " + " " .join (f"-l{ lib } " for lib in library .libraries )
77- flags += " " + " " .join (f"-L{ lib } " for lib in library .library_dirs )
7890 flags += " " + " " .join (f"-D{ macro } " for macro in library .define_macros )
7991 flags += " " + " " .join (f"-U{ macro } " for macro in library .undef_macros )
80- flags += f" -o { library .name } .so"
8192 elif self .toolchain == "clang" :
8293 flags = f"-I{ get_path ('include' )} "
8394 flags += " " .join (f"-I{ d } " for d in library .include_dirs )
84- flags += " -undefined dynamic_lookup - fPIC -shared "
95+ flags += " -fPIC"
8596 flags += " " + " " .join (library .extra_compile_args )
86- flags += " " + " " .join (library .extra_link_args )
87- flags += " " + " " .join (library .extra_objects )
88- flags += " " + " " .join (f"-l{ lib } " for lib in library .libraries )
89- flags += " " + " " .join (f"-L{ lib } " for lib in library .library_dirs )
9097 flags += " " + " " .join (f"-D{ macro } " for macro in library .define_macros )
9198 flags += " " + " " .join (f"-U{ macro } " for macro in library .undef_macros )
92- flags += f" -o { library .name } .so"
9399 elif self .toolchain == "msvc" :
94100 flags = f"/I{ get_path ('include' )} "
95101 flags += " " .join (f"/I{ d } " for d in library .include_dirs )
@@ -98,7 +104,44 @@ def get_compile_flags(self, library: HatchCppLibrary) -> str:
98104 flags += " " + " " .join (library .extra_objects )
99105 flags += " " + " " .join (f"/D{ macro } " for macro in library .define_macros )
100106 flags += " " + " " .join (f"/U{ macro } " for macro in library .undef_macros )
101- flags += " /EHsc /DWIN32 /LD"
107+ flags += " /EHsc /DWIN32"
108+ # clean
109+ while flags .count (" " ):
110+ flags = flags .replace (" " , " " )
111+ return flags
112+
113+ def get_link_flags (self , library : HatchCppLibrary , build_type : BuildType = "release" ) -> str :
114+ flags = ""
115+ if self .toolchain == "gcc" :
116+ flags += " -shared"
117+ flags += " " + " " .join (library .extra_link_args )
118+ flags += " " + " " .join (library .extra_objects )
119+ flags += " " + " " .join (f"-l{ lib } " for lib in library .libraries )
120+ flags += " " + " " .join (f"-L{ lib } " for lib in library .library_dirs )
121+ flags += f" -o { library .name } .so"
122+ if self .platform == "darwin" :
123+ flags += " -undefined dynamic_lookup"
124+ if "mold" in self .ld :
125+ flags += f" -fuse-ld={ self .ld } "
126+ elif "lld" in self .ld :
127+ flags += " -fuse-ld=lld"
128+ elif self .toolchain == "clang" :
129+ flags += " -shared"
130+ flags += " " + " " .join (library .extra_link_args )
131+ flags += " " + " " .join (library .extra_objects )
132+ flags += " " + " " .join (f"-l{ lib } " for lib in library .libraries )
133+ flags += " " + " " .join (f"-L{ lib } " for lib in library .library_dirs )
134+ flags += f" -o { library .name } .so"
135+ if self .platform == "darwin" :
136+ flags += " -undefined dynamic_lookup"
137+ if "mold" in self .ld :
138+ flags += f" -fuse-ld={ self .ld } "
139+ elif "lld" in self .ld :
140+ flags += " -fuse-ld=lld"
141+ elif self .toolchain == "msvc" :
142+ flags += " " + " " .join (library .extra_link_args )
143+ flags += " " + " " .join (library .extra_objects )
144+ flags += " /LD"
102145 flags += f" /Fo:{ library .name } .obj"
103146 flags += f" /Fe:{ library .name } .pyd"
104147 flags += " /link /DLL"
@@ -111,22 +154,21 @@ def get_compile_flags(self, library: HatchCppLibrary) -> str:
111154 flags = flags .replace (" " , " " )
112155 return flags
113156
114- def get_link_flags (self , library : HatchCppLibrary ) -> str :
115- # TODO
116- flags = ""
117- return flags
118-
119157
120158class HatchCppBuildPlan (BaseModel ):
159+ build_type : BuildType = "release"
121160 libraries : List [HatchCppLibrary ] = Field (default_factory = list )
122161 platform : HatchCppPlatform = Field (default_factory = HatchCppPlatform .default )
123162 commands : List [str ] = Field (default_factory = list )
124163
125164 def generate (self ):
126165 self .commands = []
127166 for library in self .libraries :
128- flags = self .platform .get_compile_flags (library )
129- self .commands .append (f"{ self .platform .cc } { ' ' .join (library .sources )} { flags } " )
167+ compile_flags = self .platform .get_compile_flags (library , self .build_type )
168+ link_flags = self .platform .get_link_flags (library , self .build_type )
169+ self .commands .append (
170+ f"{ self .platform .cc if library .language == 'c' else self .platform .cxx } { ' ' .join (library .sources )} { compile_flags } { link_flags } "
171+ )
130172 return self .commands
131173
132174 def execute (self ):
@@ -148,9 +190,3 @@ class HatchCppBuildConfig(BaseModel):
148190 verbose : Optional [bool ] = Field (default = False )
149191 libraries : List [HatchCppLibrary ] = Field (default_factory = list )
150192 platform : Optional [HatchCppPlatform ] = Field (default_factory = HatchCppPlatform .default )
151-
152- # build_function: str | None = None
153- # build_kwargs: t.Mapping[str, str] = field(default_factory=dict)
154- # editable_build_kwargs: t.Mapping[str, str] = field(default_factory=dict)
155- # ensured_targets: list[str] = field(default_factory=list)
156- # skip_if_exists: list[str] = field(default_factory=list)
0 commit comments