@@ -126,7 +126,7 @@ class InstallOptions:
126
126
"cmake_path" : "Path to the cmake executable, if not in system PATH." ,
127
127
"python_path" : "Path to the python executable, if not in system PATH." ,
128
128
"java_path" : "Path to the java executable, if not in system PATH." ,
129
- "ninja_path" : "Path to the ninja executable. Leave empty to download it automatically." ,
129
+ "ninja_path" : "Path to the ninja executable. Leave empty to look for ninja in PATH or to download it automatically." ,
130
130
"mrdocs_src_dir" : "MrDocs source directory." ,
131
131
"mrdocs_repo" : "URL of the MrDocs repository to clone." ,
132
132
"mrdocs_branch" : "Branch or tag of the MrDocs repository to use." ,
@@ -602,6 +602,93 @@ def is_executable(self, path):
602
602
else :
603
603
return os .access (path , os .X_OK )
604
604
605
+ @lru_cache (maxsize = 1 )
606
+ def get_vs_install_locations (self ):
607
+ p = os .environ .get ('ProgramFiles(x86)' , r"C:\Program Files (x86)" )
608
+ path_vswhere = os .path .join (p ,
609
+ "Microsoft Visual Studio" , "Installer" , "vswhere.exe" )
610
+ if not self .is_executable (path_vswhere ):
611
+ return None
612
+ cmd = [path_vswhere ,
613
+ "-latest" , "-products" , "*" ,
614
+ "-requires" , "Microsoft.Component.MSBuild" ,
615
+ "-format" , "json" ]
616
+ data = subprocess .check_output (cmd , universal_newlines = True )
617
+ info = json .loads (data )
618
+ if not info :
619
+ return None
620
+ return [inst .get ("installationPath" ) for inst in info ]
621
+
622
+ def find_vs_tool (self , tool ):
623
+ vs_tools = ["cmake" , "ninja" , "git" , "python" ]
624
+ if tool not in vs_tools :
625
+ return None
626
+ vs_roots = self .get_vs_install_locations ()
627
+ for vs_root in vs_roots or []:
628
+ ms_cext_path = os .path .join (vs_root , "Common7" , "IDE" , "CommonExtensions" , "Microsoft" )
629
+ toolpaths = {
630
+ 'cmake' : os .path .join (ms_cext_path , "CMake" , "CMake" , "bin" , "cmake.exe" ),
631
+ 'git' : os .path .join (ms_cext_path , "TeamFoundation" , "Team Explorer" , "Git" , "cmd" , "git.exe" ),
632
+ 'ninja' : os .path .join (ms_cext_path , "CMake" , "Ninja" , "ninja.exe" )
633
+ }
634
+ path = toolpaths .get (tool )
635
+ if path and self .is_executable (path ):
636
+ return path
637
+ return None
638
+
639
+ def find_java (self ):
640
+ # 1. check JAVA_HOME env variable
641
+ java_home = os .environ .get ("JAVA_HOME" )
642
+ if java_home :
643
+ exe = os .path .join (java_home , "bin" , "java.exe" )
644
+ if os .path .isfile (exe ):
645
+ return exe
646
+
647
+ # 2. check registry (64+32-bit)
648
+ import winreg
649
+ def reg_lookup (base , subkey ):
650
+ try :
651
+ with winreg .OpenKey (base , subkey ) as key :
652
+ ver , _ = winreg .QueryValueEx (key , "CurrentVersion" )
653
+ key2 = winreg .OpenKey (base , subkey + "\\ " + ver )
654
+ path , _ = winreg .QueryValueEx (key2 , "JavaHome" )
655
+ exe = os .path .join (path , "bin" , "java.exe" )
656
+ if os .path .isfile (exe ):
657
+ return exe
658
+ except OSError :
659
+ return None
660
+
661
+ for hive , sub in [
662
+ (winreg .HKEY_LOCAL_MACHINE , r"SOFTWARE\JavaSoft\Java Runtime Environment" ),
663
+ (winreg .HKEY_LOCAL_MACHINE , r"SOFTWARE\Wow6432Node\JavaSoft\Java Runtime Environment" )
664
+ ]:
665
+ result = reg_lookup (hive , sub )
666
+ if result :
667
+ return result
668
+
669
+ # 3. check common folders under Program Files
670
+ for base in [os .environ .get ("ProgramFiles" ), os .environ .get ("ProgramFiles(x86)" )]:
671
+ if not base :
672
+ continue
673
+ jroot = os .path .join (base , "Java" )
674
+ if os .path .isdir (jroot ):
675
+ for entry in os .listdir (jroot ):
676
+ candidate = os .path .join (jroot , entry , "bin" , "java.exe" )
677
+ if os .path .isfile (candidate ):
678
+ return candidate
679
+
680
+ return None
681
+
682
+ def find_tool (self , tool ):
683
+ tool_path = shutil .which (tool )
684
+ if not tool_path and self .is_windows ():
685
+ tool_path = self .find_vs_tool (tool )
686
+ if not tool_path and tool == "java" :
687
+ tool_path = self .find_java ()
688
+ if not tool_path and tool == "python" :
689
+ tool_path = sys .executable
690
+ return tool_path
691
+
605
692
def check_tool (self , tool ):
606
693
"""
607
694
Checks if the required tools are available as a command line argument or
@@ -617,7 +704,9 @@ def check_tool(self, tool):
617
704
618
705
:return: None
619
706
"""
620
- default_value = shutil .which (tool )
707
+ default_value = self .find_tool (tool )
708
+ if not default_value :
709
+ default_value = tool
621
710
setattr (self .default_options , f"{ tool } _path" , default_value )
622
711
tool_path = self .prompt_option (f"{ tool } _path" )
623
712
if not self .is_executable (tool_path ):
@@ -717,7 +806,7 @@ def probe_compilers(self):
717
806
f .write ("\n " .join (cmake_lists ))
718
807
719
808
# Build command
720
- cmd = ["cmake" , "-S" , probe_dir ]
809
+ cmd = [self . options . cmake_path , "-S" , probe_dir ]
721
810
env = os .environ .copy ()
722
811
if self .options .cc :
723
812
cmd += ["-DCMAKE_C_COMPILER=" + self .options .cc ]
@@ -762,13 +851,13 @@ def install_ninja(self):
762
851
# 1. Check if the user has set a ninja_path option
763
852
if self .prompt_option ("ninja_path" ):
764
853
if not os .path .isabs (self .options .ninja_path ):
765
- self .options .ninja_path = shutil . which (self .options .ninja_path )
854
+ self .options .ninja_path = self . find_tool (self .options .ninja_path )
766
855
if not self .is_executable (self .options .ninja_path ):
767
856
raise FileNotFoundError (f"Ninja executable not found at { self .options .ninja_path } ." )
768
857
return
769
858
770
859
# 2. If ninja_path is not set, but does the user have it available in PATH?
771
- ninja_path = shutil . which ("ninja" )
860
+ ninja_path = self . find_tool ("ninja" )
772
861
if ninja_path :
773
862
print (f"Ninja found in PATH at { ninja_path } . Using it." )
774
863
self .options .ninja_path = ninja_path
@@ -973,10 +1062,10 @@ def install_llvm(self):
973
1062
self .prompt_option ("llvm_repo" )
974
1063
self .prompt_option ("llvm_commit" )
975
1064
os .makedirs (self .options .llvm_src_dir , exist_ok = True )
976
- self .run_cmd ("git init" , self .options .llvm_src_dir )
977
- self .run_cmd (f"git remote add origin { self .options .llvm_repo } " , self .options .llvm_src_dir )
978
- self .run_cmd (f"git fetch --depth 1 origin { self .options .llvm_commit } " , self .options .llvm_src_dir )
979
- self .run_cmd ("git checkout FETCH_HEAD" , self .options .llvm_src_dir )
1065
+ self .run_cmd ([ self . options . git_path , " init"] , self .options .llvm_src_dir )
1066
+ self .run_cmd ([ self . options . git_path , " remote" , " add" , " origin" , self .options .llvm_repo ] , self .options .llvm_src_dir )
1067
+ self .run_cmd ([ self . options . git_path , " fetch" , " --depth" , "1" , " origin" , self .options .llvm_commit ] , self .options .llvm_src_dir )
1068
+ self .run_cmd ([ self . options . git_path , " checkout" , " FETCH_HEAD"] , self .options .llvm_src_dir )
980
1069
981
1070
llvm_subproject_dir = os .path .join (self .options .llvm_src_dir , "llvm" )
982
1071
llvm_patches = os .path .join (self .options .mrdocs_src_dir , 'third-party' , 'llvm' )
0 commit comments