2013-06-24 2 views
0

저는 Scipy 스택에 대한 자동화 된 빌드 스크립트를 작성하기 위해 subprocess.Popen을 사용하고 있습니다.subprocess.Popen 호출에 대한 실행 순서 보장

현재 프로세스는 다음과 같습니다.

mathbuild.json :

{"suitesparse": {"version": "4.2.1", 
     "dependencies": ["metis"], 
     "downloads": ["http://www.cise.ufl.edu/research/sparse/SuiteSparse/SuiteSparse-4.2.1.tar.gz"], 
     "build": ["cd $DL_DIR", 
       "tar xvfz SuiteSparse-4.2.1.tar.gz", 
       "cd SuiteSparse", 
       "cp -r $DL_DIR/metis-4.0.3 metis-4.0.3"]}, 

"metis": {"version": "4.0.3", 
     "dependencies": [], 
     "downloads": ["http://glaros.dtc.umn.edu/gkhome/fetch/sw/metis/OLD/metis-4.0.3.tar.gz"], 
     "build": ["cd $DL_DIR", 
      "tar xvfz metis-4.0.3.tar.gz", 
      "cd metis-4.0.3", 
      "make"]}} 

mathbuild.py :

def package_list(package, config): 
    for dependency in config[package]['dependencies']: 
     yield from package_list(dependency, config) 
    yield package 

def build_package(package, config): 
    command = '; '.join(config[package]['build']) 
    build = subprocess.Popen(command, shell=True) 


def process_package(package, config, env_dir, dl_dir): 
    print('INSTALLING {0}'.format(package)) 
    print('Downloading...') 
    download_package(package, config, dl_dir) 
    print('Building...') 
    build_package(package, config) 


if __name__ == '__main__': 
    parser = argparse.ArgumentParser(description='Install Pylab in a new venv.') 
    parser.add_argument('env_dir', help='target directory for new environment') 
    args = parser.parse_args() 
    os.environ['ENV_DIR'], os.environ['DL_DIR'] = create_venv(args.env_dir) 
    with open('mathbuild.json') as f: 
     cfg = json.load(f) 
    processed = [] 
    for package in package_list('suitesparse', cfg): 
     if package not in processed: 
      process_package(package, cfg, 
          os.environ['ENV_DIR'], 
          os.environ['DL_DIR']) 
      processed += [package] 

그런 다음 종속성리스트 (나중에 항목 이전들에 종속되도록)을 생성하고 각각을 프로세싱 (다운로드 json 파일의 명령을 기반으로 작성).

종속성이 완전히 빌드되기 전에 새로운 subprocess.Popen 호출을 통해 패키지가 빌드된다는 것이 문제입니다. 위의 예에서는 metis 빌드가 완료되기 전에 suitesparse 실행이 시작됩니다. 이전 서브 프로세스가 수행되었는지 여부에 상관없이 루프가 for package in package_list('suitesparse', cfg) 루프가 될 때마다 새로운 서브 프로세스를 열려고하기 때문입니다.

질문 루프 기반는 popen 각 통화는 popen에 대한 이전 호출 (목록 즉, 이전 항목을) 할 경우에만 시작되도록 호출을 동기화하는 가장 좋은 방법은 무엇입니까?

내가 (두 패키지 빌드와 함께)은 하나는 popen을 결합하여 구축하도록 루프를 변경 시도했습니다 나는 시도했다, 그러나 그것은 hackish 보인다 무엇.

답변

2

Popen 대신 subprocess.check_call()이 필요합니다. the docs :

인수를 사용하여 명령을 실행하십시오. 명령이 완료 될 때까지 기다리십시오. 반환 코드가 0이면 반환하고 그렇지 않으면 CalledProcessError를 발생시킵니다. 부속 명령이 완료 될 때까지

def build_package(package, config): 
    command = '; '.join(config[package]['build']) 
    subprocess.check_call(command, shell=True) 

실제로는 popen 개체를 사용하는 경우, 당신은 기다릴 wait() 메소드를 호출 할 수 있습니다 : 같은

빌드 기능이 보일 것입니다

def build_package(package, config): 
    command = '; '.join(config[package]['build']) 
    build = subprocess.Popen(command, shell=True) 
    # do something with the build object 
    build.wait() 
    # command is done