• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2#
3# Copyright 2018 - The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#     http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17"""Functional test for aidegen project files."""
18
19from __future__ import absolute_import
20from __future__ import print_function
21
22import argparse
23import functools
24import itertools
25import json
26import logging
27import os
28import subprocess
29import sys
30import xml.etree.ElementTree
31import xml.parsers.expat
32
33from aidegen import aidegen_main
34from aidegen import constant
35from aidegen.lib import clion_project_file_gen
36# pylint: disable=no-name-in-module
37from aidegen.lib import common_util
38from aidegen.lib import errors
39from aidegen.lib import module_info_util
40from aidegen.lib import project_config
41from aidegen.lib import project_file_gen
42
43from atest import module_info
44
45_PRODUCT_DIR = '$PROJECT_DIR$'
46_ROOT_DIR = os.path.join(common_util.get_android_root_dir(),
47                         'tools/asuite/aidegen_functional_test')
48_TEST_DATA_PATH = os.path.join(_ROOT_DIR, 'test_data')
49_VERIFY_COMMANDS_JSON = os.path.join(_TEST_DATA_PATH, 'verify_commands.json')
50_GOLDEN_SAMPLES_JSON = os.path.join(_TEST_DATA_PATH, 'golden_samples.json')
51_VERIFY_BINARY_JSON = os.path.join(_TEST_DATA_PATH, 'verify_binary_upload.json')
52_VERIFY_PRESUBMIT_JSON = os.path.join(_TEST_DATA_PATH, 'verify_presubmit.json')
53_ANDROID_COMMON = 'android_common'
54_LINUX_GLIBC_COMMON = 'linux_glibc_common'
55_SRCS = 'srcs'
56_JARS = 'jars'
57_URL = 'url'
58_TEST_ERROR = 'AIDEGen functional test error: {}-{} is different.'
59_MSG_NOT_IN_PROJECT_FILE = ('{} is expected, but not found in the created '
60                            'project file: {}')
61_MSG_NOT_IN_SAMPLE_DATA = ('{} is unexpected, but found in the created project '
62                           'file: {}')
63_ALL_PASS = 'All tests passed!'
64_GIT_COMMIT_ID_JSON = os.path.join(
65    _TEST_DATA_PATH, 'baseline_code_commit_id.json')
66_GIT = 'git'
67_CHECKOUT = 'checkout'
68_BRANCH = 'branch'
69_COMMIT = 'commit'
70_LOG = 'log'
71_ALL = '--all'
72_COMMIT_ID_NOT_EXIST_ERROR = ('Commit ID: {} does not exist in path: {}. '
73                              'Please use "git log" command to check if it '
74                              'exists. If it does not, try to update your '
75                              'source files to the latest version or do not '
76                              'use "repo init --depth=1" command.')
77_GIT_LOG_ERROR = 'Command "git log -n 1" failed.'
78_BE_REPLACED = '${config.X86_64GccRoot}'
79_TO_REPLACE = 'prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9'
80
81
82def _parse_args(args):
83    """Parse command line arguments.
84
85    Args:
86        args: A list of arguments.
87
88    Returns:
89        An argparse.Namespace object holding parsed args.
90    """
91    parser = argparse.ArgumentParser(
92        description=__doc__,
93        formatter_class=argparse.RawDescriptionHelpFormatter,
94        usage='aidegen_functional_test [-c | -u | -b | -a] -v -r')
95    group = parser.add_mutually_exclusive_group()
96    parser.required = False
97    parser.add_argument(
98        'targets',
99        type=str,
100        nargs='*',
101        default=[''],
102        help='Android module name or path.e.g. frameworks/base')
103    group.add_argument(
104        '-c',
105        '--create-sample',
106        action='store_true',
107        dest='create_sample',
108        help=('Create aidegen project files and write data to sample json file '
109              'for aidegen_functional_test to compare.'))
110    parser.add_argument(
111        '-v',
112        '--verbose',
113        action='store_true',
114        help='Show DEBUG level logging.')
115    parser.add_argument(
116        '-r',
117        '--remove_bp_json',
118        action='store_true',
119        help='Remove module_bp_java_deps.json for each use case test.')
120    parser.add_argument(
121        '-m',
122        '--make_clean',
123        action='store_true',
124        help=('Make clean before testing to create a clean environment, the '
125              'aidegen_functional_test can run only once if users command it.'))
126    group.add_argument(
127        '-u',
128        '--use_cases',
129        action='store_true',
130        dest='use_cases_verified',
131        help='Verify various use cases of executing aidegen.')
132    group.add_argument(
133        '-b',
134        action='store_true',
135        dest='binary_upload_verified',
136        help=('Verify aidegen\'s use cases by executing different aidegen '
137              'commands.'))
138    group.add_argument(
139        '-p',
140        action='store_true',
141        dest='binary_presubmit_verified',
142        help=('Verify aidegen\'s tool in presubmit test by executing'
143              'different aidegen commands.'))
144    group.add_argument(
145        '-a',
146        '--test-all',
147        action='store_true',
148        dest='test_all_samples',
149        help='Test all modules listed in module-info.json.')
150    group.add_argument(
151        '-n',
152        '--compare-sample-native',
153        action='store_true',
154        dest='compare_sample_native',
155        help=('Compare if sample native project file is the same as generated '
156              'by the build system.'))
157    return parser.parse_args(args)
158
159
160def _import_project_file_xml_etree(filename):
161    """Import iml project file and load its data into a dictionary.
162
163    Args:
164        filename: The input project file name.
165
166    Returns:
167        A dictionary contains dependent files' data of project file's contents.
168        The samples are like:
169        "srcs": [
170            ...
171            "file://$PROJECT_DIR$/frameworks/base/cmds/am/src",
172            "file://$PROJECT_DIR$/frameworks/base/cmds/appwidget/src",
173            ...
174        ]
175        "jars": [
176            ...
177            "jar://$PROJECT_DIR$/out/host/common/obj/**/classes-header.jar!/"
178            ...
179        ]
180
181    Raises:
182        EnvironmentError and xml parsing or format errors.
183    """
184    data = {}
185    try:
186        tree = xml.etree.ElementTree.parse(filename)
187        data[_SRCS] = []
188        root = tree.getroot()
189        for element in root.iter('sourceFolder'):
190            src = element.get(_URL).replace(common_util.get_android_root_dir(),
191                                            _PRODUCT_DIR)
192            data[_SRCS].append(src)
193        data[_JARS] = []
194        for element in root.iter('root'):
195            jar = element.get(_URL).replace(common_util.get_android_root_dir(),
196                                            _PRODUCT_DIR)
197            data[_JARS].append(jar)
198    except (EnvironmentError, ValueError, LookupError,
199            xml.parsers.expat.ExpatError) as err:
200        print("{0}: import error: {1}".format(os.path.basename(filename), err))
201        raise
202    return data
203
204
205def _get_project_file_names(abs_path):
206    """Get project file name and depenencies name by relative path.
207
208    Args:
209        abs_path: an absolute module's path.
210
211    Returns:
212        file_name: a string of the project file name.
213        dep_name: a string of the merged project and dependencies file's name,
214                  e.g., frameworks-dependencies.iml.
215    """
216    # pylint: disable=maybe-no-member
217    code_name = project_file_gen.ProjectFileGenerator.get_unique_iml_name(
218        abs_path)
219    file_name = ''.join([code_name, '.iml'])
220    dep_name = ''.join([constant.KEY_DEPENDENCIES, '.iml'])
221    return file_name, dep_name
222
223
224def _get_unique_module_name(rel_path, filename):
225    """Get a unique project file name or dependencies name by relative path.
226
227    Args:
228        rel_path: a relative module's path to aosp root path.
229        filename: the file name to be generated in module_in type file name.
230
231    Returns:
232        A string, the unique file name for the whole module-info.json.
233    """
234    code_names = rel_path.split(os.sep)
235    code_names[-1] = filename
236    return '-'.join(code_names)
237
238
239def _get_git_current_commit_id(abs_path):
240    """Get target's git checkout command list.
241
242    When we run command 'git log -n 1' and get the top first git log record, the
243    commit id is next to the specific word 'commit'.
244
245    Args:
246        abs_path: a string of the absolute path of the target branch.
247
248    Return:
249        The current git commit id.
250
251    Raises:
252        Call subprocess.check_output cause subprocess.CalledProcessError.
253    """
254    cwd = os.getcwd()
255    os.chdir(abs_path)
256    git_log_cmds = [_GIT, _LOG, '-n', '1']
257    try:
258        out_put = subprocess.check_output(git_log_cmds).decode("utf-8")
259    except subprocess.CalledProcessError:
260        logging.error(_GIT_LOG_ERROR)
261        raise
262    finally:
263        os.chdir(cwd)
264    com_list = out_put.split()
265    return com_list[com_list.index(_COMMIT) + 1]
266
267
268def _get_commit_id_dictionary():
269    """Get commit id from dictionary of key, value 'module': 'commit id'."""
270    data_id_dict = {}
271    with open(_GIT_COMMIT_ID_JSON, 'r') as jsfile:
272        data_id_dict = json.load(jsfile)
273    return data_id_dict
274
275
276def _git_checkout_commit_id(abs_path, commit_id):
277    """Command to checkout specific commit id.
278
279    Change directory to the module's absolute path which users want to get its
280    specific commit id.
281
282    Args:
283        abs_path: the absolute path of the target branch. E.g., abs_path/.git
284        commit_id: the commit id users want to checkout.
285
286    Raises:
287        Call git checkout commit id failed, raise errors.CommitIDNotExistError.
288    """
289    git_chekout_cmds = [_GIT, _CHECKOUT, commit_id]
290    cwd = os.getcwd()
291    try:
292        os.chdir(abs_path)
293        subprocess.check_output(git_chekout_cmds)
294    except subprocess.CalledProcessError:
295        err = _COMMIT_ID_NOT_EXIST_ERROR.format(commit_id, abs_path)
296        logging.error(err)
297        raise errors.CommitIDNotExistError(err)
298    finally:
299        os.chdir(cwd)
300
301
302def _git_checkout_target_commit_id(target, commit_id):
303    """Command to checkout target commit id.
304
305    Switch code base to specific commit id which is kept in data_id_dict with
306    target: commit_id as key: value. If the data is missing in data_id_dict, the
307    target isn't a selected golden sample raise error for it.
308
309    Args:
310        target: the string of target's module name or module path to checkout
311                the relevant git to its specific commit id.
312        commit_id: a string represent target's specific commit id.
313
314    Returns:
315        current_commit_id: the current commit id of the target which should be
316            switched back to.
317    """
318    atest_module_info = module_info.ModuleInfo()
319    _, abs_path = common_util.get_related_paths(atest_module_info, target)
320    current_commit_id = _get_git_current_commit_id(abs_path)
321    _git_checkout_commit_id(abs_path, commit_id)
322    return current_commit_id
323
324
325def _checkout_baseline_code_to_spec_commit_id():
326    """Get a dict of target, commit id listed in baseline_code_commit_id.json.
327
328    To make sure all samples run on the same environment, we need to keep all
329    the baseline code in a specific commit id. For example, all samples should
330    be created in the same specific commit id of the baseline code
331    'frameworks/base' for comparison except 'frameworks/base' itself.
332
333    Returns:
334        A dictionary contains target, specific and current commit id.
335    """
336    spec_and_cur_commit_id_dict = {}
337    data_id_dict = _get_commit_id_dictionary()
338    for target in data_id_dict:
339        commit_id = data_id_dict.get(target, '')
340        current_commit_id = _git_checkout_target_commit_id(target, commit_id)
341        spec_and_cur_commit_id_dict[target] = {}
342        spec_and_cur_commit_id_dict[target]['current'] = current_commit_id
343    return spec_and_cur_commit_id_dict
344
345
346def _generate_target_real_iml_data(target):
347    """Generate single target's real iml file content's data.
348
349    Args:
350        target: Android module name or path to be create iml data.
351
352    Returns:
353        data: A dictionary contains key: unique file name and value: iml
354              content.
355    """
356    data = {}
357    try:
358        aidegen_main.main([target, '-s', '-n', '-v'])
359    except (errors.FakeModuleError,
360            errors.ProjectOutsideAndroidRootError,
361            errors.ProjectPathNotExistError,
362            errors.NoModuleDefinedInModuleInfoError) as err:
363        logging.error(str(err))
364        return data
365
366    atest_module_info = module_info.ModuleInfo()
367    rel_path, abs_path = common_util.get_related_paths(atest_module_info,
368                                                       target)
369    for filename in iter(_get_project_file_names(abs_path)):
370        real_iml_file = os.path.join(abs_path, filename)
371        item_name = _get_unique_module_name(rel_path, filename)
372        data[item_name] = _import_project_file_xml_etree(real_iml_file)
373    return data
374
375
376def _generate_sample_json(test_list):
377    """Generate sample iml data.
378
379    We use all baseline code samples on the version of their own specific commit
380    id which is kept in _GIT_COMMIT_ID_JSON file. We need to switch back to
381    their current commit ids after generating golden samples' data.
382
383    Args:
384        test_list: a list of module name and module path.
385
386    Returns:
387        data: a dictionary contains dependent files' data of project file's
388              contents.
389        The sample is like:
390            "frameworks-base.iml": {
391                "srcs": [
392                    ....
393                    "file://$PROJECT_DIR$/frameworks/base/cmds/am/src",
394                    "file://$PROJECT_DIR$/frameworks/base/cmds/appwidget/src",
395                    ....
396                ],
397                "jars": [
398                    ....
399                    "jar://$PROJECT_DIR$/out/target/common/**/aapt2.srcjar!/",
400                    ....
401                ]
402            }
403    """
404    data = {}
405    spec_and_cur_commit_id_dict = _checkout_baseline_code_to_spec_commit_id()
406    for target in test_list:
407        data.update(_generate_target_real_iml_data(target))
408    atest_module_info = module_info.ModuleInfo()
409    for target in spec_and_cur_commit_id_dict:
410        _, abs_path = common_util.get_related_paths(atest_module_info, target)
411        _git_checkout_commit_id(
412            abs_path, spec_and_cur_commit_id_dict[target]['current'])
413    return data
414
415
416def _create_some_sample_json_file(targets):
417    """Write some samples' iml data into a json file.
418
419    Args:
420        targets: Android module name or path to be create iml data.
421
422    linked_function: _generate_sample_json()
423    """
424    data = _generate_sample_json(targets)
425    data_sample = {}
426    with open(_GOLDEN_SAMPLES_JSON, 'r') as infile:
427        try:
428            data_sample = json.load(infile)
429        # pylint: disable=maybe-no-member
430        except json.JSONDecodeError as err:
431            print("Json decode error: {}".format(err))
432            data_sample = {}
433    data_sample.update(data)
434    with open(_GOLDEN_SAMPLES_JSON, 'w') as outfile:
435        json.dump(data_sample, outfile, indent=4, sort_keys=False)
436
437
438def test_samples(func):
439    """Decorate a function to deal with preparing and verifying staffs of it.
440
441    Args:
442        func: a function is to be compared its iml data with the json file's
443              data.
444
445    Returns:
446        The wrapper function.
447    """
448
449    @functools.wraps(func)
450    def wrapper(*args, **kwargs):
451        """A wrapper function."""
452
453        test_successful = True
454        with open(_GOLDEN_SAMPLES_JSON, 'r') as outfile:
455            data_sample = json.load(outfile)
456
457        data_real = func(*args, **kwargs)
458
459        for name in data_real:
460            for item in [_SRCS, _JARS]:
461                s_items = data_sample[name][item]
462                r_items = data_real[name][item]
463                if set(s_items) != set(r_items):
464                    diff_iter = _compare_content(name, item, s_items, r_items)
465                    if diff_iter:
466                        print('\n{} {}'.format(
467                            common_util.COLORED_INFO('Test error:'),
468                            _TEST_ERROR.format(name, item)))
469                        print('{} {} contents are different:'.format(
470                            name, item))
471                        for diff in diff_iter:
472                            print(diff)
473                        test_successful = False
474        if test_successful:
475            print(common_util.COLORED_PASS(_ALL_PASS))
476        return test_successful
477
478    return wrapper
479
480
481@test_samples
482def _test_some_sample_iml(targets=None):
483    """Compare with sample iml's data to assure the project's contents is right.
484
485    Args:
486        targets: Android module name or path to be create iml data.
487    """
488    if targets:
489        return _generate_sample_json(targets)
490    return _generate_sample_json(_get_commit_id_dictionary().keys())
491
492
493@test_samples
494def _test_all_samples_iml():
495    """Compare all imls' data with all samples' data.
496
497    It's to make sure each iml's contents is right. The function is implemented
498    but hasn't been used yet.
499    """
500    all_module_list = module_info.ModuleInfo().name_to_module_info.keys()
501    return _generate_sample_json(all_module_list)
502
503
504def _compare_content(module_name, item_type, s_items, r_items):
505    """Compare src or jar files' data of two dictionaries.
506
507    Args:
508        module_name: the test module name.
509        item_type: the type is src or jar.
510        s_items: sample jars' items.
511        r_items: real jars' items.
512
513    Returns:
514        An iterator of not equal sentences after comparison.
515    """
516    if item_type == _SRCS:
517        cmp_iter1 = _compare_srcs_content(module_name, s_items, r_items,
518                                          _MSG_NOT_IN_PROJECT_FILE)
519        cmp_iter2 = _compare_srcs_content(module_name, r_items, s_items,
520                                          _MSG_NOT_IN_SAMPLE_DATA)
521    else:
522        cmp_iter1 = _compare_jars_content(module_name, s_items, r_items,
523                                          _MSG_NOT_IN_PROJECT_FILE)
524        cmp_iter2 = _compare_jars_content(module_name, r_items, s_items,
525                                          _MSG_NOT_IN_SAMPLE_DATA)
526    return itertools.chain(cmp_iter1, cmp_iter2)
527
528
529def _compare_srcs_content(module_name, s_items, r_items, msg):
530    """Compare src or jar files' data of two dictionaries.
531
532    Args:
533        module_name: the test module name.
534        s_items: sample jars' items.
535        r_items: real jars' items.
536        msg: the message will be written into log file.
537
538    Returns:
539        An iterator of not equal sentences after comparison.
540    """
541    for sample in s_items:
542        if sample not in r_items:
543            yield msg.format(sample, module_name)
544
545
546def _compare_jars_content(module_name, s_items, r_items, msg):
547    """Compare src or jar files' data of two dictionaries.
548
549    AIDEGen treats the jars in folder names 'linux_glib_common' and
550    'android_common' as the same content. If the paths are different only
551    because of these two names, we ignore it.
552
553    Args:
554        module_name: the test module name.
555        s_items: sample jars' items.
556        r_items: real jars' items.
557        msg: the message will be written into log file.
558
559    Returns:
560        An iterator of not equal sentences after comparison.
561    """
562    for sample in s_items:
563        if sample not in r_items:
564            lnew = sample
565            if constant.LINUX_GLIBC_COMMON in sample:
566                lnew = sample.replace(constant.LINUX_GLIBC_COMMON,
567                                      constant.ANDROID_COMMON)
568            else:
569                lnew = sample.replace(constant.ANDROID_COMMON,
570                                      constant.LINUX_GLIBC_COMMON)
571            if not lnew in r_items:
572                yield msg.format(sample, module_name)
573
574
575# pylint: disable=broad-except
576# pylint: disable=eval-used
577@common_util.back_to_cwd
578@common_util.time_logged
579def _verify_aidegen(verified_file_path, forced_remove_bp_json,
580                    is_presubmit=False):
581    """Verify various use cases of executing aidegen.
582
583    There are two types of running commands:
584    1. Use 'eval' to run the commands for present codes in aidegen_main.py,
585       such as:
586           aidegen_main.main(['tradefed', '-n', '-v'])
587    2. Use 'subprocess.check_call' to run the commands for the binary codes of
588       aidegen such as:
589       aidegen tradefed -n -v
590
591    Remove module_bp_java_deps.json in the beginning of running use cases. If
592    users need to remove module_bp_java_deps.json between each use case they
593    can set forced_remove_bp_json true.
594
595    Args:
596        verified_file_path: The json file path to be verified.
597        forced_remove_bp_json: Remove module_bp_java_deps.json for each use case
598                               test.
599
600    Raises:
601        There are two type of exceptions:
602        1. aidegen.lib.errors for projects' or modules' issues such as,
603           ProjectPathNotExistError.
604        2. Any exceptions other than aidegen.lib.errors such as,
605           subprocess.CalledProcessError.
606    """
607    bp_json_path = common_util.get_blueprint_json_path(
608        constant.BLUEPRINT_JAVA_JSONFILE_NAME)
609    use_eval = (verified_file_path == _VERIFY_COMMANDS_JSON)
610    try:
611        with open(verified_file_path, 'r') as jsfile:
612            data = json.load(jsfile)
613    except IOError as err:
614        raise errors.JsonFileNotExistError(
615            '%s does not exist, error: %s.' % (verified_file_path, err))
616
617    if not is_presubmit:
618        _compare_sample_native_content()
619
620    os.chdir(common_util.get_android_root_dir())
621    for use_case in data:
622        print('Use case "{}" is running.'.format(use_case))
623        if forced_remove_bp_json and os.path.exists(bp_json_path):
624            os.remove(bp_json_path)
625        for cmd in data[use_case]:
626            print('Command "{}" is running.'.format(cmd))
627            try:
628                if use_eval:
629                    eval(cmd)
630                else:
631                    subprocess.check_call(cmd, shell=True)
632            except (errors.ProjectOutsideAndroidRootError,
633                    errors.ProjectPathNotExistError,
634                    errors.NoModuleDefinedInModuleInfoError,
635                    errors.IDENotExistError) as err:
636                print('"{}" raises error: {}.'.format(use_case, err))
637                continue
638            except BaseException:
639                exc_type, _, _ = sys.exc_info()
640                print('"{}.{}" command {}.'.format(
641                    use_case, cmd, common_util.COLORED_FAIL('executes failed')))
642                raise BaseException(
643                    'Unexpected command "{}" exception: {}.'.format(
644                        use_case, exc_type))
645        print('"{}" command {}!'.format(
646            use_case, common_util.COLORED_PASS('test passed')))
647    print(common_util.COLORED_PASS(_ALL_PASS))
648
649
650@common_util.back_to_cwd
651def _make_clean():
652    """Make a command to clean out folder for a pure environment to test.
653
654    Raises:
655        Call subprocess.check_call to execute
656        'build/soong/soong_ui.bash --make-mode clean' and cause
657        subprocess.CalledProcessError.
658    """
659    try:
660        os.chdir(common_util.get_android_root_dir())
661        subprocess.check_call(
662            ['build/soong/soong_ui.bash --make-mode clean', '-j'],
663            shell=True)
664    except subprocess.CalledProcessError:
665        print('"build/soong/soong_ui.bash --make-mode clean" command failed.')
666        raise
667
668
669def _read_file_content(path):
670    """Read file's content.
671
672    Args:
673        path: Path of input file.
674
675    Returns:
676        A list of content strings.
677    """
678    with open(path, encoding='utf8') as template:
679        contents = []
680        for cnt in template:
681            if cnt.startswith('#'):
682                continue
683            contents.append(cnt.rstrip())
684        return contents
685
686
687# pylint: disable=protected-access
688def _compare_sample_native_content():
689    """Compares 'libui' sample module's project file.
690
691    Compares 'libui' sample module's project file generated by AIDEGen with that
692    generated by the soong build system. Check if their contents are the same.
693    There should be only one different:
694        ${config.X86_64GccRoot} # in the soong build sytem
695        becomes
696        prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9 # in AIDEGen
697    """
698    target_arch_variant = 'x86_64'
699    env_on = {
700        'TARGET_PRODUCT': 'aosp_x86_64',
701        'TARGET_BUILD_VARIANT': 'eng',
702        'TARGET_ARCH_VARIANT': target_arch_variant,
703        'SOONG_COLLECT_JAVA_DEPS': 'true',
704        'SOONG_GEN_CMAKEFILES': '1',
705        'SOONG_COLLECT_CC_DEPS': '1'
706    }
707
708    try:
709        project_config.ProjectConfig(
710            aidegen_main._parse_args(['-n', '-v'])).init_environment()
711        module_info_util.generate_merged_module_info(env_on)
712        cc_path = os.path.join(common_util.get_soong_out_path(),
713                               constant.BLUEPRINT_CC_JSONFILE_NAME)
714        mod_name = 'libui'
715        mod_info = common_util.get_json_dict(cc_path)['modules'][mod_name]
716        if mod_info:
717            clion_project_file_gen.CLionProjectFileGenerator(
718                mod_info).generate_cmakelists_file()
719            out_dir = os.path.join(common_util.get_android_root_dir(),
720                                   common_util.get_android_out_dir(),
721                                   constant.RELATIVE_NATIVE_PATH,
722                                   mod_info['path'][0])
723            content1 = _read_file_content(os.path.join(
724                out_dir, mod_name, constant.CLION_PROJECT_FILE_NAME))
725            cc_file_name = ''.join(
726                [mod_name, '-', target_arch_variant, '-android'])
727            cc_file_path = os.path.join(
728                out_dir, cc_file_name, constant.CLION_PROJECT_FILE_NAME)
729            content2 = _read_file_content(cc_file_path)
730            same = True
731            for lino, (cnt1, cnt2) in enumerate(
732                    zip(content1, content2), start=1):
733                if _BE_REPLACED in cnt2:
734                    cnt2 = cnt2.replace(_BE_REPLACED, _TO_REPLACE)
735                if cnt1 != cnt2:
736                    print('Contents {} and {} are different in line:{}.'.format(
737                        cnt1, cnt2, lino))
738                    same = False
739            if same:
740                print('Files {} and {} are the same.'.format(
741                    mod_name, cc_file_name))
742    except errors.BuildFailureError:
743        print('Compare native content failed.')
744
745
746def main(argv):
747    """Main entry.
748
749    1. Create the iml file data of each module in module-info.json and write it
750       into single_module.json.
751    2. Verify every use case of AIDEGen.
752    3. Compare all or some iml project files' data to the data recorded in
753       single_module.json.
754
755    Args:
756        argv: A list of system arguments.
757    """
758    args = _parse_args(argv)
759    common_util.configure_logging(args.verbose)
760    os.environ[constant.AIDEGEN_TEST_MODE] = 'true'
761
762    if args.make_clean:
763        _make_clean()
764
765    if args.create_sample:
766        _create_some_sample_json_file(args.targets)
767    elif args.use_cases_verified:
768        _verify_aidegen(_VERIFY_COMMANDS_JSON, args.remove_bp_json)
769    elif args.binary_upload_verified:
770        _verify_aidegen(_VERIFY_BINARY_JSON, args.remove_bp_json)
771    elif args.binary_presubmit_verified:
772        _verify_aidegen(_VERIFY_PRESUBMIT_JSON, args.remove_bp_json, True)
773    elif args.test_all_samples:
774        _test_all_samples_iml()
775    elif args.compare_sample_native:
776        _compare_sample_native_content()
777    else:
778        if not args.targets[0]:
779            _test_some_sample_iml()
780        else:
781            _test_some_sample_iml(args.targets)
782
783    del os.environ[constant.AIDEGEN_TEST_MODE]
784
785
786if __name__ == '__main__':
787    main(sys.argv[1:])
788