@@ -129,17 +129,58 @@ def _create_alias(
129129 return
130130
131131 existing_bytes = b''
132- try :
133- with open (p , 'rb' ) as f :
134- existing_bytes = f .read (len (launcher_bytes ) + 1 )
135- except FileNotFoundError :
136- pass
137- except OSError :
138- LOGGER .debug ("Failed to read existing alias launcher." )
132+ if getattr (cmd , "force" , False ):
133+ # Only expect InstallCommand to have .force
134+ unlink (p )
135+ else :
136+ try :
137+ with open (p , 'rb' ) as f :
138+ existing_bytes = f .read (len (launcher_bytes ) + 1 )
139+ except FileNotFoundError :
140+ pass
141+ except OSError :
142+ LOGGER .debug ("Failed to read existing alias launcher." )
139143
140144 launcher_remap = cmd .scratch .setdefault ("aliasutils.create_alias.launcher_remap" , {})
141- if not allow_link or not _link :
142- # If links are disallowed, always replace the target with a copy.
145+
146+ if existing_bytes != launcher_bytes and allow_link and _link :
147+ # Try to find an existing launcher we can hard-link
148+ launcher2 = launcher_remap .get (launcher .name )
149+ if not launcher2 :
150+ # None known, so search existing files
151+ try :
152+ LOGGER .debug ("Searching %s for suitable launcher to link" , cmd .global_dir )
153+ for p2 in cmd .global_dir .glob ("*.exe" ):
154+ try :
155+ with open (p2 , 'rb' ) as f :
156+ existing_bytes2 = f .read (len (launcher_bytes ) + 1 )
157+ except OSError :
158+ LOGGER .debug ("Failed to check %s contents" , p2 , exc_info = True )
159+ else :
160+ if existing_bytes2 == launcher_bytes :
161+ launcher2 = p2
162+ break
163+ else :
164+ LOGGER .debug ("No existing launcher available" )
165+ except Exception :
166+ LOGGER .debug ("Failed to find existing launcher" , exc_info = True )
167+
168+ if launcher2 :
169+ # We know that the target either doesn't exist or needs replacing
170+ unlink (p )
171+ try :
172+ _link (launcher2 , p )
173+ existing_bytes = launcher_bytes
174+ launcher_remap [launcher .name ] = launcher2
175+ LOGGER .debug ("Created %s as hard link to %s" , p .name , launcher2 .name )
176+ except FileNotFoundError :
177+ raise
178+ except OSError :
179+ LOGGER .debug ("Failed to create hard link to %s" , launcher2 .name )
180+ launcher2 = None
181+
182+ # Recheck - existing_bytes will have been updated if we successfully linked
183+ if existing_bytes != launcher_bytes :
143184 unlink (p )
144185 try :
145186 p .write_bytes (launcher_bytes )
@@ -148,43 +189,6 @@ def _create_alias(
148189 except OSError :
149190 LOGGER .error ("Failed to create global command %s." , name )
150191 LOGGER .debug ("TRACEBACK" , exc_info = True )
151- elif existing_bytes == launcher_bytes :
152- # Valid existing launcher, so save its path in case we need it later
153- # for a hard link.
154- launcher_remap .setdefault (launcher .name , p )
155- else :
156- # Links are allowed and we need to create one, so try to make a link,
157- # falling back to a link to another existing alias (that we've checked
158- # already during this run), and then falling back to a copy.
159- # This handles the case where our links are on a different volume to the
160- # install (so hard links don't work), but limits us to only a single
161- # copy (each) of the redirector(s), thus saving space.
162- unlink (p )
163- try :
164- _link (launcher , p )
165- LOGGER .debug ("Created %s as hard link to %s" , p .name , launcher .name )
166- except OSError as ex :
167- if ex .winerror != 17 :
168- # Report errors other than cross-drive links
169- LOGGER .debug ("Failed to create hard link for command." , exc_info = True )
170- launcher2 = launcher_remap .get (launcher .name )
171- if launcher2 :
172- try :
173- _link (launcher2 , p )
174- LOGGER .debug ("Created %s as hard link to %s" , p .name , launcher2 .name )
175- except FileNotFoundError :
176- raise
177- except OSError :
178- LOGGER .debug ("Failed to create hard link to fallback launcher" )
179- launcher2 = None
180- if not launcher2 :
181- try :
182- p .write_bytes (launcher_bytes )
183- LOGGER .debug ("Created %s as copy of %s" , p .name , launcher .name )
184- launcher_remap [launcher .name ] = p
185- except OSError :
186- LOGGER .error ("Failed to create global command %s." , name )
187- LOGGER .debug ("TRACEBACK" , exc_info = True )
188192
189193 p_target = p .with_name (p .name + ".__target__" )
190194 do_update = True
0 commit comments