1# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2"""
3This module keeps commonly used components.
4"""
5from __future__ import absolute_import
6from __future__ import division
7from __future__ import print_function
8from __future__ import unicode_literals
9try:
10    from builtins import object
11except ImportError:
12    from __builtin__ import object
13import subprocess
14import sys
15import os
16import time
17
18class ColorString(object):
19    """ Generate colorful strings on terminal """
20    HEADER = '\033[95m'
21    BLUE = '\033[94m'
22    GREEN = '\033[92m'
23    WARNING = '\033[93m'
24    FAIL = '\033[91m'
25    ENDC = '\033[0m'
26
27    @staticmethod
28    def _make_color_str(text, color):
29        # In Python2, default encoding for unicode string is ASCII
30        if sys.version_info.major <= 2:
31            return "".join(
32                [color, text.encode('utf-8'), ColorString.ENDC])
33        # From Python3, default encoding for unicode string is UTF-8
34        return "".join(
35            [color, text, ColorString.ENDC])
36
37    @staticmethod
38    def ok(text):
39        if ColorString.is_disabled:
40            return text
41        return ColorString._make_color_str(text, ColorString.GREEN)
42
43    @staticmethod
44    def info(text):
45        if ColorString.is_disabled:
46            return text
47        return ColorString._make_color_str(text, ColorString.BLUE)
48
49    @staticmethod
50    def header(text):
51        if ColorString.is_disabled:
52            return text
53        return ColorString._make_color_str(text, ColorString.HEADER)
54
55    @staticmethod
56    def error(text):
57        if ColorString.is_disabled:
58            return text
59        return ColorString._make_color_str(text, ColorString.FAIL)
60
61    @staticmethod
62    def warning(text):
63        if ColorString.is_disabled:
64            return text
65        return ColorString._make_color_str(text, ColorString.WARNING)
66
67    is_disabled = False
68
69
70def run_shell_command(shell_cmd, cmd_dir=None):
71    """ Run a single shell command.
72        @returns a tuple of shell command return code, stdout, stderr """
73
74    if cmd_dir is not None and not os.path.exists(cmd_dir):
75        run_shell_command("mkdir -p %s" % cmd_dir)
76
77    start = time.time()
78    print("\t>>> Running: " + shell_cmd)
79    p = subprocess.Popen(shell_cmd,
80                         shell=True,
81                         stdout=subprocess.PIPE,
82                         stderr=subprocess.PIPE,
83                         cwd=cmd_dir)
84    stdout, stderr = p.communicate()
85    end = time.time()
86
87    # Report time if we spent more than 5 minutes executing a command
88    execution_time = end - start
89    if execution_time > (60 * 5):
90        mins = (execution_time / 60)
91        secs = (execution_time % 60)
92        print("\t>time spent: %d minutes %d seconds" % (mins, secs))
93
94
95    return p.returncode, stdout, stderr
96
97
98def run_shell_commands(shell_cmds, cmd_dir=None, verbose=False):
99    """ Execute a sequence of shell commands, which is equivalent to
100        running `cmd1 && cmd2 && cmd3`
101        @returns boolean indication if all commands succeeds.
102    """
103
104    if cmd_dir:
105        print("\t=== Set current working directory => %s" % cmd_dir)
106
107    for shell_cmd in shell_cmds:
108        ret_code, stdout, stderr = run_shell_command(shell_cmd, cmd_dir)
109        if stdout:
110            if verbose or ret_code != 0:
111                print(ColorString.info("stdout: \n"), stdout)
112        if stderr:
113            # contents in stderr is not necessarily to be error messages.
114            if verbose or ret_code != 0:
115                print(ColorString.error("stderr: \n"), stderr)
116        if ret_code != 0:
117            return False
118
119    return True
120