#!/usr/bin/env python3 """ Script to extract binary names from setup.py files and create placeholder files. Usage: setup_binaries.py /path/to/setup.py """ import os import sys import ast class SetupVisitor(ast.NodeVisitor): """AST visitor that finds setup() function calls and extracts scripts.""" def __init__(self): self.scripts = [] def visit_Call(self, node): # Check if this is a call to setup() if getattr(node.func, 'id', None) == 'setup': # Look through keyword arguments for scripts for kw in node.keywords: if kw.arg == 'scripts': self._extract_scripts(kw.value) # Also check entry_points which might contain console_scripts elif kw.arg == 'entry_points': self._extract_entry_points(kw.value) # Continue traversing the AST self.generic_visit(node) def _extract_scripts(self, node): """Extract script paths from a list or list comprehension.""" if isinstance(node, ast.List): for elt in node.elts: if isinstance(elt, ast.Str): if elt.s.startswith('bin/'): self.scripts.append(elt.s) # Handle variables and other expressions by looking for string literals else: # This is a simple fallback for complex expressions script_finder = StringLiteralFinder('bin/') script_finder.visit(node) self.scripts.extend(script_finder.matching_strings) def _extract_entry_points(self, node): """Extract console_scripts from entry_points argument.""" # This would need more complex parsing based on how entry_points is specified # Often it's a dict with 'console_scripts' key or a string with [console_scripts] section # For this example, we'll just log that we found it pass class StringLiteralFinder(ast.NodeVisitor): """Find string literals that start with a specific prefix.""" def __init__(self, prefix): self.prefix = prefix self.matching_strings = [] def visit_Str(self, node): if node.s.startswith(self.prefix): self.matching_strings.append(node.s) def extract_setup_binaries(setup_path): """Extract binary names from a setup.py file and create placeholder files.""" try: # Read the setup.py file with open(setup_path, 'r') as f: setup_content = f.read() # Parse the file tree = ast.parse(setup_content) # Find setup() calls and extract scripts visitor = SetupVisitor() visitor.visit(tree) # Get the scripts scripts = visitor.scripts if not scripts: print(f"No bin scripts found in {setup_path}") return True # Not an error, just no scripts # Create placeholder files base_dir = os.path.dirname(setup_path) created_count = 0 for script in scripts: script_path = os.path.join(base_dir, script) script_dir = os.path.dirname(script_path) # Create directory if it doesn't exist os.makedirs(script_dir, exist_ok=True) # Create an empty file with open(script_path, 'w') as f: pass # Make the file executable os.chmod(script_path, 0o755) created_count += 1 print(f"Created {created_count} placeholder binary files from {setup_path}") return True except Exception as e: print(f"Error processing {setup_path}: {e}") return False if __name__ == "__main__": if len(sys.argv) != 2: print(f"Usage: {sys.argv[0]} /path/to/setup.py") sys.exit(1) setup_path = sys.argv[1] if not os.path.exists(setup_path): print(f"Error: {setup_path} not found") sys.exit(1) success = extract_setup_binaries(setup_path) sys.exit(0 if success else 1)