#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright 2014 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# This script computes the number of concurrent links we want to run
# in the build as a function of machine spec. It's based
# on GetDefaultConcurrentLinks in GYP.

import multiprocessing
import optparse
import os
import re
import subprocess
import sys


def _get_total_memory_in_bytes():
    if sys.platform in ('win32', 'cygwin'):
        import ctypes

        class MEMORYSTATUSEX(ctypes.Structure):
            _fields_ = [
                ("dwLength", ctypes.c_ulong),
                ("dwMemoryLoad", ctypes.c_ulong),
                ("ullTotalPhys", ctypes.c_ulonglong),
                ("ullAvailPhys", ctypes.c_ulonglong),
                ("ullTotalPageFile", ctypes.c_ulonglong),
                ("ullAvailPageFile", ctypes.c_ulonglong),
                ("ullTotalVirtual", ctypes.c_ulonglong),
                ("ullAvailVirtual", ctypes.c_ulonglong),
                ("sullAvailExtendedVirtual", ctypes.c_ulonglong),
            ]

        stat = MEMORYSTATUSEX(dwLength=ctypes.sizeof(MEMORYSTATUSEX))
        ctypes.windll.kernel32.GlobalMemoryStatusEx(ctypes.byref(stat))
        return stat.ullTotalPhys
    elif sys.platform.startswith('linux'):
        if os.path.exists("/proc/meminfo"):
            with open("/proc/meminfo") as meminfo:
                memtotal_re = re.compile(r'^MemTotal:\s*(\d*)\s*kB')
                for line in meminfo:
                    match = memtotal_re.match(line)
                    if not match:
                        continue
                    return float(match.group(1)) * 2**10
    elif sys.platform == 'darwin':
        try:
            return int(subprocess.check_output(['sysctl', '-n', 'hw.memsize']))
        except Exception:
            return 0
    return 0


def _get_default_concurrent_links(mem_per_link_gb, reserve_mem_gb):
    mem_total_bytes = _get_total_memory_in_bytes()
    mem_total_bytes = max(0, mem_total_bytes - reserve_mem_gb * 2**30)
    num_concurrent_links = int(
        max(1, mem_total_bytes / mem_per_link_gb / 2**30))
    hard_cap = max(1, int(os.getenv('GYP_LINK_CONCURRENCY_MAX', 2**32)))

    try:
        cpu_cap = multiprocessing.cpu_count()
    except: # noqa E722
        cpu_cap = 1

    return min(num_concurrent_links, hard_cap, cpu_cap)


def main():
    parser = optparse.OptionParser()
    parser.add_option('--mem_per_link_gb',
                      action="store",
                      type="int",
                      default=8)
    parser.add_option('--reserve_mem_gb',
                      action="store",
                      type="int",
                      default=0)
    parser.disable_interspersed_args()
    options, _ = parser.parse_args()

    print(
        _get_default_concurrent_links(options.mem_per_link_gb,
                                      options.reserve_mem_gb))
    return 0


if __name__ == '__main__':
    sys.exit(main())