mirror of https://github.com/oxen-io/session-ios
				
				
				
			
			You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			374 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			374 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Python
		
	
| #!/usr/bin/env python
 | |
| # -*- coding: utf-8 -*-
 | |
| 
 | |
| import os
 | |
| import sys
 | |
| import subprocess 
 | |
| import datetime
 | |
| import argparse
 | |
| import commands
 | |
| import re
 | |
| 
 | |
| 
 | |
| # This script is used to extract analytics event names from the codebase,
 | |
| # and convert them to constants in OWSAnalyticsEvents.
 | |
| 
 | |
| git_repo_path = os.path.abspath(subprocess.check_output(['git', 'rev-parse', '--show-toplevel']).strip())
 | |
| 
 | |
| 
 | |
| def splitall(path):
 | |
|     allparts = []
 | |
|     while 1:
 | |
|         parts = os.path.split(path)
 | |
|         if parts[0] == path:  # sentinel for absolute paths
 | |
|             allparts.insert(0, parts[0])
 | |
|             break
 | |
|         elif parts[1] == path: # sentinel for relative paths
 | |
|             allparts.insert(0, parts[1])
 | |
|             break
 | |
|         else:
 | |
|             path = parts[0]
 | |
|             allparts.insert(0, parts[1])
 | |
|     return allparts
 | |
| 
 | |
| 
 | |
| def objc_name_for_event_name(event_name):
 | |
|     while True:
 | |
|         index = event_name.find('_')
 | |
|         if index < 0:
 | |
|             break
 | |
|         if index >= len(event_name) - 1:
 | |
|             break
 | |
|         nextChar = event_name[index + 1]
 | |
|         event_name = event_name[:index] + nextChar.upper() + event_name[index + 2:]
 | |
|     return event_name
 | |
| 
 | |
| 
 | |
| event_names = []
 | |
|     
 | |
| def process(filepath, c_macros, swift_macros):
 | |
| 
 | |
|     short_filepath = filepath[len(git_repo_path):]
 | |
|     if short_filepath.startswith(os.sep):
 | |
|        short_filepath = short_filepath[len(os.sep):] 
 | |
|     
 | |
|     filename = os.path.basename(filepath)
 | |
|     if filename.startswith('.'):
 | |
|         return
 | |
|     if filename == 'OWSAnalytics.h':
 | |
|         return
 | |
|     file_ext = os.path.splitext(filename)[1]
 | |
|     
 | |
|     is_swift = file_ext in ('.swift')
 | |
|     
 | |
|     
 | |
|     if is_swift:
 | |
|         macros = swift_macros
 | |
|     else:
 | |
|         macros = c_macros
 | |
|     
 | |
|     # print short_filepath, is_swift
 | |
|     
 | |
|     with open(filepath, 'rt') as f:
 | |
|         text = f.read()
 | |
|     
 | |
|     replacement_map = {}
 | |
|     
 | |
|     position = 0
 | |
|     has_printed_filename = False
 | |
|     while True:
 | |
|         best_match = None
 | |
|         best_macro = None
 | |
|         for macro in macros:
 | |
|             pattern = r'''%s\(([^,\)]+)[,\)]''' % macro
 | |
|             # print '\t pattern', pattern
 | |
|             matcher = re.compile(pattern)
 | |
|             # matcher = re.compile(r'#define (OWSProd)')
 | |
|             match = matcher.search(text, pos=position)
 | |
|             if match:
 | |
|                 event_name = match.group(1).strip()
 | |
|                 
 | |
|                 # Ignore swift func definitions
 | |
|                 if is_swift and ':' in event_name:
 | |
|                     continue
 | |
|                     
 | |
|                 # print '\t', 'event_name', event_name
 | |
|                 
 | |
|                 if not best_match:
 | |
|                     pass
 | |
|                 elif best_match.start(1) > match.start(1):
 | |
|                     pass
 | |
|                 else:
 | |
|                     continue
 | |
| 
 | |
|                 best_match = match
 | |
|                 best_macro = macro
 | |
|         # TODO:
 | |
|         if not best_match:
 | |
|             break
 | |
|             
 | |
|         position = best_match.end(1)
 | |
|         if not has_printed_filename:
 | |
|             has_printed_filename = True
 | |
|             print short_filepath
 | |
|         
 | |
|         raw_event_name = best_match.group(1).strip()
 | |
|         if is_swift:
 | |
|             pattern = r'^"(.+)"$'
 | |
|         else:
 | |
|             pattern = r'^@"(.+)"$'
 | |
|         # print 'pattern:', pattern
 | |
|         matcher = re.compile(pattern)
 | |
|         # matcher = re.compile(r'#define (OWSProd)')
 | |
|         match = matcher.search(raw_event_name)
 | |
|         if match:
 | |
|             event_name = match.group(1).strip()
 | |
|         else:
 | |
|             print '\t', 'Ignoring event: _%s_' % raw_event_name
 | |
|             continue
 | |
|         event_names.append(event_name)
 | |
|         print '\t', 'event_name', event_name
 | |
|         
 | |
|         if is_swift:
 | |
|             before = '"%s"' % event_name
 | |
|             after = 'OWSAnalyticsEvents.%s()' % objc_name_for_event_name(event_name)
 | |
|         else:
 | |
|             before = '@"%s"' % event_name
 | |
|             after = '[OWSAnalyticsEvents %s]' % objc_name_for_event_name(event_name)
 | |
|         replacement_map[before] = after
 | |
|                 
 | |
|         # macros.append(macro)
 | |
|         
 | |
|         # break
 | |
|     
 | |
|     # print 'replacement_map', replacement_map
 | |
|     
 | |
|     for before in replacement_map:
 | |
|         after = replacement_map[before]
 | |
|         text = text.replace(before, after)
 | |
| 
 | |
|     # if original_text == text:
 | |
|     #     return
 | |
|     
 | |
|     print 'Updating:', short_filepath
 | |
|     
 | |
|     with open(filepath, 'wt') as f:
 | |
|         f.write(text)
 | |
| 
 | |
| 
 | |
| def should_ignore_path(path):
 | |
|     ignore_paths = [
 | |
|         os.path.join(git_repo_path, '.git')
 | |
|     ]
 | |
|     for ignore_path in ignore_paths:
 | |
|         if path.startswith(ignore_path):
 | |
|             return True
 | |
|     for component in splitall(path):
 | |
|         if component.startswith('.'):
 | |
|             return True
 | |
|         if component.endswith('.framework'):
 | |
|             return True
 | |
|         if component in ('Pods', 'ThirdParty', 'Carthage',):
 | |
|             return True                
 | |
|         
 | |
|     return False
 | |
|     
 | |
|     
 | |
| def process_if_appropriate(filepath, c_macros, swift_macros):
 | |
|     filename = os.path.basename(filepath)
 | |
|     if filename.startswith('.'):
 | |
|         return
 | |
|     file_ext = os.path.splitext(filename)[1]
 | |
|     if file_ext not in ('.h', '.hpp', '.cpp', '.m', '.mm', '.pch', '.swift'):
 | |
|         return
 | |
|     if should_ignore_path(filepath):
 | |
|         return
 | |
|     process(filepath, c_macros, swift_macros)
 | |
| 
 | |
|     
 | |
| def extract_macros(filepath):
 | |
| 
 | |
|     filename = os.path.basename(filepath)
 | |
|     file_ext = os.path.splitext(filename)[1]
 | |
|     is_swift = file_ext in ('.swift')
 | |
| 
 | |
|     macros = []
 | |
|     
 | |
|     with open(filepath, 'rt') as f:
 | |
|         text = f.read()
 | |
|     
 | |
|     lines = text.split('\n')
 | |
|     for line in lines:
 | |
|         # Match lines of this form: 
 | |
|         # #define OWSProdCritical(__eventName) ...
 | |
|     
 | |
|         if is_swift:
 | |
|             matcher = re.compile(r'func (OWSProd[^\(]+)\(.+[,\)]')
 | |
|         else:
 | |
|             matcher = re.compile(r'#define (OWSProd[^\(]+)\(.+[,\)]')
 | |
|         # matcher = re.compile(r'#define (OWSProd)')
 | |
|         match = matcher.search(line)
 | |
|         if match:
 | |
|             macro = match.group(1).strip()
 | |
|             # print 'macro', macro
 | |
|             macros.append(macro)
 | |
|     
 | |
|     return macros
 | |
|     
 | |
|     
 | |
| def update_event_names(header_file_path, source_file_path):
 | |
|     # global event_names
 | |
|     # event_names = sorted(set(event_names))
 | |
|     code_generation_marker = '#pragma mark - Code Generation Marker'
 | |
|     
 | |
|     # Source
 | |
|     filepath = source_file_path
 | |
|     with open(filepath, 'rt') as f:
 | |
|         text = f.read()
 | |
|     
 | |
|     code_generation_start = text.find(code_generation_marker)
 | |
|     code_generation_end = text.rfind(code_generation_marker)
 | |
|     if code_generation_start < 0:
 | |
|         print 'Could not find marker in file:', file
 | |
|         sys.exit(1)
 | |
|     if code_generation_end < 0 or code_generation_end == code_generation_start:
 | |
|         print 'Could not find marker in file:', file
 | |
|         sys.exit(1)
 | |
| 
 | |
|     event_name_map = {}
 | |
| 
 | |
|     print
 | |
|     print 'Parsing old generated code'
 | |
|     print
 | |
| 
 | |
|     old_generated = text[code_generation_start + len(code_generation_marker):code_generation_end]
 | |
|     # print 'old_generated', old_generated
 | |
|     for split in old_generated.split('+'):
 | |
|         split = split.strip()
 | |
|         # print 'split:', split
 | |
|         if not split:
 | |
|             continue
 | |
|         
 | |
|         # Example:
 | |
|         #(NSString *)call_service_call_already_set
 | |
|         #{
 | |
|         #    return @"call_service_call_already_set";
 | |
|         #}        
 | |
|         
 | |
|         pattern = r'\(NSString \*\)([^\s\r\n\t]+)[\s\r\n\t]'
 | |
|         matcher = re.compile(pattern)
 | |
|         match = matcher.search(split)
 | |
|         if not match:
 | |
|             print 'Could not parse:', split
 | |
|             print 'In file:', filepath
 | |
|             sys.exit(1)
 | |
| 
 | |
|         method_name = match.group(1).strip()
 | |
|         print 'method_name:', method_name
 | |
|         
 | |
|         pattern = r'return @"(.+)";'
 | |
|         matcher = re.compile(pattern)
 | |
|         match = matcher.search(split)
 | |
|         if not match:
 | |
|             print 'Could not parse:', split
 | |
|             print 'In file:', filepath
 | |
|             sys.exit(1)
 | |
| 
 | |
|         event_name = match.group(1).strip()
 | |
|         print 'event_name:', event_name
 | |
|         
 | |
|         event_name_map[event_name] = method_name
 | |
|     
 | |
|     print
 | |
|     
 | |
|     
 | |
|     all_event_names = sorted(set(event_name_map.keys() + event_names))
 | |
|     print 'all_event_names', all_event_names
 | |
|         
 | |
|     generated = code_generation_marker
 | |
|     for event_name in all_event_names:
 | |
|         # Example:
 | |
|         # + (NSString *)call_service_call_already_set;
 | |
|         if event_name in event_name_map:
 | |
|             objc_name = event_name_map[event_name]
 | |
|         else:
 | |
|             objc_name = objc_name_for_event_name(event_name)
 | |
|         text_for_event = '''+ (NSString *)%s
 | |
| {
 | |
|     return @"%s";
 | |
| }''' % (objc_name, event_name)
 | |
|         generated = generated + '\n\n' + text_for_event
 | |
|     generated = generated + '\n\n' + code_generation_marker
 | |
|     print 'generated', generated
 | |
|     new_text = text[:code_generation_start] + generated + text[code_generation_end + len(code_generation_marker):]
 | |
|     print 'text', new_text
 | |
|     with open(filepath, 'wt') as f:
 | |
|         f.write(new_text)
 | |
| 
 | |
|     
 | |
|     # Header
 | |
|     filepath = header_file_path
 | |
|     with open(filepath, 'rt') as f:
 | |
|         text = f.read()
 | |
|     
 | |
|     code_generation_start = text.find(code_generation_marker)
 | |
|     code_generation_end = text.rfind(code_generation_marker)
 | |
|     if code_generation_start < 0:
 | |
|         print 'Could not find marker in file:', file
 | |
|         sys.exit(1)
 | |
|     if code_generation_end < 0 or code_generation_end == code_generation_start:
 | |
|         print 'Could not find marker in file:', file
 | |
|         sys.exit(1)
 | |
|     
 | |
|     generated = code_generation_marker
 | |
|     for event_name in all_event_names:
 | |
|         # Example:
 | |
|         # + (NSString *)call_service_call_already_set;
 | |
|         objc_name = objc_name_for_event_name(event_name)
 | |
|         text_for_event = '+ (NSString *)%s;' % (objc_name,)
 | |
|         generated = generated + '\n\n' + text_for_event
 | |
|     generated = generated + '\n\n' + code_generation_marker
 | |
|     print 'generated', generated
 | |
|     new_text = text[:code_generation_start] + generated + text[code_generation_end + len(code_generation_marker):]
 | |
|     print 'text', new_text
 | |
|     with open(filepath, 'wt') as f:
 | |
|         f.write(new_text)
 | |
|     
 | |
|     
 | |
|     
 | |
| if __name__ == "__main__":
 | |
|     # print 'git_repo_path', git_repo_path
 | |
|     
 | |
|     macros_header_file_path = os.path.join(git_repo_path, 'SignalServiceKit', 'src', 'Util', 'OWSAnalytics.h')
 | |
|     if not os.path.exists(macros_header_file_path):
 | |
|         print 'Macros header does not exist:', macros_header_file_path
 | |
|         sys.exit(1)
 | |
|     c_macros = extract_macros(macros_header_file_path)
 | |
|     print 'c_macros:', c_macros
 | |
| 
 | |
|     macros_header_file_path = os.path.join(git_repo_path, 'Signal', 'src', 'util', 'OWSAnalytics.swift')
 | |
|     if not os.path.exists(macros_header_file_path):
 | |
|         print 'Macros header does not exist:', macros_header_file_path
 | |
|         sys.exit(1)
 | |
|     swift_macros = extract_macros(macros_header_file_path)
 | |
|     print 'swift_macros:', swift_macros
 | |
| 
 | |
|     event_names_header_file_path = os.path.join(git_repo_path, 'SignalServiceKit', 'src', 'Util', 'OWSAnalyticsEvents.h')
 | |
|     if not os.path.exists(event_names_header_file_path):
 | |
|         print 'event_names_header_file_path does not exist:', event_names_header_file_path
 | |
|         sys.exit(1)
 | |
| 
 | |
|     event_names_source_file_path = os.path.join(git_repo_path, 'SignalServiceKit', 'src', 'Util', 'OWSAnalyticsEvents.m')
 | |
|     if not os.path.exists(event_names_source_file_path):
 | |
|         print 'event_names_source_file_path does not exist:', event_names_source_file_path
 | |
|         sys.exit(1)
 | |
|         
 | |
|     for rootdir, dirnames, filenames in os.walk(git_repo_path):
 | |
|         for filename in filenames:
 | |
|             file_path = os.path.abspath(os.path.join(rootdir, filename))
 | |
|             process_if_appropriate(file_path, c_macros, swift_macros)
 | |
| 
 | |
|     print
 | |
|     print 'event_names', sorted(set(event_names))
 | |
|     update_event_names(event_names_header_file_path, event_names_source_file_path)
 |