chromium/docs/ios/working_with_files.md

# Working With Files

Adding, removing, and renaming files in iOS Chromium needs to follow a specific
procedure that will be unfamiliar to engineers coming from other iOS projects.
Conceptually, every file is recorded in _four_ locations: the local filesystem,
git, the `BUILD.gn` files, and the XCode projects. Of these, the XCode project
is wholly generated from the others.

[TOC]

## Overview

**Do not use XCode to manipulate files.** The XCode project used for iOS
Chromium is _generated_; it's functionally a build artifact. The various
`BUILD.gn` files in Chromium define structure of the XCode project file. Running
`gclient runhooks` causes the project files to be regenerated.

Individual files can have their contents edited within XCode, and all of the
regular testing and debugging activities can be done within XCode. It just can't
be used to create files, rename files, delete files, or manipulate the project
group structure in any way. To do these things, follow the procedures below.

## Adding files

To add any files (new headers, `.mm` or `.cc` implementation files, asset files
of any kind), the following general steps need to happen:

1.  The file needs to exist in the correct directory of the file system for your
    Chromium checkout. New files need to be created, or assets need to be
    copied to the right location.

1.  The new files need to be added to git.

1.  The new files need to be added to a target in a `BUILD.gn` file (usually in
    the same directory as the newly added file).

1.  The XCode project needs to be regenerated.

For adding new header or implementation files, the following procedure is
recommended:

1.  Generate the new files using `tools/boilerplate.py`. This will generate
    header guard macros and include the copyright boilerplate. Make sure to run
    this from root (`src/`) so the header guards include the correct, full
    path.

2.  Add the newly created files using `git add`.

3.  Edit the `BUILD.gn` file for the directory where the files were added. For
    each file, add it to the `sources` list for the correct `source_set` in the
    `BUILD.gn` file. Note that `gn format` (which is run as part of `git cl
    format`) will take care of alphabetizing the lists along with other
    formatting, so there's no need to manually do these things. (`BUILD.gn`
    files cand be edited directly in XCode, or in another editor such as `vi`).

4.  Once all of the files have been created, `add`ed and all `BUILD.gn` files
    have been updated, run `gclient runhooks` to regenerate all XCode projects.

5.  If XCode is open, it may prompt to "Autocreate Schemes". If so, click on the
    highlighted "Automatically Create Schemes" button.

In the shell, this procedure would look like this:
```bash
// Step 1 -- generate new files.
$ tools/boilerplate.py ios/chrome/browser/some_feature/feature_class.h
$ tools/boilerplate.py ios/chrome/browser/some_feature/feature_class.mm
$ tools/boilerplate.py ios/chrome/browser/some_feature/feature_class_unittest.mm
// Step 2 -- add the new files.
$ git add ios/chrome/browser/some_feature/feature_class*
// Step 3 -- edit the BUILD.gn file in the editor of your choice
$ vi ios/chrome/browser/some_feature/BUILD.gn
// Step 4 -- regenerate the XCode Projects
$ gclient runhooks
```

To add asset files, follow this procedure:

1.  Copy the asset files to the correct directory, with the correct names
    (including `@2x` and `@3x` suffixes) Note that images are stored in
    `.imageset` directories, conventionally inside `resources` directories for a
    given UI feature. New directories, if needed, are created in the usual way
    (`mkdir`). Note that there is no equivalent of `boilerplate.py` for images
    or other asset files.

1.  Create or copy `Contents.json` files for each new `.imageset` directory,
    with the appropriate contents.

1.  Add all image and `Contents.json` files using `git add`.

1.  Edit the `BUILD.gn` file in the containing `resources` directory, adding
    `imageset` entries for each added `.imageset` directory, and then grouping
    all assets into a new or existing `group()` declaration with a `public_deps`
    list containing all of the `imageset` targets.

1.  Regenerate the XCode project with `gclient runhooks`.

To add Markdown documentation files, the procedure is much simpler. These files
are automatically added to the XCode project without `BUILD.gn` entries, and
they have no required boilerplate. So adding new docs is as simple as:

1.  Create a new `.md` file in the appropriate directory (`docs/ios`, for
    example).

1. `git add` the file.

The newly added file will be visible in XCode after the next `gclient runhooks`.

## Moving and renaming files.

Renaming a file involves updating the filename in all of the places where it
exists: the file names in the filesystem, the file names in git, header guards
in files, import declarations in files, listings in BUILD.gn files, and
internally in the XCode project. As with adding a file, different tools are used
for each of these. Unlike creating a file, which starts with actually adding a
file to the filesystem, a rename starts with updating git (via `git mv`), then
using the `mass-rename` tool to update file contents.

`tools/git/mass-rename.py` works by looking at _uncommitted_ file moves in git,
and then updating all includes, header guards, and BUILD.gn entries to use the
new name. It doesn't update some other files, such as `Contents.json` files for
image assets. It also doesn't change any symbols in code, so class and variable
names won't be changed.

For many file moves, it will be simpler to use another tool,
`tools/git/move_source_file.py`, which combines `git mv` and `mass-rename` in a
single action. For example, renaming `feature_class` to `renamed_class` would be
done like this:
```bash
    $ tools/git/move_source_file.py ios/chrome/browser/some_feature/feature_class.h \
        ios/chrome/browser/some_feature/renamed_class.h
    $ tools/git/move_source_file.py ios/chrome/browser/some_feature/feature_class.mm \
        ios/chrome/browser/some_feature/renamed_class.mm
```

The step-by-step procedure for a rename is:

1.  If there are other uncommitted changes before the move, it's usually
    cleanest to commit before starting the move.

1.  `move_source_file` each file that needs to be renamed. This renames the file
    in both the file system and in git, and in most places where it's used in
    code.

1.  Run `gclient runhooks` to update the XCode project. Check that all of the
    needed name changes have been made (for example, by building all targets).
    Make any other needed fixes.

1.  If any classes or other symbols need to be renamed (remember that the name
    of the primary interface in each file must match the file name), make those
    changes. Find-and-replace tools like `tools/git/mffr.py` or XCode's
    Find/Replace can help here, but there are no compiler-aware tools that can
    do a "smart" rename.

1.  Commit all changes (`git commit -a -m <your comment>`).

A move—where a file is moved to a different directory—is in most respects
performed using the same steps as a rename. However, while `mass-rename.py` (and
thus `move_source_file.py`) will update existing file names in `BUILD.gn` files,
it won't move entries from one `BUILD.gn` file to another. To move files to a
different directory, the preceding procedure is used, but between steps 2 and 3
(after moving the files, but before regenerating the XCode project), the old
filenames will need to be removed from the `BUILD.gn` files in the old
directories and added to the `BUILD.gn` files in the new directories.

Also note that while `move_source_file` must be used separately for each file
being renamed within a directory, it (just like `git mv`) can move multiple
files without renaming to a new directory in a single command:

```bash
$ tools/git/mass-rename.py ios/chrome/browser/some_feature/feature_class.* \
    ios/chrome/browser/some_feature/feature_class_unittest.mm \
    ios/chrome/browser/other_feature/
```

## Deleting files.

Deleting files follows the same patterns as adding and moving files. As with a
file move, it's best to begin with deleting the files from git.

Typically, before actually removing a file, first all usage of the interface(s)
in the file(s) will be removed, and the file will no longer be `#imported`
anywhere.

Step-by step:

1.  `git rm` the files you want to remove. This will also remove the files
    from the filesystem.

1.  Manually remove the `BUILD.gn` entries for the files.

1.  Regenerate the XCode project (with `gclient runhooks`) to remove the files
    from XCode.

## Finally.

It's easy to miss some uses of a file that was renamed or deleted, and fixing
compilation errors discovered in the commit queue means another
commit-upload-dry run cycle (at least). To minimize this, after any change that
adds, renames, moves, or deletes files, be sure to take the following steps:

1.  `git cl format` to update the formatting of all files.

1.  `gn check` to make sure that any new or moved files follow the dependency
    rules (for example: `gn check -C out/Debug-iphonesimulator/`).

1.  Build all targets, to make sure that everything has been added, changed, or
    removed correctly. This can be done by selecting the "All" target in XCode
    and building (`⌘-B`), or from the command line (for example, `autoninja -C
    out/Debug-iphonesimulator/`).

Changes that involve adding or deleting more than a few files, and most renames
of any size, should be in a single CL with no other changes, for ease of
reviewing and (if necessary) reverting or cherry-picking.

## Recovering from accidental XCode Project usage.

If files are accidentally added, renamed, or moved through XCode, other settings
in the XCode project may be changed that will introduce strange local build
failures. In this case, take the following steps to recover.

1. Quit XCode.

1. Delete all generated XCode projects and associated files: `rm -rf out/build`.

1. Regenerate all XCode projects: `gclient runhooks`.