protected void generate() throws IOException
	{
		MakeFormatter
		output = new MakeFormatter(new File(build_dir, MAKEFILE_NAME));
		String to_root = root.findRelPath(this);

		/* Collect subdirectory names into Makefile-format list */
		String[] subdir_names = new String[subdirs.size()];
		String subdirs_val;
		int subdir_i = 0;

		for (Makefile subdir : subdirs) {
			subdir_names[subdir_i++] = subdir.getName();
		}
		subdirs_val = MakeFormatter.genList(subdir_names);

		/* Collect object names into Makefile-format list */
		String
		objects_val = MakeFormatter.genList(toArray(object_names));

		/* Collect target names into Makefile-format list */
		String[] target_names = new String[targets.size()];
		String targets_val;
		int target_i = 0;

		for (Target target : targets) {
			target_names[target_i++] = target.getName();
		}
		targets_val = MakeFormatter.genList(target_names);

		/* Declare phony rules */
		output.write(PHONY_DECL + subdirs_val);
		output.newLine();

		/* Import common resources file */
		String common_path = to_root + Mainfile.COMMON_NAME;
		output.write(MakeFormatter.genList(INCLUDE_CMD, common_path));
		output.newLine();

		/* Create C preprocessor headers */
		/* Declare INCLUDE, if it exists */
		if (root.hasInclude()) {
			String include_path = to_root + Mainfile.INCLUDE_NAME;
			output.assignVar(INCLUDE_VAR, INCLUDE_FLAG +
						      include_path);
		}
		output.assignVar(CPPFLAGS_VAR, CPPFLAGS_VAL);

		/* Define additional variables */
		for (VarAssignment assignment : assignments.values()) {
			assignment.doAssign(output);
		}

		/* Define SUBDIRS, OBJS and TARGETS */
		output.assignVar(SUBDIRS_VAR, subdirs_val);
		output.assignVar(OBJECTS_VAR, objects_val);
		output.assignVar(TARGETS_VAR, targets_val);

		/* Define default rule */
		writeRuleHeader(output, MAKE_ALL_RULE,
				MakeFormatter.genUseVar(SUBDIRS_VAR),
				MakeFormatter.genUseVar(OBJECTS_VAR),
				MakeFormatter.genUseVar(TARGETS_VAR));

		/* Define special target rules */
		for (Target target : targets) {
			target.genRule(output);
		}

		/* Define subdirectory rules */
		for (Makefile subdir : subdirs) {
			writeRuleHeader(output, subdir.getName());
			output.indent();
			output
			.write(MakeFormatter
			       .genList(MakeFormatter.genUseVar(MAKE_VAR),
					MAKE_SWITCH_FLAG, subdir.getName()));
			output.unindent();
			output.newLine();
		}

		/* Define cleanup rule */
		writeRuleHeader(output, MAKE_CLEAN_RULE);
		output.indent();
		/* clean up own objects and targets */
		output.write(MakeFormatter
			     .genList(MakeFormatter.genUseVar(RM_VAR),
				      MakeFormatter.genUseVar(RM_FLAGS_VAR),
				      MakeFormatter.genUseVar(OBJECTS_VAR),
				      MakeFormatter.genUseVar(TARGETS_VAR)));
		output.newLine();
		/*
		 * Clean up subdirectories
		 * if they want to be automatically cleaned.
		 */
		for (Makefile subdir : subdirs) {
			if (subdir.auto_clean) {
				output
				.write(MakeFormatter
				       .genList(MakeFormatter
						.genUseVar(MAKE_VAR),
						MAKE_SWITCH_FLAG, subdir
								  .getName(),
						MAKE_CLEAN_RULE));
				output.newLine();
			}
		}
		output.unindent();
		output.close();

		/* Write Makefiles in subdirectories */
		for (Makefile subdir : subdirs) {
			subdir.generate();
		}
	}